diff --git a/src/Artemis.UI.Shared/Routing/Router/IRouter.cs b/src/Artemis.UI.Shared/Routing/Router/IRouter.cs
index e23be6eae..2901fdbaf 100644
--- a/src/Artemis.UI.Shared/Routing/Router/IRouter.cs
+++ b/src/Artemis.UI.Shared/Routing/Router/IRouter.cs
@@ -45,6 +45,12 @@ public interface IRouter
///
/// A task containing a boolean value which indicates whether there was a forward path to go back to.
Task GoForward();
+
+ ///
+ /// Asynchronously navigates upwards to the parent route.
+ ///
+ ///
+ Task GoUp();
///
/// Clears the navigation history.
diff --git a/src/Artemis.UI.Shared/Routing/Router/Router.cs b/src/Artemis.UI.Shared/Routing/Router/Router.cs
index 39e067ec5..3f1263a29 100644
--- a/src/Artemis.UI.Shared/Routing/Router/Router.cs
+++ b/src/Artemis.UI.Shared/Routing/Router/Router.cs
@@ -161,6 +161,28 @@ internal class Router : CorePropertyChanged, IRouter, IDisposable
return true;
}
+ ///
+ public async Task GoUp()
+ {
+ string? currentPath = _currentRouteSubject.Value;
+
+ // Keep removing segments until we find a parent route that resolves
+ while (currentPath != null && currentPath.Contains('/'))
+ {
+ string parentPath = currentPath[..currentPath.LastIndexOf('/')];
+ RouteResolution resolution = Resolve(parentPath);
+ if (resolution.Success)
+ {
+ await Navigate(parentPath, new RouterNavigationOptions {AddToHistory = false});
+ return true;
+ }
+
+ currentPath = parentPath;
+ }
+
+ return false;
+ }
+
///
public void ClearHistory()
{
diff --git a/src/Artemis.UI/Screens/Settings/Updating/ReleaseDetailsView.axaml b/src/Artemis.UI/Screens/Settings/Updating/ReleaseDetailsView.axaml
index 34258aae0..cdb9738aa 100644
--- a/src/Artemis.UI/Screens/Settings/Updating/ReleaseDetailsView.axaml
+++ b/src/Artemis.UI/Screens/Settings/Updating/ReleaseDetailsView.axaml
@@ -124,7 +124,7 @@
-
+
File size
-
+
- Release info
+
+
+ Release info
+
+
+
+
+
+
+
+
+
+
+
+
-
-
- Release date
-
-
-
-
+
Version
+
+
+ Release date
+
+
+
-
+
File size
Release notes
-
+
+ There are no release notes for this release.
+
+
diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseViewModel.cs b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseViewModel.cs
index d92912c5c..a44cc6e8d 100644
--- a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleaseViewModel.cs
@@ -1,8 +1,13 @@
+using System;
using System.Threading;
using System.Threading.Tasks;
using Artemis.UI.Screens.Workshop.Parameters;
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 PropertyChanged.SourceGenerator;
using StrawberryShake;
@@ -11,20 +16,84 @@ namespace Artemis.UI.Screens.Workshop.EntryReleases;
public partial class EntryReleaseViewModel : RoutableScreen
{
private readonly IWorkshopClient _client;
- [Notify] private IGetReleaseById_Release? _release;
+ private readonly IRouter _router;
+ private readonly INotificationService _notificationService;
+ private readonly IWindowService _windowService;
+ private readonly EntryInstallationHandlerFactory _factory;
+ private readonly Progress _progress = new();
- public EntryReleaseViewModel(IWorkshopClient client)
+ [Notify] private IGetReleaseById_Release? _release;
+ [Notify] private float _installProgress;
+ [Notify] private bool _installationInProgress;
+
+ private CancellationTokenSource? _cts;
+
+ public EntryReleaseViewModel(IWorkshopClient client, IRouter router, INotificationService notificationService, IWindowService windowService, EntryInstallationHandlerFactory factory)
{
_client = client;
+ _router = router;
+ _notificationService = notificationService;
+ _windowService = windowService;
+ _factory = factory;
+ _progress.ProgressChanged += (_, f) => InstallProgress = f.ProgressPercentage;
+ }
+
+ public async Task Close()
+ {
+ await _router.GoUp();
+ }
+
+ public async Task Install()
+ {
+ if (Release == null)
+ return;
+
+ _cts = new CancellationTokenSource();
+ InstallProgress = 0;
+ InstallationInProgress = true;
+ try
+ {
+ IEntryInstallationHandler handler = _factory.CreateHandler(Release.Entry.EntryType);
+ EntryInstallResult result = await handler.InstallAsync(Release.Entry, Release, _progress, _cts.Token);
+ if (result.IsSuccess)
+ _notificationService.CreateNotification().WithTitle("Installation succeeded").WithSeverity(NotificationSeverity.Success).Show();
+ else if (!_cts.IsCancellationRequested)
+ _notificationService.CreateNotification().WithTitle("Installation failed").WithMessage(result.Message).WithSeverity(NotificationSeverity.Error).Show();
+ }
+ catch (Exception e)
+ {
+ _windowService.ShowExceptionDialog("Failed to install workshop entry", e);
+ }
+ finally
+ {
+ InstallationInProgress = false;
+ }
+ }
+
+ public void Cancel()
+ {
+ _cts?.Cancel();
}
///
public override async Task OnNavigating(ReleaseDetailParameters parameters, NavigationArguments args, CancellationToken cancellationToken)
{
IOperationResult result = await _client.GetReleaseById.ExecuteAsync(parameters.ReleaseId, cancellationToken);
- if (result.IsErrorResult())
- return;
-
Release = result.Data?.Release;
}
+
+ #region Overrides of RoutableScreen
+
+ ///
+ public override Task OnClosing(NavigationArguments args)
+ {
+ if (!InstallationInProgress)
+ return Task.CompletedTask;
+
+ args.Cancel();
+ _notificationService.CreateNotification().WithMessage("Please wait for the installation to finish").Show();
+ return Task.CompletedTask;
+ }
+
+ #endregion
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml
index 899065ec7..cf62f0e5b 100644
--- a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml
+++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml
@@ -2,78 +2,38 @@
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"
+ xmlns:entryReleases="clr-namespace:Artemis.UI.Screens.Workshop.EntryReleases"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
- x:Class="Artemis.UI.Screens.Workshop.Entries.Details.EntryReleasesView"
- x:DataType="details:EntryReleasesViewModel">
+ x:Class="Artemis.UI.Screens.Workshop.EntryReleases.EntryReleasesView"
+ x:DataType="entryReleases:EntryReleasesViewModel">
+
Releases
-
-
-
-
-
-
-
-
-
+
+
-
+
+
+
+
+
+ Created
+
+
+
+
-
-
-
+
+
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml.cs b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml.cs
index 523edc7e5..39b603187 100644
--- a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml.cs
+++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesView.axaml.cs
@@ -1,10 +1,9 @@
-using Avalonia;
using Avalonia.Controls;
-using Avalonia.Markup.Xaml;
+using Avalonia.ReactiveUI;
-namespace Artemis.UI.Screens.Workshop.Entries.Details;
+namespace Artemis.UI.Screens.Workshop.EntryReleases;
-public partial class EntryReleasesView : UserControl
+public partial class EntryReleasesView : ReactiveUserControl
{
public EntryReleasesView()
{
diff --git a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesViewModel.cs b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesViewModel.cs
index 4ec497e37..c21b841dc 100644
--- a/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/EntryReleases/EntryReleasesViewModel.cs
@@ -2,51 +2,49 @@
using System.Collections.Generic;
using System.Linq;
using System.Reactive;
-using System.Threading;
+using System.Reactive.Disposables;
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 PropertyChanged.SourceGenerator;
using ReactiveUI;
-namespace Artemis.UI.Screens.Workshop.Entries.Details;
+namespace Artemis.UI.Screens.Workshop.EntryReleases;
-public class EntryReleasesViewModel : ViewModelBase
+public partial class EntryReleasesViewModel : ActivatableViewModelBase
{
- private readonly EntryInstallationHandlerFactory _factory;
- private readonly IWindowService _windowService;
- private readonly INotificationService _notificationService;
private readonly IRouter _router;
+ [Notify] private IRelease? _selectedRelease;
- public EntryReleasesViewModel(IEntryDetails entry, EntryInstallationHandlerFactory factory, IWindowService windowService, INotificationService notificationService, IRouter router)
+ public EntryReleasesViewModel(IEntryDetails entry, 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().ToList();
-
- DownloadLatestRelease = ReactiveCommand.CreateFromTask(ExecuteDownloadLatestRelease);
- OnInstallationStarted = Confirm;
+ Releases = Entry.Releases.OrderByDescending(r => r.CreatedAt).Take(5).Cast().ToList();
NavigateToRelease = ReactiveCommand.CreateFromTask(ExecuteNavigateToRelease);
+
+ this.WhenActivated(d =>
+ {
+ router.CurrentPath.Subscribe(p => SelectedRelease = p != null && p.Contains("releases") && float.TryParse(p.Split('/').Last(), out float releaseId)
+ ? Releases.FirstOrDefault(r => r.Id == releaseId)
+ : null)
+ .DisposeWith(d);
+
+ this.WhenAnyValue(vm => vm.SelectedRelease)
+ .WhereNotNull()
+ .Subscribe(s => ExecuteNavigateToRelease(s))
+ .DisposeWith(d);
+ });
}
public IEntryDetails Entry { get; }
- public IRelease? LatestRelease { get; }
- public List OtherReleases { get; }
+ public List Releases { get; }
- public ReactiveCommand DownloadLatestRelease { get; }
public ReactiveCommand NavigateToRelease { get; }
-
+
public Func> OnInstallationStarted { get; set; }
public Func? OnInstallationFinished { get; set; }
@@ -67,39 +65,4 @@ public class EntryReleasesViewModel : ViewModelBase
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(), 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 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;
- }
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsViewModel.cs b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsViewModel.cs
index 69853e0f9..c03896bf8 100644
--- a/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Layout/LayoutDetailsViewModel.cs
@@ -7,6 +7,7 @@ using System.Threading.Tasks;
using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Screens.Workshop.Entries.Details;
+using Artemis.UI.Screens.Workshop.EntryReleases;
using Artemis.UI.Screens.Workshop.Layout.Dialogs;
using Artemis.UI.Screens.Workshop.Parameters;
using Artemis.UI.Shared.Routing;
@@ -40,13 +41,15 @@ public partial class LayoutDetailsViewModel : RoutableHostScreen getEntryReleasesViewModel,
Func getEntryImagesViewModel)
{
- LayoutDescriptionViewModel = layoutDescriptionViewModel;
_client = client;
_deviceService = deviceService;
_windowService = windowService;
_getEntryInfoViewModel = getEntryInfoViewModel;
_getEntryReleasesViewModel = getEntryReleasesViewModel;
_getEntryImagesViewModel = getEntryImagesViewModel;
+
+ LayoutDescriptionViewModel = layoutDescriptionViewModel;
+ RecycleScreen = false;
}
public LayoutDescriptionViewModel LayoutDescriptionViewModel { get; }
diff --git a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsViewModel.cs b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsViewModel.cs
index b2c222fbb..a1b6e0939 100644
--- a/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Plugins/PluginDetailsViewModel.cs
@@ -8,6 +8,7 @@ using Artemis.Core;
using Artemis.Core.Services;
using Artemis.UI.Screens.Workshop.Entries.Details;
using Artemis.UI.Screens.Workshop.Entries.List;
+using Artemis.UI.Screens.Workshop.EntryReleases;
using Artemis.UI.Screens.Workshop.Parameters;
using Artemis.UI.Screens.Workshop.Plugins.Dialogs;
using Artemis.UI.Shared.Routing;
@@ -41,13 +42,15 @@ public partial class PluginDetailsViewModel : RoutableHostScreen getEntryReleasesViewModel,
Func getEntryImagesViewModel)
{
- PluginDescriptionViewModel = pluginDescriptionViewModel;
_client = client;
_windowService = windowService;
_pluginManagementService = pluginManagementService;
_getEntryInfoViewModel = getEntryInfoViewModel;
_getEntryReleasesViewModel = getEntryReleasesViewModel;
_getEntryImagesViewModel = getEntryImagesViewModel;
+
+ PluginDescriptionViewModel = pluginDescriptionViewModel;
+ RecycleScreen = false;
}
public PluginDescriptionViewModel PluginDescriptionViewModel { get; }
diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs
index 1c26abb8d..bd08e49d9 100644
--- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs
+++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs
@@ -5,6 +5,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Artemis.UI.Screens.Workshop.Entries.Details;
+using Artemis.UI.Screens.Workshop.EntryReleases;
using Artemis.UI.Screens.Workshop.Parameters;
using Artemis.UI.Shared.Routing;
using Artemis.WebClient.Workshop;
@@ -31,12 +32,13 @@ public partial class ProfileDetailsViewModel : RoutableHostScreen getEntryReleasesViewModel,
Func getEntryImagesViewModel)
{
- ProfileDescriptionViewModel = profileDescriptionViewModel;
-
_client = client;
_getEntryInfoViewModel = getEntryInfoViewModel;
_getEntryReleasesViewModel = getEntryReleasesViewModel;
_getEntryImagesViewModel = getEntryImagesViewModel;
+
+ ProfileDescriptionViewModel = profileDescriptionViewModel;
+ RecycleScreen = false;
}
public ProfileDescriptionViewModel ProfileDescriptionViewModel { get; }
diff --git a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/IEntryInstallationHandler.cs b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/IEntryInstallationHandler.cs
index aacd0d580..555ed11c4 100644
--- a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/IEntryInstallationHandler.cs
+++ b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/IEntryInstallationHandler.cs
@@ -6,6 +6,6 @@ namespace Artemis.WebClient.Workshop.Handlers.InstallationHandlers;
public interface IEntryInstallationHandler
{
- Task InstallAsync(IEntryDetails entry, IRelease release, Progress progress, CancellationToken cancellationToken);
+ Task InstallAsync(IEntrySummary entry, IRelease release, Progress progress, CancellationToken cancellationToken);
Task UninstallAsync(InstalledEntry installedEntry, CancellationToken cancellationToken);
}
\ No newline at end of file
diff --git a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/LayoutEntryInstallationHandler.cs b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/LayoutEntryInstallationHandler.cs
index 3014950f7..4e8b469bd 100644
--- a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/LayoutEntryInstallationHandler.cs
+++ b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/LayoutEntryInstallationHandler.cs
@@ -25,7 +25,7 @@ public class LayoutEntryInstallationHandler : IEntryInstallationHandler
_defaultLayoutProvider = defaultLayoutProvider;
}
- public async Task InstallAsync(IEntryDetails entry, IRelease release, Progress progress, CancellationToken cancellationToken)
+ public async Task InstallAsync(IEntrySummary entry, IRelease release, Progress progress, CancellationToken cancellationToken)
{
using MemoryStream stream = new();
diff --git a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/PluginEntryInstallationHandler.cs b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/PluginEntryInstallationHandler.cs
index 592b13037..476b658dc 100644
--- a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/PluginEntryInstallationHandler.cs
+++ b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/PluginEntryInstallationHandler.cs
@@ -22,7 +22,7 @@ public class PluginEntryInstallationHandler : IEntryInstallationHandler
_pluginManagementService = pluginManagementService;
}
- public async Task InstallAsync(IEntryDetails entry, IRelease release, Progress progress, CancellationToken cancellationToken)
+ public async Task InstallAsync(IEntrySummary entry, IRelease release, Progress progress, CancellationToken cancellationToken)
{
// Ensure there is an installed entry
InstalledEntry? installedEntry = _workshopService.GetInstalledEntry(entry.Id);
diff --git a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/ProfileEntryInstallationHandler.cs b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/ProfileEntryInstallationHandler.cs
index 1219f84d6..1264c43b7 100644
--- a/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/ProfileEntryInstallationHandler.cs
+++ b/src/Artemis.WebClient.Workshop/Handlers/InstallationHandlers/Implementations/ProfileEntryInstallationHandler.cs
@@ -20,7 +20,7 @@ public class ProfileEntryInstallationHandler : IEntryInstallationHandler
_workshopService = workshopService;
}
- public async Task InstallAsync(IEntryDetails entry, IRelease release, Progress progress, CancellationToken cancellationToken)
+ public async Task InstallAsync(IEntrySummary entry, IRelease release, Progress progress, CancellationToken cancellationToken)
{
using MemoryStream stream = new();
diff --git a/src/Artemis.WebClient.Workshop/Models/InstalledEntry.cs b/src/Artemis.WebClient.Workshop/Models/InstalledEntry.cs
index ce90444b0..401e5495e 100644
--- a/src/Artemis.WebClient.Workshop/Models/InstalledEntry.cs
+++ b/src/Artemis.WebClient.Workshop/Models/InstalledEntry.cs
@@ -16,7 +16,7 @@ public class InstalledEntry
Load();
}
- public InstalledEntry(IEntryDetails entry, IRelease release)
+ public InstalledEntry(IEntrySummary entry, IRelease release)
{
Entity = new EntryEntity();
diff --git a/src/Artemis.WebClient.Workshop/Queries/GetReleaseById.graphql b/src/Artemis.WebClient.Workshop/Queries/GetReleaseById.graphql
index 4b9aaa912..84ee9604a 100644
--- a/src/Artemis.WebClient.Workshop/Queries/GetReleaseById.graphql
+++ b/src/Artemis.WebClient.Workshop/Queries/GetReleaseById.graphql
@@ -2,5 +2,8 @@ query GetReleaseById($id: Long!) {
release(id: $id) {
...release
changelog
+ entry {
+ ...entrySummary
+ }
}
}
\ No newline at end of file