mirror of
https://github.com/osukey/osukey.git
synced 2025-08-05 07:33:55 +09:00
Merge branch 'master' into chat-mention
This commit is contained in:
@ -22,7 +22,7 @@ namespace osu.Game.Online.Chat
|
||||
public readonly ObservableCollection<User> Users = new ObservableCollection<User>();
|
||||
|
||||
[JsonProperty(@"users")]
|
||||
private long[] userIds
|
||||
private int[] userIds
|
||||
{
|
||||
set
|
||||
{
|
||||
@ -61,7 +61,7 @@ namespace osu.Game.Online.Chat
|
||||
/// </summary>
|
||||
public event Action<Message> MessageRemoved;
|
||||
|
||||
public bool ReadOnly => false; //todo not yet used.
|
||||
public bool ReadOnly => false; // todo: not yet used.
|
||||
|
||||
public override string ToString() => Name;
|
||||
|
||||
@ -84,7 +84,8 @@ namespace osu.Game.Online.Chat
|
||||
public long? LastReadId;
|
||||
|
||||
/// <summary>
|
||||
/// Signalles if the current user joined this channel or not. Defaults to false.
|
||||
/// Signals if the current user joined this channel or not. Defaults to false.
|
||||
/// Note that this does not guarantee a join has completed. Check Id > 0 for confirmation.
|
||||
/// </summary>
|
||||
public Bindable<bool> Joined = new Bindable<bool>();
|
||||
|
||||
|
@ -18,7 +18,7 @@ namespace osu.Game.Online.Chat
|
||||
/// <summary>
|
||||
/// Manages everything channel related
|
||||
/// </summary>
|
||||
public class ChannelManager : PollingComponent
|
||||
public class ChannelManager : PollingComponent, IChannelPostTarget
|
||||
{
|
||||
/// <summary>
|
||||
/// The channels the player joins on startup
|
||||
@ -48,7 +48,8 @@ namespace osu.Game.Online.Chat
|
||||
/// </summary>
|
||||
public IBindableList<Channel> AvailableChannels => availableChannels;
|
||||
|
||||
private IAPIProvider api;
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
public readonly BindableBool HighPollRate = new BindableBool();
|
||||
|
||||
@ -56,7 +57,7 @@ namespace osu.Game.Online.Chat
|
||||
{
|
||||
CurrentChannel.ValueChanged += currentChannelChanged;
|
||||
|
||||
HighPollRate.BindValueChanged(enabled => TimeBetweenPolls = enabled.NewValue ? 1000 : 6000, true);
|
||||
HighPollRate.BindValueChanged(enabled => TimeBetweenPolls.Value = enabled.NewValue ? 1000 : 6000, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -85,7 +86,7 @@ namespace osu.Game.Online.Chat
|
||||
return;
|
||||
|
||||
CurrentChannel.Value = JoinedChannels.FirstOrDefault(c => c.Type == ChannelType.PM && c.Users.Count == 1 && c.Users.Any(u => u.Id == user.Id))
|
||||
?? new Channel(user);
|
||||
?? JoinChannel(new Channel(user));
|
||||
}
|
||||
|
||||
private void currentChannelChanged(ValueChangedEvent<Channel> e)
|
||||
@ -107,8 +108,7 @@ namespace osu.Game.Online.Chat
|
||||
/// <param name="target">An optional target channel. If null, <see cref="CurrentChannel"/> will be used.</param>
|
||||
public void PostMessage(string text, bool isAction = false, Channel target = null)
|
||||
{
|
||||
if (target == null)
|
||||
target = CurrentChannel.Value;
|
||||
target ??= CurrentChannel.Value;
|
||||
|
||||
if (target == null)
|
||||
return;
|
||||
@ -139,7 +139,7 @@ namespace osu.Game.Online.Chat
|
||||
target.AddLocalEcho(message);
|
||||
|
||||
// if this is a PM and the first message, we need to do a special request to create the PM channel
|
||||
if (target.Type == ChannelType.PM && !target.Joined.Value)
|
||||
if (target.Type == ChannelType.PM && target.Id == 0)
|
||||
{
|
||||
var createNewPrivateMessageRequest = new CreateNewPrivateMessageRequest(target.Users.First(), message);
|
||||
|
||||
@ -152,7 +152,7 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
createNewPrivateMessageRequest.Failure += exception =>
|
||||
{
|
||||
Logger.Error(exception, "Posting message failed.");
|
||||
handlePostException(exception);
|
||||
target.ReplaceMessage(message, null);
|
||||
dequeueAndRun();
|
||||
};
|
||||
@ -171,7 +171,7 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
req.Failure += exception =>
|
||||
{
|
||||
Logger.Error(exception, "Posting message failed.");
|
||||
handlePostException(exception);
|
||||
target.ReplaceMessage(message, null);
|
||||
dequeueAndRun();
|
||||
};
|
||||
@ -184,6 +184,14 @@ namespace osu.Game.Online.Chat
|
||||
dequeueAndRun();
|
||||
}
|
||||
|
||||
private static void handlePostException(Exception exception)
|
||||
{
|
||||
if (exception is APIException apiException)
|
||||
Logger.Log(apiException.Message, level: LogLevel.Important);
|
||||
else
|
||||
Logger.Error(exception, "Posting message failed.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Posts a command locally. Commands like /help will result in a help message written in the current channel.
|
||||
/// </summary>
|
||||
@ -191,18 +199,21 @@ namespace osu.Game.Online.Chat
|
||||
/// <param name="target">An optional target channel. If null, <see cref="CurrentChannel"/> will be used.</param>
|
||||
public void PostCommand(string text, Channel target = null)
|
||||
{
|
||||
if (target == null)
|
||||
target = CurrentChannel.Value;
|
||||
target ??= CurrentChannel.Value;
|
||||
|
||||
if (target == null)
|
||||
return;
|
||||
|
||||
var parameters = text.Split(new[] { ' ' }, 2);
|
||||
var parameters = text.Split(' ', 2);
|
||||
string command = parameters[0];
|
||||
string content = parameters.Length == 2 ? parameters[1] : string.Empty;
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case "np":
|
||||
AddInternal(new NowPlayingCommand());
|
||||
break;
|
||||
|
||||
case "me":
|
||||
if (string.IsNullOrWhiteSpace(content))
|
||||
{
|
||||
@ -229,11 +240,10 @@ namespace osu.Game.Online.Chat
|
||||
}
|
||||
|
||||
JoinChannel(channel);
|
||||
CurrentChannel.Value = channel;
|
||||
break;
|
||||
|
||||
case "help":
|
||||
target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action], /join [channel]"));
|
||||
target.AddNewMessages(new InfoMessage("Supported commands: /help, /me [action], /join [channel], /np"));
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -264,7 +274,7 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
// join any channels classified as "defaults"
|
||||
if (joinDefaults && defaultChannels.Any(c => c.Equals(channel.Name, StringComparison.OrdinalIgnoreCase)))
|
||||
JoinChannel(ch);
|
||||
joinChannel(ch);
|
||||
}
|
||||
};
|
||||
req.Failure += error =>
|
||||
@ -285,7 +295,7 @@ namespace osu.Game.Online.Chat
|
||||
/// <param name="channel">The channel </param>
|
||||
private void fetchInitalMessages(Channel channel)
|
||||
{
|
||||
if (channel.Id <= 0) return;
|
||||
if (channel.Id <= 0 || channel.MessagesLoaded) return;
|
||||
|
||||
var fetchInitialMsgReq = new GetMessagesRequest(channel);
|
||||
fetchInitialMsgReq.Success += messages =>
|
||||
@ -337,12 +347,13 @@ namespace osu.Game.Online.Chat
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Joins a channel if it has not already been joined.
|
||||
/// Joins a channel if it has not already been joined. Must be called from the update thread.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to join.</param>
|
||||
/// <param name="alreadyJoined">Whether the channel has already been joined server-side. Will skip a join request.</param>
|
||||
/// <returns>The joined channel. Note that this may not match the parameter channel as it is a backed object.</returns>
|
||||
public Channel JoinChannel(Channel channel, bool alreadyJoined = false)
|
||||
public Channel JoinChannel(Channel channel) => joinChannel(channel, true);
|
||||
|
||||
private Channel joinChannel(Channel channel, bool fetchInitialMessages = false)
|
||||
{
|
||||
if (channel == null) return null;
|
||||
|
||||
@ -351,35 +362,56 @@ namespace osu.Game.Online.Chat
|
||||
// ensure we are joined to the channel
|
||||
if (!channel.Joined.Value)
|
||||
{
|
||||
if (alreadyJoined)
|
||||
channel.Joined.Value = true;
|
||||
else
|
||||
channel.Joined.Value = true;
|
||||
|
||||
switch (channel.Type)
|
||||
{
|
||||
switch (channel.Type)
|
||||
{
|
||||
case ChannelType.Public:
|
||||
var req = new JoinChannelRequest(channel, api.LocalUser.Value);
|
||||
req.Success += () => JoinChannel(channel, true);
|
||||
req.Failure += ex => LeaveChannel(channel);
|
||||
api.Queue(req);
|
||||
return channel;
|
||||
}
|
||||
case ChannelType.Multiplayer:
|
||||
// join is implicit. happens when you join a multiplayer game.
|
||||
// this will probably change in the future.
|
||||
joinChannel(channel, fetchInitialMessages);
|
||||
return channel;
|
||||
|
||||
case ChannelType.PM:
|
||||
var createRequest = new CreateChannelRequest(channel);
|
||||
createRequest.Success += resChannel =>
|
||||
{
|
||||
if (resChannel.ChannelID.HasValue)
|
||||
{
|
||||
channel.Id = resChannel.ChannelID.Value;
|
||||
|
||||
handleChannelMessages(resChannel.RecentMessages);
|
||||
channel.MessagesLoaded = true; // this will mark the channel as having received messages even if there were none.
|
||||
}
|
||||
};
|
||||
|
||||
api.Queue(createRequest);
|
||||
break;
|
||||
|
||||
default:
|
||||
var req = new JoinChannelRequest(channel);
|
||||
req.Success += () => joinChannel(channel, fetchInitialMessages);
|
||||
req.Failure += ex => LeaveChannel(channel);
|
||||
api.Queue(req);
|
||||
return channel;
|
||||
}
|
||||
}
|
||||
|
||||
if (CurrentChannel.Value == null)
|
||||
CurrentChannel.Value = channel;
|
||||
|
||||
if (!channel.MessagesLoaded)
|
||||
else
|
||||
{
|
||||
// let's fetch a small number of messages to bring us up-to-date with the backlog.
|
||||
fetchInitalMessages(channel);
|
||||
if (fetchInitialMessages)
|
||||
fetchInitalMessages(channel);
|
||||
}
|
||||
|
||||
CurrentChannel.Value ??= channel;
|
||||
|
||||
return channel;
|
||||
}
|
||||
|
||||
public void LeaveChannel(Channel channel)
|
||||
/// <summary>
|
||||
/// Leave the specified channel. Can be called from any thread.
|
||||
/// </summary>
|
||||
/// <param name="channel">The channel to leave.</param>
|
||||
public void LeaveChannel(Channel channel) => Schedule(() =>
|
||||
{
|
||||
if (channel == null) return;
|
||||
|
||||
@ -390,10 +422,10 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
if (channel.Joined.Value)
|
||||
{
|
||||
api.Queue(new LeaveChannelRequest(channel, api.LocalUser.Value));
|
||||
api.Queue(new LeaveChannelRequest(channel));
|
||||
channel.Joined.Value = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
private long lastMessageId;
|
||||
|
||||
@ -415,7 +447,8 @@ namespace osu.Game.Online.Chat
|
||||
foreach (var channel in updates.Presence)
|
||||
{
|
||||
// we received this from the server so should mark the channel already joined.
|
||||
JoinChannel(channel, true);
|
||||
channel.Joined.Value = true;
|
||||
joinChannel(channel);
|
||||
}
|
||||
|
||||
//todo: handle left channels
|
||||
@ -466,12 +499,6 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
api.Queue(req);
|
||||
}
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(IAPIProvider api)
|
||||
{
|
||||
this.api = api;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -12,5 +12,6 @@ namespace osu.Game.Online.Chat
|
||||
Temporary,
|
||||
PM,
|
||||
Group,
|
||||
System,
|
||||
}
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Graphics.Containers;
|
||||
using osu.Game.Graphics.UserInterface;
|
||||
using osu.Game.Overlays;
|
||||
using osuTK;
|
||||
|
||||
namespace osu.Game.Online.Chat
|
||||
@ -20,7 +21,10 @@ namespace osu.Game.Online.Chat
|
||||
/// <summary>
|
||||
/// Each word part of a chat link (split for word-wrap support).
|
||||
/// </summary>
|
||||
public List<Drawable> Parts;
|
||||
public readonly List<Drawable> Parts;
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private OverlayColourProvider overlayColourProvider { get; set; }
|
||||
|
||||
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => Parts.Any(d => d.ReceivePositionalInputAt(screenSpacePos));
|
||||
|
||||
@ -34,7 +38,7 @@ namespace osu.Game.Online.Chat
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(OsuColour colours)
|
||||
{
|
||||
IdleColour = colours.Blue;
|
||||
IdleColour = overlayColourProvider?.Light2 ?? colours.Blue;
|
||||
}
|
||||
|
||||
protected override IEnumerable<Drawable> EffectTargets => Parts;
|
||||
|
@ -13,15 +13,17 @@ namespace osu.Game.Online.Chat
|
||||
{
|
||||
public class ExternalLinkOpener : Component
|
||||
{
|
||||
private GameHost host;
|
||||
private DialogOverlay dialogOverlay;
|
||||
[Resolved]
|
||||
private GameHost host { get; set; }
|
||||
|
||||
[Resolved(CanBeNull = true)]
|
||||
private DialogOverlay dialogOverlay { get; set; }
|
||||
|
||||
private Bindable<bool> externalLinkWarning;
|
||||
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(GameHost host, DialogOverlay dialogOverlay, OsuConfigManager config)
|
||||
private void load(OsuConfigManager config)
|
||||
{
|
||||
this.host = host;
|
||||
this.dialogOverlay = dialogOverlay;
|
||||
externalLinkWarning = config.GetBindable<bool>(OsuSetting.ExternalLinkWarning);
|
||||
}
|
||||
|
||||
|
19
osu.Game/Online/Chat/IChannelPostTarget.cs
Normal file
19
osu.Game/Online/Chat/IChannelPostTarget.cs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
|
||||
namespace osu.Game.Online.Chat
|
||||
{
|
||||
[Cached(typeof(IChannelPostTarget))]
|
||||
public interface IChannelPostTarget
|
||||
{
|
||||
/// <summary>
|
||||
/// Posts a message to the currently opened channel.
|
||||
/// </summary>
|
||||
/// <param name="text">The message text that is going to be posted</param>
|
||||
/// <param name="isAction">Is true if the message is an action, e.g.: user is currently eating </param>
|
||||
/// <param name="target">An optional target channel. If null, <see cref="ChannelManager.CurrentChannel"/> will be used.</param>
|
||||
void PostMessage(string text, bool isAction = false, Channel target = null);
|
||||
}
|
||||
}
|
@ -8,10 +8,8 @@ namespace osu.Game.Online.Chat
|
||||
{
|
||||
public class InfoMessage : LocalMessage
|
||||
{
|
||||
private static int infoID = -1;
|
||||
|
||||
public InfoMessage(string message)
|
||||
: base(infoID--)
|
||||
: base(null)
|
||||
{
|
||||
Timestamp = DateTimeOffset.Now;
|
||||
Content = message;
|
||||
|
@ -59,7 +59,7 @@ namespace osu.Game.Online.Chat
|
||||
return Id.Value.CompareTo(other.Id.Value);
|
||||
}
|
||||
|
||||
public virtual bool Equals(Message other) => Id == other?.Id;
|
||||
public virtual bool Equals(Message other) => Id.HasValue && Id == other?.Id;
|
||||
|
||||
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
|
||||
public override int GetHashCode() => Id.GetHashCode();
|
||||
|
@ -49,6 +49,18 @@ namespace osu.Game.Online.Chat
|
||||
// Unicode emojis
|
||||
private static readonly Regex emoji_regex = new Regex(@"(\uD83D[\uDC00-\uDE4F])");
|
||||
|
||||
/// <summary>
|
||||
/// The root URL for the website, used for chat link matching.
|
||||
/// </summary>
|
||||
public static string WebsiteRootUrl
|
||||
{
|
||||
set => websiteRootUrl = value
|
||||
.Trim('/') // trim potential trailing slash/
|
||||
.Split('/').Last(); // only keep domain name, ignoring protocol.
|
||||
}
|
||||
|
||||
private static string websiteRootUrl = "osu.ppy.sh";
|
||||
|
||||
private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null, char[] escapeChars = null)
|
||||
{
|
||||
int captureOffset = 0;
|
||||
@ -78,13 +90,13 @@ namespace osu.Game.Online.Chat
|
||||
{
|
||||
result.Text = result.Text.Remove(index, m.Length).Insert(index, displayText);
|
||||
|
||||
//since we just changed the line display text, offset any already processed links.
|
||||
// since we just changed the line display text, offset any already processed links.
|
||||
result.Links.ForEach(l => l.Index -= l.Index > index ? m.Length - displayText.Length : 0);
|
||||
|
||||
var details = GetLinkDetails(linkText);
|
||||
result.Links.Add(new Link(linkText, index, displayText.Length, linkActionOverride ?? details.Action, details.Argument));
|
||||
|
||||
//adjust the offset for processing the current matches group.
|
||||
// adjust the offset for processing the current matches group.
|
||||
captureOffset += m.Length - displayText.Length;
|
||||
}
|
||||
}
|
||||
@ -111,7 +123,7 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
public static LinkDetails GetLinkDetails(string url)
|
||||
{
|
||||
var args = url.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
|
||||
var args = url.Split('/', StringSplitOptions.RemoveEmptyEntries);
|
||||
args[0] = args[0].TrimEnd(':');
|
||||
|
||||
switch (args[0])
|
||||
@ -119,22 +131,42 @@ namespace osu.Game.Online.Chat
|
||||
case "http":
|
||||
case "https":
|
||||
// length > 3 since all these links need another argument to work
|
||||
if (args.Length > 3 && (args[1] == "osu.ppy.sh" || args[1] == "new.ppy.sh"))
|
||||
if (args.Length > 3 && args[1].EndsWith(websiteRootUrl, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var mainArg = args[3];
|
||||
|
||||
switch (args[2])
|
||||
{
|
||||
// old site only
|
||||
case "b":
|
||||
case "beatmaps":
|
||||
return new LinkDetails(LinkAction.OpenBeatmap, args[3]);
|
||||
{
|
||||
string trimmed = mainArg.Split('?').First();
|
||||
if (int.TryParse(trimmed, out var id))
|
||||
return new LinkDetails(LinkAction.OpenBeatmap, id.ToString());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "s":
|
||||
case "beatmapsets":
|
||||
case "d":
|
||||
return new LinkDetails(LinkAction.OpenBeatmapSet, args[3]);
|
||||
{
|
||||
if (args.Length > 4 && int.TryParse(args[4], out var id))
|
||||
// https://osu.ppy.sh/beatmapsets/1154158#osu/2768184
|
||||
return new LinkDetails(LinkAction.OpenBeatmap, id.ToString());
|
||||
|
||||
// https://osu.ppy.sh/beatmapsets/1154158#whatever
|
||||
string trimmed = mainArg.Split('#').First();
|
||||
if (int.TryParse(trimmed, out id))
|
||||
return new LinkDetails(LinkAction.OpenBeatmapSet, id.ToString());
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case "u":
|
||||
case "users":
|
||||
return new LinkDetails(LinkAction.OpenUserProfile, args[3]);
|
||||
return new LinkDetails(LinkAction.OpenUserProfile, mainArg);
|
||||
}
|
||||
}
|
||||
|
||||
@ -183,10 +215,9 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
case "osump":
|
||||
return new LinkDetails(LinkAction.JoinMultiplayerMatch, args[1]);
|
||||
|
||||
default:
|
||||
return new LinkDetails(LinkAction.External, null);
|
||||
}
|
||||
|
||||
return new LinkDetails(LinkAction.External, null);
|
||||
}
|
||||
|
||||
private static MessageFormatterResult format(string toFormat, int startIndex = 0, int space = 3)
|
||||
@ -259,8 +290,9 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
public class LinkDetails
|
||||
{
|
||||
public LinkAction Action;
|
||||
public string Argument;
|
||||
public readonly LinkAction Action;
|
||||
|
||||
public readonly string Argument;
|
||||
|
||||
public LinkDetails(LinkAction action, string argument)
|
||||
{
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
@ -34,7 +35,7 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
private Bindable<bool> notifyOnMention;
|
||||
private Bindable<bool> notifyOnPM;
|
||||
private Bindable<User> localUser;
|
||||
private IBindable<User> localUser;
|
||||
private readonly BindableList<Channel> joinedChannels = new BindableList<Channel>();
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
@ -47,17 +48,25 @@ namespace osu.Game.Online.Chat
|
||||
channelManager.JoinedChannels.BindTo(joinedChannels);
|
||||
|
||||
// Listen for new messages
|
||||
joinedChannels.ItemsAdded += joinedChannels =>
|
||||
{
|
||||
foreach (var channel in joinedChannels)
|
||||
channel.NewMessagesArrived += newMessagesArrived;
|
||||
};
|
||||
joinedChannels.CollectionChanged += channelsChanged;
|
||||
}
|
||||
|
||||
joinedChannels.ItemsRemoved += leftChannels =>
|
||||
private void channelsChanged(object sender, NotifyCollectionChangedEventArgs e)
|
||||
{
|
||||
switch (e.Action)
|
||||
{
|
||||
foreach (var channel in leftChannels)
|
||||
channel.NewMessagesArrived -= newMessagesArrived;
|
||||
};
|
||||
case NotifyCollectionChangedAction.Add:
|
||||
foreach (var channel in e.NewItems.Cast<Channel>())
|
||||
channel.NewMessagesArrived += newMessagesArrived;
|
||||
|
||||
break;
|
||||
|
||||
case NotifyCollectionChangedAction.Remove:
|
||||
foreach (var channel in e.OldItems.Cast<Channel>())
|
||||
channel.NewMessagesArrived -= newMessagesArrived;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void newMessagesArrived(IEnumerable<Message> messages)
|
||||
|
55
osu.Game/Online/Chat/NowPlayingCommand.cs
Normal file
55
osu.Game/Online/Chat/NowPlayingCommand.cs
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using osu.Framework.Allocation;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Framework.Graphics;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Online.API;
|
||||
using osu.Game.Users;
|
||||
|
||||
namespace osu.Game.Online.Chat
|
||||
{
|
||||
public class NowPlayingCommand : Component
|
||||
{
|
||||
[Resolved]
|
||||
private IChannelPostTarget channelManager { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private IAPIProvider api { get; set; }
|
||||
|
||||
[Resolved]
|
||||
private Bindable<WorkingBeatmap> currentBeatmap { get; set; }
|
||||
|
||||
protected override void LoadComplete()
|
||||
{
|
||||
base.LoadComplete();
|
||||
|
||||
string verb;
|
||||
BeatmapInfo beatmap;
|
||||
|
||||
switch (api.Activity.Value)
|
||||
{
|
||||
case UserActivity.SoloGame solo:
|
||||
verb = "playing";
|
||||
beatmap = solo.Beatmap;
|
||||
break;
|
||||
|
||||
case UserActivity.Editing edit:
|
||||
verb = "editing";
|
||||
beatmap = edit.Beatmap;
|
||||
break;
|
||||
|
||||
default:
|
||||
verb = "listening to";
|
||||
beatmap = currentBeatmap.Value.BeatmapInfo;
|
||||
break;
|
||||
}
|
||||
|
||||
var beatmapString = beatmap.OnlineBeatmapID.HasValue ? $"[{api.WebsiteRootUrl}/b/{beatmap.OnlineBeatmapID} {beatmap}]" : beatmap.ToString();
|
||||
|
||||
channelManager.PostMessage($"is {verb} {beatmapString}", true);
|
||||
Expire();
|
||||
}
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
protected ChannelManager ChannelManager;
|
||||
|
||||
private DrawableChannel drawableChannel;
|
||||
private StandAloneDrawableChannel drawableChannel;
|
||||
|
||||
private readonly bool postingTextbox;
|
||||
|
||||
@ -59,12 +59,13 @@ namespace osu.Game.Online.Chat
|
||||
RelativeSizeAxes = Axes.X,
|
||||
Height = textbox_height,
|
||||
PlaceholderText = "type your message",
|
||||
OnCommit = postMessage,
|
||||
ReleaseFocusOnCommit = false,
|
||||
HoldFocus = true,
|
||||
Anchor = Anchor.BottomLeft,
|
||||
Origin = Anchor.BottomLeft,
|
||||
});
|
||||
|
||||
textbox.OnCommit += postMessage;
|
||||
}
|
||||
|
||||
Channel.BindValueChanged(channelChanged);
|
||||
@ -73,10 +74,12 @@ namespace osu.Game.Online.Chat
|
||||
[BackgroundDependencyLoader(true)]
|
||||
private void load(ChannelManager manager)
|
||||
{
|
||||
if (ChannelManager == null)
|
||||
ChannelManager = manager;
|
||||
ChannelManager ??= manager;
|
||||
}
|
||||
|
||||
protected virtual StandAloneDrawableChannel CreateDrawableChannel(Channel channel) =>
|
||||
new StandAloneDrawableChannel(channel);
|
||||
|
||||
private void postMessage(TextBox sender, bool newtext)
|
||||
{
|
||||
var text = textbox.Text.Trim();
|
||||
@ -92,18 +95,6 @@ namespace osu.Game.Online.Chat
|
||||
textbox.Text = string.Empty;
|
||||
}
|
||||
|
||||
public void Contract()
|
||||
{
|
||||
this.FadeIn(300);
|
||||
this.MoveToY(0, 500, Easing.OutQuint);
|
||||
}
|
||||
|
||||
public void Expand()
|
||||
{
|
||||
this.FadeOut(200);
|
||||
this.MoveToY(100, 500, Easing.In);
|
||||
}
|
||||
|
||||
protected virtual ChatLine CreateMessage(Message message) => new StandAloneMessage(message);
|
||||
|
||||
private void channelChanged(ValueChangedEvent<Channel> e)
|
||||
@ -112,14 +103,14 @@ namespace osu.Game.Online.Chat
|
||||
|
||||
if (e.NewValue == null) return;
|
||||
|
||||
AddInternal(drawableChannel = new StandAloneDrawableChannel(e.NewValue)
|
||||
{
|
||||
CreateChatLineAction = CreateMessage,
|
||||
Padding = new MarginPadding { Bottom = postingTextbox ? textbox_height : 0 }
|
||||
});
|
||||
drawableChannel = CreateDrawableChannel(e.NewValue);
|
||||
drawableChannel.CreateChatLineAction = CreateMessage;
|
||||
drawableChannel.Padding = new MarginPadding { Bottom = postingTextbox ? textbox_height : 0 };
|
||||
|
||||
AddInternal(drawableChannel);
|
||||
}
|
||||
|
||||
protected class StandAloneDrawableChannel : DrawableChannel
|
||||
public class StandAloneDrawableChannel : DrawableChannel
|
||||
{
|
||||
public Func<Message, ChatLine> CreateChatLineAction;
|
||||
|
||||
|
Reference in New Issue
Block a user