165 lines
5.2 KiB
Swift
165 lines
5.2 KiB
Swift
|
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")
|
||
|
}
|
||
|
}
|