diff --git a/Yattee/Localizable.xcstrings b/Yattee/Localizable.xcstrings index 8fa92680..264b131c 100644 --- a/Yattee/Localizable.xcstrings +++ b/Yattee/Localizable.xcstrings @@ -2287,6 +2287,17 @@ } } }, + "downloads.cancel" : { + "comment" : "Cancel in-progress download context menu item", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Cancel Download" + } + } + } + }, "downloads.delete" : { "localizations" : { "en" : { @@ -2341,6 +2352,17 @@ } } }, + "downloads.pause" : { + "comment" : "Pause download context menu item", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Pause" + } + } + } + }, "downloads.retry" : { "comment" : "Retry failed download button", "localizations" : { @@ -2352,6 +2374,17 @@ } } }, + "downloads.resume" : { + "comment" : "Resume paused download context menu item", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Resume" + } + } + } + }, "downloads.search.placeholder" : { "localizations" : { "en" : { diff --git a/Yattee/Views/Downloads/DownloadRowView.swift b/Yattee/Views/Downloads/DownloadRowView.swift index 3f1a148c..70ab8c59 100644 --- a/Yattee/Views/Downloads/DownloadRowView.swift +++ b/Yattee/Views/Downloads/DownloadRowView.swift @@ -17,7 +17,6 @@ struct DownloadRowView: View { let download: Download let isActive: Bool - var onDelete: (() -> Void)? = nil // Queue context (optional, enables auto-play when provided) var queueSource: QueueSource? = nil @@ -82,19 +81,8 @@ struct DownloadRowView: View { if isActive { // Active downloads: custom row with progress indicators, no tap actions activeDownloadContent - .if(onDelete != nil) { view in - view.videoContextMenu( - video: video, - customActions: [ - VideoContextAction( - String(localized: "downloads.delete"), - systemImage: "trash", - role: .destructive, - action: { onDelete?() } - ) - ], - context: .downloads - ) + .contextMenu { + activeDownloadMenuItems } } else { // Completed downloads: use VideoRowView with tap zones (thumbnail plays, text opens info) @@ -120,7 +108,9 @@ struct DownloadRowView: View { String(localized: "downloads.delete"), systemImage: "trash", role: .destructive, - action: { onDelete?() } + action: { + Task { await appEnvironment?.downloadManager.delete(download) } + } ) ], context: .downloads @@ -131,6 +121,44 @@ struct DownloadRowView: View { } } + /// Menu items for an in-progress download: pause/resume/retry plus cancel. + /// The `.downloads` video context menu hides the built-in cancel item, and swipe + /// actions are iOS-only, so this is the only cancel affordance on macOS. + @ViewBuilder + private var activeDownloadMenuItems: some View { + switch download.status { + case .queued, .downloading: + Button { + Task { await appEnvironment?.downloadManager.pause(download) } + } label: { + Label(String(localized: "downloads.pause"), systemImage: "pause.fill") + } + + case .paused: + Button { + Task { await appEnvironment?.downloadManager.resume(download) } + } label: { + Label(String(localized: "downloads.resume"), systemImage: "play.fill") + } + + case .failed: + Button { + Task { await appEnvironment?.downloadManager.resume(download) } + } label: { + Label(String(localized: "downloads.retry"), systemImage: "arrow.clockwise") + } + + case .completed: + EmptyView() + } + + Button(role: .destructive) { + Task { await appEnvironment?.downloadManager.cancel(download) } + } label: { + Label(String(localized: "downloads.cancel"), systemImage: "xmark.circle") + } + } + private var activeDownloadContent: some View { HStack(spacing: 12) { // Thumbnail (download status shown automatically) diff --git a/Yattee/Views/Home/HomeView.swift b/Yattee/Views/Home/HomeView.swift index 3d6750a9..1cb1c7b5 100644 --- a/Yattee/Views/Home/HomeView.swift +++ b/Yattee/Views/Home/HomeView.swift @@ -1315,11 +1315,6 @@ struct HomeView: View { DownloadRowView( download: download, isActive: false, - onDelete: { - Task { - await downloadManager?.delete(download) - } - }, queueSource: .manual, sourceLabel: String(localized: "queue.source.downloads"), videoList: videoList,