Merge branch 'claude/desktop-auto-updater-0264a3'
ci / build and test (push) Successful in 1m56s
ci / android head (push) Failing after 6s
ci / api image (push) Successful in 45s

This commit is contained in:
2026-08-04 17:56:50 +02:00
@@ -346,7 +346,22 @@ internal sealed partial class UpdateViewModel : ObservableObject, IAsyncDisposab
State = UpdateState.Downloading; State = UpdateState.Downloading;
DownloadPercent = 0; DownloadPercent = 0;
var progress = new Progress<int>(percent => DownloadPercent = percent); // Monotonic, and it has to be. Progress<T> delivers its callbacks by posting them to the captured
// synchronisation context rather than invoking them inline, so a report can arrive after the
// download has already returned — and an unguarded assignment then puts a stale smaller number
// back on the bar and leaves it there, because nothing reports again. Seen for real: a run of this
// finished at 50 with the state already Ready.
//
// Guarding here rather than reaching for an inline IProgress, because the posting is wanted: in the
// application these callbacks come off whichever thread the updater downloads on, and an observable
// property changed off the UI thread is a binding exception rather than a stale number.
var progress = new Progress<int>(percent =>
{
if (percent > DownloadPercent)
{
DownloadPercent = percent;
}
});
await updates.DownloadAsync(found, progress, cancellationToken).ConfigureAwait(true); await updates.DownloadAsync(found, progress, cancellationToken).ConfigureAwait(true);