New Settings tab: enable toggle, config fields, sign-in/out + status. OnlineLoginService runs the PKCE loopback flow (Duende.IdentityModel.OidcClient 7.1.0), opens the system browser, captures the callback, hands the refresh token to the Worker. en/de localized. Fixes: loopback callback URL built from host:port base (avoids doubled redirect path); PollIntervalSeconds threaded through the state DTO so it loads instead of resetting to 60. Visual layout + the live sign-in round-trip need manual verification. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
111 lines
4.6 KiB
C#
111 lines
4.6 KiB
C#
using System.Linq;
|
|
using ClaudeDo.Data;
|
|
using ClaudeDo.Localization;
|
|
using ClaudeDo.Ui.Localization;
|
|
using ClaudeDo.Ui.Services;
|
|
using ClaudeDo.Ui.ViewModels.Modals.Settings;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using CommunityToolkit.Mvvm.Input;
|
|
|
|
namespace ClaudeDo.Ui.ViewModels.Modals;
|
|
|
|
public sealed partial class SettingsModalViewModel : ViewModelBase
|
|
{
|
|
private readonly IWorkerClient _worker;
|
|
|
|
public GeneralSettingsTabViewModel General { get; }
|
|
public WorktreesSettingsTabViewModel Worktrees { get; }
|
|
public FilesSettingsTabViewModel Files { get; }
|
|
public PrimeClaudeTabViewModel Prime { get; }
|
|
public OnlineInboxSettingsViewModel OnlineInbox { get; }
|
|
|
|
[ObservableProperty] private string _validationError = "";
|
|
[ObservableProperty] private bool _isBusy;
|
|
[ObservableProperty] private string _statusMessage = "";
|
|
|
|
public Action? CloseAction { get; set; }
|
|
|
|
public SettingsModalViewModel(IWorkerClient worker, PrimeClaudeTabViewModel prime,
|
|
IOnlineLoginService onlineLoginService,
|
|
ILocalizer localizer, AppSettings appSettings)
|
|
{
|
|
_worker = worker;
|
|
General = new GeneralSettingsTabViewModel(localizer, code =>
|
|
{
|
|
appSettings.Language = code;
|
|
appSettings.Save();
|
|
});
|
|
Worktrees = new WorktreesSettingsTabViewModel(worker);
|
|
Files = new FilesSettingsTabViewModel(worker);
|
|
Prime = prime;
|
|
OnlineInbox = new OnlineInboxSettingsViewModel(worker, onlineLoginService);
|
|
}
|
|
|
|
public async Task LoadAsync()
|
|
{
|
|
IsBusy = true;
|
|
try
|
|
{
|
|
var dto = await _worker.GetAppSettingsAsync();
|
|
if (dto is not null)
|
|
{
|
|
General.DefaultClaudeInstructions = dto.DefaultClaudeInstructions ?? "";
|
|
General.DefaultModel = dto.DefaultModel ?? "sonnet";
|
|
General.DefaultMaxTurns = dto.DefaultMaxTurns;
|
|
General.DefaultPermissionMode = dto.DefaultPermissionMode ?? "auto";
|
|
General.MaxParallelExecutions = dto.MaxParallelExecutions;
|
|
Worktrees.WorktreeStrategy = dto.WorktreeStrategy ?? "sibling";
|
|
Worktrees.CentralWorktreeRoot = dto.CentralWorktreeRoot;
|
|
Worktrees.WorktreeAutoCleanupEnabled = dto.WorktreeAutoCleanupEnabled;
|
|
Worktrees.WorktreeAutoCleanupDays = dto.WorktreeAutoCleanupDays;
|
|
General.ReportExcludedPaths = string.IsNullOrWhiteSpace(dto.ReportExcludedPaths)
|
|
? @"C:\Private"
|
|
: string.Join(Environment.NewLine,
|
|
System.Text.Json.JsonSerializer.Deserialize<List<string>>(dto.ReportExcludedPaths) ?? new());
|
|
General.StandupWeekday = dto.StandupWeekday is >= 0 and <= 6 ? dto.StandupWeekday : (int)DayOfWeek.Wednesday;
|
|
Prime.DailyPrepMaxTasks = dto.DailyPrepMaxTasks < 1 ? 5 : dto.DailyPrepMaxTasks;
|
|
}
|
|
else StatusMessage = Loc.T("vm.settingsModal.workerOffline");
|
|
|
|
await Prime.LoadAsync();
|
|
await OnlineInbox.LoadAsync();
|
|
}
|
|
finally { IsBusy = false; }
|
|
}
|
|
|
|
[RelayCommand]
|
|
private async Task Save()
|
|
{
|
|
var err = General.Validate() ?? Worktrees.Validate() ?? Prime.Validate();
|
|
if (err is not null) { ValidationError = err; return; }
|
|
ValidationError = "";
|
|
|
|
IsBusy = true;
|
|
try
|
|
{
|
|
var dto = new AppSettingsDto(
|
|
General.DefaultClaudeInstructions ?? "",
|
|
General.DefaultModel ?? "sonnet",
|
|
General.DefaultMaxTurns,
|
|
General.DefaultPermissionMode ?? "auto",
|
|
General.MaxParallelExecutions,
|
|
Worktrees.WorktreeStrategy ?? "sibling",
|
|
string.IsNullOrWhiteSpace(Worktrees.CentralWorktreeRoot) ? null : Worktrees.CentralWorktreeRoot,
|
|
Worktrees.WorktreeAutoCleanupEnabled,
|
|
Worktrees.WorktreeAutoCleanupDays,
|
|
System.Text.Json.JsonSerializer.Serialize(
|
|
General.ReportExcludedPaths
|
|
.Split('\n').Select(l => l.Trim().TrimEnd('\r')).Where(l => l.Length > 0).ToList()),
|
|
General.StandupWeekday,
|
|
Prime.DailyPrepMaxTasks);
|
|
await _worker.UpdateAppSettingsAsync(dto);
|
|
await Prime.SaveAsync();
|
|
CloseAction?.Invoke();
|
|
}
|
|
catch (Exception ex) { StatusMessage = Loc.T("vm.settingsModal.saveFailed", ex.Message); }
|
|
finally { IsBusy = false; }
|
|
}
|
|
|
|
[RelayCommand] private void Cancel() => CloseAction?.Invoke();
|
|
}
|