Add bool flag for checking tag vote threshold & utilise as required

Closes https://github.com/ppy/osu/issues/36453.

My omission was in assuming that web was going to start filtering out
the tags below the threshold from API responses, which is not the case.

Whether or not the consumers want or not to display tags below threshold
is subjective. I figured that the matchmaking card tooltip might want to
display below threshold but I dunno.
This commit is contained in:
Bartłomiej Dach
2026-03-03 12:55:14 +01:00
committed by LA
parent 6e882cde18
commit 1840cfe759
4 changed files with 14 additions and 6 deletions

View File

@@ -123,7 +123,15 @@ namespace osu.Game.Online.API.Requests.Responses
/// </summary>
public const int MINIMUM_USER_TAG_VOTES_FOR_DISPLAY = 5;
public (APITag Tag, int VoteCount)[] GetTopUserTags()
/// <summary>
/// Retrieves top user tags for the beatmap, ordered in a way matching osu!web.
/// Requires <see cref="BeatmapSet"/> to be populated.
/// </summary>
/// <param name="confirmedOnly">
/// If <see langword="true"/>, only tags above <see cref="MINIMUM_USER_TAG_VOTES_FOR_DISPLAY"/> will be shown.
/// If <see langword="false"/>, all tags regardless of vote count will be shown.
/// </param>
public (APITag Tag, int VoteCount)[] GetTopUserTags(bool confirmedOnly)
{
if (TopTags == null || TopTags.Length == 0 || BeatmapSet?.RelatedTags == null)
return [];
@@ -132,7 +140,7 @@ namespace osu.Game.Online.API.Requests.Responses
return TopTags
.Select(t => (topTag: t, relatedTag: tagsById.GetValueOrDefault(t.TagId)))
.Where(t => t.relatedTag != null)
.Where(t => t.relatedTag != null && (!confirmedOnly || t.topTag.VoteCount >= MINIMUM_USER_TAG_VOTES_FOR_DISPLAY))
// 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)

View File

@@ -129,7 +129,7 @@ namespace osu.Game.Overlays.BeatmapSet
private void updateUserTags()
{
userTags.Metadata = Beatmap.Value?.GetTopUserTags().Select(t => t.Tag.Name).ToArray();
userTags.Metadata = Beatmap.Value?.GetTopUserTags(confirmedOnly: true).Select(t => t.Tag.Name).ToArray();
}
[BackgroundDependencyLoader]

View File

@@ -458,7 +458,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
new OsuSpriteText
{
Padding = new MarginPadding { Vertical = 3, Horizontal = 8 },
Text = beatmap.GetTopUserTags().FirstOrDefault().Tag?.Name ?? string.Empty,
Text = beatmap.GetTopUserTags(confirmedOnly: true).FirstOrDefault().Tag?.Name ?? string.Empty,
AlwaysPresent = true,
Colour = colourProvider.Content2,
Font = OsuFont.Style.Caption2,
@@ -468,7 +468,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.Match.BeatmapSelect
};
}
public LocalisableString TooltipText => string.Join('\n', beatmap.GetTopUserTags().Select(t => $"{t.Tag.Name} ({t.VoteCount})"));
public LocalisableString TooltipText => string.Join('\n', beatmap.GetTopUserTags(confirmedOnly: false).Select(t => $"{t.Tag.Name} ({t.VoteCount})"));
}
}
}

View File

@@ -102,7 +102,7 @@ namespace osu.Game.Screens.Select
dbBeatmap.Status = onlineBeatmap.Status;
onlineBeatmap.BeatmapSet = onlineBeatmapSet;
HashSet<string> userTags = onlineBeatmap.GetTopUserTags()?
HashSet<string> userTags = onlineBeatmap.GetTopUserTags(confirmedOnly: true)
.Select(t => t.Tag.Name)
.ToHashSet() ?? [];