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}