From 279761abd31b00aa99790b13ca1cd5d2378ff241 Mon Sep 17 00:00:00 2001 From: Robert Date: Mon, 25 Sep 2023 19:52:56 +0200 Subject: [PATCH] Workshop - Added button to copy share link to profile detail page --- src/Artemis.Core/Utilities/StringUtilities.cs | 150 ++++++++++++++++++ .../Workshop/Profile/ProfileDetailsView.axaml | 26 ++- .../Profile/ProfileDetailsViewModel.cs | 15 +- 3 files changed, 182 insertions(+), 9 deletions(-) create mode 100644 src/Artemis.Core/Utilities/StringUtilities.cs diff --git a/src/Artemis.Core/Utilities/StringUtilities.cs b/src/Artemis.Core/Utilities/StringUtilities.cs new file mode 100644 index 000000000..aba045180 --- /dev/null +++ b/src/Artemis.Core/Utilities/StringUtilities.cs @@ -0,0 +1,150 @@ +using System.Text; + +namespace Artemis.Core; + +/// +/// Provides some random string utilities. +/// +public static class StringUtilities +{ + /// + /// Produces optional, URL-friendly version of a title, "like-this-one". + /// hand-tuned for speed, reflects performance refactoring contributed + /// by John Gietzen (user otac0n) + /// + /// Source: https://stackoverflow.com/a/25486 + public static string UrlFriendly(string? title) + { + if (title == null) return ""; + + const int maxlen = 80; + int len = title.Length; + bool prevdash = false; + StringBuilder sb = new(len); + char c; + + for (int i = 0; i < len; i++) + { + c = title[i]; + if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9')) + { + sb.Append(c); + prevdash = false; + } + else if (c >= 'A' && c <= 'Z') + { + // tricky way to convert to lowercase + sb.Append((char) (c | 32)); + prevdash = false; + } + else if (c == ' ' || c == ',' || c == '.' || c == '/' || + c == '\\' || c == '-' || c == '_' || c == '=') + { + if (!prevdash && sb.Length > 0) + { + sb.Append('-'); + prevdash = true; + } + } + else if (c >= 128) + { + int prevlen = sb.Length; + sb.Append(RemapInternationalCharToAscii(c)); + if (prevlen != sb.Length) prevdash = false; + } + + if (i == maxlen) break; + } + + if (prevdash) + return sb.ToString().Substring(0, sb.Length - 1); + return sb.ToString(); + } + + /// + /// Remaps internation characters to their ASCII equivalent. + /// Source: https://meta.stackexchange.com/a/7696 + /// + /// The character to remap + /// The ASCII equivalent. + public static string RemapInternationalCharToAscii(char c) + { + string s = c.ToString().ToLowerInvariant(); + if ("àåáâäãåą".Contains(s)) + { + return "a"; + } + else if ("èéêëę".Contains(s)) + { + return "e"; + } + else if ("ìíîïı".Contains(s)) + { + return "i"; + } + else if ("òóôõöøőð".Contains(s)) + { + return "o"; + } + else if ("ùúûüŭů".Contains(s)) + { + return "u"; + } + else if ("çćčĉ".Contains(s)) + { + return "c"; + } + else if ("żźž".Contains(s)) + { + return "z"; + } + else if ("śşšŝ".Contains(s)) + { + return "s"; + } + else if ("ñń".Contains(s)) + { + return "n"; + } + else if ("ýÿ".Contains(s)) + { + return "y"; + } + else if ("ğĝ".Contains(s)) + { + return "g"; + } + else if (c == 'ř') + { + return "r"; + } + else if (c == 'ł') + { + return "l"; + } + else if (c == 'đ') + { + return "d"; + } + else if (c == 'ß') + { + return "ss"; + } + else if (c == 'Þ') + { + return "th"; + } + else if (c == 'ĥ') + { + return "h"; + } + else if (c == 'ĵ') + { + return "j"; + } + else + { + return ""; + } + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml index e8dbea39a..2ca2544a5 100644 --- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsView.axaml @@ -22,14 +22,24 @@ - - - + + + + + + + private readonly IWorkshopClient _client; private readonly ProfileEntryInstallationHandler _installationHandler; private readonly INotificationService _notificationService; - private readonly IWindowService _windowService; private readonly ObservableAsPropertyHelper _updatedAt; + private readonly IWindowService _windowService; private IGetEntryById_Entry? _entry; public ProfileDetailsViewModel(IWorkshopClient client, ProfileEntryInstallationHandler installationHandler, INotificationService notificationService, IWindowService windowService) @@ -34,8 +35,11 @@ public class ProfileDetailsViewModel : RoutableScreen _updatedAt = this.WhenAnyValue(vm => vm.Entry).Select(e => e?.LatestRelease?.CreatedAt ?? e?.CreatedAt).ToProperty(this, vm => vm.UpdatedAt); DownloadLatestRelease = ReactiveCommand.CreateFromTask(ExecuteDownloadLatestRelease); + CopyShareLink = ReactiveCommand.CreateFromTask(ExecuteCopyShareLink); } + public ReactiveCommand CopyShareLink { get; set; } + public ReactiveCommand DownloadLatestRelease { get; } public DateTimeOffset? UpdatedAt => _updatedAt.Value; @@ -75,4 +79,13 @@ public class ProfileDetailsViewModel : RoutableScreen else _notificationService.CreateNotification().WithTitle("Failed to install profile").WithMessage(result.Message).WithSeverity(NotificationSeverity.Error).Show(); } + + private async Task ExecuteCopyShareLink(CancellationToken arg) + { + if (Entry == null) + return; + + await Shared.UI.Clipboard.SetTextAsync($"{WorkshopConstants.WORKSHOP_URL}/entries/{Entry.Id}/{StringUtilities.UrlFriendly(Entry.Name)}"); + _notificationService.CreateNotification().WithTitle("Copied share link to clipboard.").Show(); + } } \ No newline at end of file