mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-12 11:56:30 +00:00
Workshop - Added dedicated release page
This commit is contained in:
1 parent
9c04932afa
commit
9e994840f6
36 files changed
+535
-190
No files matched your search
@@ -0,0 +1,92 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:mdxaml="https://github.com/whistyun/Markdown.Avalonia.Tight"
|
||||
xmlns:entryReleases="clr-namespace:Artemis.UI.Screens.Workshop.EntryReleases"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
||||
xmlns:sharedConverters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Workshop.EntryReleases.EntryReleaseView"
|
||||
x:DataType="entryReleases:EntryReleaseViewModel">
|
||||
<UserControl.Resources>
|
||||
<converters:DateTimeConverter x:Key="DateTimeConverter" />
|
||||
<sharedConverters:BytesToStringConverter x:Key="BytesToStringConverter" />
|
||||
</UserControl.Resources>
|
||||
<UserControl.Styles>
|
||||
<Style Selector="Grid.info-container">
|
||||
<Setter Property="Margin" Value="10" />
|
||||
</Style>
|
||||
<Style Selector="avalonia|MaterialIcon.info-icon">
|
||||
<Setter Property="VerticalAlignment" Value="Top" />
|
||||
<Setter Property="Margin" Value="0 3 10 0" />
|
||||
</Style>
|
||||
<Style Selector="TextBlock.info-title">
|
||||
<Setter Property="Margin" Value="0 0 0 5" />
|
||||
<Setter Property="Opacity" Value="0.8" />
|
||||
</Style>
|
||||
<Style Selector="TextBlock.info-body">
|
||||
</Style>
|
||||
<Style Selector="TextBlock.info-link">
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource SystemAccentColorLight3}" />
|
||||
</Style>
|
||||
<Style Selector="TextBlock.info-link:pointerover">
|
||||
<Setter Property="Cursor" Value="Hand" />
|
||||
<Setter Property="Foreground" Value="{DynamicResource SystemAccentColorLight1}" />
|
||||
</Style>
|
||||
</UserControl.Styles>
|
||||
|
||||
<Grid RowDefinitions="Auto,Auto">
|
||||
<Border Grid.Row="0" Classes="card" Margin="0 0 0 10">
|
||||
<StackPanel>
|
||||
<TextBlock Classes="h4 no-margin">Release info</TextBlock>
|
||||
<Border Classes="card-separator" />
|
||||
<Grid Margin="-5 -10" ColumnDefinitions="*,*,*">
|
||||
<Grid Grid.Column="0" ColumnDefinitions="*,*" RowDefinitions="*,*,*" Classes="info-container" HorizontalAlignment="Left">
|
||||
<avalonia:MaterialIcon Kind="Calendar" Grid.Column="0" Grid.RowSpan="2" Classes="info-icon" />
|
||||
<TextBlock Grid.Column="1" Grid.Row="0" Classes="info-title">Release date</TextBlock>
|
||||
<TextBlock Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Classes="info-body"
|
||||
Text="{CompiledBinding Release.CreatedAt, Converter={StaticResource DateTimeConverter}}" />
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Column="1" ColumnDefinitions="*,*" RowDefinitions="*,*" Classes="info-container" HorizontalAlignment="Center">
|
||||
<avalonia:MaterialIcon Kind="TickNetworkOutline" Grid.Column="0" Grid.RowSpan="2" Classes="info-icon" />
|
||||
<TextBlock Grid.Column="1" Grid.Row="0" Classes="info-title">Version</TextBlock>
|
||||
<TextBlock Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Classes="info-body"
|
||||
Cursor="Hand"
|
||||
Text="{CompiledBinding Release.Version}" />
|
||||
</Grid>
|
||||
|
||||
<Grid Grid.Column="2" ColumnDefinitions="*,*" RowDefinitions="*,*" Classes="info-container" HorizontalAlignment="Right">
|
||||
<avalonia:MaterialIcon Kind="BoxOutline" Grid.Column="0" Grid.RowSpan="2" Classes="info-icon" />
|
||||
<TextBlock Grid.Column="1" Grid.Row="0" Classes="info-title">File size</TextBlock>
|
||||
<TextBlock Grid.Column="1"
|
||||
Grid.Row="1"
|
||||
Classes="info-body"
|
||||
Text="{CompiledBinding Release.DownloadSize, Converter={StaticResource BytesToStringConverter}, Mode=OneWay}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<Border Grid.Row="1" Classes="card">
|
||||
<Grid RowDefinitions="Auto,Auto,*">
|
||||
<TextBlock Grid.Row="0" Classes="h5 no-margin">Release notes</TextBlock>
|
||||
<Border Grid.Row="1" Classes="card-separator" />
|
||||
|
||||
<mdxaml:MarkdownScrollViewer Grid.Row="2" Markdown="{CompiledBinding Release.Changelog}" MarkdownStyleName="FluentAvalonia">
|
||||
<mdxaml:MarkdownScrollViewer.Styles>
|
||||
<StyleInclude Source="/Styles/Markdown.axaml" />
|
||||
</mdxaml:MarkdownScrollViewer.Styles>
|
||||
</mdxaml:MarkdownScrollViewer>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,14 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
using Avalonia.ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.EntryReleases;
|
||||
|
||||
public partial class EntryReleaseView : ReactiveUserControl<EntryReleaseViewModel>
|
||||
{
|
||||
public EntryReleaseView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.UI.Screens.Workshop.Parameters;
|
||||
using Artemis.UI.Shared.Routing;
|
||||
using Artemis.WebClient.Workshop;
|
||||
using PropertyChanged.SourceGenerator;
|
||||
using StrawberryShake;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.EntryReleases;
|
||||
|
||||
public partial class EntryReleaseViewModel : RoutableScreen<ReleaseDetailParameters>
|
||||
{
|
||||
private readonly IWorkshopClient _client;
|
||||
[Notify] private IGetReleaseById_Release? _release;
|
||||
|
||||
public EntryReleaseViewModel(IWorkshopClient client)
|
||||
{
|
||||
_client = client;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override async Task OnNavigating(ReleaseDetailParameters parameters, NavigationArguments args, CancellationToken cancellationToken)
|
||||
{
|
||||
IOperationResult<IGetReleaseByIdResult> result = await _client.GetReleaseById.ExecuteAsync(parameters.ReleaseId, cancellationToken);
|
||||
if (result.IsErrorResult())
|
||||
return;
|
||||
|
||||
Release = result.Data?.Release;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<UserControl xmlns="https://github.com/avaloniaui"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:details="clr-namespace:Artemis.UI.Screens.Workshop.Entries.Details"
|
||||
xmlns:converters="clr-namespace:Artemis.UI.Converters"
|
||||
xmlns:sharedConverters="clr-namespace:Artemis.UI.Shared.Converters;assembly=Artemis.UI.Shared"
|
||||
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
|
||||
xmlns:workshop="clr-namespace:Artemis.WebClient.Workshop;assembly=Artemis.WebClient.Workshop"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Workshop.Entries.Details.EntryReleasesView"
|
||||
x:DataType="details:EntryReleasesViewModel">
|
||||
<UserControl.Resources>
|
||||
<converters:DateTimeConverter x:Key="DateTimeConverter" />
|
||||
<sharedConverters:BytesToStringConverter x:Key="BytesToStringConverter" />
|
||||
</UserControl.Resources>
|
||||
<StackPanel>
|
||||
<TextBlock Theme="{StaticResource SubtitleTextBlockStyle}">Releases</TextBlock>
|
||||
<Border Classes="card-separator" />
|
||||
|
||||
<Button Margin="0 0 0 5"
|
||||
HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Command="{CompiledBinding NavigateToRelease}"
|
||||
CommandParameter="{CompiledBinding LatestRelease}">
|
||||
<Grid ColumnDefinitions="Auto,*">
|
||||
<!-- Icon -->
|
||||
<avalonia:MaterialIcon Grid.Column="0" Margin="0 6" Width="50" Height="50" Kind="BoxStar"/>
|
||||
|
||||
<!-- Body -->
|
||||
<StackPanel Grid.Column="1" Margin="10 0" VerticalAlignment="Center">
|
||||
<TextBlock Text="{CompiledBinding LatestRelease.Version, FallbackValue=Version}"></TextBlock>
|
||||
<TextBlock Classes="subtitle">
|
||||
<avalonia:MaterialIcon Kind="File" />
|
||||
<Run Text="{CompiledBinding LatestRelease.DownloadSize, Converter={StaticResource BytesToStringConverter}, Mode=OneWay}"></Run>
|
||||
</TextBlock>
|
||||
<TextBlock Classes="subtitle"
|
||||
ToolTip.Tip="{CompiledBinding LatestRelease.CreatedAt, Converter={StaticResource DateTimeConverter}}">
|
||||
<avalonia:MaterialIcon Kind="Calendar" />
|
||||
<Run>Created</Run>
|
||||
<Run Text="{CompiledBinding LatestRelease.CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter='humanize'}"></Run>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Button>
|
||||
|
||||
<ItemsControl ItemsSource="{CompiledBinding OtherReleases}">
|
||||
<ItemsControl.ItemsPanel>
|
||||
<ItemsPanelTemplate>
|
||||
<StackPanel Spacing="5" />
|
||||
</ItemsPanelTemplate>
|
||||
</ItemsControl.ItemsPanel>
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate x:DataType="workshop:IRelease">
|
||||
<Button HorizontalAlignment="Stretch"
|
||||
HorizontalContentAlignment="Stretch"
|
||||
Command="{Binding $parent[details:EntryReleasesView].DataContext.NavigateToRelease}"
|
||||
CommandParameter="{CompiledBinding}">
|
||||
<Grid ColumnDefinitions="Auto,*">
|
||||
<!-- Icon -->
|
||||
<avalonia:MaterialIcon Grid.Column="0" Margin="0 6" Width="25" Height="25" Kind="Archive"/>
|
||||
|
||||
<!-- Body -->
|
||||
<StackPanel Grid.Column="1" Margin="10 0" VerticalAlignment="Center">
|
||||
<TextBlock Text="{CompiledBinding Version, FallbackValue=Version}"></TextBlock>
|
||||
<TextBlock Classes="subtitle" ToolTip.Tip="{CompiledBinding CreatedAt, Converter={StaticResource DateTimeConverter}}">
|
||||
<avalonia:MaterialIcon Kind="Calendar" />
|
||||
<Run>Created</Run>
|
||||
<Run Text="{CompiledBinding CreatedAt, Converter={StaticResource DateTimeConverter}, ConverterParameter='humanize'}"></Run>
|
||||
</TextBlock>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Button>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
</StackPanel>
|
||||
</UserControl>
|
||||
@@ -0,0 +1,13 @@
|
||||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Markup.Xaml;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.Entries.Details;
|
||||
|
||||
public partial class EntryReleasesView : UserControl
|
||||
{
|
||||
public EntryReleasesView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Artemis.UI.Shared;
|
||||
using Artemis.UI.Shared.Routing;
|
||||
using Artemis.UI.Shared.Services;
|
||||
using Artemis.UI.Shared.Services.Builders;
|
||||
using Artemis.UI.Shared.Utilities;
|
||||
using Artemis.WebClient.Workshop;
|
||||
using Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
|
||||
using Artemis.WebClient.Workshop.Models;
|
||||
using Humanizer;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.Entries.Details;
|
||||
|
||||
public class EntryReleasesViewModel : ViewModelBase
|
||||
{
|
||||
private readonly EntryInstallationHandlerFactory _factory;
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly INotificationService _notificationService;
|
||||
private readonly IRouter _router;
|
||||
|
||||
public EntryReleasesViewModel(IEntryDetails entry, EntryInstallationHandlerFactory factory, IWindowService windowService, INotificationService notificationService, IRouter router)
|
||||
{
|
||||
_factory = factory;
|
||||
_windowService = windowService;
|
||||
_notificationService = notificationService;
|
||||
_router = router;
|
||||
|
||||
Entry = entry;
|
||||
LatestRelease = Entry.Releases.MaxBy(r => r.CreatedAt);
|
||||
OtherReleases = Entry.Releases.OrderByDescending(r => r.CreatedAt).Skip(1).Take(4).Cast<IRelease>().ToList();
|
||||
|
||||
DownloadLatestRelease = ReactiveCommand.CreateFromTask(ExecuteDownloadLatestRelease);
|
||||
OnInstallationStarted = Confirm;
|
||||
NavigateToRelease = ReactiveCommand.CreateFromTask<IRelease>(ExecuteNavigateToRelease);
|
||||
}
|
||||
|
||||
public IEntryDetails Entry { get; }
|
||||
public IRelease? LatestRelease { get; }
|
||||
public List<IRelease> OtherReleases { get; }
|
||||
|
||||
public ReactiveCommand<Unit, Unit> DownloadLatestRelease { get; }
|
||||
public ReactiveCommand<IRelease, Unit> NavigateToRelease { get; }
|
||||
|
||||
public Func<IEntryDetails, IRelease, Task<bool>> OnInstallationStarted { get; set; }
|
||||
public Func<InstalledEntry, Task>? OnInstallationFinished { get; set; }
|
||||
|
||||
private async Task ExecuteNavigateToRelease(IRelease release)
|
||||
{
|
||||
switch (Entry.EntryType)
|
||||
{
|
||||
case EntryType.Profile:
|
||||
await _router.Navigate($"workshop/entries/profiles/details/{Entry.Id}/releases/{release.Id}");
|
||||
break;
|
||||
case EntryType.Layout:
|
||||
await _router.Navigate($"workshop/entries/layouts/details/{Entry.Id}/releases/{release.Id}");
|
||||
break;
|
||||
case EntryType.Plugin:
|
||||
await _router.Navigate($"workshop/entries/plugins/details/{Entry.Id}/releases/{release.Id}");
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(Entry.EntryType));
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ExecuteDownloadLatestRelease(CancellationToken cancellationToken)
|
||||
{
|
||||
if (LatestRelease == null)
|
||||
return;
|
||||
|
||||
if (await OnInstallationStarted(Entry, LatestRelease))
|
||||
return;
|
||||
|
||||
IEntryInstallationHandler installationHandler = _factory.CreateHandler(Entry.EntryType);
|
||||
EntryInstallResult result = await installationHandler.InstallAsync(Entry, LatestRelease, new Progress<StreamProgress>(), cancellationToken);
|
||||
if (result.IsSuccess && result.Entry != null)
|
||||
{
|
||||
if (OnInstallationFinished != null)
|
||||
await OnInstallationFinished(result.Entry);
|
||||
_notificationService.CreateNotification().WithTitle($"{Entry.EntryType.Humanize(LetterCasing.Sentence)} installed").WithSeverity(NotificationSeverity.Success).Show();
|
||||
}
|
||||
else
|
||||
{
|
||||
_notificationService.CreateNotification()
|
||||
.WithTitle($"Failed to install {Entry.EntryType.Humanize(LetterCasing.LowerCase)}")
|
||||
.WithMessage(result.Message)
|
||||
.WithSeverity(NotificationSeverity.Error).Show();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> Confirm(IEntryDetails entryDetails, IRelease release)
|
||||
{
|
||||
bool confirm = await _windowService.ShowConfirmContentDialog(
|
||||
"Install latest release",
|
||||
$"Are you sure you want to download and install version {release.Version} of {entryDetails.Name}?"
|
||||
);
|
||||
|
||||
return !confirm;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user