summaryrefslogtreecommitdiff
path: root/Hutch/App
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-04-13 09:57:23 -0500
committerChristian Cleberg <[email protected]>2026-04-13 09:57:23 -0500
commit0e461a382257979037e8927e5f6b468635b0a7fe (patch)
tree0aa3a19f6f8f966b0e90f4874ed54359a7898107 /Hutch/App
parentee9f2904aa319231b7047fca3cb069f0c07019cd (diff)
downloadhutch-0e461a382257979037e8927e5f6b468635b0a7fe.tar.gz
hutch-0e461a382257979037e8927e5f6b468635b0a7fe.tar.bz2
hutch-0e461a382257979037e8927e5f6b468635b0a7fe.zip
feat: add power user actions
Implements: https://todo.sr.ht/~ccleberg/hutch/52 Implements: https://todo.sr.ht/~ccleberg/hutch/53 Implements: https://todo.sr.ht/~ccleberg/hutch/54
Diffstat (limited to 'Hutch/App')
-rw-r--r--Hutch/App/AppState.swift33
-rw-r--r--Hutch/App/AppStorageKeys.swift1
-rw-r--r--Hutch/App/RootView.swift7
3 files changed, 41 insertions, 0 deletions
diff --git a/Hutch/App/AppState.swift b/Hutch/App/AppState.swift
index 2f14011..ebd25d1 100644
--- a/Hutch/App/AppState.swift
+++ b/Hutch/App/AppState.swift
@@ -1,5 +1,6 @@
import Foundation
import SwiftUI
+import UIKit
import WebKit
/// Central application state shared across the view hierarchy.
@@ -63,6 +64,13 @@ final class AppState {
private(set) var systemStatusRepository: SystemStatusRepository
private var activeSession: AccountSession?
private(set) var sessionIdentity = UUID()
+ var isDebugModeEnabled = UserDefaults.standard.bool(forKey: AppStorageKeys.debugModeEnabled) {
+ didSet {
+ UserDefaults.standard.set(isDebugModeEnabled, forKey: AppStorageKeys.debugModeEnabled)
+ }
+ }
+ private(set) var copyConfirmationMessage: String?
+ private var copyConfirmationTask: Task<Void, Never>?
var accountDefaults: UserDefaults {
activeSession?.defaults ?? .standard
@@ -210,6 +218,7 @@ final class AppState {
clearAllAccountArtifacts()
authPhase = .unauthenticated
selectedTab = .home
+ dismissCopyConfirmation()
}
func resetAppData() async {
@@ -230,6 +239,19 @@ final class AppState {
authPhase = .unauthenticated
selectedTab = .home
+ isDebugModeEnabled = false
+ dismissCopyConfirmation()
+ }
+
+ func copyToPasteboard(_ value: String, label: String) {
+ UIPasteboard.general.string = value
+ showCopyConfirmation(message: "Copied \(label)")
+ }
+
+ func dismissCopyConfirmation() {
+ copyConfirmationTask?.cancel()
+ copyConfirmationTask = nil
+ copyConfirmationMessage = nil
}
// MARK: - Deep link resolution
@@ -525,6 +547,17 @@ final class AppState {
}
}
}
+
+ private func showCopyConfirmation(message: String) {
+ copyConfirmationTask?.cancel()
+ copyConfirmationMessage = message
+ copyConfirmationTask = Task { @MainActor in
+ try? await Task.sleep(for: .seconds(1.6))
+ guard !Task.isCancelled else { return }
+ copyConfirmationMessage = nil
+ copyConfirmationTask = nil
+ }
+ }
}
enum AppStateError: LocalizedError {
diff --git a/Hutch/App/AppStorageKeys.swift b/Hutch/App/AppStorageKeys.swift
index e151617..4be8004 100644
--- a/Hutch/App/AppStorageKeys.swift
+++ b/Hutch/App/AppStorageKeys.swift
@@ -19,4 +19,5 @@ enum AppStorageKeys {
static let ticketSavedFilters = "ticketSavedFilters"
static let appTheme = "appTheme"
static let displayDensity = "displayDensity"
+ static let debugModeEnabled = "debugModeEnabled"
}
diff --git a/Hutch/App/RootView.swift b/Hutch/App/RootView.swift
index e3baa76..f65bced 100644
--- a/Hutch/App/RootView.swift
+++ b/Hutch/App/RootView.swift
@@ -123,6 +123,13 @@ struct RootView: View {
get: { appState.selectedTab },
set: { appState.selectedTab = $0 }
)))
+ .safeAreaInset(edge: .bottom) {
+ if let message = appState.copyConfirmationMessage {
+ CopyConfirmationBadge(message: message)
+ .padding(.bottom, 4)
+ .transition(.move(edge: .bottom).combined(with: .opacity))
+ }
+ }
.overlay {
if isResolvingDeepLink {
ZStack {