Skip to content

Upload Progress in Sync Tasks View#1452

Merged
GianniCarlo merged 2 commits intoTortugaPower:developfrom
Hirobreak:tasks-progress
Feb 7, 2026
Merged

Upload Progress in Sync Tasks View#1452
GianniCarlo merged 2 commits intoTortugaPower:developfrom
Hirobreak:tasks-progress

Conversation

@Hirobreak
Copy link
Contributor

Purpose

  • Display the upload progress of files when the task is upload

Approach

  • Handle a dictionary to store progress and retrieve it when asking for sync tasks

Things to be aware of / Things to focus on

  • When retrieving tasks from TaskStore you need to pass the dictionary with the respective relation path-progress

Screenshots

image

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds upload progress reporting to the queued sync tasks UI by plumbing upload progress values from notifications into the task list and rendering them per task.

Changes:

  • Track upload progress updates in SyncJobScheduler and pass a progress dictionary into task retrieval.
  • Extend SyncTaskReference with a progress field and populate it in SyncTasksStorage.
  • Update profile queued tasks UI to show a circular progress indicator for upload tasks and live-update it via notifications.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
Shared/Services/Sync/SyncTasksStorage.swift Adds progress parameter to getAllTasks and maps it into SyncTaskReference.
Shared/Services/Sync/SyncJobScheduler.swift Stores per-relativePath progress and passes it when fetching queued jobs; clears progress on completion/cancel.
Shared/Services/Sync/Model/SyncTask.swift Extends SyncTaskReference to include progress.
BookPlayer/Profile/Profile/QueuedSyncTasksView.swift Passes progress/isUpload into row view for upload tasks.
BookPlayer/Profile/Profile/QueuedSyncTaskRowView.swift Displays circular progress and updates it from .uploadProgressUpdated notifications.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


/// Reference for observer
private var syncTasksObserver: NSKeyValueObservation?
private var progressObservation: NSKeyValueObservation?
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

progressObservation is introduced but never used anywhere in this type. Removing it avoids confusion and prevents dead code from accumulating.

Suggested change
private var progressObservation: NSKeyValueObservation?

Copilot uses AI. Check for mistakes.
Comment on lines +40 to +42
.onAppear {
self.progress = self.initialProgress
}
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

progress is initialized to 0.0 and only updated from initialProgress in onAppear, which can briefly render the row without progress and can get out of sync if the view stays mounted while initialProgress changes (e.g., list reload). Prefer initializing the @State from initialProgress in a custom init (e.g., _progress = State(initialValue: initialProgress)) and/or add onChange(of: initialProgress) to keep state consistent.

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +46
.onReceive(
NotificationCenter.default.publisher(for: .uploadProgressUpdated)
.receive(on: DispatchQueue.main)
) { notification in
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upload progress notifications can arrive very frequently (URLSession may report many updates per second), and this onReceive will trigger a SwiftUI re-render each time. Consider throttling/debouncing the .uploadProgressUpdated publisher before updating @State (similar to the throttling used for .folderProgressUpdated elsewhere) to reduce UI churn.

Copilot uses AI. Check for mistakes.
for path: String,
value: Double
) {
tasksProgress[path] = value
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tasksProgress[path] = value mutates shared state without any synchronization, but tasksProgress is also accessed from other executors/queues. To avoid races, perform this write on the same serialization mechanism you use for reads (e.g., lockQueue.async), or isolate tasksProgress in an actor/@mainactor context.

Suggested change
tasksProgress[path] = value
lockQueue.async { [weak self] in
self?.tasksProgress[path] = value
}

Copilot uses AI. Check for mistakes.
Comment on lines 258 to 261
public func getAllQueuedJobs() async -> [SyncTaskReference] {
_ = await initializeStoreTask?.result
return await taskStore.getAllTasks()
return await taskStore.getAllTasks(progress: tasksProgress)
}
Copy link

Copilot AI Feb 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tasksProgress is read here while it’s also being mutated from other queues/executors (main queue in the .uploadProgressUpdated sink, lockQueue in handleFinishedTask, etc.). This is a real data race risk. Consider taking a thread-safe snapshot (e.g., via lockQueue.sync { tasksProgress }) or moving tasksProgress behind an actor/@mainactor so reads and writes share the same executor.

Copilot uses AI. Check for mistakes.
@GianniCarlo GianniCarlo merged commit 2f8a378 into TortugaPower:develop Feb 7, 2026
7 checks passed
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.

2 participants