diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 9f1b44af44..8edae7a976 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -23,14 +23,16 @@ namespace osu.Game.Graphics.Containers public override bool HandleMouseInput => true; private OsuGame game; + private UserProfileOverlay userProfile; private Action showNotImplementedError; [BackgroundDependencyLoader(true)] - private void load(OsuGame game, NotificationOverlay notifications) + private void load(OsuGame game, NotificationOverlay notifications, UserProfileOverlay userProfile) { // will be null in tests this.game = game; + this.userProfile = userProfile; showNotImplementedError = () => notifications?.Post(new SimpleNotification { @@ -90,6 +92,9 @@ namespace osu.Game.Graphics.Containers case LinkAction.External: Process.Start(url); break; + case LinkAction.OpenUserProfile: + userProfile?.ShowUser(Convert.ToInt64(linkArgument)); + break; default: throw new NotImplementedException($"This {nameof(LinkAction)} ({linkType.ToString()}) is missing an associated action."); } diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 906f42d50e..3fdce3ec12 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -118,6 +118,8 @@ namespace osu.Game.Online.Chat case "beatmapsets": case "d": return new LinkDetails(LinkAction.OpenBeatmapSet, args[3]); + case "u": + return new LinkDetails(LinkAction.OpenUserProfile, args[3]); } } @@ -146,6 +148,9 @@ namespace osu.Game.Online.Chat case "spectate": linkType = LinkAction.Spectate; break; + case "u": + linkType = LinkAction.OpenUserProfile; + break; default: linkType = LinkAction.External; break; @@ -205,6 +210,15 @@ namespace osu.Game.Online.Chat return inputMessage; } + public static List GetLinks(string text) + { + var result = format(text); + + result.Links.Sort(); + + return result.Links; + } + public class MessageFormatterResult { public List Links = new List(); @@ -239,6 +253,7 @@ namespace osu.Game.Online.Chat OpenEditorTimestamp, JoinMultiplayerMatch, Spectate, + OpenUserProfile, } public class Link : IComparable diff --git a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs index e0f7a97140..6abf68e3e9 100644 --- a/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs +++ b/osu.Game/Overlays/Profile/Sections/Recent/DrawableRecentActivity.cs @@ -7,10 +7,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API; using osu.Game.Online.API.Requests; +using osu.Game.Online.Chat; using osu.Game.Screens.Select.Leaderboards; using osu.Game.Users; +using static osu.Game.Online.API.Requests.RecentActivity; namespace osu.Game.Overlays.Profile.Sections.Recent { @@ -18,19 +22,31 @@ namespace osu.Game.Overlays.Profile.Sections.Recent { private RecentActivity activity; private User user; + private APIAccess api; + + private string userLinkTemplate; + private string beatmapLinkTemplate; + + private LinkFlowContainer content; public DrawableRecentActivity(RecentActivity activity, User user) { this.activity = activity; this.user = user; + + userLinkTemplate = $"[{activity.User?.Username}]({urlToAbsolute(activity.User?.Url)})"; + beatmapLinkTemplate = $"[{activity.Beatmap?.Title}]({urlToAbsolute(activity.Beatmap?.Url)})"; } [BackgroundDependencyLoader] - private void load() + private void load(APIAccess api) { - LeftFlowContainer.Add(new OsuSpriteText + this.api = api; + + LeftFlowContainer.Add(content = new LinkFlowContainer { - Text = activityToString(), + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, }); RightFlowContainer.Add(new OsuSpriteText @@ -42,6 +58,10 @@ namespace osu.Game.Overlays.Profile.Sections.Recent TextSize = 12, Colour = OsuColour.Gray(0xAA), }); + + string text = activityToString(); + + content.AddLinks(text, MessageFormatter.GetLinks(text)); } protected override Drawable CreateLeftVisual() @@ -66,48 +86,50 @@ namespace osu.Game.Overlays.Profile.Sections.Recent } } + private string urlToAbsolute(string url) => $"{api?.Endpoint ?? @"https://osu.ppy.sh"}{url}"; + private string activityToString() { switch (activity.Type) { case RecentActivityType.Achievement: - return $"{activity.User.Username} unlocked the {activity.AchivementName} achievement!"; + return $"{userLinkTemplate} unlocked the {activity.AchivementName} achievement!"; case RecentActivityType.BeatmapPlaycount: - return $"{activity.Beatmap.Title} has been played {activity.Count} times!"; + return $"{beatmapLinkTemplate} has been played {activity.Count} times!"; case RecentActivityType.BeatmapsetDelete: - return $"{activity.Beatmap.Title} has been deleted."; + return $"{beatmapLinkTemplate} has been deleted."; case RecentActivityType.BeatmapsetRevive: - return $"{activity.Beatmap.Title} has been revived from eternal slumber by ${activity.User.Username}"; + return $"{beatmapLinkTemplate} has been revived from eternal slumber by ${userLinkTemplate}"; case RecentActivityType.BeatmapsetUpdate: - return $"{activity.User.Username} has updated the beatmap ${activity.Beatmap.Title}"; + return $"{userLinkTemplate} has updated the beatmap ${beatmapLinkTemplate}"; case RecentActivityType.BeatmapsetUpload: - return $"{activity.User.Username} has submitted a new beatmap ${activity.Beatmap.Title}"; + return $"{userLinkTemplate} has submitted a new beatmap ${beatmapLinkTemplate}"; case RecentActivityType.Medal: - return $"{activity.User.Username} has unlocked the {activity.AchivementName} medal!"; + return $"{userLinkTemplate} has unlocked the {activity.AchivementName} medal!"; case RecentActivityType.Rank: - return $"{activity.User.Username} achieved rank #{activity.Rank} on {activity.Beatmap?.Title}"; + return $"{userLinkTemplate} achieved rank #{activity.Rank} on {beatmapLinkTemplate}"; case RecentActivityType.RankLost: - return $"{activity.User.Username} has lost first place on {activity.Beatmap.Title}!"; + return $"{userLinkTemplate} has lost first place on {beatmapLinkTemplate}!"; case RecentActivityType.UserSupportAgain: - return $"{activity.User.Username} has once again chosen to support osu! - thanks for your generosity!"; + return $"{userLinkTemplate} has once again chosen to support osu! - thanks for your generosity!"; case RecentActivityType.UserSupportFirst: - return $"{activity.User.Username} has become an osu! supporter - thanks for your generosity!"; + return $"{userLinkTemplate} has become an osu! supporter - thanks for your generosity!"; case RecentActivityType.UsernameChange: - return $"{activity.User.PreviousUsername} has changed their username to {activity.User.Username}"; + return $"{activity.User.PreviousUsername} has changed their username to {userLinkTemplate}"; case RecentActivityType.UserSupportGift: - return $"{activity.User.Username} has received the gift of osu! supporter!"; + return $"{userLinkTemplate} has received the gift of osu! supporter!"; default: return string.Empty; diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 3bc12ccb24..39cb71ea27 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -73,6 +73,11 @@ namespace osu.Game.Overlays FadeEdgeEffectTo(0, DISAPPEAR_DURATION, Easing.Out); } + public void ShowUser(long userId) + { + ShowUser(new User { Id = userId }, true); + } + public void ShowUser(User user, bool fetchOnline = true) { userReq?.Cancel();