PadXcode-iPad/Validation/UnsavedChangesGuard.swift

60 lines
2.2 KiB
Swift
Raw Normal View History

2026-04-12 00:46:30 -07:00
import SwiftUI
struct RequestTabCloseAction {
let perform: (UUID) -> Void
func callAsFunction(_ id: UUID) { perform(id) }
}
private struct RequestTabCloseKey: EnvironmentKey {
static let defaultValue = RequestTabCloseAction { _ in }
}
extension EnvironmentValues {
var requestTabClose: RequestTabCloseAction {
get { self[RequestTabCloseKey.self] }
set { self[RequestTabCloseKey.self] = newValue }
}
}
struct UnsavedChangesAlert: ViewModifier {
@ObservedObject var editorState: EditorState
let buildService: BuildService
@State private var pendingTabId: UUID?
@State private var showAlert = false
func body(content: Content) -> some View {
content
.environment(\.requestTabClose, RequestTabCloseAction { tabId in
if let tab = editorState.tabs.first(where: { $0.id == tabId }), tab.isDirty {
pendingTabId = tabId; showAlert = true
} else {
editorState.closeTab(id: tabId)
}
})
.alert("Unsaved Changes", isPresented: $showAlert) {
Button("Save & Close") {
if let id = pendingTabId,
let tab = editorState.tabs.first(where: { $0.id == id }) {
Task {
await buildService.saveFile(path: tab.path, content: tab.content)
editorState.closeTab(id: id)
}
}
}
Button("Discard", role: .destructive) {
if let id = pendingTabId { editorState.closeTab(id: id) }
}
Button("Cancel", role: .cancel) { pendingTabId = nil }
} message: {
if let id = pendingTabId,
let name = editorState.tabs.first(where: { $0.id == id })?.fileName {
Text("\"\(name)\" has unsaved changes.")
}
}
}
}
extension View {
func unsavedChangesGuard(editorState: EditorState, buildService: BuildService) -> some View {
modifier(UnsavedChangesAlert(editorState: editorState, buildService: buildService))
}
}