Compare commits
1 Commits
feat/subta
...
v1.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
| 33fedc7e26 |
@@ -8,21 +8,14 @@ using ClaudeDo.Ui.ViewModels;
|
|||||||
using Microsoft.EntityFrameworkCore;
|
using Microsoft.EntityFrameworkCore;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace ClaudeDo.App;
|
namespace ClaudeDo.App;
|
||||||
|
|
||||||
sealed class Program
|
sealed class Program
|
||||||
{
|
{
|
||||||
[DllImport("shell32.dll", CharSet = CharSet.Unicode)]
|
|
||||||
private static extern int SetCurrentProcessExplicitAppUserModelID(string appId);
|
|
||||||
|
|
||||||
[STAThread]
|
[STAThread]
|
||||||
public static void Main(string[] args)
|
public static void Main(string[] args)
|
||||||
{
|
{
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
SetCurrentProcessExplicitAppUserModelID("ClaudeDo.App");
|
|
||||||
|
|
||||||
var services = BuildServices();
|
var services = BuildServices();
|
||||||
App.Services = services;
|
App.Services = services;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
using System.Collections.ObjectModel;
|
|
||||||
using Avalonia.Media;
|
using Avalonia.Media;
|
||||||
using ClaudeDo.Data;
|
|
||||||
using ClaudeDo.Data.Models;
|
using ClaudeDo.Data.Models;
|
||||||
using ClaudeDo.Data.Repositories;
|
|
||||||
using CommunityToolkit.Mvvm.ComponentModel;
|
using CommunityToolkit.Mvvm.ComponentModel;
|
||||||
using CommunityToolkit.Mvvm.Input;
|
using CommunityToolkit.Mvvm.Input;
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;
|
||||||
|
|
||||||
namespace ClaudeDo.Ui.ViewModels;
|
namespace ClaudeDo.Ui.ViewModels;
|
||||||
@@ -19,11 +15,6 @@ public partial class TaskItemViewModel : ViewModelBase
|
|||||||
[ObservableProperty] private string? _description;
|
[ObservableProperty] private string? _description;
|
||||||
[ObservableProperty] private TaskStatus _status;
|
[ObservableProperty] private TaskStatus _status;
|
||||||
[ObservableProperty] private bool _isStarting;
|
[ObservableProperty] private bool _isStarting;
|
||||||
[ObservableProperty] private bool _isExpanded;
|
|
||||||
[ObservableProperty] private bool _hasSubtasks;
|
|
||||||
[ObservableProperty] private int _subtaskCount;
|
|
||||||
|
|
||||||
public ObservableCollection<SubtaskItemViewModel> Subtasks { get; } = new();
|
|
||||||
|
|
||||||
public string Id { get; }
|
public string Id { get; }
|
||||||
public string ListId { get; }
|
public string ListId { get; }
|
||||||
@@ -32,13 +23,9 @@ public partial class TaskItemViewModel : ViewModelBase
|
|||||||
private readonly Func<string, Task>? _runNow;
|
private readonly Func<string, Task>? _runNow;
|
||||||
private readonly Func<bool> _canRunNow;
|
private readonly Func<bool> _canRunNow;
|
||||||
private readonly Func<string, Task>? _toggleDone;
|
private readonly Func<string, Task>? _toggleDone;
|
||||||
private readonly IDbContextFactory<ClaudeDoDbContext> _dbFactory;
|
|
||||||
private bool _subtasksLoaded;
|
|
||||||
|
|
||||||
public TaskItemViewModel(TaskEntity entity, IReadOnlyList<TagEntity> tags,
|
public TaskItemViewModel(TaskEntity entity, IReadOnlyList<TagEntity> tags,
|
||||||
Func<string, Task>? runNow, Func<bool> canRunNow,
|
Func<string, Task>? runNow, Func<bool> canRunNow, Func<string, Task>? toggleDone = null)
|
||||||
IDbContextFactory<ClaudeDoDbContext> dbFactory, int subtaskCount,
|
|
||||||
Func<string, Task>? toggleDone = null)
|
|
||||||
{
|
{
|
||||||
Entity = entity;
|
Entity = entity;
|
||||||
Id = entity.Id;
|
Id = entity.Id;
|
||||||
@@ -52,9 +39,6 @@ public partial class TaskItemViewModel : ViewModelBase
|
|||||||
_runNow = runNow;
|
_runNow = runNow;
|
||||||
_canRunNow = canRunNow;
|
_canRunNow = canRunNow;
|
||||||
_toggleDone = toggleDone;
|
_toggleDone = toggleDone;
|
||||||
_dbFactory = dbFactory;
|
|
||||||
_subtaskCount = subtaskCount;
|
|
||||||
_hasSubtasks = subtaskCount > 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsDone => Status == TaskStatus.Done;
|
public bool IsDone => Status == TaskStatus.Done;
|
||||||
@@ -120,55 +104,4 @@ public partial class TaskItemViewModel : ViewModelBase
|
|||||||
if (_toggleDone is not null)
|
if (_toggleDone is not null)
|
||||||
await _toggleDone(Id);
|
await _toggleDone(Id);
|
||||||
}
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private async Task ToggleExpanded()
|
|
||||||
{
|
|
||||||
IsExpanded = !IsExpanded;
|
|
||||||
if (IsExpanded && !_subtasksLoaded)
|
|
||||||
await LoadSubtasksAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
private async Task LoadSubtasksAsync()
|
|
||||||
{
|
|
||||||
using var context = _dbFactory.CreateDbContext();
|
|
||||||
var repo = new SubtaskRepository(context);
|
|
||||||
var entities = await repo.GetByTaskIdAsync(Id);
|
|
||||||
Subtasks.Clear();
|
|
||||||
foreach (var e in entities)
|
|
||||||
Subtasks.Add(SubtaskItemViewModel.From(e));
|
|
||||||
_subtasksLoaded = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
[RelayCommand]
|
|
||||||
private async Task ToggleSubtaskDone(string subtaskId)
|
|
||||||
{
|
|
||||||
var vm = Subtasks.FirstOrDefault(s => s.Id == subtaskId);
|
|
||||||
if (vm is null) return;
|
|
||||||
vm.Completed = !vm.Completed;
|
|
||||||
|
|
||||||
using var context = _dbFactory.CreateDbContext();
|
|
||||||
var entity = await context.Subtasks.FindAsync(subtaskId);
|
|
||||||
if (entity is not null)
|
|
||||||
{
|
|
||||||
entity.Completed = vm.Completed;
|
|
||||||
await context.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task RefreshSubtasksAsync(int newCount)
|
|
||||||
{
|
|
||||||
SubtaskCount = newCount;
|
|
||||||
HasSubtasks = newCount > 0;
|
|
||||||
if (!HasSubtasks)
|
|
||||||
{
|
|
||||||
IsExpanded = false;
|
|
||||||
Subtasks.Clear();
|
|
||||||
_subtasksLoaded = false;
|
|
||||||
}
|
|
||||||
else if (_subtasksLoaded || IsExpanded)
|
|
||||||
{
|
|
||||||
await LoadSubtasksAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,17 +91,10 @@ public partial class TaskListViewModel : ViewModelBase
|
|||||||
using var context = _dbFactory.CreateDbContext();
|
using var context = _dbFactory.CreateDbContext();
|
||||||
var taskRepo = new TaskRepository(context);
|
var taskRepo = new TaskRepository(context);
|
||||||
var entities = await taskRepo.GetByListIdAsync(listId);
|
var entities = await taskRepo.GetByListIdAsync(listId);
|
||||||
var taskIds = entities.Select(e => e.Id).ToList();
|
|
||||||
var subtaskCounts = await context.Subtasks
|
|
||||||
.Where(s => taskIds.Contains(s.TaskId))
|
|
||||||
.GroupBy(s => s.TaskId)
|
|
||||||
.ToDictionaryAsync(g => g.Key, g => g.Count());
|
|
||||||
foreach (var e in entities)
|
foreach (var e in entities)
|
||||||
{
|
{
|
||||||
var tags = await taskRepo.GetEffectiveTagsAsync(e.Id);
|
var tags = await taskRepo.GetEffectiveTagsAsync(e.Id);
|
||||||
subtaskCounts.TryGetValue(e.Id, out var count);
|
Tasks.Add(new TaskItemViewModel(e, tags, RunNowAsync, () => _worker.IsConnected, ToggleDoneAsync));
|
||||||
Tasks.Add(new TaskItemViewModel(e, tags, RunNowAsync, () => _worker.IsConnected,
|
|
||||||
_dbFactory, count, ToggleDoneAsync));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@@ -142,8 +135,7 @@ public partial class TaskListViewModel : ViewModelBase
|
|||||||
var taskRepo = new TaskRepository(context);
|
var taskRepo = new TaskRepository(context);
|
||||||
await taskRepo.AddAsync(entity);
|
await taskRepo.AddAsync(entity);
|
||||||
var tags = await taskRepo.GetEffectiveTagsAsync(entity.Id);
|
var tags = await taskRepo.GetEffectiveTagsAsync(entity.Id);
|
||||||
var vm = new TaskItemViewModel(entity, tags, RunNowAsync, () => _worker.IsConnected,
|
var vm = new TaskItemViewModel(entity, tags, RunNowAsync, () => _worker.IsConnected, ToggleDoneAsync);
|
||||||
_dbFactory, 0, ToggleDoneAsync);
|
|
||||||
Tasks.Add(vm);
|
Tasks.Add(vm);
|
||||||
SelectedTask = vm;
|
SelectedTask = vm;
|
||||||
InlineAddTitle = "";
|
InlineAddTitle = "";
|
||||||
@@ -191,8 +183,7 @@ public partial class TaskListViewModel : ViewModelBase
|
|||||||
}
|
}
|
||||||
|
|
||||||
var tags = await taskRepo.GetEffectiveTagsAsync(saved.Id);
|
var tags = await taskRepo.GetEffectiveTagsAsync(saved.Id);
|
||||||
Tasks.Add(new TaskItemViewModel(saved, tags, RunNowAsync, () => _worker.IsConnected,
|
Tasks.Add(new TaskItemViewModel(saved, tags, RunNowAsync, () => _worker.IsConnected, ToggleDoneAsync));
|
||||||
_dbFactory, 0, ToggleDoneAsync));
|
|
||||||
|
|
||||||
// Auto wake-queue if agent+queued
|
// Auto wake-queue if agent+queued
|
||||||
if (saved.Status == TaskStatus.Queued &&
|
if (saved.Status == TaskStatus.Queued &&
|
||||||
@@ -291,11 +282,7 @@ public partial class TaskListViewModel : ViewModelBase
|
|||||||
}
|
}
|
||||||
var tags = await taskRepo.GetEffectiveTagsAsync(taskId);
|
var tags = await taskRepo.GetEffectiveTagsAsync(taskId);
|
||||||
if (existing is not null)
|
if (existing is not null)
|
||||||
{
|
|
||||||
existing.Refresh(entity, tags);
|
existing.Refresh(entity, tags);
|
||||||
var subtaskCount = await context.Subtasks.CountAsync(s => s.TaskId == taskId);
|
|
||||||
await existing.RefreshSubtasksAsync(subtaskCount);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RunNowAsync(string taskId)
|
private async Task RunNowAsync(string taskId)
|
||||||
|
|||||||
@@ -31,128 +31,72 @@
|
|||||||
KeyDown="OnTaskListKeyDown">
|
KeyDown="OnTaskListKeyDown">
|
||||||
<ListBox.ItemTemplate>
|
<ListBox.ItemTemplate>
|
||||||
<DataTemplate x:DataType="vm:TaskItemViewModel">
|
<DataTemplate x:DataType="vm:TaskItemViewModel">
|
||||||
<Grid RowDefinitions="Auto,Auto"
|
<Grid ColumnDefinitions="Auto,*" Margin="4,4"
|
||||||
Background="Transparent"
|
Background="Transparent"
|
||||||
Opacity="{Binding RowOpacity}">
|
Opacity="{Binding RowOpacity}"
|
||||||
<!-- Row 0: Task row -->
|
DoubleTapped="OnTaskItemDoubleTapped"
|
||||||
<Grid Grid.Row="0" ColumnDefinitions="20,Auto,*" Margin="4,4"
|
PointerPressed="OnTaskItemPointerPressed">
|
||||||
DoubleTapped="OnTaskItemDoubleTapped"
|
<Grid.ContextFlyout>
|
||||||
PointerPressed="OnTaskItemPointerPressed">
|
<MenuFlyout>
|
||||||
<Grid.ContextFlyout>
|
<MenuItem Header="Edit"
|
||||||
<MenuFlyout>
|
Command="{Binding #Root.((vm:TaskListViewModel)DataContext).EditTaskCommand}"/>
|
||||||
<MenuItem Header="Edit"
|
<MenuItem Header="Delete"
|
||||||
Command="{Binding #Root.((vm:TaskListViewModel)DataContext).EditTaskCommand}"/>
|
Command="{Binding #Root.((vm:TaskListViewModel)DataContext).DeleteTaskCommand}"/>
|
||||||
<MenuItem Header="Delete"
|
<Separator/>
|
||||||
Command="{Binding #Root.((vm:TaskListViewModel)DataContext).DeleteTaskCommand}"/>
|
<MenuItem Header="Run Now"
|
||||||
<Separator/>
|
Command="{Binding RunNowCommand}"/>
|
||||||
<MenuItem Header="Run Now"
|
</MenuFlyout>
|
||||||
Command="{Binding RunNowCommand}"/>
|
</Grid.ContextFlyout>
|
||||||
</MenuFlyout>
|
|
||||||
</Grid.ContextFlyout>
|
|
||||||
|
|
||||||
<!-- Expand/collapse chevron -->
|
<!-- Circular checkbox -->
|
||||||
<Button Grid.Column="0"
|
<Border Grid.Column="0" Width="22" Height="22"
|
||||||
Command="{Binding ToggleExpandedCommand}"
|
CornerRadius="11"
|
||||||
IsVisible="{Binding HasSubtasks}"
|
BorderThickness="2"
|
||||||
Background="Transparent"
|
BorderBrush="{Binding StatusText, Converter={x:Static conv:CheckboxBorderConverter.Instance}}"
|
||||||
BorderThickness="0"
|
Background="Transparent"
|
||||||
Padding="0"
|
VerticalAlignment="Center" Margin="0,0,10,0"
|
||||||
Width="16" Height="16"
|
Cursor="Hand"
|
||||||
VerticalAlignment="Center"
|
PointerPressed="OnCheckboxPressed">
|
||||||
Cursor="Hand">
|
<Panel>
|
||||||
<Panel>
|
<!-- Checkmark for done -->
|
||||||
<Canvas Width="10" Height="10"
|
<Canvas Width="12" Height="12"
|
||||||
IsVisible="{Binding !IsExpanded}">
|
IsVisible="{Binding IsDone}"
|
||||||
<Path Stroke="{StaticResource TextDimBrush}" StrokeThickness="1.5"
|
HorizontalAlignment="Center" VerticalAlignment="Center">
|
||||||
Data="M 2,0 L 8,5 L 2,10"/>
|
<Path Stroke="{StaticResource StatusGreenBrush}" StrokeThickness="2"
|
||||||
</Canvas>
|
Data="M 1,6 L 4.5,9.5 L 11,3"/>
|
||||||
<Canvas Width="10" Height="10"
|
</Canvas>
|
||||||
IsVisible="{Binding IsExpanded}">
|
<!-- Running dot -->
|
||||||
<Path Stroke="{StaticResource TextDimBrush}" StrokeThickness="1.5"
|
<Ellipse Width="8" Height="8"
|
||||||
Data="M 0,2 L 5,8 L 10,2"/>
|
Fill="{StaticResource StatusOrangeBrush}"
|
||||||
</Canvas>
|
IsVisible="{Binding IsRunning}"
|
||||||
</Panel>
|
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
</Button>
|
<!-- Starting dot -->
|
||||||
|
<Ellipse Width="8" Height="8" Fill="#FFD700"
|
||||||
|
IsVisible="{Binding IsStarting}"
|
||||||
|
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
|
</Panel>
|
||||||
|
</Border>
|
||||||
|
|
||||||
<!-- Circular checkbox -->
|
<!-- Task content -->
|
||||||
<Border Grid.Column="1" Width="22" Height="22"
|
<StackPanel Grid.Column="1" VerticalAlignment="Center">
|
||||||
CornerRadius="11"
|
<TextBlock Text="{Binding Title}" FontWeight="Medium"
|
||||||
BorderThickness="2"
|
Foreground="{Binding TitleForeground}"
|
||||||
BorderBrush="{Binding StatusText, Converter={x:Static conv:CheckboxBorderConverter.Instance}}"
|
TextDecorations="{Binding TitleDecorations}"
|
||||||
Background="Transparent"
|
TextTrimming="CharacterEllipsis"/>
|
||||||
VerticalAlignment="Center" Margin="0,0,10,0"
|
<TextBlock FontSize="11"
|
||||||
Cursor="Hand"
|
Foreground="{StaticResource TextDimBrush}"
|
||||||
PointerPressed="OnCheckboxPressed">
|
IsVisible="{Binding TagsText, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
|
||||||
<Panel>
|
<TextBlock.Text>
|
||||||
<Canvas Width="12" Height="12"
|
<MultiBinding StringFormat="{}{0} · {1}">
|
||||||
IsVisible="{Binding IsDone}"
|
<Binding Path="TagsText"/>
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Center">
|
<Binding Path="StatusText"/>
|
||||||
<Path Stroke="{StaticResource StatusGreenBrush}" StrokeThickness="2"
|
</MultiBinding>
|
||||||
Data="M 1,6 L 4.5,9.5 L 11,3"/>
|
</TextBlock.Text>
|
||||||
</Canvas>
|
</TextBlock>
|
||||||
<Ellipse Width="8" Height="8"
|
<TextBlock Text="{Binding StatusText}" FontSize="11"
|
||||||
Fill="{StaticResource StatusOrangeBrush}"
|
Foreground="{StaticResource TextDimBrush}"
|
||||||
IsVisible="{Binding IsRunning}"
|
IsVisible="{Binding TagsText, Converter={x:Static StringConverters.IsNullOrEmpty}}"/>
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
</StackPanel>
|
||||||
<Ellipse Width="8" Height="8" Fill="#FFD700"
|
|
||||||
IsVisible="{Binding IsStarting}"
|
|
||||||
HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
|
||||||
</Panel>
|
|
||||||
</Border>
|
|
||||||
|
|
||||||
<!-- Task content -->
|
|
||||||
<StackPanel Grid.Column="2" VerticalAlignment="Center">
|
|
||||||
<TextBlock Text="{Binding Title}" FontWeight="Medium"
|
|
||||||
Foreground="{Binding TitleForeground}"
|
|
||||||
TextDecorations="{Binding TitleDecorations}"
|
|
||||||
TextTrimming="CharacterEllipsis"/>
|
|
||||||
<TextBlock FontSize="11"
|
|
||||||
Foreground="{StaticResource TextDimBrush}"
|
|
||||||
IsVisible="{Binding TagsText, Converter={x:Static StringConverters.IsNotNullOrEmpty}}">
|
|
||||||
<TextBlock.Text>
|
|
||||||
<MultiBinding StringFormat="{}{0} · {1}">
|
|
||||||
<Binding Path="TagsText"/>
|
|
||||||
<Binding Path="StatusText"/>
|
|
||||||
</MultiBinding>
|
|
||||||
</TextBlock.Text>
|
|
||||||
</TextBlock>
|
|
||||||
<TextBlock Text="{Binding StatusText}" FontSize="11"
|
|
||||||
Foreground="{StaticResource TextDimBrush}"
|
|
||||||
IsVisible="{Binding TagsText, Converter={x:Static StringConverters.IsNullOrEmpty}}"/>
|
|
||||||
</StackPanel>
|
|
||||||
</Grid>
|
|
||||||
|
|
||||||
<!-- Row 1: Subtask list (visible when expanded) -->
|
|
||||||
<ItemsControl Grid.Row="1"
|
|
||||||
ItemsSource="{Binding Subtasks}"
|
|
||||||
IsVisible="{Binding IsExpanded}"
|
|
||||||
Margin="40,0,0,4">
|
|
||||||
<ItemsControl.ItemTemplate>
|
|
||||||
<DataTemplate x:DataType="vm:SubtaskItemViewModel">
|
|
||||||
<Grid ColumnDefinitions="Auto,*" Margin="0,2"
|
|
||||||
PointerPressed="OnSubtaskPointerPressed">
|
|
||||||
<Grid.ContextFlyout>
|
|
||||||
<MenuFlyout>
|
|
||||||
<MenuItem Header="Edit Task"
|
|
||||||
Command="{Binding #Root.((vm:TaskListViewModel)DataContext).EditTaskCommand}"/>
|
|
||||||
</MenuFlyout>
|
|
||||||
</Grid.ContextFlyout>
|
|
||||||
<CheckBox Grid.Column="0"
|
|
||||||
IsChecked="{Binding Completed, Mode=OneWay}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
Margin="0,0,6,0"
|
|
||||||
MinWidth="0"
|
|
||||||
Click="OnSubtaskCheckboxClick"/>
|
|
||||||
<TextBlock Grid.Column="1"
|
|
||||||
Text="{Binding Title}"
|
|
||||||
FontSize="12"
|
|
||||||
Foreground="{StaticResource TextDimBrush}"
|
|
||||||
VerticalAlignment="Center"
|
|
||||||
TextTrimming="CharacterEllipsis"/>
|
|
||||||
</Grid>
|
|
||||||
</DataTemplate>
|
|
||||||
</ItemsControl.ItemTemplate>
|
|
||||||
</ItemsControl>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ListBox.ItemTemplate>
|
</ListBox.ItemTemplate>
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
using System.Collections.ObjectModel;
|
|
||||||
using Avalonia.Controls;
|
using Avalonia.Controls;
|
||||||
using Avalonia.Input;
|
using Avalonia.Input;
|
||||||
using Avalonia.Interactivity;
|
using Avalonia.Interactivity;
|
||||||
@@ -98,29 +97,6 @@ public partial class TaskListView : UserControl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSubtaskPointerPressed(object? sender, PointerPressedEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.GetCurrentPoint(null).Properties.IsRightButtonPressed
|
|
||||||
&& sender is Control { DataContext: SubtaskItemViewModel subtask }
|
|
||||||
&& DataContext is TaskListViewModel vm)
|
|
||||||
{
|
|
||||||
var parent = vm.Tasks.FirstOrDefault(t => t.Subtasks.Contains(subtask));
|
|
||||||
if (parent is not null)
|
|
||||||
vm.SelectedTask = parent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void OnSubtaskCheckboxClick(object? sender, RoutedEventArgs e)
|
|
||||||
{
|
|
||||||
if (sender is CheckBox { DataContext: SubtaskItemViewModel subtask }
|
|
||||||
&& DataContext is TaskListViewModel vm)
|
|
||||||
{
|
|
||||||
var parent = vm.Tasks.FirstOrDefault(t => t.Subtasks.Contains(subtask));
|
|
||||||
if (parent is not null)
|
|
||||||
await parent.ToggleSubtaskDoneCommand.ExecuteAsync(subtask.Id);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void FocusInlineAdd()
|
public void FocusInlineAdd()
|
||||||
{
|
{
|
||||||
this.FindControl<TextBox>("InlineAddBox")?.Focus();
|
this.FindControl<TextBox>("InlineAddBox")?.Focus();
|
||||||
|
|||||||
Reference in New Issue
Block a user