Clear the app badge when the inbox is read !119
merged
merged by cmc on 2026-09-21 01:25 UTC
· krz/gitbay-ios:badge-clear into main
3 files changed, +32 −1
Layout: unified · split
gitbay/Account/NotificationsViewModel.swift
+7
| @@ -124,6 +124,13 @@ final class NotificationsViewModel { |
| 124 | 124 | |
| 125 | 125 | func markAllRead() async { |
| 126 | 126 | await perform(["notifications", "read", "--all"]) |
| 127 | // Nothing else tells iOS the count dropped: a push sets the badge |
| 128 | // on arrival and no one lowers it, so it would sit at whatever the |
| 129 | // last one said. Zero is exact here. Marking a single row read has |
| 130 | // no reliable count to work from — the inbox list is paged, so the |
| 131 | // rows in hand are not the unread total — and is left to the |
| 132 | // dashboard's resync. |
| 133 | await setAppBadge(0) |
| 127 | 134 | } |
| 128 | 135 | |
| 129 | 136 | func loadPushSettings() async { |
gitbay/Dashboard/DashboardViewModel.swift
+6 −1
| @@ -19,7 +19,12 @@ final class DashboardViewModel { |
| 19 | 19 | |
| 20 | 20 | func load() async { |
| 21 | 21 | do { |
| 22 | | state = .loaded(try await client.read(["dashboard"], as: DashboardData.self)) |
| 22 | let data = try await client.read(["dashboard"], as: DashboardData.self) |
| 23 | state = .loaded(data) |
| 24 | // The authoritative resync. A push sets the badge on arrival |
| 25 | // and nothing lowers it afterwards, so this is what corrects |
| 26 | // it — on every app open, against the account you are in. |
| 27 | await setAppBadge(data.unread) |
| 23 | 28 | } catch { |
| 24 | 29 | state = .from(error) |
| 25 | 30 | } |
gitbay/Push/AppBadge.swift
added
+19
| @@ -0,0 +1,19 @@ |
| 1 | import UserNotifications |
| 2 | |
| 3 | /// Sets the app icon badge to an unread count. |
| 4 | /// |
| 5 | /// A push carries `aps.badge` and iOS applies it on arrival, so the badge |
| 6 | /// is right the moment a notification lands. It is not right afterwards: |
| 7 | /// nothing tells iOS the count dropped when the inbox is read in the app, |
| 8 | /// and the number sits there until the next push. These calls close that. |
| 9 | /// |
| 10 | /// The value is always the active account's unread count. The badge is |
| 11 | /// per-device and unread is per-account, so with two accounts signed in |
| 12 | /// the last writer wins — a push for one account, then a dashboard load |
| 13 | /// on the other, disagree. Exact for a single account. |
| 14 | /// |
| 15 | /// Failures are ignored. A badge that did not take is not worth telling |
| 16 | /// anyone about, and the next writer corrects it. |
| 17 | func setAppBadge(_ count: Int) async { |
| 18 | try? await UNUserNotificationCenter.current().setBadgeCount(max(0, count)) |
| 19 | } |