Multi-Platform Package Manager for Stable Diffusion
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

202 lines
6.6 KiB

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AsyncAwaitBestPractices;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using CommunityToolkit.Mvvm.ComponentModel;
using FluentAvalonia.UI.Controls;
using StabilityMatrix.Avalonia.Controls;
using StabilityMatrix.Avalonia.Services;
using StabilityMatrix.Avalonia.ViewModels.Base;
using StabilityMatrix.Avalonia.ViewModels.Dialogs;
using StabilityMatrix.Avalonia.Views;
using StabilityMatrix.Avalonia.Views.Dialogs;
using StabilityMatrix.Core.Attributes;
using StabilityMatrix.Core.Helper;
using StabilityMatrix.Core.Services;
namespace StabilityMatrix.Avalonia.ViewModels;
[View(typeof(MainWindow))]
public partial class MainWindowViewModel : ViewModelBase
{
private readonly ISettingsManager settingsManager;
private readonly ServiceManager<ViewModelBase> dialogFactory;
private readonly ITrackedDownloadService trackedDownloadService;
private readonly IDiscordRichPresenceService discordRichPresenceService;
public string Greeting => "Welcome to Avalonia!";
[ObservableProperty]
private PageViewModelBase? currentPage;
[ObservableProperty]
private object? selectedCategory;
[ObservableProperty]
private List<PageViewModelBase> pages = new();
[ObservableProperty]
private List<PageViewModelBase> footerPages = new();
public ProgressManagerViewModel ProgressManagerViewModel { get; init; }
public UpdateViewModel UpdateViewModel { get; init; }
public MainWindowViewModel(
ISettingsManager settingsManager,
IDiscordRichPresenceService discordRichPresenceService,
ServiceManager<ViewModelBase> dialogFactory,
ITrackedDownloadService trackedDownloadService)
{
this.settingsManager = settingsManager;
this.dialogFactory = dialogFactory;
this.discordRichPresenceService = discordRichPresenceService;
this.trackedDownloadService = trackedDownloadService;
ProgressManagerViewModel = dialogFactory.Get<ProgressManagerViewModel>();
UpdateViewModel = dialogFactory.Get<UpdateViewModel>();
}
public override void OnLoaded()
{
base.OnLoaded();
// Set only if null, since this may be called again when content dialogs open
CurrentPage ??= Pages.FirstOrDefault();
SelectedCategory ??= Pages.FirstOrDefault();
}
public override async Task OnLoadedAsync()
{
await base.OnLoadedAsync();
// Skip if design mode
if (Design.IsDesignMode) return;
if (!await EnsureDataDirectory())
{
// False if user exited dialog, shutdown app
App.Shutdown();
return;
}
// Initialize Discord Rich Presence (this needs LibraryDir so is set here)
discordRichPresenceService.UpdateState();
// Load in-progress downloads
ProgressManagerViewModel.AddDownloads(trackedDownloadService.Downloads);
// Index checkpoints if we dont have
Task.Run(() => settingsManager.IndexCheckpoints()).SafeFireAndForget();
if (!settingsManager.Settings.InstalledPackages.Any())
{
var viewModel = dialogFactory.Get<OneClickInstallViewModel>();
var dialog = new BetterContentDialog
{
IsPrimaryButtonEnabled = false,
IsSecondaryButtonEnabled = false,
IsFooterVisible = false,
Content = new OneClickInstallDialog
{
DataContext = viewModel
},
};
EventManager.Instance.OneClickInstallFinished += (_, skipped) =>
{
dialog.Hide();
if (skipped) return;
EventManager.Instance.OnTeachingTooltipNeeded();
};
await dialog.ShowAsync();
}
}
/// <summary>
/// Check if the data directory exists, if not, show the select data directory dialog.
/// </summary>
private async Task<bool> EnsureDataDirectory()
{
// If we can't find library, show selection dialog
if (!settingsManager.TryFindLibrary())
{
var result = await ShowSelectDataDirectoryDialog();
if (!result) return false;
}
// Try to find library again, should be found now
if (!settingsManager.TryFindLibrary())
{
throw new Exception("Could not find library after setting path");
}
// Tell LaunchPage to load any packages if they selected an existing directory
EventManager.Instance.OnInstalledPackagesChanged();
// Check if there are old packages, if so show migration dialog
// TODO: Migration dialog
return true;
}
/// <summary>
/// Shows the select data directory dialog.
/// </summary>
/// <returns>true if path set successfully, false if user exited dialog.</returns>
private async Task<bool> ShowSelectDataDirectoryDialog()
{
var viewModel = dialogFactory.Get<SelectDataDirectoryViewModel>();
var dialog = new BetterContentDialog
{
IsPrimaryButtonEnabled = false,
IsSecondaryButtonEnabled = false,
IsFooterVisible = false,
Content = new SelectDataDirectoryDialog
{
DataContext = viewModel
}
};
var result = await dialog.ShowAsync();
if (result == ContentDialogResult.Primary)
{
// 1. For portable mode, call settings.SetPortableMode()
if (viewModel.IsPortableMode)
{
settingsManager.SetPortableMode();
}
// 2. For custom path, call settings.SetLibraryPath(path)
else
{
settingsManager.SetLibraryPath(viewModel.DataDirectory);
}
// Indicate success
return true;
}
return false;
}
public async Task ShowUpdateDialog()
{
var viewModel = dialogFactory.Get<UpdateViewModel>();
var dialog = new BetterContentDialog
{
ContentVerticalScrollBarVisibility = ScrollBarVisibility.Disabled,
DefaultButton = ContentDialogButton.Close,
IsPrimaryButtonEnabled = false,
IsSecondaryButtonEnabled = false,
IsFooterVisible = false,
Content = new UpdateDialog
{
DataContext = viewModel
}
};
await dialog.ShowAsync();
}
}