Merge remote-tracking branch 'upstream/master' into chat-mention

This commit is contained in:
Craftplacer
2021-06-05 11:18:06 +02:00
266 changed files with 4779 additions and 1924 deletions

View File

@ -8,6 +8,7 @@ using System.Threading.Tasks;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Logging;
using osu.Game.Database;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.Chat.Tabs;
@ -33,6 +34,16 @@ namespace osu.Game.Online.Chat
private readonly BindableList<Channel> availableChannels = new BindableList<Channel>();
private readonly BindableList<Channel> joinedChannels = new BindableList<Channel>();
/// <summary>
/// Keeps a stack of recently closed channels
/// </summary>
private readonly List<ClosedChannel> closedChannels = new List<ClosedChannel>();
// For efficiency purposes, this constant bounds the number of closed channels we store.
// This number is somewhat arbitrary; future developers are free to modify it.
// Must be a positive number.
private const int closed_channels_max_size = 50;
/// <summary>
/// The currently opened channel
/// </summary>
@ -51,6 +62,9 @@ namespace osu.Game.Online.Chat
[Resolved]
private IAPIProvider api { get; set; }
[Resolved]
private UserLookupCache users { get; set; }
public readonly BindableBool HighPollRate = new BindableBool();
public ChannelManager()
@ -420,6 +434,18 @@ namespace osu.Game.Online.Chat
joinedChannels.Remove(channel);
// Prevent the closedChannel list from exceeding the max size
// by removing the oldest element
if (closedChannels.Count >= closed_channels_max_size)
{
closedChannels.RemoveAt(0);
}
// For PM channels, we store the user ID; else, we store the channel ID
closedChannels.Add(channel.Type == ChannelType.PM
? new ClosedChannel(ChannelType.PM, channel.Users.Single().Id)
: new ClosedChannel(channel.Type, channel.Id));
if (channel.Joined.Value)
{
api.Queue(new LeaveChannelRequest(channel));
@ -427,6 +453,46 @@ namespace osu.Game.Online.Chat
}
});
/// <summary>
/// Opens the most recently closed channel that has not already been reopened,
/// Works similarly to reopening the last closed tab on a web browser.
/// </summary>
public void JoinLastClosedChannel()
{
// This loop could be eliminated if the join channel operation ensured that every channel joined
// is removed from the closedChannels list, but it'd require a linear scan of closed channels on every join.
// To keep the overhead of joining channels low, just lazily scan the list of closed channels locally.
while (closedChannels.Count > 0)
{
ClosedChannel lastClosedChannel = closedChannels.Last();
closedChannels.RemoveAt(closedChannels.Count - 1);
// If the user has already joined the channel, try the next one
if (joinedChannels.FirstOrDefault(lastClosedChannel.Matches) != null)
continue;
Channel lastChannel = AvailableChannels.FirstOrDefault(lastClosedChannel.Matches);
if (lastChannel != null)
{
// Channel exists as an available channel, directly join it
CurrentChannel.Value = JoinChannel(lastChannel);
}
else if (lastClosedChannel.Type == ChannelType.PM)
{
// Try to get user in order to open PM chat
users.GetUserAsync((int)lastClosedChannel.Id).ContinueWith(u =>
{
if (u.Result == null) return;
Schedule(() => CurrentChannel.Value = JoinChannel(new Channel(u.Result)));
});
}
return;
}
}
private long lastMessageId;
private bool channelsInitialised;
@ -511,4 +577,28 @@ namespace osu.Game.Online.Chat
{
}
}
/// <summary>
/// Stores information about a closed channel
/// </summary>
public class ClosedChannel
{
public readonly ChannelType Type;
public readonly long Id;
public ClosedChannel(ChannelType type, long id)
{
Type = type;
Id = id;
}
public bool Matches(Channel channel)
{
if (channel.Type != Type) return false;
return Type == ChannelType.PM
? channel.Users.Single().Id == Id
: channel.Id == Id;
}
}
}

View File

@ -6,6 +6,8 @@ using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
#nullable enable
namespace osu.Game.Online.Chat
{
public static class MessageFormatter
@ -61,7 +63,7 @@ namespace osu.Game.Online.Chat
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)
private static void handleMatches(Regex regex, string display, string link, MessageFormatterResult result, int startIndex = 0, LinkAction? linkActionOverride = null, char[]? escapeChars = null)
{
int captureOffset = 0;
@ -167,15 +169,18 @@ namespace osu.Game.Online.Chat
case "u":
case "users":
return new LinkDetails(LinkAction.OpenUserProfile, mainArg);
case "wiki":
return new LinkDetails(LinkAction.OpenWiki, string.Join('/', args.Skip(3)));
}
}
return new LinkDetails(LinkAction.External, null);
break;
case "osu":
// every internal link also needs some kind of argument
if (args.Length < 3)
return new LinkDetails(LinkAction.External, null);
break;
LinkAction linkType;
@ -217,7 +222,7 @@ namespace osu.Game.Online.Chat
return new LinkDetails(LinkAction.JoinMultiplayerMatch, args[1]);
}
return new LinkDetails(LinkAction.External, null);
return new LinkDetails(LinkAction.External, url);
}
private static MessageFormatterResult format(string toFormat, int startIndex = 0, int space = 3)
@ -311,7 +316,8 @@ namespace osu.Game.Online.Chat
JoinMultiplayerMatch,
Spectate,
OpenUserProfile,
Custom
OpenWiki,
Custom,
}
public class Link : IComparable<Link>