krz/hutch

an ios client for sourcehut

clone: git clone https://gitbay.org/krz/hutch.git

v3.0.0: Hutch/Views/More/AccountSwitcherView.swift · raw

  1import SwiftUI
  2
  3struct AccountSwitcherView: View {
  4    @Environment(AppState.self) private var appState
  5    @Environment(\.dismiss) private var dismiss
  6
  7    @State private var showAddAccount = false
  8    @State private var isSwitching = false
  9    @State private var switchError: String?
 10    @State private var pendingRemoval: AccountEntry?
 11
 12    var body: some View {
 13        NavigationStack {
 14            List {
 15                Section {
 16                    ForEach(appState.accounts) { account in
 17                        let isActive = account.id == appState.activeAccountID
 18                        Button {
 19                            guard !isActive else { return }
 20                            switchTo(account)
 21                        } label: {
 22                            HStack(spacing: 12) {
 23                                if isActive {
 24                                    Image(systemName: "person.crop.circle.fill")
 25                                        .foregroundStyle(.tint)
 26                                } else {
 27                                    Image(systemName: "person.crop.circle")
 28                                        .foregroundStyle(.secondary)
 29                                }
 30                                VStack(alignment: .leading, spacing: 2) {
 31                                    Text("~\(account.username)")
 32                                        .foregroundStyle(.primary)
 33                                    Text(isActive ? "Active Account" : "Tap to switch")
 34                                        .font(.caption)
 35                                        .foregroundStyle(.secondary)
 36                                }
 37                                Spacer()
 38                                if isActive {
 39                                    Image(systemName: "checkmark")
 40                                        .foregroundStyle(.tint)
 41                                }
 42                            }
 43                        }
 44                        .disabled(isSwitching)
 45                    }
 46                    .onDelete { indexSet in
 47                        for index in indexSet {
 48                            pendingRemoval = appState.accounts[index]
 49                        }
 50                    }
 51                }
 52
 53                Section {
 54                    Button {
 55                        showAddAccount = true
 56                    } label: {
 57                        Label("Add Account", systemImage: "plus.circle")
 58                    }
 59                    .disabled(isSwitching)
 60                }
 61            }
 62            .navigationTitle("Accounts")
 63            .navigationBarTitleDisplayMode(.inline)
 64            .toolbar {
 65                ToolbarItem(placement: .confirmationAction) {
 66                    Button("Done") { dismiss() }
 67                }
 68            }
 69            .overlay {
 70                if isSwitching {
 71                    ZStack {
 72                        Color.black.opacity(0.25).ignoresSafeArea()
 73                        ProgressView("Switching…")
 74                            .padding()
 75                            .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 12))
 76                    }
 77                }
 78            }
 79            .alert("Switch Failed", isPresented: Binding(
 80                get: { switchError != nil },
 81                set: { if !$0 { switchError = nil } }
 82            )) {
 83                Button("OK") { switchError = nil }
 84            } message: {
 85                Text(switchError ?? "")
 86            }
 87            .alert(
 88                "Remove Account?",
 89                isPresented: Binding(
 90                    get: { pendingRemoval != nil },
 91                    set: { isPresented in
 92                        if !isPresented {
 93                            pendingRemoval = nil
 94                        }
 95                    }
 96                )
 97            ) {
 98                Button("Cancel", role: .cancel) {}
 99                Button("Remove", role: .destructive) {
100                    guard let pendingRemoval else { return }
101                    Task { await appState.removeAccount(id: pendingRemoval.id) }
102                    self.pendingRemoval = nil
103                }
104            } message: {
105                if let pendingRemoval {
106                    Text("~\(pendingRemoval.username) and its isolated local cache will be removed from this device.")
107                }
108            }
109            .sheet(isPresented: $showAddAccount) {
110                AddAccountView()
111            }
112        }
113    }
114
115    private func switchTo(_ account: AccountEntry) {
116        isSwitching = true
117        Task {
118            do {
119                try await appState.switchAccount(to: account.id)
120                dismiss()
121            } catch {
122                switchError = error.localizedDescription
123            }
124            isSwitching = false
125        }
126    }
127}