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]}";
}
}