Merge feat/delete-list-button: add delete-list button to List Settings modal
This commit is contained in:
@@ -17,7 +17,7 @@ MVVM with CommunityToolkit.Mvvm source generators:
|
||||
- **TaskEditorView** — Modal dialog for task create/edit
|
||||
- **ListEditorView** — Modal dialog for list create/edit
|
||||
- **StatusBarView** — Connection status indicator, active task display
|
||||
- **ListSettingsModalView** — edits list name, working dir, default commit type, and per-list Model/SystemPrompt/AgentPath. Opened via context menu or gear button on a list row.
|
||||
- **ListSettingsModalView** — edits list name, working dir, default commit type, and per-list Model/SystemPrompt/AgentPath; also deletes the list (and its tasks) via a confirmed "Delete list" button. Opened via context menu or gear button on a list row.
|
||||
- **RepoImportModalView** — bulk-creates lists from git repos discovered under chosen parent folders. Opened via the folder button beside "New list" in the Lists island, or the "Add repos as lists…" Help-menu item. Repos already wired to a list show as disabled/"(already added)".
|
||||
- **DetailsIslandView** — contains an "Agent settings (overrides)" expander with per-task Model/SystemPrompt/AgentPath, showing inherited effective values. Disabled while task is running.
|
||||
|
||||
|
||||
@@ -48,7 +48,8 @@ public sealed partial class ListsIslandViewModel : ViewModelBase
|
||||
var vm = _services.GetRequiredService<ListSettingsModalViewModel>();
|
||||
await vm.LoadAsync(rawId, row.Name, row.WorkingDir, row.DefaultCommitType);
|
||||
await ShowListSettingsModal(vm);
|
||||
await RefreshRowAsync(row.Id);
|
||||
if (vm.Deleted) await LoadAsync();
|
||||
else await RefreshRowAsync(row.Id);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
@@ -225,7 +226,8 @@ public sealed partial class ListsIslandViewModel : ViewModelBase
|
||||
var vm = _services.GetRequiredService<ListSettingsModalViewModel>();
|
||||
await vm.LoadAsync(entity.Id, entity.Name, entity.WorkingDir, entity.DefaultCommitType);
|
||||
await ShowListSettingsModal(vm);
|
||||
await RefreshRowAsync(item.Id);
|
||||
if (vm.Deleted) await LoadAsync();
|
||||
else await RefreshRowAsync(item.Id);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,17 +1,28 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using ClaudeDo.Data;
|
||||
using ClaudeDo.Data.Models;
|
||||
using ClaudeDo.Data.Repositories;
|
||||
using ClaudeDo.Ui.Services;
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace ClaudeDo.Ui.ViewModels.Modals;
|
||||
|
||||
public sealed partial class ListSettingsModalViewModel : ViewModelBase
|
||||
{
|
||||
private readonly WorkerClient _worker;
|
||||
private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
|
||||
|
||||
public string ListId { get; set; } = "";
|
||||
|
||||
// True after the list was deleted, so the caller reloads the list nav instead of refreshing the row.
|
||||
public bool Deleted { get; private set; }
|
||||
|
||||
// Wired by the view to prompt yes/no before deleting and to surface a blocking-FK error.
|
||||
public Func<string, Task<bool>>? ConfirmAsync { get; set; }
|
||||
public Func<string, Task>? ShowErrorAsync { get; set; }
|
||||
|
||||
[ObservableProperty] private string _name = "";
|
||||
[ObservableProperty] private string _workingDir = "";
|
||||
[ObservableProperty] private string _defaultCommitType = CommitTypeRegistry.DefaultType;
|
||||
@@ -29,9 +40,10 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase
|
||||
|
||||
public Action? CloseAction { get; set; }
|
||||
|
||||
public ListSettingsModalViewModel(WorkerClient worker)
|
||||
public ListSettingsModalViewModel(WorkerClient worker, IDbContextFactory<ClaudeDoDbContext> dbFactory)
|
||||
{
|
||||
_worker = worker;
|
||||
_dbFactory = dbFactory;
|
||||
}
|
||||
|
||||
public async Task LoadAsync(
|
||||
@@ -78,6 +90,37 @@ public sealed partial class ListSettingsModalViewModel : ViewModelBase
|
||||
CloseAction?.Invoke();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task DeleteAsync()
|
||||
{
|
||||
var displayName = string.IsNullOrWhiteSpace(Name) ? "Untitled" : Name;
|
||||
if (ConfirmAsync is not null)
|
||||
{
|
||||
var ok = await ConfirmAsync($"Delete list \"{displayName}\" and all its tasks? This cannot be undone.");
|
||||
if (!ok) return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await using var ctx = await _dbFactory.CreateDbContextAsync();
|
||||
var lists = new ListRepository(ctx);
|
||||
await lists.DeleteAsync(ListId);
|
||||
}
|
||||
catch (Exception ex) when (
|
||||
(ex is Microsoft.Data.Sqlite.SqliteException
|
||||
|| ex.InnerException is Microsoft.Data.Sqlite.SqliteException)
|
||||
&& (ex.Message.Contains("FOREIGN KEY", StringComparison.OrdinalIgnoreCase)
|
||||
|| ex.InnerException?.Message.Contains("FOREIGN KEY", StringComparison.OrdinalIgnoreCase) == true))
|
||||
{
|
||||
if (ShowErrorAsync is not null)
|
||||
await ShowErrorAsync("This list has planning sessions with child tasks. Discard those first, then delete the list.");
|
||||
return;
|
||||
}
|
||||
|
||||
Deleted = true;
|
||||
CloseAction?.Invoke();
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private void Cancel() => CloseAction?.Invoke();
|
||||
|
||||
|
||||
@@ -26,6 +26,8 @@ public partial class ListsIslandView : UserControl
|
||||
{
|
||||
var window = new ListSettingsModalView { DataContext = modal };
|
||||
modal.CloseAction = () => window.Close();
|
||||
modal.ConfirmAsync = ShowConfirmAsync;
|
||||
modal.ShowErrorAsync = ShowErrorDialogAsync;
|
||||
var top = TopLevel.GetTopLevel(this) as Window;
|
||||
if (top is null) window.Show();
|
||||
else await window.ShowDialog(top);
|
||||
@@ -93,6 +95,43 @@ public partial class ListsIslandView : UserControl
|
||||
private static System.Threading.Tasks.Task JumpToTaskAsync(IslandsShellViewModel s, string listId, string taskId)
|
||||
=> JumpToTaskHelper.SelectAsync(s, listId, taskId);
|
||||
|
||||
private async System.Threading.Tasks.Task ShowErrorDialogAsync(string message)
|
||||
{
|
||||
var owner = TopLevel.GetTopLevel(this) as Window;
|
||||
if (owner is null) return;
|
||||
|
||||
var ok = new Button { Content = "OK", MinWidth = 90 };
|
||||
var dialog = new Window
|
||||
{
|
||||
Title = "Error",
|
||||
Width = 360,
|
||||
SizeToContent = SizeToContent.Height,
|
||||
CanResize = false,
|
||||
WindowStartupLocation = WindowStartupLocation.CenterOwner,
|
||||
ShowInTaskbar = false,
|
||||
Background = this.FindResource("SurfaceBrush") as IBrush,
|
||||
Content = new StackPanel
|
||||
{
|
||||
Spacing = 16,
|
||||
Margin = new Thickness(20),
|
||||
Children =
|
||||
{
|
||||
new TextBlock { Text = message, TextWrapping = TextWrapping.Wrap },
|
||||
new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
Spacing = 8,
|
||||
HorizontalAlignment = HorizontalAlignment.Right,
|
||||
Children = { ok },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
ok.Click += (_, _) => dialog.Close();
|
||||
await dialog.ShowDialog(owner);
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task<bool> ShowConfirmAsync(string message)
|
||||
{
|
||||
var owner = TopLevel.GetTopLevel(this) as Window;
|
||||
|
||||
@@ -42,6 +42,10 @@
|
||||
<Setter Property="Foreground" Value="{DynamicResource DeepBrush}"/>
|
||||
<Setter Property="FontWeight" Value="SemiBold"/>
|
||||
</Style>
|
||||
<Style Selector="Button.danger">
|
||||
<Setter Property="Background" Value="{DynamicResource BloodBrush}"/>
|
||||
<Setter Property="Foreground" Value="White"/>
|
||||
</Style>
|
||||
</Window.Styles>
|
||||
|
||||
<Border Background="{DynamicResource SurfaceBrush}"
|
||||
@@ -168,12 +172,14 @@
|
||||
Background="{DynamicResource DeepBrush}"
|
||||
BorderBrush="{DynamicResource LineBrush}"
|
||||
BorderThickness="0,1,0,0">
|
||||
<StackPanel Orientation="Horizontal" Spacing="8"
|
||||
HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||
Margin="16,0">
|
||||
<Button Content="Cancel" Command="{Binding CancelCommand}" MinWidth="90"/>
|
||||
<Button Content="Save" Classes="primary" Command="{Binding SaveCommand}" MinWidth="90"/>
|
||||
</StackPanel>
|
||||
<Grid ColumnDefinitions="Auto,*,Auto" VerticalAlignment="Center" Margin="16,0">
|
||||
<Button Grid.Column="0" Content="Delete list" Classes="danger"
|
||||
Command="{Binding DeleteCommand}" MinWidth="90"/>
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal" Spacing="8">
|
||||
<Button Content="Cancel" Command="{Binding CancelCommand}" MinWidth="90"/>
|
||||
<Button Content="Save" Classes="primary" Command="{Binding SaveCommand}" MinWidth="90"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
|
||||
</Grid>
|
||||
|
||||
Reference in New Issue
Block a user