Merge pull request #29537 from normalid-awa/feature/song-select/allow-copy-url-from-context-menu

Allow copying beatmap link in song select carousel context menu
This commit is contained in:
Bartłomiej Dach 2024-08-22 14:03:39 +02:00 committed by GitHub
commit bd6943ebc2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 76 additions and 23 deletions

View File

@ -2,6 +2,8 @@
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Game.Online.API;
using osu.Game.Rulesets;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
@ -48,5 +50,16 @@ namespace osu.Game.Beatmaps
} }
private static string getVersionString(IBeatmapInfo beatmapInfo) => string.IsNullOrEmpty(beatmapInfo.DifficultyName) ? string.Empty : $"[{beatmapInfo.DifficultyName}]"; private static string getVersionString(IBeatmapInfo beatmapInfo) => string.IsNullOrEmpty(beatmapInfo.DifficultyName) ? string.Empty : $"[{beatmapInfo.DifficultyName}]";
/// <summary>
/// Get the beatmap info page URL, or <c>null</c> if unavailable.
/// </summary>
public static string? GetOnlineURL(this IBeatmapInfo beatmapInfo, IAPIProvider api, IRulesetInfo? ruleset = null)
{
if (beatmapInfo.OnlineID <= 0 || beatmapInfo.BeatmapSet == null)
return null;
return $@"{api.WebsiteRootUrl}/beatmapsets/{beatmapInfo.BeatmapSet.OnlineID}#{ruleset?.ShortName ?? beatmapInfo.Ruleset.ShortName}/{beatmapInfo.OnlineID}";
}
} }
} }

View File

@ -6,6 +6,8 @@ using System.Linq;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Extensions; using osu.Game.Extensions;
using osu.Game.Models; using osu.Game.Models;
using osu.Game.Online.API;
using osu.Game.Rulesets;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
{ {
@ -29,5 +31,19 @@ namespace osu.Game.Beatmaps
/// <param name="filename">The name of the file to get the storage path of.</param> /// <param name="filename">The name of the file to get the storage path of.</param>
public static RealmNamedFileUsage? GetFile(this IHasRealmFiles model, string filename) => public static RealmNamedFileUsage? GetFile(this IHasRealmFiles model, string filename) =>
model.Files.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase)); model.Files.SingleOrDefault(f => string.Equals(f.Filename, filename, StringComparison.OrdinalIgnoreCase));
/// <summary>
/// Get the beatmapset info page URL, or <c>null</c> if unavailable.
/// </summary>
public static string? GetOnlineURL(this IBeatmapSetInfo beatmapSetInfo, IAPIProvider api, IRulesetInfo? ruleset = null)
{
if (beatmapSetInfo.OnlineID <= 0)
return null;
if (ruleset != null)
return $@"{api.WebsiteRootUrl}/beatmapsets/{beatmapSetInfo.OnlineID}#{ruleset.ShortName}";
return $@"{api.WebsiteRootUrl}/beatmapsets/{beatmapSetInfo.OnlineID}";
}
} }
} }

View File

@ -10,9 +10,6 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input.Events; using osu.Framework.Input.Events;
using osu.Framework.Localisation; using osu.Framework.Localisation;
using osu.Framework.Platform;
using osu.Game.Overlays;
using osu.Game.Overlays.OSD;
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
@ -25,13 +22,7 @@ namespace osu.Game.Graphics.UserInterface
private Color4 hoverColour; private Color4 hoverColour;
[Resolved] [Resolved]
private GameHost host { get; set; } = null!; private OsuGame? game { get; set; }
[Resolved]
private Clipboard clipboard { get; set; } = null!;
[Resolved]
private OnScreenDisplay? onScreenDisplay { get; set; }
private readonly SpriteIcon linkIcon; private readonly SpriteIcon linkIcon;
@ -71,7 +62,7 @@ namespace osu.Game.Graphics.UserInterface
protected override bool OnClick(ClickEvent e) protected override bool OnClick(ClickEvent e)
{ {
if (Link != null) if (Link != null)
host.OpenUrlExternally(Link); game?.OpenUrlExternally(Link);
return true; return true;
} }
@ -85,8 +76,8 @@ namespace osu.Game.Graphics.UserInterface
if (Link != null) if (Link != null)
{ {
items.Add(new OsuMenuItem("Open", MenuItemType.Highlighted, () => host.OpenUrlExternally(Link))); items.Add(new OsuMenuItem("Open", MenuItemType.Highlighted, () => game?.OpenUrlExternally(Link)));
items.Add(new OsuMenuItem("Copy URL", MenuItemType.Standard, copyUrl)); items.Add(new OsuMenuItem("Copy link", MenuItemType.Standard, copyUrl));
} }
return items.ToArray(); return items.ToArray();
@ -95,11 +86,9 @@ namespace osu.Game.Graphics.UserInterface
private void copyUrl() private void copyUrl()
{ {
if (Link != null) if (Link == null) return;
{
clipboard.SetText(Link); game?.CopyUrlToClipboard(Link);
onScreenDisplay?.Display(new CopyUrlToast());
}
} }
} }
} }

