Fixes #29 — Delete downloaded video inline on the guide detail view
The middle button in the Stream/Download/Play row now cycles through the full download lifecycle instead of ending at a disabled "Downloaded" checkmark. Once a video is on disk the button becomes a red, destructive "Delete" with a trash icon; tapping presents a confirmation dialog, and confirming removes the file + SwiftData row, flipping the button back to "Download" and disabling Play. Settings → Downloaded Videos retains the swipe-delete and "Delete all" affordances for bulk management. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -20,6 +20,7 @@ struct VideoActionsButtonRow: View {
|
||||
@State private var isDownloaded: Bool
|
||||
@State private var playerVideoId: String?
|
||||
@State private var downloadError: String?
|
||||
@State private var confirmDelete: Bool = false
|
||||
|
||||
init(video: YouTubeVideoStore.VideoEntry) {
|
||||
self.video = video
|
||||
@@ -64,6 +65,19 @@ struct VideoActionsButtonRow: View {
|
||||
} message: {
|
||||
Text(downloadError ?? "")
|
||||
}
|
||||
.confirmationDialog(
|
||||
"Delete this downloaded video?",
|
||||
isPresented: $confirmDelete,
|
||||
titleVisibility: .visible
|
||||
) {
|
||||
Button("Delete", role: .destructive) {
|
||||
downloadService.delete(videoId: video.videoId, modelContext: modelContext)
|
||||
isDownloaded = false
|
||||
}
|
||||
Button("Cancel", role: .cancel) {}
|
||||
} message: {
|
||||
Text("You can re-download it at any time.")
|
||||
}
|
||||
.onAppear {
|
||||
// Refresh on appear in case the user deleted the file via Settings.
|
||||
isDownloaded = VideoDownloadService.isDownloaded(videoId: video.videoId)
|
||||
@@ -88,29 +102,42 @@ struct VideoActionsButtonRow: View {
|
||||
|
||||
@ViewBuilder
|
||||
private var downloadButton: some View {
|
||||
Button {
|
||||
Task { await startDownload() }
|
||||
} label: {
|
||||
Group {
|
||||
if let progress = activeProgress {
|
||||
HStack(spacing: 6) {
|
||||
ProgressView(value: progress)
|
||||
.frame(width: 40)
|
||||
Text("\(Int(progress * 100))%")
|
||||
.font(.caption.monospacedDigit())
|
||||
}
|
||||
} else if isDownloaded {
|
||||
Label("Downloaded", systemImage: "checkmark.circle.fill")
|
||||
} else {
|
||||
Label("Download", systemImage: "arrow.down.to.line")
|
||||
// Single slot whose role flips through the download lifecycle:
|
||||
// Download → progress (disabled) → Delete.
|
||||
if isDownloading, let progress = activeProgress {
|
||||
Button {} label: {
|
||||
HStack(spacing: 6) {
|
||||
ProgressView(value: progress).frame(width: 40)
|
||||
Text("\(Int(progress * 100))%")
|
||||
.font(.caption.monospacedDigit())
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.buttonStyle(.bordered)
|
||||
.tint(.blue)
|
||||
.controlSize(.large)
|
||||
.disabled(true)
|
||||
} else if isDownloaded {
|
||||
Button(role: .destructive) {
|
||||
confirmDelete = true
|
||||
} label: {
|
||||
Label("Delete", systemImage: "trash")
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.tint(.red)
|
||||
.controlSize(.large)
|
||||
} else {
|
||||
Button {
|
||||
Task { await startDownload() }
|
||||
} label: {
|
||||
Label("Download", systemImage: "arrow.down.to.line")
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.tint(.blue)
|
||||
.controlSize(.large)
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
.tint(.blue)
|
||||
.controlSize(.large)
|
||||
.disabled(isDownloaded || isDownloading)
|
||||
}
|
||||
|
||||
private var playButton: some View {
|
||||
|
||||
Reference in New Issue
Block a user