import SwiftUI import CryptoKit let keyManager = try! KeyManagement() let server = Client(server: URL(string: "https://christophhagen.de/sesame/")!) struct ContentView: View { @State var state: ClientState = .initial var canShareKey = false @State var showNewKeyWarning = false @State var showKeyGenerationFailedWarning = false @State var showShareSheetForNewKeys = false @State var activeRequestCount = 0 var isPerformingRequests: Bool { activeRequestCount > 0 } var keyText: String { let totalKeys = keyManager.numberOfKeys guard totalKeys > 0 else { return "No keys available" } let unusedKeys = keyManager.unusedKeyCount guard unusedKeys > 0 else { return "All keys used" } return "\(totalKeys - unusedKeys) / \(totalKeys) keys used" } private let buttonWidth: CGFloat = 200 private let topButtonHeight: CGFloat = 60 var body: some View { VStack(spacing: 20) { Text(keyText) Button("Generate new keys", action: { showNewKeyWarning = true print("Key regeneration requested") }) .padding() .frame(width: buttonWidth, height: topButtonHeight) .background(.blue) .foregroundColor(.white) .cornerRadius(topButtonHeight / 2) Button("Share one-time key", action: shareKey) .padding() .frame(width: buttonWidth, height: topButtonHeight) .background(.mint) .foregroundColor(.white) .cornerRadius(topButtonHeight / 2) .disabled(!canShareKey) Spacer() HStack { if isPerformingRequests { ProgressView() .progressViewStyle(CircularProgressViewStyle()) } Text(state.description) .padding() } Button(state.openButtonText, action: mainButtonPressed) .frame(width: buttonWidth, height: 80, alignment: .center) .background(state.openButtonColor) .cornerRadius(100) .foregroundColor(.white) .font(.title2) .disabled(!state.openActionIsEnabled) } .padding(20) .onAppear { checkInitialDeviceStatus() }.alert(isPresented: $showKeyGenerationFailedWarning) { Alert(title: Text("The keys could not be generated"), message: Text("All previous keys will be deleted and the lock will be blocked. Are you sure?"), dismissButton: .default(Text("Okay"))) }.shareSheet(isPresented: $showShareSheetForNewKeys, items: [keyManager.exportFile]) .alert(isPresented: $showNewKeyWarning) { Alert(title: Text("Generate new keys"), message: Text("All previous keys will be deleted and the lock will be blocked. Are you sure?"), primaryButton: .destructive(Text("Generate"), action: regenerateKeys), secondaryButton: .cancel()) } } func mainButtonPressed() { print("Main button pressed") if state.canSendKey { sendKey() } else { checkInitialDeviceStatus() } } func sendKey() { guard let key = keyManager.useNextKey() else { state = .allKeysUsed return } state = .waitingForResponse activeRequestCount += 1 print("Sending key \(key.id)") Task { let newState = try await server.keyResponse(key: key.key, id: key.id) activeRequestCount -= 1 state = newState } } func checkInitialDeviceStatus() { print("Checking device status") Task { do { activeRequestCount += 1 let newState = try await server.deviceStatus() activeRequestCount -= 1 print("Device status: \(newState)") switch newState { case .noKeysAvailable, .allKeysUsed: return default: state = newState } } catch { print("Failed to get device status: \(error)") state = .statusRequestFailed } } } func regenerateKeys() { print("Regenerate keys") do { try keyManager.regenerateKeys() state = .newKeysGenerated showKeyGenerationFailedWarning = false showShareSheetForNewKeys = true checkInitialDeviceStatus() } catch { state = .noKeysAvailable showKeyGenerationFailedWarning = true showShareSheetForNewKeys = false } } func shareKey() { } } struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() .previewDevice("iPhone 8") } }