From e545d2f3daa6400b443062b9c6f26276d8983b18 Mon Sep 17 00:00:00 2001 From: Robert Date: Thu, 31 Aug 2023 22:04:57 +0200 Subject: [PATCH] Workshop - Added library view models --- src/Artemis.UI/Routing/RouteViewModel.cs | 26 ++++++++++ src/Artemis.UI/Routing/Routes.cs | 9 +++- .../Screens/Settings/SettingsTab.cs | 20 -------- .../Screens/Settings/SettingsViewModel.cs | 23 +++++---- .../Entries/EntryInstallationDialogView.axaml | 8 +++ .../EntryInstallationDialogView.axaml.cs | 13 +++++ .../EntryInstallationDialogViewModel.cs | 8 +++ .../Workshop/Home/WorkshopHomeView.axaml | 24 ++++++--- .../Library/Tabs/LibraryInstalledView.axaml | 8 +++ .../Tabs/LibraryInstalledView.axaml.cs | 14 ++++++ .../Library/Tabs/LibraryInstalledViewModel.cs | 8 +++ .../Library/Tabs/LibrarySubmissionsView.axaml | 8 +++ .../Tabs/LibrarySubmissionsView.axaml.cs | 14 ++++++ .../Tabs/LibrarySubmissionsViewModel.cs | 8 +++ .../Library/WorkshopLibraryVIew.axaml | 27 ++++++++++ .../Library/WorkshopLibraryVIew.axaml.cs | 37 ++++++++++++++ .../Library/WorkshopLibraryViewModel.cs | 50 +++++++++++++++++++ .../Profile/ProfileDetailsViewModel.cs | 8 ++- 18 files changed, 272 insertions(+), 41 deletions(-) create mode 100644 src/Artemis.UI/Routing/RouteViewModel.cs delete mode 100644 src/Artemis.UI/Screens/Settings/SettingsTab.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogView.axaml create mode 100644 src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogViewModel.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Library/Tabs/LibraryInstalledView.axaml create mode 100644 src/Artemis.UI/Screens/Workshop/Library/Tabs/LibraryInstalledView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Library/Tabs/LibraryInstalledViewModel.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Library/Tabs/LibrarySubmissionsView.axaml create mode 100644 src/Artemis.UI/Screens/Workshop/Library/Tabs/LibrarySubmissionsView.axaml.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Library/Tabs/LibrarySubmissionsViewModel.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Library/WorkshopLibraryVIew.axaml create mode 100644 src/Artemis.UI/Screens/Workshop/Library/WorkshopLibraryVIew.axaml.cs create mode 100644 src/Artemis.UI/Screens/Workshop/Library/WorkshopLibraryViewModel.cs diff --git a/src/Artemis.UI/Routing/RouteViewModel.cs b/src/Artemis.UI/Routing/RouteViewModel.cs new file mode 100644 index 000000000..5ecc0f356 --- /dev/null +++ b/src/Artemis.UI/Routing/RouteViewModel.cs @@ -0,0 +1,26 @@ +using System; + +namespace Artemis.UI.Routing; + +public class RouteViewModel +{ + public RouteViewModel(string path, string name) + { + Path = path; + Name = name; + } + + public string Path { get; } + public string Name { get; } + + public bool Matches(string path) + { + return path.StartsWith(Path, StringComparison.InvariantCultureIgnoreCase); + } + + /// + public override string ToString() + { + return Name; + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Routing/Routes.cs b/src/Artemis.UI/Routing/Routes.cs index ba0949669..4baf359a6 100644 --- a/src/Artemis.UI/Routing/Routes.cs +++ b/src/Artemis.UI/Routing/Routes.cs @@ -8,6 +8,8 @@ using Artemis.UI.Screens.SurfaceEditor; using Artemis.UI.Screens.Workshop; using Artemis.UI.Screens.Workshop.Home; using Artemis.UI.Screens.Workshop.Layout; +using Artemis.UI.Screens.Workshop.Library; +using Artemis.UI.Screens.Workshop.Library.Tabs; using Artemis.UI.Screens.Workshop.Profile; using Artemis.UI.Shared.Routing; @@ -28,7 +30,12 @@ public static class Routes new RouteRegistration("profiles/{page:int}"), new RouteRegistration("profiles/{entryId:guid}"), new RouteRegistration("layouts/{page:int}"), - new RouteRegistration("layouts/{entryId:guid}") + new RouteRegistration("layouts/{entryId:guid}"), + new RouteRegistration("library") {Children = new List() + { + new RouteRegistration("installed"), + new RouteRegistration("submissions"), + }} } }, #endif diff --git a/src/Artemis.UI/Screens/Settings/SettingsTab.cs b/src/Artemis.UI/Screens/Settings/SettingsTab.cs deleted file mode 100644 index f25200fca..000000000 --- a/src/Artemis.UI/Screens/Settings/SettingsTab.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; - -namespace Artemis.UI.Screens.Settings; - -public class SettingsTab -{ - public SettingsTab(string path, string name) - { - Path = path; - Name = name; - } - - public string Path { get; set; } - public string Name { get; set; } - - public bool Matches(string path) - { - return path.StartsWith($"settings/{Path}", StringComparison.InvariantCultureIgnoreCase); - } -} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Settings/SettingsViewModel.cs b/src/Artemis.UI/Screens/Settings/SettingsViewModel.cs index 2a747e5f9..561c4e3d6 100644 --- a/src/Artemis.UI/Screens/Settings/SettingsViewModel.cs +++ b/src/Artemis.UI/Screens/Settings/SettingsViewModel.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Reactive.Disposables; using System.Threading; using System.Threading.Tasks; +using Artemis.UI.Routing; using Artemis.UI.Shared; using Artemis.UI.Shared.Routing; using ReactiveUI; @@ -13,30 +14,30 @@ namespace Artemis.UI.Screens.Settings; public class SettingsViewModel : RoutableScreen, IMainScreenViewModel { private readonly IRouter _router; - private SettingsTab? _selectedTab; + private RouteViewModel? _selectedTab; public SettingsViewModel(IRouter router) { _router = router; - SettingTabs = new ObservableCollection + SettingTabs = new ObservableCollection { - new("general", "General"), - new("plugins", "Plugins"), - new("devices", "Devices"), - new("releases", "Releases"), - new("about", "About"), + new("settings/general", "General"), + new("settings/plugins", "Plugins"), + new("settings/devices", "Devices"), + new("settings/releases", "Releases"), + new("settings/about", "About"), }; // Navigate on tab change this.WhenActivated(d => this.WhenAnyValue(vm => vm.SelectedTab) .WhereNotNull() - .Subscribe(s => _router.Navigate($"settings/{s.Path}", new RouterNavigationOptions {IgnoreOnPartialMatch = true})) + .Subscribe(s => _router.Navigate(s.Path, new RouterNavigationOptions {IgnoreOnPartialMatch = true})) .DisposeWith(d)); } - public ObservableCollection SettingTabs { get; } + public ObservableCollection SettingTabs { get; } - public SettingsTab? SelectedTab + public RouteViewModel? SelectedTab { get => _selectedTab; set => RaiseAndSetIfChanged(ref _selectedTab, value); @@ -52,6 +53,6 @@ public class SettingsViewModel : RoutableScreen, IMain // Always show a tab, if there is none forward to the first if (SelectedTab == null) - await _router.Navigate($"settings/{SettingTabs.First().Path}"); + await _router.Navigate(SettingTabs.First().Path); } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogView.axaml b/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogView.axaml new file mode 100644 index 000000000..af75201c2 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogView.axaml @@ -0,0 +1,8 @@ + + Welcome to Avalonia! + diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogView.axaml.cs new file mode 100644 index 000000000..b228f4b0f --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogView.axaml.cs @@ -0,0 +1,13 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; + +namespace Artemis.UI.Screens.Workshop.Entries; + +public partial class EntryInstallationDialogView : UserControl +{ + public EntryInstallationDialogView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogViewModel.cs b/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogViewModel.cs new file mode 100644 index 000000000..7b7cbca67 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Entries/EntryInstallationDialogViewModel.cs @@ -0,0 +1,8 @@ +using Artemis.UI.Shared; + +namespace Artemis.UI.Screens.Workshop.Entries; + +public class EntryInstallationDialogViewModel : ContentDialogViewModelBase +{ + +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Home/WorkshopHomeView.axaml b/src/Artemis.UI/Screens/Workshop/Home/WorkshopHomeView.axaml index 0695a7554..c3b4593f6 100644 --- a/src/Artemis.UI/Screens/Workshop/Home/WorkshopHomeView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Home/WorkshopHomeView.axaml @@ -41,14 +41,6 @@ - - + + + + diff --git a/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibraryInstalledView.axaml b/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibraryInstalledView.axaml new file mode 100644 index 000000000..208718542 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibraryInstalledView.axaml @@ -0,0 +1,8 @@ + + Installed entries management here 🫡 + diff --git a/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibraryInstalledView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibraryInstalledView.axaml.cs new file mode 100644 index 000000000..b98518ae5 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibraryInstalledView.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.ReactiveUI; + +namespace Artemis.UI.Screens.Workshop.Library.Tabs; + +public partial class LibraryInstalledView : ReactiveUserControl +{ + public LibraryInstalledView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibraryInstalledViewModel.cs b/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibraryInstalledViewModel.cs new file mode 100644 index 000000000..b615c18b1 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibraryInstalledViewModel.cs @@ -0,0 +1,8 @@ +using Artemis.UI.Shared; + +namespace Artemis.UI.Screens.Workshop.Library.Tabs; + +public class LibraryInstalledViewModel : ActivatableViewModelBase +{ + +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibrarySubmissionsView.axaml b/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibrarySubmissionsView.axaml new file mode 100644 index 000000000..4ab44f1fc --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibrarySubmissionsView.axaml @@ -0,0 +1,8 @@ + + Submission management here 😗 + diff --git a/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibrarySubmissionsView.axaml.cs b/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibrarySubmissionsView.axaml.cs new file mode 100644 index 000000000..baf08341b --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibrarySubmissionsView.axaml.cs @@ -0,0 +1,14 @@ +using Avalonia; +using Avalonia.Controls; +using Avalonia.Markup.Xaml; +using Avalonia.ReactiveUI; + +namespace Artemis.UI.Screens.Workshop.Library.Tabs; + +public partial class LibrarySubmissionsView : ReactiveUserControl +{ + public LibrarySubmissionsView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibrarySubmissionsViewModel.cs b/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibrarySubmissionsViewModel.cs new file mode 100644 index 000000000..cbb98d615 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Library/Tabs/LibrarySubmissionsViewModel.cs @@ -0,0 +1,8 @@ +using Artemis.UI.Shared; + +namespace Artemis.UI.Screens.Workshop.Library.Tabs; + +public class LibrarySubmissionsViewModel : ActivatableViewModelBase +{ + +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Library/WorkshopLibraryVIew.axaml b/src/Artemis.UI/Screens/Workshop/Library/WorkshopLibraryVIew.axaml new file mode 100644 index 000000000..f6e2f5740 --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Library/WorkshopLibraryVIew.axaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + diff --git a/src/Artemis.UI/Screens/Workshop/Library/WorkshopLibraryVIew.axaml.cs b/src/Artemis.UI/Screens/Workshop/Library/WorkshopLibraryVIew.axaml.cs new file mode 100644 index 000000000..f2faa7e5a --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Library/WorkshopLibraryVIew.axaml.cs @@ -0,0 +1,37 @@ +using System; +using System.Reactive.Disposables; +using Artemis.UI.Shared; +using Avalonia.ReactiveUI; +using Avalonia.Threading; +using FluentAvalonia.UI.Media.Animation; +using FluentAvalonia.UI.Navigation; +using ReactiveUI; + +namespace Artemis.UI.Screens.Workshop.Library; + +public partial class WorkshopLibraryView : ReactiveUserControl +{ + private int _lastIndex; + + public WorkshopLibraryView() + { + InitializeComponent(); + this.WhenActivated(d => { ViewModel.WhenAnyValue(vm => vm.Screen).WhereNotNull().Subscribe(Navigate).DisposeWith(d); }); + } + + private void Navigate(ViewModelBase viewModel) + { + Dispatcher.UIThread.Invoke(() => + { + if (ViewModel == null) + return; + + SlideNavigationTransitionInfo transitionInfo = new() + { + Effect = ViewModel.Tabs.IndexOf(ViewModel.SelectedTab) > _lastIndex ? SlideNavigationTransitionEffect.FromRight : SlideNavigationTransitionEffect.FromLeft + }; + TabFrame.NavigateFromObject(viewModel, new FrameNavigationOptions {TransitionInfoOverride = transitionInfo}); + _lastIndex = ViewModel.Tabs.IndexOf(ViewModel.SelectedTab); + }); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Library/WorkshopLibraryViewModel.cs b/src/Artemis.UI/Screens/Workshop/Library/WorkshopLibraryViewModel.cs new file mode 100644 index 000000000..707d22cde --- /dev/null +++ b/src/Artemis.UI/Screens/Workshop/Library/WorkshopLibraryViewModel.cs @@ -0,0 +1,50 @@ +using System.Collections.ObjectModel; +using System.Linq; +using System.Reactive.Disposables; +using System.Threading; +using System.Threading.Tasks; +using Artemis.UI.Routing; +using Artemis.UI.Shared; +using Artemis.UI.Shared.Routing; +using Artemis.WebClient.Workshop; +using ReactiveUI; +using System; + +namespace Artemis.UI.Screens.Workshop.Library; + +public class WorkshopLibraryViewModel : RoutableScreen, IWorkshopViewModel +{ + private RouteViewModel? _selectedTab; + + /// + public WorkshopLibraryViewModel(IRouter router) + { + Tabs = new ObservableCollection + { + new("workshop/library/installed", "Installed"), + new("workshop/library/submissions", "Submissions") + }; + + // Navigate on tab change + this.WhenActivated(d => this.WhenAnyValue(vm => vm.SelectedTab) + .WhereNotNull() + .Subscribe(s => router.Navigate(s.Path, new RouterNavigationOptions {IgnoreOnPartialMatch = true})) + .DisposeWith(d)); + } + + public EntryType? EntryType => null; + public ObservableCollection Tabs { get; } + + public RouteViewModel? SelectedTab + { + get => _selectedTab; + set => RaiseAndSetIfChanged(ref _selectedTab, value); + } + + public override async Task OnNavigating(NavigationArguments args, CancellationToken cancellationToken) + { + SelectedTab = Tabs.FirstOrDefault(t => t.Matches(args.Path)); + if (SelectedTab == null) + await args.Router.Navigate(Tabs.First().Path); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs index f15ca7cf6..83e5280f3 100644 --- a/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Profile/ProfileDetailsViewModel.cs @@ -23,14 +23,16 @@ public class ProfileDetailsViewModel : RoutableScreen _updatedAt; private IGetEntryById_Entry? _entry; - public ProfileDetailsViewModel(IWorkshopClient client, ProfileEntryDownloadHandler downloadHandler, INotificationService notificationService) + public ProfileDetailsViewModel(IWorkshopClient client, ProfileEntryDownloadHandler downloadHandler, INotificationService notificationService, IWindowService windowService) { _client = client; _downloadHandler = downloadHandler; _notificationService = notificationService; + _windowService = windowService; _updatedAt = this.WhenAnyValue(vm => vm.Entry).Select(e => e?.LatestRelease?.CreatedAt ?? e?.CreatedAt).ToProperty(this, vm => vm.UpdatedAt); DownloadLatestRelease = ReactiveCommand.CreateFromTask(ExecuteDownloadLatestRelease); @@ -65,6 +67,10 @@ public class ProfileDetailsViewModel : RoutableScreen result = await _downloadHandler.InstallProfileAsync(Entry.LatestRelease.Id, new Progress(), cancellationToken); if (result.IsSuccess) _notificationService.CreateNotification().WithTitle("Profile installed").WithSeverity(NotificationSeverity.Success).Show();