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