~ knotless Search/
↑↓ move↵ open⇧↵ all results

johnandersen777.bsky.social/atirc-ios-client

0
1
main
111 lines · 3.4 KB Raw History
//
//  LoginView.swift
//  AT-IRC
//

import SwiftUI

struct LoginView: View {
    @Bindable var store: ChatStore

    @State private var handle = ""
    @State private var authURL: IdentifiableURL?
    @State private var errorText: String?
    @State private var isBusy = false

    var body: some View {
        VStack(spacing: 24) {
            Spacer()

            Image(systemName: "bubble.left.and.bubble.right.fill")
                .font(.system(size: 56))
                .foregroundStyle(.tint)
            Text("AT-IRC")
                .font(.largeTitle.bold())
            Text("Sign in with your atproto account to join the chat.")
                .font(.subheadline)
                .foregroundStyle(.secondary)
                .multilineTextAlignment(.center)

            TextField("handle (e.g. alice.bsky.social)", text: $handle)
                .textInputAutocapitalization(.never)
                .autocorrectionDisabled()
                .keyboardType(.URL)
                .textFieldStyle(.roundedBorder)
                .disabled(isBusy)

            Button(action: signIn) {
                HStack(spacing: 8) {
                    if isBusy {
                        ProgressView()
                            .controlSize(.small)
                    }
                    Text("Sign in with Bluesky")
                }
                .frame(maxWidth: .infinity)
            }
            .buttonStyle(.borderedProminent)
            .controlSize(.large)
            .disabled(trimmedHandle.isEmpty || isBusy)

            if let errorText {
                Text(errorText)
                    .font(.footnote)
                    .foregroundStyle(.red)
                    .multilineTextAlignment(.center)
            }

            Spacer()
        }
        .padding(24)
        .sheet(item: $authURL) { item in
            SafariView(url: item.url)
                .ignoresSafeArea()
        }
        .task { autoSignInIfRequested() }
    }

    /// Simulator sign-in automation. See SignInTestHook and scripts/sim-signin.sh.
    private func autoSignInIfRequested() {
        #if DEBUG
        guard let auto = SignInTestHook.autoHandle, handle.isEmpty, !isBusy else { return }
        handle = auto
        signIn()
        #endif
    }

    private var trimmedHandle: String {
        handle.trimmingCharacters(in: .whitespacesAndNewlines)
    }

    private func signIn() {
        isBusy = true
        errorText = nil
        Task {
            do {
                try await store.signIn(handle: trimmedHandle) { url in
                    #if DEBUG
                    if SignInTestHook.isEnabled {
                        // The headless browser is the only one to open the
                        // authorization request; the PAR request_uri is
                        // single-use ("initiated from another device").
                        SignInTestHook.writeAuthorizationURL(url)
                        return
                    }
                    #endif
                    authURL = IdentifiableURL(url: url)
                }
                #if DEBUG
                if SignInTestHook.isEnabled { SignInTestHook.finish(.success(store)) }
                #endif
            } catch {
                errorText = error.localizedDescription
                #if DEBUG
                if SignInTestHook.isEnabled { SignInTestHook.finish(.failure(error)) }
                #endif
            }
            authURL = nil
            isBusy = false
        }
    }
}