Live notifications
This commit is contained in:
parent
93543cad6b
commit
a8fb18559d
|
@ -393,7 +393,7 @@
|
||||||
CODE_SIGN_IDENTITY = "-";
|
CODE_SIGN_IDENTITY = "-";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 300;
|
CURRENT_PROJECT_VERSION = 350;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"IceCubesApp/Resources\"";
|
DEVELOPMENT_ASSET_PATHS = "\"IceCubesApp/Resources\"";
|
||||||
DEVELOPMENT_TEAM = Z6P74P6T99;
|
DEVELOPMENT_TEAM = Z6P74P6T99;
|
||||||
|
@ -415,7 +415,7 @@
|
||||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 0.3.0;
|
MARKETING_VERSION = 0.3.5;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp;
|
PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = auto;
|
SDKROOT = auto;
|
||||||
|
@ -437,7 +437,7 @@
|
||||||
CODE_SIGN_IDENTITY = "-";
|
CODE_SIGN_IDENTITY = "-";
|
||||||
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 300;
|
CURRENT_PROJECT_VERSION = 350;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_ASSET_PATHS = "\"IceCubesApp/Resources\"";
|
DEVELOPMENT_ASSET_PATHS = "\"IceCubesApp/Resources\"";
|
||||||
DEVELOPMENT_TEAM = Z6P74P6T99;
|
DEVELOPMENT_TEAM = Z6P74P6T99;
|
||||||
|
@ -459,7 +459,7 @@
|
||||||
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "@executable_path/Frameworks";
|
||||||
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
"LD_RUNPATH_SEARCH_PATHS[sdk=macosx*]" = "@executable_path/../Frameworks";
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 0.3.0;
|
MARKETING_VERSION = 0.3.5;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp;
|
PRODUCT_BUNDLE_IDENTIFIER = com.thomasricouard.IceCubesApp;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = auto;
|
SDKROOT = auto;
|
||||||
|
|
|
@ -47,6 +47,7 @@ struct IceCubesApp: App {
|
||||||
.tabItem {
|
.tabItem {
|
||||||
Label("Notifications", systemImage: "bell")
|
Label("Notifications", systemImage: "bell")
|
||||||
}
|
}
|
||||||
|
.badge(watcher.unreadNotificationsCount)
|
||||||
.tag(Tab.notifications)
|
.tag(Tab.notifications)
|
||||||
ExploreTab(popToRootTab: $popToRootTab)
|
ExploreTab(popToRootTab: $popToRootTab)
|
||||||
.tabItem {
|
.tabItem {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import Network
|
||||||
import Notifications
|
import Notifications
|
||||||
|
|
||||||
struct NotificationsTab: View {
|
struct NotificationsTab: View {
|
||||||
|
@EnvironmentObject private var watcher: StreamWatcher
|
||||||
@StateObject private var routeurPath = RouterPath()
|
@StateObject private var routeurPath = RouterPath()
|
||||||
@Binding var popToRootTab: IceCubesApp.Tab
|
@Binding var popToRootTab: IceCubesApp.Tab
|
||||||
|
|
||||||
|
@ -14,6 +15,9 @@ struct NotificationsTab: View {
|
||||||
.withAppRouteur()
|
.withAppRouteur()
|
||||||
.withSheetDestinations(sheetDestinations: $routeurPath.presentedSheet)
|
.withSheetDestinations(sheetDestinations: $routeurPath.presentedSheet)
|
||||||
}
|
}
|
||||||
|
.onAppear {
|
||||||
|
watcher.unreadNotificationsCount = 0
|
||||||
|
}
|
||||||
.environmentObject(routeurPath)
|
.environmentObject(routeurPath)
|
||||||
.onChange(of: $popToRootTab.wrappedValue) { popToRootTab in
|
.onChange(of: $popToRootTab.wrappedValue) { popToRootTab in
|
||||||
if popToRootTab == .notifications {
|
if popToRootTab == .notifications {
|
||||||
|
|
|
@ -17,6 +17,7 @@ public class StreamWatcher: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Published public var events: [any StreamEvent] = []
|
@Published public var events: [any StreamEvent] = []
|
||||||
|
@Published public var unreadNotificationsCount: Int = 0
|
||||||
@Published public var latestEvent: (any StreamEvent)?
|
@Published public var latestEvent: (any StreamEvent)?
|
||||||
|
|
||||||
public init() {
|
public init() {
|
||||||
|
@ -38,6 +39,9 @@ public class StreamWatcher: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func watch(stream: Stream) {
|
public func watch(stream: Stream) {
|
||||||
|
if client?.isAuth == false && stream == .user {
|
||||||
|
return
|
||||||
|
}
|
||||||
if task == nil {
|
if task == nil {
|
||||||
connect()
|
connect()
|
||||||
}
|
}
|
||||||
|
@ -71,6 +75,9 @@ public class StreamWatcher: ObservableObject {
|
||||||
Task { @MainActor in
|
Task { @MainActor in
|
||||||
self.events.append(event)
|
self.events.append(event)
|
||||||
self.latestEvent = event
|
self.latestEvent = event
|
||||||
|
if event is StreamEventNotification {
|
||||||
|
self.unreadNotificationsCount += 1
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
|
|
@ -3,8 +3,10 @@ import Network
|
||||||
import Models
|
import Models
|
||||||
import Shimmer
|
import Shimmer
|
||||||
import DesignSystem
|
import DesignSystem
|
||||||
|
import Env
|
||||||
|
|
||||||
public struct NotificationsListView: View {
|
public struct NotificationsListView: View {
|
||||||
|
@EnvironmentObject private var watcher: StreamWatcher
|
||||||
@EnvironmentObject private var client: Client
|
@EnvironmentObject private var client: Client
|
||||||
@StateObject private var viewModel = NotificationsViewModel()
|
@StateObject private var viewModel = NotificationsViewModel()
|
||||||
|
|
||||||
|
@ -39,6 +41,11 @@ public struct NotificationsListView: View {
|
||||||
.refreshable {
|
.refreshable {
|
||||||
await viewModel.fetchNotifications()
|
await viewModel.fetchNotifications()
|
||||||
}
|
}
|
||||||
|
.onChange(of: watcher.latestEvent?.id, perform: { _ in
|
||||||
|
if let latestEvent = watcher.latestEvent {
|
||||||
|
viewModel.handleEvent(event: latestEvent)
|
||||||
|
}
|
||||||
|
})
|
||||||
.navigationTitle(Text("Notifications"))
|
.navigationTitle(Text("Notifications"))
|
||||||
.navigationBarTitleDisplayMode(.inline)
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
}
|
}
|
||||||
|
|
|
@ -71,4 +71,11 @@ class NotificationsViewModel: ObservableObject {
|
||||||
state = .error(error: error)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue