95 lines
3.0 KiB
Swift
95 lines
3.0 KiB
Swift
import SwiftUI
|
|
import AppIntents
|
|
|
|
@main
|
|
struct BusinessCardClipApp: App {
|
|
@State private var recordName: String?
|
|
@State private var launchErrorMessage: String?
|
|
#if DEBUG
|
|
@State private var debugState: ClipDebugState?
|
|
#endif
|
|
|
|
var body: some Scene {
|
|
WindowGroup {
|
|
Group {
|
|
#if DEBUG
|
|
if let debugState {
|
|
ClipDebugHarnessView(initialState: debugState)
|
|
} else if let recordName {
|
|
ClipRootView(recordName: recordName)
|
|
} else if let launchErrorMessage {
|
|
ClipErrorView(message: launchErrorMessage) {
|
|
self.launchErrorMessage = nil
|
|
}
|
|
} else {
|
|
ClipLoadingView()
|
|
}
|
|
#else
|
|
if let recordName {
|
|
ClipRootView(recordName: recordName)
|
|
} else if let launchErrorMessage {
|
|
ClipErrorView(message: launchErrorMessage) {
|
|
self.launchErrorMessage = nil
|
|
}
|
|
} else {
|
|
ClipLoadingView()
|
|
}
|
|
#endif
|
|
}
|
|
.onContinueUserActivity(NSUserActivityTypeBrowsingWeb) { activity in
|
|
handleUserActivity(activity)
|
|
}
|
|
.onOpenURL { url in
|
|
handleURL(url)
|
|
}
|
|
.task {
|
|
#if DEBUG
|
|
debugState = parseDebugStateArgument()
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
private func handleUserActivity(_ activity: NSUserActivity) {
|
|
guard let url = activity.webpageURL else { return }
|
|
handleURL(url)
|
|
}
|
|
|
|
private func handleURL(_ url: URL) {
|
|
guard let id = extractRecordName(from: url) else {
|
|
launchErrorMessage = ClipError.invalidRecord.localizedDescription
|
|
return
|
|
}
|
|
|
|
launchErrorMessage = nil
|
|
recordName = id
|
|
}
|
|
|
|
private func extractRecordName(from url: URL) -> String? {
|
|
if let components = URLComponents(url: url, resolvingAgainstBaseURL: true),
|
|
let id = components.queryItems?
|
|
.first(where: { $0.name == ClipDesign.URL.recordQueryName })?
|
|
.value,
|
|
!id.isEmpty {
|
|
return id
|
|
}
|
|
|
|
// Fallback for path-based links (e.g. /clip/{recordName}).
|
|
let candidate = url.lastPathComponent.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
guard !candidate.isEmpty, candidate != "/" else { return nil }
|
|
return candidate
|
|
}
|
|
|
|
#if DEBUG
|
|
private func parseDebugStateArgument() -> ClipDebugState? {
|
|
let prefix = "--clip-debug="
|
|
guard let argument = ProcessInfo.processInfo.arguments.first(where: { $0.hasPrefix(prefix) }) else {
|
|
return nil
|
|
}
|
|
|
|
let value = String(argument.dropFirst(prefix.count))
|
|
return ClipDebugState(rawValue: value)
|
|
}
|
|
#endif
|
|
}
|