Skip to content

Commit 1f06c3e

Browse files
committed
Fix retry button by re-adding failed downloads to tracking dict before resuming
1 parent 944d27a commit 1f06c3e

5 files changed

Lines changed: 33 additions & 13 deletions

File tree

StabilityMatrix.Avalonia/ViewModels/Progress/DownloadProgressItemViewModel.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,11 @@ public override Task Resume()
9999

100100
/// <inheritdoc />
101101
/// Resets the internal retry counter so the user gets a fresh 3-attempt budget,
102-
/// then re-queues the download exactly as if it were being resumed from pause.
102+
/// then re-registers the download in the service dictionary (it was removed on
103+
/// failure) and resumes it through the normal concurrency queue.
103104
public override Task Retry()
104105
{
105106
download.ResetAttempts();
106-
return downloadService.TryResumeDownload(download);
107+
return downloadService.TryRestartDownload(download);
107108
}
108109
}

StabilityMatrix.Avalonia/Views/ProgressManagerPage.axaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@
120120
Command="{Binding RetryCommand}"
121121
IsVisible="{Binding CanRetry}"
122122
ToolTip.Tip="Retry download">
123-
<ui:SymbolIcon Symbol="ArrowCounterclockwise" />
123+
<ui:SymbolIcon Symbol="Refresh" />
124124
</Button>
125125
</StackPanel>
126126

StabilityMatrix.Core/Models/TrackedDownload.cs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -318,11 +318,8 @@ private void DoCleanup()
318318
}
319319

320320
/// <summary>
321-
/// Returns true for transient network/SSL exceptions that are safe to retry.
322-
/// Catches direct IOException/AuthenticationException, the same types wrapped as
323-
/// InnerException (common AggregateException shape from HttpClient), and any leg
324-
/// of a multi-inner AggregateException — covering VPN tunnel resets
325-
/// ("Connection reset by peer") and TLS re-key failures (OpenSSL SSL_ERROR_SSL).
321+
/// Returns true for transient network/SSL exceptions that are safe to retry (ie: VPN tunnel resets or TLS re-key failures)
322+
/// (IOException, AuthenticationException, or either wrapped in an AggregateException).
326323
/// </summary>
327324
private static bool IsTransientNetworkException(Exception? ex) =>
328325
ex is IOException or AuthenticationException
@@ -385,8 +382,7 @@ private void OnDownloadTaskCompleted(Task task)
385382
attempts
386383
);
387384

388-
// Persist Inactive state to disk before the delay so that a restart
389-
// during the backoff window loads the download as a resumable entry.
385+
// Persist Inactive to disk before the delay so a restart during backoff loads it as resumable.
390386
OnProgressStateChanging(ProgressState.Inactive);
391387
ProgressState = ProgressState.Inactive;
392388
OnProgressStateChanged(ProgressState.Inactive);
@@ -424,13 +420,14 @@ private void OnDownloadTaskCompleted(Task task)
424420
}
425421

426422
/// <summary>
427-
/// Resets the internal retry attempt counter back to zero.
428-
/// Call this before a user-initiated retry so the download gets
429-
/// a fresh budget of automatic retries on the new attempt.
423+
/// Resets the retry counter and silently sets state to Inactive without firing events.
424+
/// Must be called before re-adding to TrackedDownloadService to avoid events
425+
/// firing while the download is absent from the dictionary.
430426
/// </summary>
431427
public void ResetAttempts()
432428
{
433429
attempts = 0;
430+
ProgressState = ProgressState.Inactive;
434431
}
435432

436433
public void SetDownloadService(IDownloadService service)

StabilityMatrix.Core/Services/ITrackedDownloadService.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,7 @@ TrackedDownload NewDownload(string downloadUrl, FilePath downloadPath) =>
1515
NewDownload(new Uri(downloadUrl), downloadPath);
1616
Task TryStartDownload(TrackedDownload download);
1717
Task TryResumeDownload(TrackedDownload download);
18+
Task TryRestartDownload(TrackedDownload download);
19+
1820
void UpdateMaxConcurrentDownloads(int newMax);
1921
}

StabilityMatrix.Core/Services/TrackedDownloadService.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,26 @@ public async Task TryStartDownload(TrackedDownload download)
129129
}
130130
}
131131

132+
public async Task TryRestartDownload(TrackedDownload download)
133+
{
134+
// Re-create the backing JSON file and re-add to the dictionary.
135+
// Downloads are removed on failure, so this restores the tracking entry
136+
// so that subsequent state-change events can persist normally.
137+
var downloadsDir = new DirectoryPath(settingsManager.DownloadsDirectory);
138+
downloadsDir.Create();
139+
var jsonFile = downloadsDir.JoinFile($"{download.Id}.json");
140+
141+
var jsonFileStream = jsonFile.Info.Open(FileMode.Create, FileAccess.ReadWrite, FileShare.Read);
142+
var json = JsonSerializer.Serialize(download);
143+
jsonFileStream.Write(Encoding.UTF8.GetBytes(json));
144+
jsonFileStream.Flush();
145+
146+
// Handlers are already attached from the original AddDownload call.
147+
downloads.TryAdd(download.Id, (download, jsonFileStream));
148+
149+
await TryResumeDownload(download).ConfigureAwait(false);
150+
}
151+
132152
public async Task TryResumeDownload(TrackedDownload download)
133153
{
134154
if (IsQueueEnabled && ActiveDownloads >= MaxConcurrentDownloads)

0 commit comments

Comments
 (0)