fix(ui): unfreeze the operation spinner and attribute rebind churn
Three causes behind the stuck indicator: - OperationStatus.End did not retire the generation, so tick work posted while the dispatcher was blocked drained afterwards and set ShowIndicator back to true on a finished operation. - The startup update check ran in Task.Run; OperationStatus writes its observable properties on the calling thread, so the final ShowIndicator=false never reached the binding. - OperationIndicator overwrote its own DataContext from the Status property, which re-targeted the call-site binding. It now scopes the DataContext to an inner panel and collapses itself when Status is null; every call site switched from DataContext= to Status=. Also gates the footer connection pill in the command body instead of CanExecute (a disabled command greyed out the ONLINE chip), and extends OperationTiming: pid per line, 4 MB rollover, fast successes dropped (failures always kept), and DetailsIsland.BindAsync now carries the selection trigger via TasksIslandViewModel.SelectFrom.
This commit is contained in:
@@ -578,7 +578,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
|
||||
|
||||
public void ShowNotes()
|
||||
{
|
||||
Bind(null);
|
||||
Bind(null, "notes");
|
||||
IsPrepMode = false;
|
||||
IsNotesMode = true;
|
||||
_ = Notes.LoadDayAsync(DateOnly.FromDateTime(DateTime.Today));
|
||||
@@ -586,13 +586,15 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
|
||||
|
||||
public void ShowPrep()
|
||||
{
|
||||
Bind(null);
|
||||
Bind(null, "prep");
|
||||
IsNotesMode = false;
|
||||
IsPrepMode = true;
|
||||
_ = Prep.LoadLastPrepLogIfEmptyAsync();
|
||||
}
|
||||
|
||||
public void Bind(TaskRowViewModel? row)
|
||||
/// <param name="source">What triggered the selection (<see cref="TasksIslandViewModel.SelectionSource"/>);
|
||||
/// logged with the bind timing so rebind churn can be attributed to a trigger.</param>
|
||||
public void Bind(TaskRowViewModel? row, string source = "?")
|
||||
{
|
||||
IsNotesMode = false;
|
||||
IsPrepMode = false;
|
||||
@@ -634,10 +636,10 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
|
||||
// agent output, so the Output tab would open empty -- its work shows up as git changes.
|
||||
SelectedTab = row.IsManual || row.HasInteractiveSession ? "git" : "output";
|
||||
|
||||
_ = BindAsync(row, ct);
|
||||
_ = BindAsync(row, ct, source);
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task BindAsync(TaskRowViewModel row, CancellationToken ct)
|
||||
private async System.Threading.Tasks.Task BindAsync(TaskRowViewModel row, CancellationToken ct, string source)
|
||||
{
|
||||
var sw = System.Diagnostics.Stopwatch.StartNew();
|
||||
var ok = false;
|
||||
@@ -735,7 +737,7 @@ public sealed partial class DetailsIslandViewModel : ViewModelBase, IDisposable
|
||||
ok = true;
|
||||
}
|
||||
catch (OperationCanceledException) { }
|
||||
finally { OperationTiming.Shared.Record("db", "DetailsIsland.BindAsync", sw.Elapsed, ok); }
|
||||
finally { OperationTiming.Shared.Record("db", $"DetailsIsland.BindAsync:{source}", sw.Elapsed, ok); }
|
||||
}
|
||||
|
||||
private async System.Threading.Tasks.Task LoadChildOutcomesAsync(string parentTaskId, CancellationToken ct)
|
||||
|
||||
Reference in New Issue
Block a user