New: custom alert for merge/replace imported events

This commit is contained in:
neon443
2025-05-28 14:47:05 +01:00
parent 1902c5102c
commit 0f3b86fe8c
3 changed files with 92 additions and 40 deletions

View File

@@ -55,7 +55,6 @@
A98C20CE2DE7308E0008D61C /* ArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20CD2DE7308E0008D61C /* ArchiveView.swift */; }; A98C20CE2DE7308E0008D61C /* ArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20CD2DE7308E0008D61C /* ArchiveView.swift */; };
A98C20D02DE731BD0008D61C /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20CF2DE731BD0008D61C /* HomeView.swift */; }; A98C20D02DE731BD0008D61C /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20CF2DE731BD0008D61C /* HomeView.swift */; };
A98C20D42DE7339E0008D61C /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20D32DE7339E0008D61C /* AboutView.swift */; }; A98C20D42DE7339E0008D61C /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20D32DE7339E0008D61C /* AboutView.swift */; };
A98C20D52DE7339E0008D61C /* AboutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A98C20D32DE7339E0008D61C /* AboutView.swift */; };
A9FC7EEA2D2823920020D75B /* NearFutureWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FC7EE92D28238A0020D75B /* NearFutureWidgets.swift */; }; A9FC7EEA2D2823920020D75B /* NearFutureWidgets.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9FC7EE92D28238A0020D75B /* NearFutureWidgets.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
@@ -526,7 +525,6 @@
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
A920C28C2D24011400E4F9B1 /* Events.swift in Sources */, A920C28C2D24011400E4F9B1 /* Events.swift in Sources */,
A98C20D52DE7339E0008D61C /* AboutView.swift in Sources */,
A949F84B2DCAABE00064DCA0 /* ArchiveView.swift in Sources */, A949F84B2DCAABE00064DCA0 /* ArchiveView.swift in Sources */,
A914FA4F2DD276D200856265 /* AboutView.swift in Sources */, A914FA4F2DD276D200856265 /* AboutView.swift in Sources */,
A949F84C2DCAABE00064DCA0 /* AddEventView.swift in Sources */, A949F84C2DCAABE00064DCA0 /* AddEventView.swift in Sources */,

View File

@@ -15,47 +15,97 @@ struct ImportView: View {
@State private var text: String = "Ready..." @State private var text: String = "Ready..."
@State private var fgColor: Color = .yellow @State private var fgColor: Color = .yellow
@State private var showAlert: Bool = false
@State private var replaceCurrentEvents: Bool = false
var body: some View { var body: some View {
List { ZStack(alignment: .center) {
Section("Status") { List {
Label(text, systemImage: image) Section("Status") {
.contentTransition(.numericText()) Label(text, systemImage: image)
.foregroundStyle(fgColor) .contentTransition(.numericText())
} .foregroundStyle(fgColor)
TextField("", text: $importStr) }
Button() { TextField("", text: $importStr)
do throws { Button() {
try viewModel.importEvents(importStr) withAnimation {
withAnimation { showAlert.toggle()
image = "checkmark.circle.fill" }
text = "Complete" } label: {
fgColor = .green Label("Import", systemImage: "tray.and.arrow.down.fill")
} }
} catch importError.invalidB64 { .disabled(importStr.isEmpty)
withAnimation { .onAppear() {
image = "xmark.app.fill" importStr = ""
text = "Invalid base64 input." image = "clock.fill"
fgColor = .red text = "Ready..."
} fgColor = .yellow
} catch {
withAnimation {
image = "xmark.app.fill"
text = error.localizedDescription
fgColor = .red
}
} }
} label: {
Label("Import", systemImage: "tray.and.arrow.down.fill")
} }
.disabled(importStr.isEmpty) .blur(radius: showAlert ? 2 : 0)
.onAppear() { Group {
importStr = "" Rectangle()
image = "clock.fill" .frame(maxWidth: .infinity, maxHeight: .infinity)
text = "Ready..." .foregroundStyle(replaceCurrentEvents ? .red.opacity(0.3) : .black.opacity(0.2))
fgColor = .yellow .animation(.default, value: replaceCurrentEvents)
.ignoresSafeArea()
ZStack {
RoundedRectangle(cornerRadius: 30)
.foregroundStyle(.one.opacity(0.8))
.blur(radius: 1)
VStack(alignment: .center) {
Text("Are you sure?")
.font(.largeTitle)
.bold()
.foregroundStyle(replaceCurrentEvents ? .red : .two)
.animation(.default, value: replaceCurrentEvents)
Text("This will replace your current events!")
.lineLimit(nil)
.multilineTextAlignment(.center)
.opacity(replaceCurrentEvents ? 1 : 0)
.animation(.default, value: replaceCurrentEvents)
.foregroundStyle(.two)
Toggle("Replace Events", isOn: $replaceCurrentEvents)
.foregroundStyle(.two)
Spacer()
Button() {
withAnimation {
showAlert.toggle()
}
do throws {
try viewModel.importEvents(importStr, replace: replaceCurrentEvents)
withAnimation {
image = "checkmark.circle.fill"
text = "Complete"
fgColor = .green
}
} catch importError.invalidB64 {
withAnimation {
image = "xmark.app.fill"
text = "Invalid base64 input."
fgColor = .red
}
} catch {
withAnimation {
image = "xmark.app.fill"
text = error.localizedDescription
fgColor = .red
}
}
} label: {
Text("yes")
.font(.title2)
.bold()
}
.buttonStyle(BorderedProminentButtonStyle())
}
.padding()
}
.frame(maxWidth: 250, maxHeight: 250)
} }
.opacity(showAlert ? 1 : 0)
} }
} }
} }

View File

@@ -325,14 +325,18 @@ class EventViewModel: ObservableObject, @unchecked Sendable {
return "" return ""
} }
func importEvents(_ imported: String) throws { func importEvents(_ imported: String, replace: Bool) throws {
guard let data = Data(base64Encoded: imported) else { guard let data = Data(base64Encoded: imported) else {
throw importError.invalidB64 throw importError.invalidB64
} }
let decoder = JSONDecoder() let decoder = JSONDecoder()
do { do {
let decoded = try decoder.decode([Event].self, from: data) let decoded = try decoder.decode([Event].self, from: data)
self.events = decoded if replace {
self.events = decoded
} else {
self.events = self.events + decoded
}
saveEvents() saveEvents()
} catch { } catch {
throw error throw error