View File

@ -45,9 +45,9 @@ namespace osu.Game.Localisation
public static LocalisableString SkinSaved => new TranslatableString(getKey(@"skin_saved"), @"Skin saved"); public static LocalisableString SkinSaved => new TranslatableString(getKey(@"skin_saved"), @"Skin saved");
/// <summary> /// <summary>
/// "URL copied" /// "Link copied to clipboard"
/// </summary> /// </summary>
public static LocalisableString UrlCopied => new TranslatableString(getKey(@"url_copied"), @"URL copied"); public static LocalisableString UrlCopied => new TranslatableString(getKey(@"url_copied"), @"Link copied to clipboard");
/// <summary> /// <summary>
/// "Speed changed to {0:N2}x" /// "Speed changed to {0:N2}x"

View File

@ -60,7 +60,7 @@ namespace osu.Game.Online.Chat
}, },
new PopupDialogCancelButton new PopupDialogCancelButton
{ {
Text = @"Copy URL to the clipboard", Text = @"Copy link",
Action = copyExternalLinkAction Action = copyExternalLinkAction
}, },
new PopupDialogCancelButton new PopupDialogCancelButton

View File

@ -54,6 +54,7 @@ using osu.Game.Overlays.BeatmapListing;
using osu.Game.Overlays.Mods; using osu.Game.Overlays.Mods;
using osu.Game.Overlays.Music; using osu.Game.Overlays.Music;
using osu.Game.Overlays.Notifications; using osu.Game.Overlays.Notifications;
using osu.Game.Overlays.OSD;
using osu.Game.Overlays.SkinEditor; using osu.Game.Overlays.SkinEditor;
using osu.Game.Overlays.Toolbar; using osu.Game.Overlays.Toolbar;
using osu.Game.Overlays.Volume; using osu.Game.Overlays.Volume;
@ -142,6 +143,8 @@ namespace osu.Game
private Container overlayOffsetContainer; private Container overlayOffsetContainer;
private OnScreenDisplay onScreenDisplay;
[Resolved] [Resolved]
private FrameworkConfigManager frameworkConfig { get; set; } private FrameworkConfigManager frameworkConfig { get; set; }
@ -497,6 +500,12 @@ namespace osu.Game
} }
}); });
public void CopyUrlToClipboard(string url) => waitForReady(() => onScreenDisplay, _ =>
{
dependencies.Get<Clipboard>().SetText(url);
onScreenDisplay.Display(new CopyUrlToast());
});
public void OpenUrlExternally(string url, bool forceBypassExternalUrlWarning = false) => waitForReady(() => externalLinkOpener, _ => public void OpenUrlExternally(string url, bool forceBypassExternalUrlWarning = false) => waitForReady(() => externalLinkOpener, _ =>
{ {
bool isTrustedDomain; bool isTrustedDomain;
@ -1078,7 +1087,7 @@ namespace osu.Game
loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add, true); loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add, true);
var onScreenDisplay = new OnScreenDisplay(); onScreenDisplay = new OnScreenDisplay();
onScreenDisplay.BeginTracking(this, frameworkConfig); onScreenDisplay.BeginTracking(this, frameworkConfig);
onScreenDisplay.BeginTracking(this, LocalConfig); onScreenDisplay.BeginTracking(this, LocalConfig);

View File

@ -200,7 +200,8 @@ namespace osu.Game.Overlays.BeatmapSet
private void updateExternalLink() private void updateExternalLink()
{ {
if (externalLink != null) externalLink.Link = $@"{api.WebsiteRootUrl}/beatmapsets/{BeatmapSet.Value?.OnlineID}#{Picker.Beatmap.Value?.Ruleset.ShortName}/{Picker.Beatmap.Value?.OnlineID}"; if (externalLink != null)
externalLink.Link = Picker.Beatmap.Value?.GetOnlineURL(api) ?? BeatmapSet.Value?.GetOnlineURL(api);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]

View File

@ -25,6 +25,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Resources.Localisation.Web; using osu.Game.Resources.Localisation.Web;
using osu.Game.Rulesets; using osu.Game.Rulesets;
@ -79,6 +80,12 @@ namespace osu.Game.Screens.Select.Carousel
[Resolved] [Resolved]
private IBindable<IReadOnlyList<Mod>> mods { get; set; } = null!; private IBindable<IReadOnlyList<Mod>> mods { get; set; } = null!;
[Resolved]
private IAPIProvider api { get; set; } = null!;
[Resolved]
private OsuGame? game { get; set; }
private IBindable<StarDifficulty?> starDifficultyBindable = null!; private IBindable<StarDifficulty?> starDifficultyBindable = null!;
private CancellationTokenSource? starDifficultyCancellationSource; private CancellationTokenSource? starDifficultyCancellationSource;
@ -288,6 +295,9 @@ namespace osu.Game.Screens.Select.Carousel
items.Add(new OsuMenuItem("Collections") { Items = collectionItems }); items.Add(new OsuMenuItem("Collections") { Items = collectionItems });
if (beatmapInfo.GetOnlineURL(api, ruleset.Value) is string url)
items.Add(new OsuMenuItem("Copy link", MenuItemType.Standard, () => game?.CopyUrlToClipboard(url)));
if (hideRequested != null) if (hideRequested != null)
items.Add(new OsuMenuItem(CommonStrings.ButtonsHide.ToSentence(), MenuItemType.Destructive, () => hideRequested(beatmapInfo))); items.Add(new OsuMenuItem(CommonStrings.ButtonsHide.ToSentence(), MenuItemType.Destructive, () => hideRequested(beatmapInfo)));

View File

@ -8,6 +8,7 @@ using System.Linq;
using System.Threading; using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -19,7 +20,9 @@ using osu.Game.Beatmaps;
using osu.Game.Collections; using osu.Game.Collections;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Rulesets;
namespace osu.Game.Screens.Select.Carousel namespace osu.Game.Screens.Select.Carousel
{ {
@ -39,6 +42,15 @@ namespace osu.Game.Screens.Select.Carousel
[Resolved] [Resolved]
private RealmAccess realm { get; set; } = null!; private RealmAccess realm { get; set; } = null!;
[Resolved]
private IAPIProvider api { get; set; } = null!;
[Resolved]
private OsuGame? game { get; set; }
[Resolved]
private IBindable<RulesetInfo> ruleset { get; set; } = null!;
public IEnumerable<DrawableCarouselItem> DrawableBeatmaps => beatmapContainer?.IsLoaded != true ? Enumerable.Empty<DrawableCarouselItem>() : beatmapContainer.AliveChildren; public IEnumerable<DrawableCarouselItem> DrawableBeatmaps => beatmapContainer?.IsLoaded != true ? Enumerable.Empty<DrawableCarouselItem>() : beatmapContainer.AliveChildren;
private Container<DrawableCarouselItem>? beatmapContainer; private Container<DrawableCarouselItem>? beatmapContainer;
@ -287,6 +299,9 @@ namespace osu.Game.Screens.Select.Carousel
if (beatmapSet.Beatmaps.Any(b => b.Hidden)) if (beatmapSet.Beatmaps.Any(b => b.Hidden))
items.Add(new OsuMenuItem("Restore all hidden", MenuItemType.Standard, () => restoreHiddenRequested(beatmapSet))); items.Add(new OsuMenuItem("Restore all hidden", MenuItemType.Standard, () => restoreHiddenRequested(beatmapSet)));
if (beatmapSet.GetOnlineURL(api, ruleset.Value) is string url)
items.Add(new OsuMenuItem("Copy link", MenuItemType.Standard, () => game?.CopyUrlToClipboard(url)));
if (dialogOverlay != null) if (dialogOverlay != null)
items.Add(new OsuMenuItem("Delete...", MenuItemType.Destructive, () => dialogOverlay.Push(new BeatmapDeleteDialog(beatmapSet)))); items.Add(new OsuMenuItem("Delete...", MenuItemType.Destructive, () => dialogOverlay.Push(new BeatmapDeleteDialog(beatmapSet))));
return items.ToArray(); return items.ToArray();