diff --git a/src/Artemis.UI.Shared/Routing/Router/Router.cs b/src/Artemis.UI.Shared/Routing/Router/Router.cs index 3eae340c9..d10cd2ede 100644 --- a/src/Artemis.UI.Shared/Routing/Router/Router.cs +++ b/src/Artemis.UI.Shared/Routing/Router/Router.cs @@ -103,7 +103,9 @@ internal class Router : CorePropertyChanged, IRouter, IDisposable { if (_root == null) throw new ArtemisRoutingException("Cannot navigate without a root having been set"); - if (PathEquals(path, options) || (_currentNavigation != null && _currentNavigation.PathEquals(path, options))) + + // If navigating to the same path, don't do anything + if ((_currentNavigation == null && PathEquals(path, options)) || (_currentNavigation != null && _currentNavigation.PathEquals(path, options))) return; string? previousPath = _currentRouteSubject.Value; @@ -128,12 +130,8 @@ internal class Router : CorePropertyChanged, IRouter, IDisposable await navigation.Navigate(args); // If it was cancelled before completion, don't add it to history or update the current path - // Do reload the current path because it may have been partially navigated away from if (navigation.Cancelled) - { - await Reload(); return; - } if (options.AddToHistory && previousPath != null) { diff --git a/src/Artemis.UI/Routing/Routes.cs b/src/Artemis.UI/Routing/Routes.cs index ddc260fda..11cf16abf 100644 --- a/src/Artemis.UI/Routing/Routes.cs +++ b/src/Artemis.UI/Routing/Routes.cs @@ -64,7 +64,10 @@ namespace Artemis.UI.Routing new RouteRegistration("account"), new RouteRegistration("about") ]), - new RouteRegistration("profile-editor/{profileConfigurationId:guid}") + new RouteRegistration("profile/{profileConfigurationId:guid}", [ + new RouteRegistration("editor"), + new RouteRegistration("workshop") + ]), ]; } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorViewModel.cs index c92e34dcc..352a49339 100644 --- a/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorViewModel.cs +++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileEditorViewModel.cs @@ -15,11 +15,8 @@ using Artemis.UI.Screens.ProfileEditor.StatusBar; using Artemis.UI.Screens.ProfileEditor.VisualEditor; using Artemis.UI.Shared; using Artemis.UI.Shared.Routing; -using Artemis.UI.Shared.Services; using Artemis.UI.Shared.Services.MainWindow; using Artemis.UI.Shared.Services.ProfileEditor; -using Artemis.WebClient.Workshop.Models; -using Artemis.WebClient.Workshop.Services; using DynamicData; using DynamicData.Binding; using PropertyChanged.SourceGenerator; @@ -27,14 +24,12 @@ using ReactiveUI; namespace Artemis.UI.Screens.ProfileEditor; -public partial class ProfileEditorViewModel : RoutableScreen, IMainScreenViewModel +public partial class ProfileEditorViewModel : RoutableScreen, IMainScreenViewModel { private readonly IProfileEditorService _profileEditorService; private readonly IProfileService _profileService; private readonly ISettingsService _settingsService; private readonly IMainWindowService _mainWindowService; - private readonly IWorkshopService _workshopService; - private readonly IWindowService _windowService; private readonly SourceList _tools; private ObservableAsPropertyHelper? _history; private ObservableAsPropertyHelper? _suspendedEditing; @@ -53,16 +48,12 @@ public partial class ProfileEditorViewModel : RoutableScreen toolViewModels, IMainWindowService mainWindowService, - IInputService inputService, - IWorkshopService workshopService, - IWindowService windowService) + IInputService inputService) { _profileService = profileService; _profileEditorService = profileEditorService; _settingsService = settingsService; _mainWindowService = mainWindowService; - _workshopService = workshopService; - _windowService = windowService; _tools = new SourceList(); _tools.AddRange(toolViewModels); @@ -75,6 +66,7 @@ public partial class ProfileEditorViewModel : RoutableScreen /// - public override async Task OnNavigating(ProfileEditorViewModelParameters parameters, NavigationArguments args, CancellationToken cancellationToken) + public override async Task OnNavigating(ProfileViewModelParameters parameters, NavigationArguments args, CancellationToken cancellationToken) { ProfileConfiguration? profileConfiguration = _profileService.ProfileCategories.SelectMany(c => c.ProfileConfigurations).FirstOrDefault(c => c.ProfileId == parameters.ProfileId); @@ -204,23 +196,6 @@ public partial class ProfileEditorViewModel : RoutableScreen + + + + + + diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileView.axaml.cs b/src/Artemis.UI/Screens/ProfileEditor/ProfileView.axaml.cs new file mode 100644 index 000000000..032140095 --- /dev/null +++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileView.axaml.cs @@ -0,0 +1,19 @@ +using System; +using System.Reactive.Disposables; +using Artemis.UI.Shared.Routing; +using Avalonia.ReactiveUI; +using Avalonia.Threading; +using ReactiveUI; +namespace Artemis.UI.Screens.ProfileEditor; + +public partial class ProfileView : ReactiveUserControl +{ + public ProfileView() + { + InitializeComponent(); + this.WhenActivated(d => ViewModel.WhenAnyValue(vm => vm.Screen) + .WhereNotNull() + .Subscribe(screen => RouterFrame.NavigateFromObject(screen)) + .DisposeWith(d)); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/ProfileViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/ProfileViewModel.cs new file mode 100644 index 000000000..42b99d05e --- /dev/null +++ b/src/Artemis.UI/Screens/ProfileEditor/ProfileViewModel.cs @@ -0,0 +1,62 @@ +using System; +using System.Linq; +using System.Reactive.Linq; +using System.Threading; +using System.Threading.Tasks; +using Artemis.Core; +using Artemis.Core.Services; +using Artemis.UI.Shared; +using Artemis.UI.Shared.Routing; +using Artemis.WebClient.Workshop.Models; +using Artemis.WebClient.Workshop.Services; +using ReactiveUI; + +namespace Artemis.UI.Screens.ProfileEditor; + +public class ProfileViewModel : RoutableHostScreen, IMainScreenViewModel +{ + private readonly IProfileService _profileService; + private readonly IWorkshopService _workshopService; + private readonly ObservableAsPropertyHelper _titleBarViewModel; + + public ProfileViewModel(IProfileService profileService, IWorkshopService workshopService) + { + _profileService = profileService; + _workshopService = workshopService; + + _titleBarViewModel = this.WhenAnyValue(vm => vm.Screen).Select(screen => screen as IMainScreenViewModel) + .Select(mainScreen => mainScreen?.TitleBarViewModel) + .ToProperty(this, vm => vm.TitleBarViewModel); + } + + public ViewModelBase? TitleBarViewModel => _titleBarViewModel.Value; + + /// + public override async Task OnNavigating(ProfileViewModelParameters parameters, NavigationArguments args, CancellationToken cancellationToken) + { + ProfileConfiguration? profileConfiguration = _profileService.ProfileCategories.SelectMany(c => c.ProfileConfigurations).FirstOrDefault(c => c.ProfileId == parameters.ProfileId); + + // If the profile doesn't exist, cancel navigation + if (profileConfiguration == null) + { + args.Cancel(); + return; + } + + // If the profile is from the workshop, redirect to the workshop page + InstalledEntry? workshopEntry = _workshopService.GetInstalledEntryByProfile(profileConfiguration); + if (workshopEntry != null && workshopEntry.AutoUpdate) + { + if (!args.Path.EndsWith("workshop")) + await args.Router.Navigate($"profile/{parameters.ProfileId}/workshop"); + } + // Otherwise, show the profile editor if not already on the editor page + else if (!args.Path.EndsWith("editor")) + await args.Router.Navigate($"profile/{parameters.ProfileId}/editor"); + } +} + +public class ProfileViewModelParameters +{ + public Guid ProfileId { get; set; } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/WorkshopProfileView.axaml b/src/Artemis.UI/Screens/ProfileEditor/WorkshopProfileView.axaml new file mode 100644 index 000000000..109d960fd --- /dev/null +++ b/src/Artemis.UI/Screens/ProfileEditor/WorkshopProfileView.axaml @@ -0,0 +1,23 @@ + + + + + + + The profile you're opening is a workshop profile. + + + You cannot make change to it without disabling auto-update, as any updates to the profile on the workshop would override your own modifications. + + + + + + \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/WorkshopProfileView.axaml.cs b/src/Artemis.UI/Screens/ProfileEditor/WorkshopProfileView.axaml.cs new file mode 100644 index 000000000..926bf205e --- /dev/null +++ b/src/Artemis.UI/Screens/ProfileEditor/WorkshopProfileView.axaml.cs @@ -0,0 +1,11 @@ +using Avalonia.ReactiveUI; + +namespace Artemis.UI.Screens.ProfileEditor; + +public partial class WorkshopProfileView : ReactiveUserControl +{ + public WorkshopProfileView() + { + InitializeComponent(); + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/ProfileEditor/WorkshopProfileViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/WorkshopProfileViewModel.cs new file mode 100644 index 000000000..87e091dab --- /dev/null +++ b/src/Artemis.UI/Screens/ProfileEditor/WorkshopProfileViewModel.cs @@ -0,0 +1,65 @@ +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Artemis.Core; +using Artemis.Core.Services; +using Artemis.UI.Screens.Workshop.Library.Tabs; +using Artemis.UI.Shared.Routing; +using Artemis.WebClient.Workshop.Models; +using Artemis.WebClient.Workshop.Services; +using PropertyChanged.SourceGenerator; + +namespace Artemis.UI.Screens.ProfileEditor; + +public partial class WorkshopProfileViewModel : RoutableScreen +{ + private readonly IProfileService _profileService; + private readonly IWorkshopService _workshopService; + private readonly IRouter _router; + private readonly Func _getInstalledTabItemViewModel; + + [Notify] private ProfileConfiguration? _profileConfiguration; + [Notify] private InstalledEntry? _workshopEntry; + [Notify] private InstalledTabItemViewModel? _entryViewModel; + + public WorkshopProfileViewModel(IProfileService profileService, IWorkshopService workshopService, IRouter router, Func getInstalledTabItemViewModel) + { + _profileService = profileService; + _workshopService = workshopService; + _router = router; + _getInstalledTabItemViewModel = getInstalledTabItemViewModel; + ParameterSource = ParameterSource.Route; + } + + public async Task DisableAutoUpdate() + { + if (WorkshopEntry != null) + { + _workshopService.SetAutoUpdate(WorkshopEntry, false); + } + + if (ProfileConfiguration != null) + { + await _router.Navigate($"profile/{ProfileConfiguration.ProfileId}/editor"); + } + } + + public override Task OnNavigating(ProfileViewModelParameters parameters, NavigationArguments args, CancellationToken cancellationToken) + { + ProfileConfiguration = _profileService.ProfileCategories.SelectMany(c => c.ProfileConfigurations).FirstOrDefault(c => c.ProfileId == parameters.ProfileId); + + // If the profile doesn't exist, cancel navigation + if (ProfileConfiguration == null) + { + args.Cancel(); + return Task.CompletedTask; + } + + WorkshopEntry = _workshopService.GetInstalledEntryByProfile(ProfileConfiguration); + EntryViewModel = WorkshopEntry != null ? _getInstalledTabItemViewModel(WorkshopEntry) : null; + if (EntryViewModel != null) + EntryViewModel.DisplayManagement = false; + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs b/src/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs index 9ad46d8ce..96805d293 100644 --- a/src/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/SidebarCategoryViewModel.cs @@ -65,7 +65,7 @@ public partial class SidebarCategoryViewModel : ActivatableViewModelBase // Navigate on selection change this.WhenAnyValue(vm => vm.SelectedProfileConfiguration) .WhereNotNull() - .Subscribe(s => _router.Navigate($"profile-editor/{s.ProfileConfiguration.ProfileId}", new RouterNavigationOptions {IgnoreOnPartialMatch = true, RecycleScreens = false})) + .Subscribe(s => _router.Navigate($"profile/{s.ProfileConfiguration.ProfileId}/editor", new RouterNavigationOptions {IgnoreOnPartialMatch = true, RecycleScreens = false})) .DisposeWith(d); _router.CurrentPath.WhereNotNull().Subscribe(r => SelectedProfileConfiguration = ProfileConfigurations.FirstOrDefault(c => c.Matches(r))).DisposeWith(d); diff --git a/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs b/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs index 99f9330d7..ee5e5c111 100644 --- a/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs +++ b/src/Artemis.UI/Screens/Sidebar/SidebarProfileConfigurationViewModel.cs @@ -132,6 +132,6 @@ public class SidebarProfileConfigurationViewModel : ActivatableViewModelBase public bool Matches(string s) { - return s.StartsWith("profile-editor") && s.EndsWith(ProfileConfiguration.ProfileId.ToString()); + return s == $"profile/{ProfileConfiguration.ProfileId}/editor"; } } \ No newline at end of file diff --git a/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntryItemViewModel.cs b/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntryItemViewModel.cs index 1fb9958b3..0aa1d901e 100644 --- a/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntryItemViewModel.cs +++ b/src/Artemis.UI/Screens/StartupWizard/Steps/DefaultEntryItemViewModel.cs @@ -71,6 +71,9 @@ public partial class DefaultEntryItemViewModel : ActivatableViewModelBase else if (result.Entry?.EntryType == EntryType.Plugin) { await EnablePluginAndFeatures(result.Entry); + } else if (result.Entry?.EntryType == EntryType.Profile) + { + } return result.IsSuccess; diff --git a/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemView.axaml b/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemView.axaml index 71ab5983c..39eb9d5a8 100644 --- a/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemView.axaml +++ b/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemView.axaml @@ -92,7 +92,7 @@ - + diff --git a/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemViewModel.cs b/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemViewModel.cs index 6307a8557..665129c0c 100644 --- a/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemViewModel.cs +++ b/src/Artemis.UI/Screens/Workshop/Library/Tabs/InstalledTabItemViewModel.cs @@ -35,6 +35,7 @@ public partial class InstalledTabItemViewModel : ActivatableViewModelBase [Notify] private bool _updateAvailable; [Notify] private bool _autoUpdate; + [Notify] private bool _displayManagement = true; public InstalledTabItemViewModel(InstalledEntry entry, IWorkshopClient client, @@ -87,7 +88,7 @@ public partial class InstalledTabItemViewModel : ActivatableViewModelBase public async Task ViewLocal() { if (Entry.EntryType == EntryType.Profile && Entry.TryGetMetadata("ProfileId", out Guid profileId)) - await _router.Navigate($"profile-editor/{profileId}"); + await _router.Navigate($"profile/{profileId}/editor"); else if (Entry.EntryType == EntryType.Plugin) await _router.Navigate($"workshop/entries/plugins/details/{Entry.Id}/manage"); else if (Entry.EntryType == EntryType.Layout)