mirror of
https://github.com/utmapp/UTM.git
synced 2026-10-12 02:24:01 +00:00
toolbar: use updated confirmation on iOS 15+
This commit is contained in:
@@ -16,65 +16,167 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
enum ConfirmAction: Int, Identifiable {
|
||||
case confirmCloneVM
|
||||
case confirmDeleteVM
|
||||
case confirmDeleteShortcut
|
||||
case confirmStopVM
|
||||
case confirmMoveVM
|
||||
|
||||
var id: Int { rawValue }
|
||||
enum ConfirmAction: Identifiable, Equatable {
|
||||
case confirmCloneVM(vm: VMData)
|
||||
case confirmDeleteVM(vm: VMData)
|
||||
case confirmStopVM(vm: VMData)
|
||||
case confirmMoveVM(vm: VMData)
|
||||
|
||||
struct ID: Hashable {
|
||||
let index: Int
|
||||
let uuid: UUID
|
||||
}
|
||||
|
||||
@MainActor
|
||||
var id: ID {
|
||||
switch self {
|
||||
case .confirmCloneVM(let vm):
|
||||
return ID(index: 0, uuid: vm.id)
|
||||
case .confirmDeleteVM(let vm):
|
||||
return ID(index: 1, uuid: vm.id)
|
||||
case .confirmStopVM(let vm):
|
||||
return ID(index: 2, uuid: vm.id)
|
||||
case .confirmMoveVM(let vm):
|
||||
return ID(index: 3, uuid: vm.id)
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
static func == (lhs: Self, rhs: Self) -> Bool {
|
||||
lhs.id == rhs.id
|
||||
}
|
||||
}
|
||||
|
||||
struct VMConfirmActionModifier: ViewModifier {
|
||||
let vm: VMData
|
||||
@Binding var confirmAction: ConfirmAction?
|
||||
let onConfirm: () -> Void
|
||||
let onConfirm: (ConfirmAction) -> Void
|
||||
@EnvironmentObject private var data: UTMData
|
||||
|
||||
func body(content: Content) -> some View {
|
||||
@State private var isPresented: Bool = false
|
||||
|
||||
@ViewBuilder func body(content: Content) -> some View {
|
||||
if #available(iOS 15, macOS 12, *) {
|
||||
newBody(content: content)
|
||||
} else {
|
||||
oldBody(content: content)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder func oldBody(content: Content) -> some View {
|
||||
content.alert(item: $confirmAction) { action in
|
||||
switch action {
|
||||
case .confirmCloneVM:
|
||||
case .confirmCloneVM(let vm):
|
||||
if vm.isShortcut {
|
||||
return Alert(title: Text("Do you want to copy this VM and all its data to internal storage?"), primaryButton: .cancel(), secondaryButton: .default(Text("Yes")) {
|
||||
return Alert(title: Text("Do you want to copy this VM and all its data to internal storage?"), primaryButton: .cancel(), secondaryButton: .default(Text("Copy")) {
|
||||
data.busyWorkAsync {
|
||||
try await data.clone(vm: vm)
|
||||
}
|
||||
onConfirm()
|
||||
onConfirm(action)
|
||||
})
|
||||
} else {
|
||||
return Alert(title: Text("Do you want to duplicate this VM and all its data?"), primaryButton: .cancel(), secondaryButton: .default(Text("Yes")) {
|
||||
return Alert(title: Text("Do you want to duplicate this VM and all its data?"), primaryButton: .cancel(), secondaryButton: .default(Text("Copy")) {
|
||||
data.busyWorkAsync {
|
||||
try await data.clone(vm: vm)
|
||||
}
|
||||
onConfirm()
|
||||
onConfirm(action)
|
||||
})
|
||||
}
|
||||
case .confirmDeleteVM:
|
||||
return Alert(title: Text("Do you want to delete this VM and all its data?"), primaryButton: .cancel(), secondaryButton: .destructive(Text("Delete")) {
|
||||
data.busyWorkAsync {
|
||||
try await data.delete(vm: vm)
|
||||
}
|
||||
onConfirm()
|
||||
})
|
||||
case .confirmDeleteShortcut:
|
||||
return Alert(title: Text("Do you want to remove this shortcut? The data will not be deleted."), primaryButton: .cancel(), secondaryButton: .destructive(Text("Remove")) {
|
||||
data.busyWorkAsync {
|
||||
await data.listRemove(vm: vm)
|
||||
}
|
||||
onConfirm()
|
||||
})
|
||||
case .confirmStopVM:
|
||||
case .confirmDeleteVM(let vm):
|
||||
if vm.wrapped?.isShortcut ?? false {
|
||||
return Alert(title: Text("Do you want to remove this shortcut? The data will not be deleted."), primaryButton: .cancel(), secondaryButton: .destructive(Text("Remove")) {
|
||||
data.busyWorkAsync {
|
||||
await data.listRemove(vm: vm)
|
||||
}
|
||||
onConfirm(action)
|
||||
})
|
||||
} else {
|
||||
return Alert(title: Text("Do you want to delete this VM and all its data?"), primaryButton: .cancel(), secondaryButton: .destructive(Text("Delete")) {
|
||||
data.busyWorkAsync {
|
||||
try await data.delete(vm: vm)
|
||||
}
|
||||
onConfirm(action)
|
||||
})
|
||||
}
|
||||
case .confirmStopVM(let vm):
|
||||
return Alert(title: Text("Do you want to force stop this VM and lose all unsaved data?"), primaryButton: .cancel(), secondaryButton: .destructive(Text("Stop")) {
|
||||
data.stop(vm: vm)
|
||||
onConfirm()
|
||||
onConfirm(action)
|
||||
})
|
||||
case .confirmMoveVM:
|
||||
case .confirmMoveVM(_):
|
||||
return Alert(title: Text("Do you want to move this VM to another location? This will copy the data to the new location, delete the data from the original location, and then create a shortcut."), primaryButton: .cancel(), secondaryButton: .destructive(Text("Confirm")) {
|
||||
onConfirm()
|
||||
onConfirm(action)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@available(iOS 15, macOS 12, *)
|
||||
@ViewBuilder func newBody(content: Content) -> some View {
|
||||
content.confirmationDialog("Confirm", isPresented: $isPresented, presenting: confirmAction) { action in
|
||||
Button("Cancel", role: .cancel) {}
|
||||
switch action {
|
||||
case .confirmCloneVM(let vm):
|
||||
Button("Copy") {
|
||||
data.busyWorkAsync {
|
||||
try await data.clone(vm: vm)
|
||||
}
|
||||
onConfirm(action)
|
||||
}
|
||||
case .confirmDeleteVM(let vm):
|
||||
if vm.wrapped?.isShortcut ?? false {
|
||||
Button("Remove", role: .destructive) {
|
||||
data.busyWorkAsync {
|
||||
await data.listRemove(vm: vm)
|
||||
}
|
||||
onConfirm(action)
|
||||
}
|
||||
} else {
|
||||
Button("Delete", role: .destructive) {
|
||||
data.busyWorkAsync {
|
||||
try await data.delete(vm: vm)
|
||||
}
|
||||
onConfirm(action)
|
||||
}
|
||||
}
|
||||
case .confirmStopVM(let vm):
|
||||
Button("Stop", role: .destructive) {
|
||||
data.stop(vm: vm)
|
||||
onConfirm(action)
|
||||
}
|
||||
case .confirmMoveVM(_):
|
||||
if #available(iOS 26, macOS 26, visionOS 26, *) {
|
||||
Button("Confirm", role: .confirm) {
|
||||
onConfirm(action)
|
||||
}
|
||||
} else {
|
||||
Button("Confirm") {
|
||||
onConfirm(action)
|
||||
}
|
||||
}
|
||||
}
|
||||
} message: { action in
|
||||
switch action {
|
||||
case .confirmCloneVM(let vm):
|
||||
if vm.isShortcut {
|
||||
Text("Do you want to copy this VM and all its data to internal storage?")
|
||||
} else {
|
||||
Text("Do you want to duplicate this VM and all its data?")
|
||||
}
|
||||
case .confirmDeleteVM(let vm):
|
||||
if vm.wrapped?.isShortcut ?? false {
|
||||
Text("Do you want to remove this shortcut? The data will not be deleted.")
|
||||
} else {
|
||||
Text("Do you want to delete this VM and all its data?")
|
||||
}
|
||||
case .confirmStopVM(_):
|
||||
Text("Do you want to force stop this VM and lose all unsaved data?")
|
||||
case .confirmMoveVM(_):
|
||||
Text("Do you want to move this VM to another location? This will copy the data to the new location, delete the data from the original location, and then create a shortcut.")
|
||||
}
|
||||
}
|
||||
.onChange(of: confirmAction) { newValue in
|
||||
if newValue != nil {
|
||||
isPresented = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ struct VMContextMenuModifier: ViewModifier {
|
||||
#endif
|
||||
if vm.hasSuspendState || !vm.isStopped {
|
||||
Button {
|
||||
confirmAction = .confirmStopVM
|
||||
confirmAction = .confirmStopVM(vm: vm)
|
||||
} label: {
|
||||
Label("Stop", systemImage: "stop")
|
||||
}.help("Stop the running VM.")
|
||||
@@ -131,7 +131,7 @@ struct VMContextMenuModifier: ViewModifier {
|
||||
#if os(macOS)
|
||||
if !vm.isShortcut {
|
||||
Button {
|
||||
confirmAction = .confirmMoveVM
|
||||
confirmAction = .confirmMoveVM(vm: vm)
|
||||
} label: {
|
||||
Label("Move…", systemImage: "arrow.down.doc")
|
||||
}.disabled(!vm.isModifyAllowed)
|
||||
@@ -139,7 +139,7 @@ struct VMContextMenuModifier: ViewModifier {
|
||||
}
|
||||
#endif
|
||||
Button {
|
||||
confirmAction = .confirmCloneVM
|
||||
confirmAction = .confirmCloneVM(vm: vm)
|
||||
} label: {
|
||||
Label("Clone…", systemImage: "doc.on.doc")
|
||||
}.help("Duplicate this VM along with all its data.")
|
||||
@@ -153,14 +153,14 @@ struct VMContextMenuModifier: ViewModifier {
|
||||
Divider()
|
||||
if vm.isShortcut {
|
||||
DestructiveButton {
|
||||
confirmAction = .confirmDeleteShortcut
|
||||
confirmAction = .confirmDeleteVM(vm: vm)
|
||||
} label: {
|
||||
Label("Remove", systemImage: "trash")
|
||||
}.disabled(!vm.isModifyAllowed)
|
||||
.help("Delete this shortcut. The underlying data will not be deleted.")
|
||||
} else {
|
||||
DestructiveButton {
|
||||
confirmAction = .confirmDeleteVM
|
||||
confirmAction = .confirmDeleteVM(vm: vm)
|
||||
} label: {
|
||||
Label("Delete", systemImage: "trash")
|
||||
}.disabled(!vm.isModifyAllowed)
|
||||
@@ -169,8 +169,8 @@ struct VMContextMenuModifier: ViewModifier {
|
||||
#endif
|
||||
}
|
||||
.modifier(VMShareItemModifier(isPresented: $showSharePopup, shareItem: shareItem))
|
||||
.modifier(VMConfirmActionModifier(vm: vm, confirmAction: $confirmAction) {
|
||||
if confirmAction == .confirmMoveVM {
|
||||
.modifier(VMConfirmActionModifier(confirmAction: $confirmAction) { action in
|
||||
if case .confirmMoveVM(let vm) = action {
|
||||
shareItem = .utmMove(vm)
|
||||
showSharePopup.toggle()
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ struct VMToolbarModifier: ViewModifier {
|
||||
#if !WITH_REMOTE // FIXME: implement remote feature
|
||||
if vm.isShortcut {
|
||||
DestructiveButton {
|
||||
confirmAction = .confirmDeleteShortcut
|
||||
confirmAction = .confirmDeleteVM(vm: vm)
|
||||
} label: {
|
||||
Label("Remove", systemImage: "trash")
|
||||
.labelStyle(.iconOnly)
|
||||
@@ -63,7 +63,7 @@ struct VMToolbarModifier: ViewModifier {
|
||||
.padding(.leading, padding)
|
||||
} else {
|
||||
DestructiveButton {
|
||||
confirmAction = .confirmDeleteVM
|
||||
confirmAction = .confirmDeleteVM(vm: vm)
|
||||
} label: {
|
||||
Label("Delete", systemImage: "trash")
|
||||
.labelStyle(.iconOnly)
|
||||
@@ -77,7 +77,7 @@ struct VMToolbarModifier: ViewModifier {
|
||||
}
|
||||
#endif
|
||||
Button {
|
||||
confirmAction = .confirmCloneVM
|
||||
confirmAction = .confirmCloneVM(vm: vm)
|
||||
} label: {
|
||||
Label("Clone", systemImage: "doc.on.doc")
|
||||
.labelStyle(.iconOnly)
|
||||
@@ -91,7 +91,7 @@ struct VMToolbarModifier: ViewModifier {
|
||||
#if os(macOS)
|
||||
if !vm.isShortcut {
|
||||
Button {
|
||||
confirmAction = .confirmMoveVM
|
||||
confirmAction = .confirmMoveVM(vm: vm)
|
||||
} label: {
|
||||
Label("Move", systemImage: "arrow.down.doc")
|
||||
.labelStyle(.iconOnly)
|
||||
@@ -116,7 +116,7 @@ struct VMToolbarModifier: ViewModifier {
|
||||
#endif
|
||||
if vm.hasSuspendState || !vm.isStopped {
|
||||
Button {
|
||||
confirmAction = .confirmStopVM
|
||||
confirmAction = .confirmStopVM(vm: vm)
|
||||
} label: {
|
||||
Label("Stop", systemImage: "stop")
|
||||
.labelStyle(.iconOnly)
|
||||
@@ -150,8 +150,8 @@ struct VMToolbarModifier: ViewModifier {
|
||||
}
|
||||
}
|
||||
.modifier(VMShareItemModifier(isPresented: $showSharePopup, shareItem: shareItem))
|
||||
.modifier(VMConfirmActionModifier(vm: vm, confirmAction: $confirmAction) {
|
||||
if confirmAction == .confirmMoveVM {
|
||||
.modifier(VMConfirmActionModifier(confirmAction: $confirmAction) { action in
|
||||
if case .confirmMoveVM(let vm) = action {
|
||||
shareItem = .utmMove(vm)
|
||||
showSharePopup.toggle()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user