206 lines
6.5 KiB
C#
206 lines
6.5 KiB
C#
using System.Windows;
|
|
using ClaudeDo.Installer.Core;
|
|
using ClaudeDo.Releases;
|
|
using ClaudeDo.Installer.Steps;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using CommunityToolkit.Mvvm.Input;
|
|
|
|
namespace ClaudeDo.Installer.Views;
|
|
|
|
public partial class SettingsViewModel : ObservableObject
|
|
{
|
|
private readonly InstallContext _context;
|
|
private readonly IReleaseClient _releases;
|
|
private readonly StopServiceStep _stopService;
|
|
private readonly StartServiceStep _startService;
|
|
private readonly DownloadAndExtractStep _downloadStep;
|
|
private readonly UninstallRunner _uninstallRunner;
|
|
|
|
public IReadOnlyList<IInstallerPage> Pages { get; }
|
|
|
|
[ObservableProperty]
|
|
private IInstallerPage? _selectedPage;
|
|
|
|
[ObservableProperty]
|
|
private string? _statusMessage;
|
|
|
|
[ObservableProperty]
|
|
private bool _isStatusError;
|
|
|
|
[ObservableProperty]
|
|
private string _versionLabel = "";
|
|
|
|
[ObservableProperty]
|
|
private bool _removeAppData;
|
|
|
|
public SettingsViewModel(
|
|
PageResolver resolver,
|
|
InstallContext context,
|
|
IReleaseClient releases,
|
|
StopServiceStep stopService,
|
|
StartServiceStep startService,
|
|
DownloadAndExtractStep downloadStep,
|
|
UninstallRunner uninstallRunner)
|
|
{
|
|
Pages = resolver.SettingsPages;
|
|
_context = context;
|
|
_releases = releases;
|
|
_stopService = stopService;
|
|
_startService = startService;
|
|
_downloadStep = downloadStep;
|
|
_uninstallRunner = uninstallRunner;
|
|
_selectedPage = Pages.FirstOrDefault();
|
|
|
|
var label = $"Installed: {context.InstalledVersion ?? "?"} Installer: {context.InstallerVersion ?? "?"}";
|
|
if (!string.IsNullOrEmpty(context.LatestVersion))
|
|
label += $" Latest: {context.LatestVersion}";
|
|
if (context.LatestTagUnparseable)
|
|
label += " (pre-release tag — auto-update disabled)";
|
|
VersionLabel = label;
|
|
|
|
_ = LoadAllAsync();
|
|
}
|
|
|
|
private async Task LoadAllAsync()
|
|
{
|
|
foreach (var page in Pages)
|
|
await page.LoadAsync();
|
|
}
|
|
|
|
[RelayCommand]
|
|
private async Task Save()
|
|
{
|
|
foreach (var page in Pages)
|
|
{
|
|
if (!page.Validate())
|
|
{
|
|
SelectedPage = page;
|
|
StatusMessage = $"Validation failed on {page.Title}. Please fix the errors.";
|
|
IsStatusError = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
foreach (var page in Pages)
|
|
await page.ApplyAsync();
|
|
|
|
var workerCfg = new InstallerWorkerConfig
|
|
{
|
|
DbPath = _context.DbPath,
|
|
SandboxRoot = _context.SandboxRoot,
|
|
LogRoot = _context.LogRoot,
|
|
WorktreeRootStrategy = _context.WorktreeRootStrategy,
|
|
CentralWorktreeRoot = _context.CentralWorktreeRoot,
|
|
QueueBackstopIntervalMs = _context.QueueBackstopIntervalMs,
|
|
SignalRPort = _context.SignalRPort,
|
|
ClaudeBin = _context.ClaudeBin,
|
|
};
|
|
workerCfg.Save();
|
|
|
|
var uiCfg = new InstallerAppSettings
|
|
{
|
|
DbPath = _context.UiDbPath,
|
|
SignalRUrl = _context.SignalRUrl,
|
|
};
|
|
uiCfg.Save();
|
|
|
|
IsStatusError = false;
|
|
StatusMessage = "Settings saved.";
|
|
|
|
// Worker reads its config at process start, so changes only take effect after a restart.
|
|
var restart = MessageBox.Show(
|
|
"Restart the worker service now so the new settings take effect?",
|
|
"Restart Worker",
|
|
MessageBoxButton.YesNo,
|
|
MessageBoxImage.Question);
|
|
|
|
if (restart != MessageBoxResult.Yes)
|
|
{
|
|
StatusMessage = "Settings saved. Restart the worker service manually to apply.";
|
|
return;
|
|
}
|
|
|
|
var progress = new Progress<string>(msg => StatusMessage = msg);
|
|
var stop = await _stopService.ExecuteAsync(_context, progress, CancellationToken.None);
|
|
if (!stop.Success)
|
|
{
|
|
StatusMessage = $"Saved, but worker stop failed: {stop.ErrorMessage}";
|
|
IsStatusError = true;
|
|
return;
|
|
}
|
|
var start = await _startService.ExecuteAsync(_context, progress, CancellationToken.None);
|
|
if (!start.Success)
|
|
{
|
|
StatusMessage = $"Saved, but worker start failed: {start.ErrorMessage}";
|
|
IsStatusError = true;
|
|
return;
|
|
}
|
|
|
|
StatusMessage = "Settings saved. Worker restarted.";
|
|
}
|
|
|
|
[RelayCommand]
|
|
private async Task Repair()
|
|
{
|
|
var confirm = MessageBox.Show(
|
|
"Re-download and reinstall the ClaudeDo binaries? Your config and database are NOT affected.",
|
|
"Repair ClaudeDo",
|
|
MessageBoxButton.OKCancel,
|
|
MessageBoxImage.Question);
|
|
|
|
if (confirm != MessageBoxResult.OK) return;
|
|
|
|
StatusMessage = "Repairing...";
|
|
IsStatusError = false;
|
|
|
|
var progress = new Progress<string>(msg => StatusMessage = msg);
|
|
var steps = new IInstallStep[] { _stopService, _downloadStep, _startService };
|
|
|
|
foreach (var step in steps)
|
|
{
|
|
var r = await step.ExecuteAsync(_context, progress, CancellationToken.None);
|
|
if (!r.Success)
|
|
{
|
|
StatusMessage = $"{step.Name} failed: {r.ErrorMessage}";
|
|
IsStatusError = true;
|
|
return;
|
|
}
|
|
}
|
|
|
|
StatusMessage = "Repair complete.";
|
|
}
|
|
|
|
[RelayCommand]
|
|
private async Task Uninstall()
|
|
{
|
|
var dataNote = RemoveAppData
|
|
? "This will remove ClaudeDo AND delete all of your tasks, configuration, and database.\n\nContinue?"
|
|
: "This will remove ClaudeDo. Your tasks, configuration, and database in ~/.todo-app will be kept.\n\nContinue?";
|
|
|
|
var confirm = MessageBox.Show(
|
|
dataNote,
|
|
"Uninstall ClaudeDo",
|
|
MessageBoxButton.YesNo,
|
|
MessageBoxImage.Warning);
|
|
|
|
if (confirm != MessageBoxResult.Yes) return;
|
|
|
|
var progress = new Progress<string>(msg => StatusMessage = msg);
|
|
var r = await _uninstallRunner.RunAsync(RemoveAppData, progress, CancellationToken.None);
|
|
|
|
if (!r.Success)
|
|
{
|
|
StatusMessage = $"Uninstall failed: {r.ErrorMessage}";
|
|
IsStatusError = true;
|
|
return;
|
|
}
|
|
|
|
MessageBox.Show("ClaudeDo has been removed.", "Uninstall complete",
|
|
MessageBoxButton.OK, MessageBoxImage.Information);
|
|
Application.Current.Shutdown();
|
|
}
|
|
|
|
[RelayCommand]
|
|
private void Close() => Application.Current.Shutdown();
|
|
}
|