// Pairing: the gateway URL and the 6 digit code that `wristcode pair` prints on the gateway. // Shown whenever there is no device token, or the gateway answers 401. import SwiftUI struct PairView: View { @Environment(AppModel.self) private var model @State private var url = DeviceStore.lastGatewayURL @State private var code = "" @State private var sending = false @State private var error: String? private var digits: String { code.filter(\.isNumber) } var body: some View { ScrollView { VStack(alignment: .leading, spacing: 8) { Text("WristCode") .font(.system(size: 22, weight: .bold, design: .rounded)) .foregroundStyle(Theme.you) Text("Run wristcode pair on the gateway, then enter the code.") .font(.system(size: 13)) .foregroundStyle(Theme.secondaryText) field("Gateway") { TextField("Gateway URL", text: $url) .textContentType(.URL) } field("Code") { TextField("6 digits", text: $code) .textContentType(.oneTimeCode) } Button(sending ? "Pairing" : "Pair") { pair() } .buttonStyle(PillButtonStyle(fill: Theme.you)) .disabled(sending || digits.count != 6) .opacity(digits.count == 6 ? 1 : 0.5) .padding(.top, 4) if let error { Text(error) .font(.system(size: 12)) .foregroundStyle(Theme.secondaryText) } Button("Try the demo") { model.startDemo() } .buttonStyle(.plain) .font(.system(size: 13, weight: .semibold)) .foregroundStyle(Theme.work) .frame(maxWidth: .infinity) .padding(.top, 6) } .padding(.horizontal, 4) } .textInputAutocapitalization(.never) .autocorrectionDisabled() } private func field(_ label: String, @ViewBuilder input: () -> some View) -> some View { VStack(alignment: .leading, spacing: 2) { Text(label) .font(.system(size: 11, weight: .semibold)) .foregroundStyle(Theme.secondaryText) input() } } private func pair() { guard let gatewayURL = DeviceStore.normalizeGatewayURL(url) else { error = "That gateway URL doesn't look right." return } sending = true error = nil Task { do { try await model.pair(gatewayURL: gatewayURL, code: digits) } catch let failure as GatewayError where failure == .unauthorized { error = "That code is wrong or expired." } catch { self.error = error.localizedDescription } sending = false } } } #Preview("Pair") { PairView() .environment(AppModel(mode: .unpaired)) .fontDesign(.rounded) }