using System; using System.Threading; using System.Threading.Tasks; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using SpotXInstaller.Models; using SpotXInstaller.Services; namespace SpotXInstaller.ViewModels; public partial class MainViewModel : ViewModelBase { private readonly InstallerService _installer = new(); private CancellationTokenSource? _cts; // --------------------------------------------------------- // SCREEN STATES // --------------------------------------------------------- [ObservableProperty] private bool _isStepLoading = true; [ObservableProperty] private bool _isStepHome; [ObservableProperty] private bool _isStepSettings; [ObservableProperty] private bool _isStepProgress; // --------------------------------------------------------- // POPUP // --------------------------------------------------------- [ObservableProperty] private bool _showPopup; [ObservableProperty] private string _popupTitle = ""; [ObservableProperty] private string _popupMessage = ""; [ObservableProperty] private string _popupColor = "#8B5CF6"; // --------------------------------------------------------- // SETTINGS // --------------------------------------------------------- [ObservableProperty] private bool _blockUpdates = true; [ObservableProperty] private bool _hidePodcasts = true; [ObservableProperty] private bool _hidePremium = true; [ObservableProperty] private bool _clearCache; // --------------------------------------------------------- // PROGRESS // --------------------------------------------------------- [ObservableProperty] private double _progressValue; [ObservableProperty] private string _progressStatus = "Подготовка..."; [ObservableProperty] private string _miniLog = ""; // --------------------------------------------------------- // STATE // --------------------------------------------------------- [ObservableProperty] private bool _isInstalling; public MainViewModel() { _ = BootAsync(); } // --------------------------------------------------------- // BOOT // --------------------------------------------------------- private async Task BootAsync() { await Task.Delay(3000); SwitchTo(home: true); } // --------------------------------------------------------- // NAVIGATION // --------------------------------------------------------- private void SwitchTo( bool loading = false, bool home = false, bool settings = false, bool progress = false) { IsStepLoading = loading; IsStepHome = home; IsStepSettings = settings; IsStepProgress = progress; } [RelayCommand] private void GoToSettings() { if (IsInstalling) return; SwitchTo(settings: true); } [RelayCommand] private void GoBackToHome() { if (IsInstalling) return; SwitchTo(home: true); } [RelayCommand] private void ClosePopup() { ShowPopup = false; } // --------------------------------------------------------- // INSTALL // --------------------------------------------------------- [RelayCommand] private async Task StartInstallAsync() { await RunInstallerAsync(isUninstall: false); } // --------------------------------------------------------- // UNINSTALL // --------------------------------------------------------- [RelayCommand] private async Task StartUninstallAsync() { await RunInstallerAsync(isUninstall: true); } // --------------------------------------------------------- // CANCEL BUTTON // --------------------------------------------------------- [RelayCommand] private async Task CancelInstallAsync() { if (!IsInstalling) return; ProgressStatus = "Отмена установки..."; try { _cts?.Cancel(); } catch { // Ignore. } await Task.CompletedTask; } // --------------------------------------------------------- // WINDOW CLOSE // --------------------------------------------------------- public async Task CancelInstallationAsync() { if (!IsInstalling) return; try { ProgressStatus = "Завершение процессов..."; _cts?.Cancel(); // Даём process tree время корректно завершиться. await Task.Delay(150); } catch { // Ignore. } } // --------------------------------------------------------- // MAIN INSTALLER // --------------------------------------------------------- private async Task RunInstallerAsync(bool isUninstall) { if (IsInstalling) return; ProgressValue = 0; MiniLog = ""; ProgressStatus = isUninstall ? "Подготовка восстановления..." : "Подготовка установки..."; SwitchTo(progress: true); _cts?.Dispose(); _cts = new CancellationTokenSource(); IsInstalling = true; var progress = new Progress<(int Percent, string Status)>( p => { ProgressValue = p.Percent; ProgressStatus = p.Status; }); var log = new Progress( line => { MiniLog = line; }); try { int code; if (isUninstall) { code = await _installer.UninstallAsync( progress, log, _cts.Token); } else { code = await _installer.InstallAsync( new InstallOptions( BlockUpdates, HidePodcasts, HidePremium, ClearCache), progress, log, _cts.Token); } IsInstalling = false; SwitchTo(home: true); if (code == 0) { ShowNotification( isUninstall ? "ГОТОВО" : "УСПЕХ", isUninstall ? "Оригинальный клиент Spotify восстановлен." : "SpotX успешно установлен.", isUninstall ? "#8B5CF6" : "#00E676"); } else { string errorText = MiniLog.Length > 500 ? MiniLog[..500] : MiniLog; ShowNotification( "ОШИБКА", $"Скрипт завершился с кодом {code}.\n\n{errorText}", "#FF3B30"); } } catch (OperationCanceledException) { IsInstalling = false; SwitchTo(home: true); ShowNotification( "ОТМЕНЕНО", "Установка была отменена.", "#FFB300"); } catch (Exception ex) { IsInstalling = false; SwitchTo(home: true); ShowNotification( "ОШИБКА", ex.Message, "#FF3B30"); } finally { IsInstalling = false; _cts?.Dispose(); _cts = null; } } // --------------------------------------------------------- // POPUP // --------------------------------------------------------- private void ShowNotification( string title, string message, string hexColor) { PopupTitle = title; PopupMessage = message; PopupColor = hexColor; ShowPopup = true; } // --------------------------------------------------------- // DISPOSE // --------------------------------------------------------- public async Task DisposeAsync() { if (!IsInstalling) return; try { _cts?.Cancel(); await Task.Delay(150); } catch { // Ignore. } _cts?.Dispose(); _cts = null; IsInstalling = false; } }