1
0
mirror of https://github.com/Artemis-RGB/Artemis synced 2025-12-12 13:28:33 +00:00

Workshop - Added button to copy share link to profile detail page

This commit is contained in:
Robert 2023-09-25 19:52:56 +02:00
parent 1b0796c68b
commit 279761abd3
3 changed files with 182 additions and 9 deletions

View File

@ -0,0 +1,150 @@
using System.Text;
namespace Artemis.Core;
/// <summary>
/// Provides some random string utilities.
/// </summary>
public static class StringUtilities
{
/// <summary>
/// Produces optional, URL-friendly version of a title, "like-this-one".
/// hand-tuned for speed, reflects performance refactoring contributed
/// by John Gietzen (user otac0n)
/// </summary>
/// <remarks>Source: https://stackoverflow.com/a/25486</remarks>
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();
}
/// <summary>
/// Remaps internation characters to their ASCII equivalent.
/// <remarks>Source: https://meta.stackexchange.com/a/7696</remarks>
/// </summary>
/// <param name="c">The character to remap</param>
/// <returns>The ASCII equivalent.</returns>
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 "";
}
}
}

View File

@ -22,14 +22,24 @@
<StackPanel Grid.Row="1" Grid.Column="0" Margin="0 0 10 0" Spacing="10">
<Border Classes="card" VerticalAlignment="Top">
<StackPanel>
<Border CornerRadius="6"
HorizontalAlignment="Left"
Margin="0 0 10 0"
Width="80"
Height="80"
ClipToBounds="True">
<Image Stretch="UniformToFill" il:ImageLoader.Source="{CompiledBinding Entry.Id, Converter={StaticResource EntryIconUriConverter}, Mode=OneWay}" />
</Border>
<Panel>
<Border CornerRadius="6"
HorizontalAlignment="Left"
Margin="0 0 10 0"
Width="80"
Height="80"
ClipToBounds="True">
<Image Stretch="UniformToFill" il:ImageLoader.Source="{CompiledBinding Entry.Id, Converter={StaticResource EntryIconUriConverter}, Mode=OneWay}" />
</Border>
<Button Classes="icon-button"
VerticalAlignment="Top"
HorizontalAlignment="Right"
Command="{CompiledBinding CopyShareLink}"
ToolTip.Tip="Copy share link">
<avalonia:MaterialIcon Kind="ShareVariant"/>
</Button>
</Panel>
<TextBlock Theme="{StaticResource TitleTextBlockStyle}"
MaxLines="3"

View File

@ -3,6 +3,7 @@ using System.Reactive;
using System.Reactive.Linq;
using System.Threading;
using System.Threading.Tasks;
using Artemis.Core;
using Artemis.UI.Screens.Workshop.Parameters;
using Artemis.UI.Shared.Routing;
using Artemis.UI.Shared.Services;
@ -21,8 +22,8 @@ public class ProfileDetailsViewModel : RoutableScreen<WorkshopDetailParameters>
private readonly IWorkshopClient _client;
private readonly ProfileEntryInstallationHandler _installationHandler;
private readonly INotificationService _notificationService;
private readonly IWindowService _windowService;
private readonly ObservableAsPropertyHelper<DateTimeOffset?> _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<WorkshopDetailParameters>
_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<Unit, Unit> CopyShareLink { get; set; }
public ReactiveCommand<Unit, Unit> DownloadLatestRelease { get; }
public DateTimeOffset? UpdatedAt => _updatedAt.Value;
@ -75,4 +79,13 @@ public class ProfileDetailsViewModel : RoutableScreen<WorkshopDetailParameters>
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();
}
}