Skip to content

Commit

Permalink
feat: added a way to cancel ongoing download with context menu
Browse files Browse the repository at this point in the history
  • Loading branch information
danielbady committed May 24, 2024
1 parent d96ef5d commit c6a2f5c
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 40 deletions.
4 changes: 2 additions & 2 deletions App/Mochi.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -376,7 +376,7 @@
CURRENT_PROJECT_VERSION = 5;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = ..;
DEVELOPMENT_TEAM = GYXF583PFT;
DEVELOPMENT_TEAM = "";
ENABLE_PREVIEWS = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = YES;
Expand Down Expand Up @@ -418,7 +418,7 @@
CURRENT_PROJECT_VERSION = 5;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_ASSET_PATHS = ..;
DEVELOPMENT_TEAM = GYXF583PFT;
DEVELOPMENT_TEAM = "";
ENABLE_PREVIEWS = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GENERATE_INFOPLIST_FILE = YES;
Expand Down
2 changes: 2 additions & 0 deletions Sources/Clients/OfflineManagerClient/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public struct OfflineManagerClient {
public var cache: @Sendable (CacheAsset) async throws -> Void
public var remove: @Sendable (RemoveType, String, String?) async throws -> Void
public var togglePause: @Sendable (Int) async throws -> Void
public var cancel: @Sendable (Int) async throws -> Void
public var observeDownloading: @Sendable () -> AsyncStream<[DownloadingItem]>
}

Expand All @@ -31,6 +32,7 @@ extension OfflineManagerClient: TestDependencyKey {
cache: unimplemented("\(Self.self).cache"),
remove: unimplemented("\(Self.self).remove"),
togglePause: unimplemented("\(Self.self).togglePause"),
cancel: unimplemented("\(Self.self).cancel"),
observeDownloading: unimplemented("\(Self.self).observeDownloading")
)
}
Expand Down
13 changes: 13 additions & 0 deletions Sources/Clients/OfflineManagerClient/Live.swift
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,9 @@ extension OfflineManagerClient: DependencyKey {
togglePause: { taskId in
downloadManager.togglePauseDownload(taskId)
},
cancel: { taskId in
downloadManager.cancelDownload(taskId)
},
observeDownloading: {
.init { continuation in
let cancellable = Task.detached {
Expand Down Expand Up @@ -178,6 +181,16 @@ private class OfflineDownloadManager: NSObject {
}
}

func cancelDownload(_ taskId: Int) {
downloadSession.getAllTasks { taskArray in
taskArray.first(where: { $0.taskIdentifier == taskId })?.cancel()
if let idx = self.downloadingItems.firstIndex(where: { $0.taskId == taskId }) {
self.downloadingItems.remove(at: idx)
}
NotificationCenter.default.post(name: .AssetDownloadTaskChanged, object: nil, userInfo: ["type": Notification.Name.AssetDownloadStateChanged, "taskId": taskId, "status": OfflineManagerClient.StatusType.cancelled])
}
}

func restorePendingDownloads() {
downloadSession.getAllTasks { tasksArray in
for task in tasksArray {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ extension DownloadQueueFeature: Reducer {
await send(.internal(.updateDownloadingItems(items)))
}
}

case let .view(.didTapCancelDownload(item)):
return .run { send in
try await offlineManagerClient.cancel(item.taskId)
}

case let .view(.pause(item)):
return .run { send in
Expand Down
90 changes: 52 additions & 38 deletions Sources/Features/DownloadQueue/DownloadQueueFeature+View.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,51 +37,65 @@ extension DownloadQueueFeature.View: View {
}

Spacer()
switch item.status {
case .suspended:
CircularProgressView(progress: item.percentComplete, barStyle: .init(fill: Theme.pastelRed.opacity(0.4), width: 4, blurRadius: 0)) {
Image(systemName: "play.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.padding(6)
.foregroundStyle(Theme.pastelRed)
}
.onTapGesture {
viewStore.send(.pause(item))
}
.frame(width: 30, height: 30)
.animation(.easeInOut, value: item.status)
case .finished:
Image(systemName: "checkmark.circle.fill")
switch item.status {
case .suspended:
CircularProgressView(progress: item.percentComplete, barStyle: .init(fill: Theme.pastelRed.opacity(0.4), width: 4, blurRadius: 0)) {
Image(systemName: "play.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 29, height: 29)
.padding(6)
.foregroundStyle(Theme.pastelRed)
.animation(.easeInOut, value: item.status)
case .cancelled:
EmptyView()
case .downloading:
CircularProgressView(progress: item.percentComplete, barStyle: .init(fill: Theme.pastelRed, width: 4, blurRadius: 0)) {
Image(systemName: "pause.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.padding(6)
.foregroundStyle(Theme.pastelRed)
}
.frame(width: 30, height: 30)
.contentShape(Rectangle())
.onTapGesture {
viewStore.send(.pause(item))
}
}
.onTapGesture {
viewStore.send(.pause(item))
}
.frame(width: 30, height: 30)
.animation(.easeInOut, value: item.status)
case .finished:
Image(systemName: "checkmark.circle")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 29, height: 29)
.foregroundStyle(Theme.pastelRed)
.animation(.easeInOut, value: item.status)
case .cancelled:
Image(systemName: "xmark.circle")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 29, height: 29)
.foregroundStyle(Color.secondary.opacity(0.4))
.animation(.easeInOut, value: item.status)
case .error:
Image(systemName: "exclamationmark.circle.fill")
case .downloading:
CircularProgressView(progress: item.percentComplete, barStyle: .init(fill: Theme.pastelRed, width: 4, blurRadius: 0)) {
Image(systemName: "pause.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 29, height: 29)
.padding(6)
.foregroundStyle(Theme.pastelRed)
.animation(.easeInOut, value: item.status)
}
}
.frame(width: 30, height: 30)
.contentShape(Rectangle())
.onTapGesture {
viewStore.send(.pause(item))
}
.animation(.easeInOut, value: item.status)
case .error:
Image(systemName: "exclamationmark.circle")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 29, height: 29)
.foregroundStyle(Theme.pastelRed)
.animation(.easeInOut, value: item.status)
}
}
.contentShape(Rectangle())
.contextMenu {
Button(role: .destructive) {
viewStore.send(.didTapCancelDownload(item))
} label: {
Label("Cancel Download", systemImage: "xmark")
}
.buttonStyle(.plain)
}
}
}
Expand Down
1 change: 1 addition & 0 deletions Sources/Features/DownloadQueue/DownloadQueueFeature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public struct DownloadQueueFeature: Feature {
@dynamicMemberLookup
public enum ViewAction: SendableAction {
case didAppear
case didTapCancelDownload(OfflineManagerClient.DownloadingItem)
case pause(OfflineManagerClient.DownloadingItem)
}

Expand Down

0 comments on commit c6a2f5c

Please sign in to comment.