using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using PSLauncher.Models; namespace PSLauncher.App.ViewModels; public enum VersionRowState { InstalledIdle, // Installée localement, pas d'action en cours AvailableIdle, // Disponible côté serveur, pas installée Downloading, Installing, Uninstalling, } public sealed partial class VersionRowViewModel : ObservableObject { public string Version { get; } public DateTime ReleasedAt { get; } public string ExecutablePath { get; } public string FolderPath { get; } public long SizeBytes { get; } public InstalledVersion? Installed { get; } public VersionManifest? Remote { get; } public bool HasReleaseNotes => Remote?.ReleaseNotesUrl is not null; [ObservableProperty] [NotifyPropertyChangedFor(nameof(StateLabel))] [NotifyPropertyChangedFor(nameof(IsBusy))] [NotifyPropertyChangedFor(nameof(IsInstalled))] [NotifyPropertyChangedFor(nameof(IsRemoteOnly))] [NotifyCanExecuteChangedFor(nameof(LaunchCommand))] [NotifyCanExecuteChangedFor(nameof(InstallCommand))] [NotifyCanExecuteChangedFor(nameof(UninstallCommand))] private VersionRowState _state; [ObservableProperty] private double _progressPercent; [ObservableProperty] private string? _progressDetail; public bool IsInstalled => State is VersionRowState.InstalledIdle or VersionRowState.Uninstalling; public bool IsRemoteOnly => State is VersionRowState.AvailableIdle; public bool IsBusy => State is VersionRowState.Downloading or VersionRowState.Installing or VersionRowState.Uninstalling; public string StateLabel => State switch { VersionRowState.InstalledIdle => "● Installée", VersionRowState.AvailableIdle => "○ Disponible", VersionRowState.Downloading => "⬇ Téléchargement…", VersionRowState.Installing => "📦 Installation…", VersionRowState.Uninstalling => "🗑 Suppression…", _ => string.Empty }; public string PrimaryDate => ReleasedAt > DateTime.MinValue ? ReleasedAt.ToLocalTime().ToString("dd/MM/yyyy") : "—"; public string SizeDisplay => FormatSize(SizeBytes); /// Constructeur ligne « installée localement » (peut aussi être présente en remote). public static VersionRowViewModel ForInstalled(InstalledVersion installed, VersionManifest? remote) { return new VersionRowViewModel( version: installed.Version, releasedAt: remote?.ReleasedAt ?? installed.InstalledAt, executablePath: installed.ExecutablePath, folderPath: installed.FolderPath, sizeBytes: installed.SizeBytes, installed: installed, remote: remote, initialState: VersionRowState.InstalledIdle); } /// Constructeur ligne « disponible côté serveur uniquement ». public static VersionRowViewModel ForRemote(VersionManifest remote) { return new VersionRowViewModel( version: remote.Version, releasedAt: remote.ReleasedAt, executablePath: string.Empty, folderPath: string.Empty, sizeBytes: remote.Download.SizeBytes, installed: null, remote: remote, initialState: VersionRowState.AvailableIdle); } private VersionRowViewModel( string version, DateTime releasedAt, string executablePath, string folderPath, long sizeBytes, InstalledVersion? installed, VersionManifest? remote, VersionRowState initialState) { Version = version; ReleasedAt = releasedAt; ExecutablePath = executablePath; FolderPath = folderPath; SizeBytes = sizeBytes; Installed = installed; Remote = remote; _state = initialState; } // Les commandes sont câblées par le MainViewModel après instanciation // (les actions ont besoin de services). public Action? LaunchHandler { get; set; } public Action? InstallHandler { get; set; } public Action? UninstallHandler { get; set; } public Action? ShowReleaseNotesHandler { get; set; } public Action? OpenFolderHandler { get; set; } [RelayCommand(CanExecute = nameof(CanLaunch))] private void Launch() => LaunchHandler?.Invoke(this); private bool CanLaunch() => State == VersionRowState.InstalledIdle; [RelayCommand(CanExecute = nameof(CanInstall))] private void Install() => InstallHandler?.Invoke(this); private bool CanInstall() => State == VersionRowState.AvailableIdle; [RelayCommand(CanExecute = nameof(CanUninstall))] private void Uninstall() => UninstallHandler?.Invoke(this); private bool CanUninstall() => State == VersionRowState.InstalledIdle; [RelayCommand] private void ShowReleaseNotes() => ShowReleaseNotesHandler?.Invoke(this); [RelayCommand] private void OpenFolder() => OpenFolderHandler?.Invoke(this); private static string FormatSize(long bytes) { if (bytes <= 0) return "—"; string[] units = { "o", "Ko", "Mo", "Go", "To" }; double v = bytes; int u = 0; while (v >= 1024 && u < units.Length - 1) { v /= 1024; u++; } return $"{v:0.#} {units[u]}"; } }