diff --git a/ios/Sources/NativeRootView.swift b/ios/Sources/NativeRootView.swift index c2b269c..5ab9d75 100644 --- a/ios/Sources/NativeRootView.swift +++ b/ios/Sources/NativeRootView.swift @@ -2,6 +2,12 @@ import SwiftUI private enum NativeDesign { static let accent = Color(red: 108 / 255, green: 58 / 255, blue: 237 / 255) + static func tint(_ dark: Bool) -> Color { + dark ? Color(red: 167 / 255, green: 139 / 255, blue: 250 / 255) : accent + } + static func button(_ dark: Bool) -> Color { + dark ? Color(red: 124 / 255, green: 58 / 255, blue: 237 / 255) : accent + } static func background(_ dark: Bool) -> Color { dark ? Color(red: 25 / 255, green: 24 / 255, blue: 22 / 255) @@ -39,8 +45,18 @@ struct NativeRootView: View { NativeBrandMark().frame(width: 64, height: 64) } } - .tint(NativeDesign.accent) + .tint(NativeDesign.tint(scheme == .dark)) .task { await store.restore() } + .onChange(of: store.user?.id) { + draft = nil + deletePath = nil + search = "" + } + .onChange(of: store.server) { + draft = nil + deletePath = nil + search = "" + } .sheet(item: $draft) { entry in NativeEditor(draft: entry, busy: store.busy, error: store.error) { body in let path: String @@ -139,7 +155,7 @@ struct NativeRootView: View { Image(systemName: page.symbol).font(.system(size: 21)) Text(page.title).font(.system(size: 11)) } - .foregroundStyle(store.page == page ? NativeDesign.accent : .secondary) + .foregroundStyle(store.page == page ? NativeDesign.tint(scheme == .dark) : .secondary) .frame(maxWidth: .infinity).padding(.vertical, 10) } .disabled(store.busy).accessibilityIdentifier("nav-\(page.rawValue)") @@ -341,6 +357,7 @@ struct NativeRootView: View { } private struct NativeAuthenticationScreen: View { + @Environment(\.colorScheme) private var scheme @ObservedObject var store: NativeStore let forget: () -> Void @State private var address = "" @@ -379,8 +396,10 @@ private struct NativeAuthenticationScreen: View { } invalidAddress = false Task { await store.connect(address) } - }.buttonStyle(.borderedProminent).disabled(store.busy || address.isEmpty) - .accessibilityIdentifier("connect") + }.buttonStyle(.borderedProminent).tint(NativeDesign.button(scheme == .dark)).disabled( + store.busy || address.isEmpty + ) + .accessibilityIdentifier("connect") } else if store.twoFactor { TextField("Code oder Wiederherstellungscode", text: $code).textContentType(.oneTimeCode) .textFieldStyle(.roundedBorder) @@ -388,7 +407,8 @@ private struct NativeAuthenticationScreen: View { let value = code code = "" Task { await store.verify(value) } - }.buttonStyle(.borderedProminent).disabled(store.busy || code.isEmpty) + }.buttonStyle(.borderedProminent).tint(NativeDesign.button(scheme == .dark)).disabled( + store.busy || code.isEmpty) Button("Zurück zur Anmeldung") { store.cancelTwoFactor() }.disabled(store.busy) } else { if store.setup { TextField("Anzeigename", text: $name).textFieldStyle(.roundedBorder) } @@ -406,7 +426,7 @@ private struct NativeAuthenticationScreen: View { Task { await store.login(username: username, password: submitted, displayName: name) } - }.buttonStyle(.borderedProminent).disabled( + }.buttonStyle(.borderedProminent).tint(NativeDesign.button(scheme == .dark)).disabled( store.busy || username.isEmpty || password.isEmpty ).accessibilityIdentifier("login") } @@ -437,6 +457,8 @@ private struct NativeAuthenticationScreen: View { private struct NativeEditor: View { @Environment(\.dismiss) private var dismiss + @Environment(\.scenePhase) private var phase + @Environment(\.colorScheme) private var scheme @State var draft: NativeDraft let busy: Bool let error: String? @@ -482,6 +504,14 @@ private struct NativeEditor: View { } } }.interactiveDismissDisabled(busy) + .overlay { + if phase != .active { + ZStack { + NativeDesign.background(scheme == .dark).ignoresSafeArea() + NativeBrandMark().frame(width: 64, height: 64) + } + } + } } } diff --git a/ios/Sources/NativeStore.swift b/ios/Sources/NativeStore.swift index 8f85a6e..e4cc1d7 100644 --- a/ios/Sources/NativeStore.swift +++ b/ios/Sources/NativeStore.swift @@ -175,26 +175,28 @@ final class NativeStore: ObservableObject { revoked = true } catch { revoked = false } await api?.close() + clearLoadedData() try vault?.clear() if let address = try? ServerAddress(server), let storage = vault { api = try NativeAPI(server: address, saved: nil, persist: storage.save) } - clearLoadedData() if !revoked { error = "Lokal abgemeldet. Die Abmeldung auf dem Server konnte nicht bestätigt werden." } } } func forget() async { guard !busy else { return } + busy = true + defer { busy = false } generation += 1 await api?.close() + clearLoadedData() do { try vault?.clear() } catch { self.error = NativeAPIError.unavailable.errorDescription return } api = nil vault = nil - clearLoadedData() server = "" appName = "Yuvomi" setup = false