diff options
| author | Christian Cleberg <[email protected]> | 2026-04-12 23:19:20 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-04-12 23:19:20 -0500 |
| commit | ee9f2904aa319231b7047fca3cb069f0c07019cd (patch) | |
| tree | 69a18234512c63f38f0d477e0023969698ec3c9e /Shared | |
| parent | 72f860e0e171f6aac4e7d896e7a571147dbcb5c5 (diff) | |
| download | hutch-ee9f2904aa319231b7047fca3cb069f0c07019cd.tar.gz hutch-ee9f2904aa319231b7047fca3cb069f0c07019cd.tar.bz2 hutch-ee9f2904aa319231b7047fca3cb069f0c07019cd.zip | |
feat: add multi-account support
Implements: https://todo.sr.ht/~ccleberg/hutch/49
Implements: https://todo.sr.ht/~ccleberg/hutch/50
Implements: https://todo.sr.ht/~ccleberg/hutch/51
Diffstat (limited to 'Shared')
| -rw-r--r-- | Shared/ContributionWidgetContext.swift | 27 | ||||
| -rw-r--r-- | Shared/NeedsAttentionSnapshot.swift | 56 | ||||
| -rw-r--r-- | Shared/SystemStatusWidgetSnapshot.swift | 27 |
3 files changed, 89 insertions, 21 deletions
diff --git a/Shared/ContributionWidgetContext.swift b/Shared/ContributionWidgetContext.swift index 2404698..33fed77 100644 --- a/Shared/ContributionWidgetContext.swift +++ b/Shared/ContributionWidgetContext.swift @@ -11,8 +11,11 @@ enum ContributionWidgetContextStore { private static let actorKey = "contributionWidget.actor" private static let enabledKey = "contributionWidget.enabled" - static func loadActor(defaults: UserDefaults? = sharedDefaults()) -> String? { - defaults?.string(forKey: actorKey) + static func loadActor( + accountID: String? = ActiveAccountContextStore.load(), + defaults: UserDefaults? = sharedDefaults() + ) -> String? { + defaults?.string(forKey: scopedActorKey(for: accountID)) } static func isEnabled(defaults: UserDefaults? = sharedDefaults()) -> Bool { @@ -24,13 +27,20 @@ enum ContributionWidgetContextStore { reloadWidgetTimelines() } - static func saveActor(_ actor: String, defaults: UserDefaults? = sharedDefaults()) { - defaults?.set(actor, forKey: actorKey) + static func saveActor( + _ actor: String, + accountID: String? = ActiveAccountContextStore.load(), + defaults: UserDefaults? = sharedDefaults() + ) { + defaults?.set(actor, forKey: scopedActorKey(for: accountID)) reloadWidgetTimelines() } - static func clear(defaults: UserDefaults? = sharedDefaults()) { - defaults?.removeObject(forKey: actorKey) + static func clear( + accountID: String? = ActiveAccountContextStore.load(), + defaults: UserDefaults? = sharedDefaults() + ) { + defaults?.removeObject(forKey: scopedActorKey(for: accountID)) reloadWidgetTimelines() } @@ -38,6 +48,11 @@ enum ContributionWidgetContextStore { UserDefaults(suiteName: HutchAppGroup.identifier) } + private static func scopedActorKey(for accountID: String?) -> String { + guard let accountID, !accountID.isEmpty else { return actorKey } + return "\(actorKey).\(accountID)" + } + private static func reloadWidgetTimelines() { #if canImport(WidgetKit) WidgetCenter.shared.reloadTimelines(ofKind: ContributionGraphWidgetConfiguration.kind) diff --git a/Shared/NeedsAttentionSnapshot.swift b/Shared/NeedsAttentionSnapshot.swift index f386af6..d4c581c 100644 --- a/Shared/NeedsAttentionSnapshot.swift +++ b/Shared/NeedsAttentionSnapshot.swift @@ -7,6 +7,26 @@ enum HutchAppGroup { static let identifier = "group.net.cleberg.Hutch" } +enum ActiveAccountContextStore { + private static let activeAccountIDKey = "activeAccount.id" + + static func load(defaults: UserDefaults? = sharedDefaults()) -> String? { + defaults?.string(forKey: activeAccountIDKey) + } + + static func save(_ accountID: String, defaults: UserDefaults? = sharedDefaults()) { + defaults?.set(accountID, forKey: activeAccountIDKey) + } + + static func clear(defaults: UserDefaults? = sharedDefaults()) { + defaults?.removeObject(forKey: activeAccountIDKey) + } + + private static func sharedDefaults() -> UserDefaults? { + UserDefaults(suiteName: HutchAppGroup.identifier) + } +} + enum NeedsAttentionWidgetConfiguration { static let kind = "NeedsAttentionWidget" } @@ -37,22 +57,29 @@ struct NeedsAttentionSnapshot: Codable, Sendable { enum NeedsAttentionSnapshotStore { private static let snapshotKey = "needsAttention.snapshot" - static func load(defaults: UserDefaults? = sharedDefaults()) -> NeedsAttentionSnapshot? { + static func load( + accountID: String? = ActiveAccountContextStore.load(), + defaults: UserDefaults? = sharedDefaults() + ) -> NeedsAttentionSnapshot? { guard let defaults, - let data = defaults.data(forKey: snapshotKey) else { + let data = defaults.data(forKey: scopedKey(for: accountID)) else { return nil } return try? JSONDecoder().decode(NeedsAttentionSnapshot.self, from: data) } - static func save(_ snapshot: NeedsAttentionSnapshot, defaults: UserDefaults? = sharedDefaults()) { + static func save( + _ snapshot: NeedsAttentionSnapshot, + accountID: String? = ActiveAccountContextStore.load(), + defaults: UserDefaults? = sharedDefaults() + ) { guard let defaults, let data = try? JSONEncoder().encode(snapshot) else { return } - defaults.set(data, forKey: snapshotKey) + defaults.set(data, forKey: scopedKey(for: accountID)) reloadWidgetTimelines() } @@ -60,23 +87,25 @@ enum NeedsAttentionSnapshotStore { unreadInboxThreads: Int? = nil, assignedOpenTickets: Int? = nil, failedBuilds: Int? = nil, + accountID: String? = ActiveAccountContextStore.load(), defaults: UserDefaults? = sharedDefaults() ) { - let existing = load(defaults: defaults) + let existing = load(accountID: accountID, defaults: defaults) let snapshot = NeedsAttentionSnapshot( unreadInboxThreads: unreadInboxThreads ?? existing?.unreadInboxThreads, assignedOpenTickets: assignedOpenTickets ?? existing?.assignedOpenTickets, failedBuilds: failedBuilds ?? existing?.failedBuilds, updatedAt: .now ) - save(snapshot, defaults: defaults) + save(snapshot, accountID: accountID, defaults: defaults) } static func adjustUnreadInboxThreads( by delta: Int, + accountID: String? = ActiveAccountContextStore.load(), defaults: UserDefaults? = sharedDefaults() ) { - guard let existing = load(defaults: defaults), + guard let existing = load(accountID: accountID, defaults: defaults), let unreadInboxThreads = existing.unreadInboxThreads else { return } @@ -88,12 +117,16 @@ enum NeedsAttentionSnapshotStore { failedBuilds: existing.failedBuilds, updatedAt: .now ), + accountID: accountID, defaults: defaults ) } - static func clear(defaults: UserDefaults? = sharedDefaults()) { - defaults?.removeObject(forKey: snapshotKey) + static func clear( + accountID: String? = ActiveAccountContextStore.load(), + defaults: UserDefaults? = sharedDefaults() + ) { + defaults?.removeObject(forKey: scopedKey(for: accountID)) reloadWidgetTimelines() } @@ -101,6 +134,11 @@ enum NeedsAttentionSnapshotStore { UserDefaults(suiteName: HutchAppGroup.identifier) } + private static func scopedKey(for accountID: String?) -> String { + guard let accountID, !accountID.isEmpty else { return snapshotKey } + return "\(snapshotKey).\(accountID)" + } + private static func reloadWidgetTimelines() { #if canImport(WidgetKit) WidgetCenter.shared.reloadTimelines(ofKind: NeedsAttentionWidgetConfiguration.kind) diff --git a/Shared/SystemStatusWidgetSnapshot.swift b/Shared/SystemStatusWidgetSnapshot.swift index 8489547..0b19365 100644 --- a/Shared/SystemStatusWidgetSnapshot.swift +++ b/Shared/SystemStatusWidgetSnapshot.swift @@ -33,25 +33,35 @@ struct SystemStatusWidgetSnapshot: Codable, Sendable { enum SystemStatusWidgetSnapshotStore { private static let snapshotKey = "systemStatus.widgetSnapshot" - static func load(defaults: UserDefaults? = sharedDefaults()) -> SystemStatusWidgetSnapshot? { + static func load( + accountID: String? = ActiveAccountContextStore.load(), + defaults: UserDefaults? = sharedDefaults() + ) -> SystemStatusWidgetSnapshot? { guard let defaults, - let data = defaults.data(forKey: snapshotKey) else { + let data = defaults.data(forKey: scopedKey(for: accountID)) else { return nil } return try? JSONDecoder().decode(SystemStatusWidgetSnapshot.self, from: data) } - static func save(_ snapshot: SystemStatusWidgetSnapshot, defaults: UserDefaults? = sharedDefaults()) { + static func save( + _ snapshot: SystemStatusWidgetSnapshot, + accountID: String? = ActiveAccountContextStore.load(), + defaults: UserDefaults? = sharedDefaults() + ) { guard let defaults, let data = try? JSONEncoder().encode(snapshot) else { return } - defaults.set(data, forKey: snapshotKey) + defaults.set(data, forKey: scopedKey(for: accountID)) reloadWidgetTimelines() } - static func clear(defaults: UserDefaults? = sharedDefaults()) { - defaults?.removeObject(forKey: snapshotKey) + static func clear( + accountID: String? = ActiveAccountContextStore.load(), + defaults: UserDefaults? = sharedDefaults() + ) { + defaults?.removeObject(forKey: scopedKey(for: accountID)) reloadWidgetTimelines() } @@ -59,6 +69,11 @@ enum SystemStatusWidgetSnapshotStore { UserDefaults(suiteName: HutchAppGroup.identifier) } + private static func scopedKey(for accountID: String?) -> String { + guard let accountID, !accountID.isEmpty else { return snapshotKey } + return "\(snapshotKey).\(accountID)" + } + private static func reloadWidgetTimelines() { #if canImport(WidgetKit) WidgetCenter.shared.reloadTimelines(ofKind: SystemStatusWidgetConfiguration.kind) |
