Some checks failed
CI / test (push) Has been cancelled
Bring the SwiftUI app in line with the Apple-native mock and keep pending approvals actionable from Live Activities and watch complications.
127 lines
4.2 KiB
Swift
127 lines
4.2 KiB
Swift
import SwiftUI
|
|
|
|
struct LoginRootView: View {
|
|
@ObservedObject var model: AppViewModel
|
|
|
|
#if !os(macOS)
|
|
@State private var isNFCSheetPresented = false
|
|
#endif
|
|
|
|
var body: some View {
|
|
#if os(macOS)
|
|
MacPairingView(model: model)
|
|
#else
|
|
NavigationStack {
|
|
ZStack(alignment: .top) {
|
|
LiveQRScannerView { payload in
|
|
model.manualPairingPayload = payload
|
|
Task {
|
|
await model.signIn(with: payload, transport: .qr)
|
|
}
|
|
}
|
|
.ignoresSafeArea()
|
|
|
|
VStack(spacing: 0) {
|
|
IdPGlassCapsule {
|
|
VStack(alignment: .leading, spacing: 6) {
|
|
Text("Scan a pairing code")
|
|
.font(.headline)
|
|
|
|
Text("Turn this iPhone into your idp.global passport with QR or NFC.")
|
|
.font(.subheadline)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
.frame(maxWidth: .infinity, alignment: .leading)
|
|
}
|
|
.padding(.horizontal, 16)
|
|
.padding(.top, 12)
|
|
|
|
Spacer()
|
|
|
|
Button {
|
|
isNFCSheetPresented = true
|
|
} label: {
|
|
IdPGlassCapsule {
|
|
HStack(spacing: 10) {
|
|
Image(systemName: "wave.3.right")
|
|
.foregroundStyle(IdP.tint)
|
|
Text("Hold near NFC tag")
|
|
.font(.headline)
|
|
.foregroundStyle(.primary)
|
|
}
|
|
}
|
|
}
|
|
.buttonStyle(.plain)
|
|
.padding(.horizontal, 16)
|
|
.padding(.bottom, 24)
|
|
}
|
|
}
|
|
.toolbar {
|
|
ToolbarItem(placement: .topBarTrailing) {
|
|
Button("Use demo payload") {
|
|
Task {
|
|
await model.signInWithSuggestedPayload()
|
|
}
|
|
}
|
|
.font(.footnote)
|
|
.disabled(model.isAuthenticating || model.suggestedPairingPayload.isEmpty)
|
|
}
|
|
}
|
|
}
|
|
.sheet(isPresented: $isNFCSheetPresented) {
|
|
NFCSheet(actionTitle: "Approve") { request in
|
|
await model.signIn(with: request)
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
#if os(macOS)
|
|
private struct MacPairingView: View {
|
|
@ObservedObject var model: AppViewModel
|
|
|
|
var body: some View {
|
|
VStack(alignment: .leading, spacing: 18) {
|
|
HStack(spacing: 12) {
|
|
Image(systemName: "shield.lefthalf.filled")
|
|
.font(.title2)
|
|
.foregroundStyle(IdP.tint)
|
|
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text("Set up idp.global")
|
|
.font(.headline)
|
|
Text("Use the demo payload or paste a pairing link.")
|
|
.font(.subheadline)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
|
|
TextEditor(text: $model.manualPairingPayload)
|
|
.font(.footnote.monospaced())
|
|
.scrollContentBackground(.hidden)
|
|
.frame(minHeight: 140)
|
|
.padding(10)
|
|
.background(Color.idpSecondaryGroupedBackground, in: RoundedRectangle(cornerRadius: IdP.cardRadius, style: .continuous))
|
|
|
|
VStack(spacing: 10) {
|
|
Button("Use demo payload") {
|
|
Task {
|
|
await model.signInWithSuggestedPayload()
|
|
}
|
|
}
|
|
.buttonStyle(PrimaryActionStyle())
|
|
|
|
Button("Link with payload") {
|
|
Task {
|
|
await model.signInWithManualPayload()
|
|
}
|
|
}
|
|
.buttonStyle(SecondaryActionStyle())
|
|
}
|
|
}
|
|
.padding(20)
|
|
}
|
|
}
|
|
#endif
|