mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-12 13:28:33 +00:00
Workshop - Added library view models
This commit is contained in:
parent
c75e839756
commit
e545d2f3da
26
src/Artemis.UI/Routing/RouteViewModel.cs
Normal file
26
src/Artemis.UI/Routing/RouteViewModel.cs
Normal file
@ -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);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string ToString()
|
||||
{
|
||||
return Name;
|
||||
}
|
||||
}
|
||||
@ -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<ProfileListViewModel>("profiles/{page:int}"),
|
||||
new RouteRegistration<ProfileDetailsViewModel>("profiles/{entryId:guid}"),
|
||||
new RouteRegistration<LayoutListViewModel>("layouts/{page:int}"),
|
||||
new RouteRegistration<LayoutDetailsViewModel>("layouts/{entryId:guid}")
|
||||
new RouteRegistration<LayoutDetailsViewModel>("layouts/{entryId:guid}"),
|
||||
new RouteRegistration<WorkshopLibraryViewModel>("library") {Children = new List<IRouterRegistration>()
|
||||
{
|
||||
new RouteRegistration<LibraryInstalledViewModel>("installed"),
|
||||
new RouteRegistration<LibrarySubmissionsViewModel>("submissions"),
|
||||
}}
|
||||
}
|
||||
},
|
||||
#endif
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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<ActivatableViewModelBase>, IMainScreenViewModel
|
||||
{
|
||||
private readonly IRouter _router;
|
||||
private SettingsTab? _selectedTab;
|
||||
private RouteViewModel? _selectedTab;
|
||||
|
||||
public SettingsViewModel(IRouter router)
|
||||
{
|
||||
_router = router;
|
||||
SettingTabs = new ObservableCollection<SettingsTab>
|
||||
SettingTabs = new ObservableCollection<RouteViewModel>
|
||||
{
|
||||
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<SettingsTab> SettingTabs { get; }
|
||||
public ObservableCollection<RouteViewModel> SettingTabs { get; }
|
||||
|
||||
public SettingsTab? SelectedTab
|
||||
public RouteViewModel? SelectedTab
|
||||
{
|
||||
get => _selectedTab;
|
||||
set => RaiseAndSetIfChanged(ref _selectedTab, value);
|
||||
@ -52,6 +53,6 @@ public class SettingsViewModel : RoutableScreen<ActivatableViewModelBase>, 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);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
<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"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Workshop.Entries.EntryInstallationDialogView">
|
||||
Welcome to Avalonia!
|
||||
</UserControl>
|
||||
@ -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();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
using Artemis.UI.Shared;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.Entries;
|
||||
|
||||
public class EntryInstallationDialogViewModel : ContentDialogViewModelBase
|
||||
{
|
||||
|
||||
}
|
||||
@ -41,14 +41,6 @@
|
||||
|
||||
<StackPanel Margin="30 -75 30 0" Grid.Row="1">
|
||||
<StackPanel Spacing="10" Orientation="Horizontal" VerticalAlignment="Top">
|
||||
<Button Width="150" Height="180" Command="{CompiledBinding AddSubmission}" VerticalContentAlignment="Top">
|
||||
<StackPanel>
|
||||
<avalonia:MaterialIcon Kind="CloudUpload" HorizontalAlignment="Left" Width="60" Height="60" Margin="0 5" />
|
||||
<TextBlock TextWrapping="Wrap" FontSize="16" Margin="0 5">Add submission</TextBlock>
|
||||
<TextBlock TextWrapping="Wrap" FontSize="12" Opacity="0.8">Upload your own creations to the workshop!</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Width="150" Height="180" Command="{CompiledBinding Navigate}" CommandParameter="workshop/profiles/1" VerticalContentAlignment="Top">
|
||||
<StackPanel>
|
||||
<avalonia:MaterialIcon Kind="FolderVideo" HorizontalAlignment="Left" Width="60" Height="60" Margin="0 5" />
|
||||
@ -64,6 +56,22 @@
|
||||
<TextBlock TextWrapping="Wrap" FontSize="12" Opacity="0.8">Layouts make your devices look great in the editor.</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Width="150" Height="180" Command="{CompiledBinding Navigate}" CommandParameter="workshop/library" VerticalContentAlignment="Top">
|
||||
<StackPanel>
|
||||
<avalonia:MaterialIcon Kind="Bookshelf" HorizontalAlignment="Left" Width="60" Height="60" Margin="0 5" />
|
||||
<TextBlock TextWrapping="Wrap" FontSize="16" Margin="0 5">Library</TextBlock>
|
||||
<TextBlock TextWrapping="Wrap" FontSize="12" Opacity="0.8">Manage your submissions and downloaded content.</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
<Button Width="150" Height="180" Command="{CompiledBinding AddSubmission}" VerticalContentAlignment="Top">
|
||||
<StackPanel>
|
||||
<avalonia:MaterialIcon Kind="CloudUpload" HorizontalAlignment="Left" Width="60" Height="60" Margin="0 5" />
|
||||
<TextBlock TextWrapping="Wrap" FontSize="16" Margin="0 5">Add submission</TextBlock>
|
||||
<TextBlock TextWrapping="Wrap" FontSize="12" Opacity="0.8">Upload your own creations to the workshop!</TextBlock>
|
||||
</StackPanel>
|
||||
</Button>
|
||||
|
||||
|
||||
</StackPanel>
|
||||
|
||||
@ -0,0 +1,8 @@
|
||||
<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"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Workshop.Library.Tabs.LibraryInstalledView">
|
||||
Installed entries management here 🫡
|
||||
</UserControl>
|
||||
@ -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<LibraryInstalledViewModel>
|
||||
{
|
||||
public LibraryInstalledView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
using Artemis.UI.Shared;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.Library.Tabs;
|
||||
|
||||
public class LibraryInstalledViewModel : ActivatableViewModelBase
|
||||
{
|
||||
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
<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"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Workshop.Library.Tabs.LibrarySubmissionsView">
|
||||
Submission management here 😗
|
||||
</UserControl>
|
||||
@ -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<LibrarySubmissionsViewModel>
|
||||
{
|
||||
public LibrarySubmissionsView()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
using Artemis.UI.Shared;
|
||||
|
||||
namespace Artemis.UI.Screens.Workshop.Library.Tabs;
|
||||
|
||||
public class LibrarySubmissionsViewModel : ActivatableViewModelBase
|
||||
{
|
||||
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
<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:ui="clr-namespace:FluentAvalonia.UI.Controls;assembly=FluentAvalonia"
|
||||
xmlns:library="clr-namespace:Artemis.UI.Screens.Workshop.Library"
|
||||
xmlns:routing="clr-namespace:Artemis.UI.Routing"
|
||||
xmlns:ui1="clr-namespace:Artemis.UI"
|
||||
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
|
||||
x:Class="Artemis.UI.Screens.Workshop.Library.WorkshopLibraryView"
|
||||
x:DataType="library:WorkshopLibraryViewModel">
|
||||
<ui:NavigationView PaneDisplayMode="Top" MenuItemsSource="{CompiledBinding Tabs}" SelectedItem="{CompiledBinding SelectedTab}">
|
||||
<ui:NavigationView.Styles>
|
||||
<Styles>
|
||||
<Style Selector="ui|NavigationView:topnavminimal /template/ SplitView Border#ContentGridBorder">
|
||||
<Setter Property="CornerRadius" Value="8 0 0 0" />
|
||||
</Style>
|
||||
</Styles>
|
||||
</ui:NavigationView.Styles>
|
||||
|
||||
<ui:Frame Name="TabFrame" IsNavigationStackEnabled="False" CacheSize="0">
|
||||
<ui:Frame.NavigationPageFactory>
|
||||
<ui1:PageFactory/>
|
||||
</ui:Frame.NavigationPageFactory>
|
||||
</ui:Frame>
|
||||
</ui:NavigationView>
|
||||
</UserControl>
|
||||
@ -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<WorkshopLibraryViewModel>
|
||||
{
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -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<ActivatableViewModelBase>, IWorkshopViewModel
|
||||
{
|
||||
private RouteViewModel? _selectedTab;
|
||||
|
||||
/// <inheritdoc />
|
||||
public WorkshopLibraryViewModel(IRouter router)
|
||||
{
|
||||
Tabs = new ObservableCollection<RouteViewModel>
|
||||
{
|
||||
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<RouteViewModel> 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);
|
||||
}
|
||||
}
|
||||
@ -23,14 +23,16 @@ public class ProfileDetailsViewModel : RoutableScreen<ActivatableViewModelBase,
|
||||
private readonly IWorkshopClient _client;
|
||||
private readonly ProfileEntryDownloadHandler _downloadHandler;
|
||||
private readonly INotificationService _notificationService;
|
||||
private readonly IWindowService _windowService;
|
||||
private readonly ObservableAsPropertyHelper<DateTimeOffset?> _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<ActivatableViewModelBase,
|
||||
if (Entry?.LatestRelease == null)
|
||||
return;
|
||||
|
||||
bool confirm = await _windowService.ShowConfirmContentDialog("Install profile?", "The profile will be downloaded and added to your sidebar automatically.");
|
||||
if (!confirm)
|
||||
return;
|
||||
|
||||
EntryInstallResult<ProfileConfiguration> result = await _downloadHandler.InstallProfileAsync(Entry.LatestRelease.Id, new Progress<StreamProgress>(), cancellationToken);
|
||||
if (result.IsSuccess)
|
||||
_notificationService.CreateNotification().WithTitle("Profile installed").WithSeverity(NotificationSeverity.Success).Show();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user