using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading.Tasks; using CommunityToolkit.Mvvm.Messaging; using Nitrox.Launcher.Models.Design; using Nitrox.Launcher.Models.Utils; using Nitrox.Launcher.ViewModels.Abstract; namespace Nitrox.Launcher.Models.Extensions; public static class ScreenExtensions { private static readonly List navigationStack = []; /// /// Navigates to a view assigned to the given ViewModel. /// /// The screen used to display the view. /// ViewModel that should be shown. /// Type of the ViewModel to show. public static async Task ShowAsync(this IRoutingScreen screen, TViewModel routableViewModel) where TViewModel : RoutableViewModelBase { if (screen == null) { return; } // When navigating away from a view in an async button command, busy states on buttons should also reset. Otherwise, when navigating back it would still show buttons being busy. NitroxAttached.AsyncCommandButtonTagger.Clear(); if (screen.ActiveViewModel is RoutableViewModelBase routableViewModelBase) { navigationStack.RemoveAllFast(screen.ActiveViewModel, (item, param) => item.GetType() == param.GetType()); await routableViewModelBase.ViewContentUnloadAsync(); navigationStack.Add(routableViewModelBase); } Stopwatch sw = Stopwatch.StartNew(); Task contentLoadTask = routableViewModel.ViewContentLoadAsync(); if (screen.ActiveViewModel != null) { // Only show loading screen if page isn't loading super quickly. await Task.Delay(50); if (!contentLoadTask.IsCompleted) { screen.ActiveViewModel = AssetHelper.GetFullAssetPath("/Assets/Icons/loading.svg"); await Task.Delay((int)Math.Max(0, 500 - sw.Elapsed.TotalMilliseconds)); } } await contentLoadTask; screen.ActiveViewModel = routableViewModel; } public static async Task BackAsync(this IRoutingScreen screen) { if (navigationStack.Count < 1) { return false; } RoutableViewModelBase backViewModel = navigationStack[^1]; navigationStack.Remove(backViewModel); await ShowAsync(screen, backViewModel); return true; } /// /// Tries to go back to the view assigned to the given ViewModel. /// /// /// True if ViewModel was found in the routing navigation stack. False when the ViewModel wasn't found and routing /// failed. /// public static async Task BackToAsync(this IRoutingScreen screen) where T : RoutableViewModelBase { for (int i = navigationStack.Count - 1; i >= 0; i--) { if (navigationStack[i] is T target) { // Cleanup the stack up and including the back-target. for (int j = i; j < navigationStack.Count; j++) { navigationStack.RemoveAt(j); } await screen.ShowAsync(target); return true; } } return false; } }