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