Skip to content

Commit 79b5bdd

Browse files
committed
Add context menu option to delete download
1 parent 871ddfb commit 79b5bdd

File tree

2 files changed

+63
-8
lines changed

2 files changed

+63
-8
lines changed

VirtualUI/Source/Installer/Steps/Restore Image Selection/Components/RestoreImageBrowser.swift

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ struct RestoreImageBrowser: View {
110110
} else if controller.isLoading {
111111
/// Placeholders are only displayed when controller is loading to avoid jumps when loading happens quickly (or not at all).
112112
ForEach(0...12, id: \.self) { _ in
113-
RestoreImageButton(image: .placeholder, isSelected: false, action: { })
113+
RestoreImageButton(image: .placeholder, isSelected: false, action: { }, deleteDownload: { })
114114
}
115115
}
116116
}
@@ -124,6 +124,8 @@ struct RestoreImageBrowser: View {
124124
ForEach(group.images) { image in
125125
RestoreImageButton(image: image, isSelected: image.id == selection?.id) {
126126
selection = image
127+
} deleteDownload: {
128+
controller.deleteLocalDownload(for: image)
127129
}
128130
.tag(image)
129131
}
@@ -134,7 +136,8 @@ struct RestoreImageBrowser: View {
134136
private struct RestoreImageButton: View {
135137
var image: ResolvedRestoreImage
136138
var isSelected: Bool
137-
var action: () -> Void
139+
var action: () -> ()
140+
var deleteDownload: () -> ()
138141

139142
var body: some View {
140143
Button {
@@ -165,6 +168,16 @@ private struct RestoreImageButton: View {
165168
Button("Copy Build Number") {
166169
Pasteboard.general.string = image.build
167170
}
171+
172+
if image.isDownloaded {
173+
Divider()
174+
175+
Button(role: .destructive) {
176+
deleteDownload()
177+
} label: {
178+
Text("Delete Download…")
179+
}
180+
}
168181
}
169182
}
170183

VirtualUI/Source/Installer/Steps/Restore Image Selection/RestoreImageSelectionController.swift

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,14 @@ final class RestoreImageSelectionController: ObservableObject {
7676
}
7777
}
7878

79+
private var inputCatalog: SoftwareCatalog?
80+
private var guestType = VBGuestType.mac
81+
7982
func loadRestoreImageOptions(for guest: VBGuestType) {
8083
logger.debug("Loading restore image options.")
8184

85+
guestType = guest
86+
8287
deferredStartLoading()
8388

8489
Task {
@@ -99,13 +104,9 @@ final class RestoreImageSelectionController: ObservableObject {
99104
#endif
100105

101106
let catalog = try await api.fetchRestoreImages(for: guest)
102-
let platform: CatalogGuestPlatform = guest == .linux ? .linux : .mac
103-
let resolved = ResolvedCatalog(environment: .current.guest(platform: platform), catalog: catalog)
107+
inputCatalog = catalog
104108

105-
await MainActor.run {
106-
self.selectedGroup = resolved.groups.first
107-
self.catalog = resolved
108-
}
109+
await refreshResolvedCatalog(with: catalog)
109110
} catch {
110111
logger.error("Loading restore images failed - \(error, privacy: .public)")
111112

@@ -116,6 +117,47 @@ final class RestoreImageSelectionController: ObservableObject {
116117
}
117118
}
118119
}
120+
121+
private func refreshResolvedCatalog(with catalog: SoftwareCatalog) async {
122+
logger.debug(#function)
123+
124+
let platform: CatalogGuestPlatform = guestType == .linux ? .linux : .mac
125+
let resolved = ResolvedCatalog(environment: .current.guest(platform: platform), catalog: catalog)
126+
127+
await MainActor.run {
128+
self.selectedGroup = resolved.groups.first(where: { $0.id == selectedGroup?.id }) ?? resolved.groups.first
129+
self.selectedRestoreImage = selectedGroup?.restoreImages.first(where: { $0.id == self.selectedRestoreImage?.id })
130+
self.catalog = resolved
131+
}
132+
}
133+
134+
func deleteLocalDownload(for image: ResolvedRestoreImage) {
135+
logger.debug("Delete download requested for \(image.id)")
136+
137+
/// Remove selection to force refresh of image browser.
138+
selectedRestoreImage = nil
139+
140+
do {
141+
let fileURL = try image.localFileURL.require("File not found.")
142+
Task {
143+
do {
144+
try await NSWorkspace.shared.recycle([fileURL])
145+
146+
if let inputCatalog {
147+
await refreshResolvedCatalog(with: inputCatalog)
148+
}
149+
} catch {
150+
logger.error("Recycle failed for \(fileURL.path) - \(error, privacy: .public)")
151+
152+
NSApp.presentError(error)
153+
}
154+
}
155+
} catch {
156+
logger.error("Delete download failed for \(image.id) - \(error, privacy: .public)")
157+
158+
NSApp.presentError(error)
159+
}
160+
}
119161
}
120162

121163
extension ChannelGroup {

0 commit comments

Comments
 (0)