Skip to content

fix: show the sidebar progress bar on the five slowest tabs - #2114

Merged
laurentiu021 merged 1 commit into
mainfrom
fix/progress-bar-on-the-slow-tabs
Sep 4, 2026
Merged

fix: show the sidebar progress bar on the five slowest tabs#2114
laurentiu021 merged 1 commit into
mainfrom
fix/progress-bar-on-the-slow-tabs

Conversation

@laurentiu021

Copy link
Copy Markdown
Owner

Closes the behaviour half of #2108 — the one claim the docs PR (#2113) deliberately left in place, because weakening the sentence would have documented a gap instead of closing it.

What was wrong

NavItem forwards ViewModelBase.IsBusy to the slim progress bar under a tab's name in the sidebar. It is the only sign, while the user is looking at a different tab, that this one is still working.

Five view-models kept their own running flag and never assigned IsBusy, so their bar never appeared — and they are the slowest screens in the app:

Tab Flag How long it runs
Speed Test IsSpeedTesting a full up/down test, up to a minute
Traceroute IsTracing up to thirty hops, each with a timeout
Network Repair IsRepairing three netsh resets in sequence
About IsDownloading an ~85 MB update download
DNS & Hosts IsDnsApplying netsh, seconds

The fix

One generated hook per view-model, copying the idiom DeepCleanupViewModel already uses:

// Forward any running state to IsBusy so the sidebar progress indicator works
partial void OnIsRepairingChanged(bool value) => IsBusy = value;

The existing flag stays the single source of truth — no second flag set alongside the first.

How the scope was decided, and a correction to my own first pass

My first measurement looked for On…Changed hooks assigning IsBusy and reported 16 unforwarded flags. That was wrong: several tabs predate the hook idiom and assign IsBusy inline in the command body, which is equally correct. Re-measured as "does this view-model assign IsBusy anywhere at all", the real list was three, plus two more found by widening the flag pattern to Running/Loading suffixes — the five above.

The one remaining match is excluded with a reason: AudioSessionRowViewModel.IsUserAdjusting is a row inside the Volume Control list with no sidebar entry to draw a bar under, and the flag means "the user is dragging this slider", not work in progress.

The guard

EveryViewModelThatTracksRunningState_ForwardsItToIsBusy — a view-model declaring an [ObservableProperty] bool _is…ing/Running/Loading must assign IsBusy somewhere. Deliberately indifferent to how, so the tabs that set it inline keep passing. Vacuity floor at 12 (fourteen such view-models exist today), and the exclusion list carries its reason inline.

Verification

Mutation proof, all five files restored byte-for-byte and re-hashed. Removing any one hook:

  • the guard reds and names that file — five for five, so it is reading every view-model rather than a fixed list
  • the behaviour test reds for the right reasonTrueException: a repair is running and the shell was never told — proving the generated hook actually fires, which a source-shape guard cannot show

Baseline and post-restore green in both proofs. Regression sweep: 184 named tests across ArchitectureTests and the five tabs' test files — 213 cases green. The two reds in that run are a Dispose method my name-extraction grep picked up and the harness-only author-header case for the throwaway runner.

Builds 0 errors / 0 warnings (app + tests), dotnet format --verify-no-changes clean on both, version consistency csproj 1.76.13 = CHANGELOG 1.76.13 = SECURITY 1.76.x.

Not verified here: that the bar renders correctly on those five tabs. That needs the app running, which this machine does not do. The binding path is unchanged and shared with the 41 tabs already using it, so the risk is in the flag wiring — which is what the two proofs cover.

NavItem forwards ViewModelBase.IsBusy to the slim progress bar under a tab's name,
which is the only sign -- while the user is on another tab -- that this one is
working. Five view-models kept their own running flag and never assigned IsBusy, so
their bar never appeared, and they are the slowest screens in the app: Speed Test
(up to a minute), Traceroute (up to thirty hops), Network Repair (three netsh resets),
About (an ~85 MB download) and DNS & Hosts.

One generated hook each, copying the idiom DeepCleanupViewModel already uses, so the
existing flag stays the single source of truth.

Scope came from a corrected measurement. Looking for On...Changed hooks reported 16
unforwarded flags, which was wrong -- several tabs assign IsBusy inline in the command
body, which is equally correct. Re-measured as "assigns IsBusy anywhere" the real
list was three, plus two more once the flag pattern covered Running/Loading suffixes.

Guarded by EveryViewModelThatTracksRunningState_ForwardsItToIsBusy, indifferent to HOW
the assignment happens so the inline tabs keep passing, with the one non-tab match
(AudioSessionRowViewModel.IsUserAdjusting, a slider drag) excluded with its reason.

This also makes the README's progress-bar sentence true, which #2113 left standing on
purpose rather than weakening it.
@laurentiu021
laurentiu021 merged commit 6f5be76 into main Sep 4, 2026
5 checks passed
@laurentiu021
laurentiu021 deleted the fix/progress-bar-on-the-slow-tabs branch September 4, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant