diff --git a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapMetadataWedge.cs b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapMetadataWedge.cs index f2d4fad69e..3cdb513b38 100644 --- a/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapMetadataWedge.cs +++ b/osu.Game.Tests/Visual/SongSelectV2/TestSceneBeatmapMetadataWedge.cs @@ -142,6 +142,7 @@ namespace osu.Game.Tests.Visual.SongSelectV2 working.BeatmapInfo.Metadata.Tags = string.Join(' ', Enumerable.Repeat(working.BeatmapInfo.Metadata.Tags, 3)); onlineSet.Genre = new BeatmapSetOnlineGenre { Id = 12, Name = "Verrrrryyyy llooonngggggg genre" }; onlineSet.Language = new BeatmapSetOnlineLanguage { Id = 12, Name = "Verrrrryyyy llooonngggggg language" }; + onlineSet.Beatmaps.Single().TopTags = Enumerable.Repeat(onlineSet.Beatmaps.Single().TopTags, 3).SelectMany(t => t!).ToArray(); currentOnlineSet = onlineSet; Beatmap.Value = working; @@ -182,6 +183,28 @@ namespace osu.Game.Tests.Visual.SongSelectV2 AddAssert("fail time wedge still hidden", () => !wedge.FailRetryVisible); } + [Test] + public void TestUserTags() + { + AddStep("user tags", () => + { + var (working, onlineSet) = createTestBeatmap(); + + currentOnlineSet = onlineSet; + Beatmap.Value = working; + }); + AddStep("no user tags", () => + { + var (working, onlineSet) = createTestBeatmap(); + + onlineSet.Beatmaps.Single().TopTags = null; + onlineSet.RelatedTags = null; + + currentOnlineSet = onlineSet; + Beatmap.Value = working; + }); + } + private (WorkingBeatmap, APIBeatmapSet) createTestBeatmap() { var working = CreateWorkingBeatmap(Ruleset.Value); @@ -198,13 +221,40 @@ namespace osu.Game.Tests.Visual.SongSelectV2 OnlineID = working.BeatmapInfo.OnlineID, PlayCount = 10000, PassCount = 4567, + TopTags = + [ + new APIBeatmapTag { TagId = 4, VoteCount = 1 }, + new APIBeatmapTag { TagId = 2, VoteCount = 1 }, + new APIBeatmapTag { TagId = 23, VoteCount = 5 }, + ], FailTimes = new APIFailTimes { Fails = Enumerable.Range(1, 100).Select(i => i % 12 - 6).ToArray(), Retries = Enumerable.Range(-2, 100).Select(i => i % 12 - 6).ToArray(), }, }, - } + }, + RelatedTags = + [ + new APITag + { + Id = 2, + Name = "song representation/simple", + Description = "Accessible and straightforward map design." + }, + new APITag + { + Id = 4, + Name = "style/clean", + Description = "Visually uncluttered and organised patterns, often involving few overlaps and equal visual spacing between objects." + }, + new APITag + { + Id = 23, + Name = "aim/aim control", + Description = "Patterns with velocity or direction changes which strongly go against a player's natural movement pattern." + } + ] }; working.BeatmapSetInfo.DateSubmitted = DateTimeOffset.Now; diff --git a/osu.Game/Screens/SelectV2/BeatmapMetadataWedge.cs b/osu.Game/Screens/SelectV2/BeatmapMetadataWedge.cs index ae9222033e..da9d5fe89b 100644 --- a/osu.Game/Screens/SelectV2/BeatmapMetadataWedge.cs +++ b/osu.Game/Screens/SelectV2/BeatmapMetadataWedge.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -23,7 +24,8 @@ namespace osu.Game.Screens.SelectV2 private MetadataDisplay source = null!; private MetadataDisplay genre = null!; private MetadataDisplay language = null!; - private MetadataDisplay tag = null!; + private MetadataDisplay userTags = null!; + private MetadataDisplay mapperTags = null!; private MetadataDisplay submitted = null!; private MetadataDisplay ranked = null!; @@ -95,6 +97,8 @@ namespace osu.Game.Screens.SelectV2 AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, Spacing = new Vector2(0f, 10f), + AutoSizeDuration = (float)transition_duration / 3, + AutoSizeEasing = Easing.OutQuint, Children = new Drawable[] { new GridContainer @@ -151,7 +155,11 @@ namespace osu.Game.Screens.SelectV2 }, }, }, - tag = new MetadataDisplay("Tags"), + userTags = new MetadataDisplay("User Tags") + { + Alpha = 0, + }, + mapperTags = new MetadataDisplay("Mapper Tags"), }, }, }, @@ -288,7 +296,7 @@ namespace osu.Game.Screens.SelectV2 else source.Data = ("-", null); - tag.Tags = (metadata.Tags.Split(' '), t => songSelect?.Search(t)); + mapperTags.Tags = (metadata.Tags.Split(' '), t => songSelect?.Search(t)); submitted.Date = beatmapSetInfo.DateSubmitted; ranked.Date = beatmapSetInfo.DateRanked; @@ -357,7 +365,34 @@ namespace osu.Game.Screens.SelectV2 } } + updateUserTags(); updateSubWedgeVisibility(); } + + private void updateUserTags() + { + var beatmapInfo = beatmap.Value.BeatmapInfo; + var onlineBeatmapSet = currentOnlineBeatmapSet; + var onlineBeatmap = onlineBeatmapSet?.Beatmaps.SingleOrDefault(b => b.OnlineID == beatmapInfo.OnlineID); + + if (onlineBeatmap?.TopTags == null || onlineBeatmap.TopTags.Length == 0 || onlineBeatmapSet?.RelatedTags == null) + { + userTags.FadeOut(transition_duration, Easing.OutQuint); + return; + } + + var tagsById = onlineBeatmapSet.RelatedTags.ToDictionary(t => t.Id); + string[] userTagsArray = onlineBeatmap.TopTags + .Select(t => (topTag: t, relatedTag: tagsById.GetValueOrDefault(t.TagId))) + .Where(t => t.relatedTag != null) + // see https://github.com/ppy/osu-web/blob/bb3bd2e7c6f84f26066df5ea20a81c77ec9bb60a/resources/js/beatmapsets-show/controller.ts#L103-L106 for sort criteria + .OrderByDescending(t => t.topTag.VoteCount) + .ThenBy(t => t.relatedTag!.Name) + .Select(t => t.relatedTag!.Name) + .ToArray(); + + userTags.FadeIn(transition_duration, Easing.OutQuint); + userTags.Tags = (userTagsArray, t => songSelect?.Search(t)); + } } }