diff --git a/IceCubesApp.xcodeproj/project.pbxproj b/IceCubesApp.xcodeproj/project.pbxproj index 8048cb13..7ecf271a 100644 --- a/IceCubesApp.xcodeproj/project.pbxproj +++ b/IceCubesApp.xcodeproj/project.pbxproj @@ -393,7 +393,7 @@ CODE_SIGN_IDENTITY = "-"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 300; + CURRENT_PROJECT_VERSION = 350; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"IceCubesApp/Resources\""; DEVELOPMENT_TEAM = Z6P74P6T99; @@ -415,7 +415,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 0.3.0; + MARKETING_VERSION = 0.3.5; PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; @@ -437,7 +437,7 @@ CODE_SIGN_IDENTITY = "-"; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 300; + CURRENT_PROJECT_VERSION = 350; DEAD_CODE_STRIPPING = YES; DEVELOPMENT_ASSET_PATHS = "\"IceCubesApp/Resources\""; DEVELOPMENT_TEAM = Z6P74P6T99; @@ -459,7 +459,7 @@ LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks"; "LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks"; MACOSX_DEPLOYMENT_TARGET = 13.0; - MARKETING_VERSION = 0.3.0; + MARKETING_VERSION = 0.3.5; PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp; PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = auto; diff --git a/IceCubesApp/App/IceCubesApp.swift b/IceCubesApp/App/IceCubesApp.swift index 07d8a52c..12ebc4c8 100644 --- a/IceCubesApp/App/IceCubesApp.swift +++ b/IceCubesApp/App/IceCubesApp.swift @@ -47,6 +47,7 @@ struct IceCubesApp: App { .tabItem { Label("Notifications", systemImage: "bell") } + .badge(watcher.unreadNotificationsCount) .tag(Tab.notifications) ExploreTab(popToRootTab: $popToRootTab) .tabItem { diff --git a/IceCubesApp/App/Tabs/NotificationTab.swift b/IceCubesApp/App/Tabs/NotificationTab.swift index 52b089d2..85a374ce 100644 --- a/IceCubesApp/App/Tabs/NotificationTab.swift +++ b/IceCubesApp/App/Tabs/NotificationTab.swift @@ -5,6 +5,7 @@ import Network import Notifications struct NotificationsTab: View { + @EnvironmentObject private var watcher: StreamWatcher @StateObject private var routeurPath = RouterPath() @Binding var popToRootTab: IceCubesApp.Tab @@ -14,6 +15,9 @@ struct NotificationsTab: View { .withAppRouteur() .withSheetDestinations(sheetDestinations: $routeurPath.presentedSheet) } + .onAppear { + watcher.unreadNotificationsCount = 0 + } .environmentObject(routeurPath) .onChange(of: $popToRootTab.wrappedValue) { popToRootTab in if popToRootTab == .notifications { diff --git a/Packages/Env/Sources/Env/StreamWatcher.swift b/Packages/Env/Sources/Env/StreamWatcher.swift index 0202e2cf..4e8eecad 100644 --- a/Packages/Env/Sources/Env/StreamWatcher.swift +++ b/Packages/Env/Sources/Env/StreamWatcher.swift @@ -17,6 +17,7 @@ public class StreamWatcher: ObservableObject { } @Published public var events: [any StreamEvent] = [] + @Published public var unreadNotificationsCount: Int = 0 @Published public var latestEvent: (any StreamEvent)? public init() { @@ -38,6 +39,9 @@ public class StreamWatcher: ObservableObject { } public func watch(stream: Stream) { + if client?.isAuth == false && stream == .user { + return + } if task == nil { connect() } @@ -71,6 +75,9 @@ public class StreamWatcher: ObservableObject { Task { @MainActor in self.events.append(event) self.latestEvent = event + if event is StreamEventNotification { + self.unreadNotificationsCount += 1 + } } } } catch { diff --git a/Packages/Notifications/Sources/Notifications/NotificationsListView.swift b/Packages/Notifications/Sources/Notifications/NotificationsListView.swift index 5446a4a1..5abe9d5e 100644 --- a/Packages/Notifications/Sources/Notifications/NotificationsListView.swift +++ b/Packages/Notifications/Sources/Notifications/NotificationsListView.swift @@ -3,8 +3,10 @@ import Network import Models import Shimmer import DesignSystem +import Env public struct NotificationsListView: View { + @EnvironmentObject private var watcher: StreamWatcher @EnvironmentObject private var client: Client @StateObject private var viewModel = NotificationsViewModel() @@ -39,6 +41,11 @@ public struct NotificationsListView: View { .refreshable { await viewModel.fetchNotifications() } + .onChange(of: watcher.latestEvent?.id, perform: { _ in + if let latestEvent = watcher.latestEvent { + viewModel.handleEvent(event: latestEvent) + } + }) .navigationTitle(Text("Notifications")) .navigationBarTitleDisplayMode(.inline) } diff --git a/Packages/Notifications/Sources/Notifications/NotificationsViewModel.swift b/Packages/Notifications/Sources/Notifications/NotificationsViewModel.swift index 1b069db3..2dee61bd 100644 --- a/Packages/Notifications/Sources/Notifications/NotificationsViewModel.swift +++ b/Packages/Notifications/Sources/Notifications/NotificationsViewModel.swift @@ -71,4 +71,11 @@ class NotificationsViewModel: ObservableObject { state = .error(error: error) } } + + func handleEvent(event: any StreamEvent) { + if let event = event as? StreamEventNotification { + notifications.insert(event.notification, at: 0) + state = .display(notifications: notifications, nextPageState: .hasNextPage) + } + } }