feat(ui): open ListSettingsModal via context menu and gear button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -85,7 +85,8 @@ sealed class Program
|
|||||||
sc.AddSingleton<ListsIslandViewModel>(sp =>
|
sc.AddSingleton<ListsIslandViewModel>(sp =>
|
||||||
new ListsIslandViewModel(
|
new ListsIslandViewModel(
|
||||||
sp.GetRequiredService<IDbContextFactory<ClaudeDoDbContext>>(),
|
sp.GetRequiredService<IDbContextFactory<ClaudeDoDbContext>>(),
|
||||||
sp));
|
sp,
|
||||||
|
sp.GetRequiredService<WorkerClient>()));
|
||||||
sc.AddSingleton<TasksIslandViewModel>(sp =>
|
sc.AddSingleton<TasksIslandViewModel>(sp =>
|
||||||
new TasksIslandViewModel(sp.GetRequiredService<IDbContextFactory<ClaudeDoDbContext>>()));
|
new TasksIslandViewModel(sp.GetRequiredService<IDbContextFactory<ClaudeDoDbContext>>()));
|
||||||
sc.AddSingleton<DetailsIslandViewModel>(sp =>
|
sc.AddSingleton<DetailsIslandViewModel>(sp =>
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ public sealed partial class ListNavItemViewModel : ViewModelBase
|
|||||||
public required ListKind Kind { get; init; }
|
public required ListKind Kind { get; init; }
|
||||||
[ObservableProperty] private int _count;
|
[ObservableProperty] private int _count;
|
||||||
[ObservableProperty] private bool _isActive;
|
[ObservableProperty] private bool _isActive;
|
||||||
|
[ObservableProperty] private string? _workingDir;
|
||||||
|
[ObservableProperty] private string _defaultCommitType = "chore";
|
||||||
public string? IconKey { get; init; }
|
public string? IconKey { get; init; }
|
||||||
public string? DotColorKey { get; init; }
|
public string? DotColorKey { get; init; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
|||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using ClaudeDo.Data;
|
using ClaudeDo.Data;
|
||||||
using ClaudeDo.Data.Repositories;
|
using ClaudeDo.Data.Repositories;
|
||||||
|
using ClaudeDo.Ui.Services;
|
||||||
using ClaudeDo.Ui.ViewModels.Modals;
|
using ClaudeDo.Ui.ViewModels.Modals;
|
||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
@@ -15,12 +16,14 @@ public sealed partial class ListsIslandViewModel : ViewModelBase
|
|||||||
{
|
{
|
||||||
private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
|
private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
|
||||||
private readonly IServiceProvider? _services;
|
private readonly IServiceProvider? _services;
|
||||||
|
private readonly WorkerClient? _worker;
|
||||||
|
|
||||||
public event EventHandler? SelectionChanged;
|
public event EventHandler? SelectionChanged;
|
||||||
public event EventHandler? FocusSearchRequested;
|
public event EventHandler? FocusSearchRequested;
|
||||||
public void RequestFocusSearch() => FocusSearchRequested?.Invoke(this, EventArgs.Empty);
|
public void RequestFocusSearch() => FocusSearchRequested?.Invoke(this, EventArgs.Empty);
|
||||||
|
|
||||||
public Func<SettingsModalViewModel, Task>? ShowSettingsModal { get; set; }
|
public Func<SettingsModalViewModel, Task>? ShowSettingsModal { get; set; }
|
||||||
|
public Func<ListSettingsModalViewModel, System.Threading.Tasks.Task>? ShowListSettingsModal { get; set; }
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private async Task OpenSettings()
|
private async Task OpenSettings()
|
||||||
@@ -31,6 +34,16 @@ public sealed partial class ListsIslandViewModel : ViewModelBase
|
|||||||
await ShowSettingsModal(settingsVm);
|
await ShowSettingsModal(settingsVm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async System.Threading.Tasks.Task OpenListSettingsAsync(ListNavItemViewModel? row)
|
||||||
|
{
|
||||||
|
if (row is null || ShowListSettingsModal is null || _services is null) return;
|
||||||
|
var vm = _services.GetRequiredService<ListSettingsModalViewModel>();
|
||||||
|
await vm.LoadAsync(row.Id, row.Name, row.WorkingDir, row.DefaultCommitType);
|
||||||
|
await ShowListSettingsModal(vm);
|
||||||
|
await RefreshRowAsync(row.Id);
|
||||||
|
}
|
||||||
|
|
||||||
public ObservableCollection<ListNavItemViewModel> Items { get; } = new();
|
public ObservableCollection<ListNavItemViewModel> Items { get; } = new();
|
||||||
public ObservableCollection<ListNavItemViewModel> SmartLists { get; } = new();
|
public ObservableCollection<ListNavItemViewModel> SmartLists { get; } = new();
|
||||||
public ObservableCollection<ListNavItemViewModel> UserLists { get; } = new();
|
public ObservableCollection<ListNavItemViewModel> UserLists { get; } = new();
|
||||||
@@ -42,16 +55,20 @@ public sealed partial class ListsIslandViewModel : ViewModelBase
|
|||||||
public string MachineName { get; } = Environment.MachineName;
|
public string MachineName { get; } = Environment.MachineName;
|
||||||
public string UserInitials { get; }
|
public string UserInitials { get; }
|
||||||
|
|
||||||
public ListsIslandViewModel(IDbContextFactory<ClaudeDoDbContext> dbFactory, IServiceProvider? services = null)
|
public ListsIslandViewModel(IDbContextFactory<ClaudeDoDbContext> dbFactory, IServiceProvider? services = null, WorkerClient? worker = null)
|
||||||
{
|
{
|
||||||
_dbFactory = dbFactory;
|
_dbFactory = dbFactory;
|
||||||
_services = services;
|
_services = services;
|
||||||
|
_worker = worker;
|
||||||
var parts = Environment.UserName.Split('.', '_', '-', ' ');
|
var parts = Environment.UserName.Split('.', '_', '-', ' ');
|
||||||
UserInitials = parts.Length >= 2
|
UserInitials = parts.Length >= 2
|
||||||
? $"{parts[0][0]}{parts[1][0]}".ToUpperInvariant()
|
? $"{parts[0][0]}{parts[1][0]}".ToUpperInvariant()
|
||||||
: Environment.UserName.Length >= 2
|
: Environment.UserName.Length >= 2
|
||||||
? Environment.UserName[..2].ToUpperInvariant()
|
? Environment.UserName[..2].ToUpperInvariant()
|
||||||
: Environment.UserName.ToUpperInvariant();
|
: Environment.UserName.ToUpperInvariant();
|
||||||
|
|
||||||
|
if (_worker is not null)
|
||||||
|
_worker.ListUpdatedEvent += id => _ = RefreshRowAsync(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task LoadAsync(CancellationToken ct = default)
|
public async Task LoadAsync(CancellationToken ct = default)
|
||||||
@@ -85,6 +102,8 @@ public sealed partial class ListsIslandViewModel : ViewModelBase
|
|||||||
Kind = ListKind.User,
|
Kind = ListKind.User,
|
||||||
IconKey = "Folder",
|
IconKey = "Folder",
|
||||||
DotColorKey = dotColors[idx % dotColors.Length],
|
DotColorKey = dotColors[idx % dotColors.Length],
|
||||||
|
WorkingDir = l.WorkingDir,
|
||||||
|
DefaultCommitType = l.DefaultCommitType,
|
||||||
};
|
};
|
||||||
Items.Add(item);
|
Items.Add(item);
|
||||||
UserLists.Add(item);
|
UserLists.Add(item);
|
||||||
@@ -109,4 +128,23 @@ public sealed partial class ListsIslandViewModel : ViewModelBase
|
|||||||
foreach (var i in Items) i.IsActive = ReferenceEquals(i, value);
|
foreach (var i in Items) i.IsActive = ReferenceEquals(i, value);
|
||||||
SelectionChanged?.Invoke(this, EventArgs.Empty);
|
SelectionChanged?.Invoke(this, EventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async System.Threading.Tasks.Task RefreshRowAsync(string rowId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var rawId = rowId.StartsWith("user:") ? rowId["user:".Length..] : rowId;
|
||||||
|
var row = UserLists.FirstOrDefault(r => r.Id == rowId);
|
||||||
|
if (row is null) return;
|
||||||
|
|
||||||
|
await using var ctx = await _dbFactory.CreateDbContextAsync();
|
||||||
|
var lists = new ListRepository(ctx);
|
||||||
|
var entity = await lists.GetByIdAsync(rawId);
|
||||||
|
if (entity is null) return;
|
||||||
|
|
||||||
|
row.WorkingDir = entity.WorkingDir;
|
||||||
|
row.DefaultCommitType = entity.DefaultCommitType;
|
||||||
|
}
|
||||||
|
catch { /* best-effort refresh */ }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,7 +66,7 @@
|
|||||||
</StackPanel>
|
</StackPanel>
|
||||||
<!-- More button -->
|
<!-- More button -->
|
||||||
<Button Grid.Column="2" Classes="icon-btn" VerticalAlignment="Center"
|
<Button Grid.Column="2" Classes="icon-btn" VerticalAlignment="Center"
|
||||||
Command="{Binding OpenSettingsCommand}"
|
Command="{Binding OpenListSettingsCommand}"
|
||||||
ToolTip.Tip="Settings">
|
ToolTip.Tip="Settings">
|
||||||
<PathIcon Data="{StaticResource Icon.MoreHorizontal}"
|
<PathIcon Data="{StaticResource Icon.MoreHorizontal}"
|
||||||
Width="14" Height="14"
|
Width="14" Height="14"
|
||||||
@@ -130,9 +130,16 @@
|
|||||||
<DataTemplate DataType="vm:ListNavItemViewModel">
|
<DataTemplate DataType="vm:ListNavItemViewModel">
|
||||||
<Border Classes="list-item" Classes.active="{Binding IsActive}"
|
<Border Classes="list-item" Classes.active="{Binding IsActive}"
|
||||||
Tapped="OnItemTapped">
|
Tapped="OnItemTapped">
|
||||||
<Grid ColumnDefinitions="20,*,Auto">
|
<Border.ContextMenu>
|
||||||
|
<ContextMenu>
|
||||||
|
<MenuItem Header="Settings..."
|
||||||
|
Command="{Binding $parent[UserControl].((vm:ListsIslandViewModel)DataContext).OpenListSettingsCommand}"
|
||||||
|
CommandParameter="{Binding}"/>
|
||||||
|
</ContextMenu>
|
||||||
|
</Border.ContextMenu>
|
||||||
|
<Grid ColumnDefinitions="20,*,Auto,Auto">
|
||||||
<!-- Left accent bar for active state -->
|
<!-- Left accent bar for active state -->
|
||||||
<Border Grid.Column="0" Grid.ColumnSpan="3"
|
<Border Grid.Column="0" Grid.ColumnSpan="4"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
CornerRadius="8" IsHitTestVisible="False"
|
CornerRadius="8" IsHitTestVisible="False"
|
||||||
IsVisible="{Binding IsActive}">
|
IsVisible="{Binding IsActive}">
|
||||||
@@ -158,6 +165,18 @@
|
|||||||
FontFamily="{DynamicResource MonoFamily}" FontSize="10"
|
FontFamily="{DynamicResource MonoFamily}" FontSize="10"
|
||||||
Foreground="{DynamicResource TextFaintBrush}"
|
Foreground="{DynamicResource TextFaintBrush}"
|
||||||
VerticalAlignment="Center"/>
|
VerticalAlignment="Center"/>
|
||||||
|
<!-- Gear button -->
|
||||||
|
<Button Grid.Column="3"
|
||||||
|
Content="⚙"
|
||||||
|
ToolTip.Tip="Settings..."
|
||||||
|
Background="Transparent"
|
||||||
|
BorderThickness="0"
|
||||||
|
Padding="4,0"
|
||||||
|
FontSize="11"
|
||||||
|
Foreground="{DynamicResource TextFaintBrush}"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Command="{Binding $parent[UserControl].((vm:ListsIslandViewModel)DataContext).OpenListSettingsCommand}"
|
||||||
|
CommandParameter="{Binding}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
|
|||||||
@@ -17,6 +17,14 @@ public partial class ListsIslandView : UserControl
|
|||||||
{
|
{
|
||||||
vm.FocusSearchRequested += (_, _) => SearchBox.Focus();
|
vm.FocusSearchRequested += (_, _) => SearchBox.Focus();
|
||||||
vm.ShowSettingsModal = ShowSettingsAsync;
|
vm.ShowSettingsModal = ShowSettingsAsync;
|
||||||
|
vm.ShowListSettingsModal = async modal =>
|
||||||
|
{
|
||||||
|
var window = new ListSettingsModalView { DataContext = modal };
|
||||||
|
modal.CloseAction = () => window.Close();
|
||||||
|
var top = TopLevel.GetTopLevel(this) as Window;
|
||||||
|
if (top is null) window.Show();
|
||||||
|
else await window.ShowDialog(top);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user