Sync with markers API for notifications

This commit is contained in:
Thomas Ricouard 2023-12-26 16:01:02 +01:00
parent 4de4c7c82a
commit 590299d102
7 changed files with 59 additions and 19 deletions

View file

@ -15,7 +15,7 @@ extension IceCubesApp {
ForEach(availableTabs) { tab in ForEach(availableTabs) { tab in
if tab == selectedTab || sideBarLoadedTabs.contains(tab) { if tab == selectedTab || sideBarLoadedTabs.contains(tab) {
tab tab
.makeContentView(popToRootTab: $popToRootTab) .makeContentView(selectedTab: $selectedTab, popToRootTab: $popToRootTab)
.opacity(tab == selectedTab ? 1 : 0) .opacity(tab == selectedTab ? 1 : 0)
.transition(.opacity) .transition(.opacity)
.id("\(tab)\(appAccountsManager.currentAccount.id)") .id("\(tab)\(appAccountsManager.currentAccount.id)")
@ -41,7 +41,8 @@ extension IceCubesApp {
} }
var notificationsSecondaryColumn: some View { var notificationsSecondaryColumn: some View {
NotificationsTab(popToRootTab: $popToRootTab, lockedType: nil) NotificationsTab(selectedTab: .constant(.notifications),
popToRootTab: $popToRootTab, lockedType: nil)
.environment(\.isSecondaryColumn, true) .environment(\.isSecondaryColumn, true)
.frame(maxWidth: .secondaryColumnWidth) .frame(maxWidth: .secondaryColumnWidth)
.id(appAccountsManager.currentAccount.id) .id(appAccountsManager.currentAccount.id)

View file

@ -20,7 +20,7 @@ extension IceCubesApp {
selectedTab = newTab selectedTab = newTab
})) { })) {
ForEach(availableTabs) { tab in ForEach(availableTabs) { tab in
tab.makeContentView(popToRootTab: $popToRootTab) tab.makeContentView(selectedTab: $selectedTab, popToRootTab: $popToRootTab)
.tabItem { .tabItem {
if userPreferences.showiPhoneTabLabel { if userPreferences.showiPhoneTabLabel {
tab.label tab.label
@ -34,7 +34,6 @@ extension IceCubesApp {
} }
} }
.id(appAccountsManager.currentClient.id) .id(appAccountsManager.currentClient.id)
.environment(\.selectedTab, selectedTab)
} }
private func badgeFor(tab: Tab) -> Int { private func badgeFor(tab: Tab) -> Int {

View file

@ -11,7 +11,6 @@ import Timeline
struct NotificationsTab: View { struct NotificationsTab: View {
@Environment(\.isSecondaryColumn) private var isSecondaryColumn: Bool @Environment(\.isSecondaryColumn) private var isSecondaryColumn: Bool
@Environment(\.scenePhase) private var scenePhase @Environment(\.scenePhase) private var scenePhase
@Environment(\.selectedTab) private var selectedTab: Tab
@Environment(Theme.self) private var theme @Environment(Theme.self) private var theme
@Environment(Client.self) private var client @Environment(Client.self) private var client
@ -22,6 +21,8 @@ struct NotificationsTab: View {
@Environment(PushNotificationsService.self) private var pushNotificationsService @Environment(PushNotificationsService.self) private var pushNotificationsService
@State private var routerPath = RouterPath() @State private var routerPath = RouterPath()
@State private var scrollToTopSignal: Int = 0 @State private var scrollToTopSignal: Int = 0
@Binding var selectedTab: Tab
@Binding var popToRootTab: Tab @Binding var popToRootTab: Tab
let lockedType: Models.Notification.NotificationType? let lockedType: Models.Notification.NotificationType?
@ -72,6 +73,9 @@ struct NotificationsTab: View {
} }
} }
} }
.onChange(of: selectedTab, { _, newValue in
clearNotifications()
})
.onChange(of: pushNotificationsService.handledNotification) { _, newValue in .onChange(of: pushNotificationsService.handledNotification) { _, newValue in
if let newValue, let type = newValue.notification.supportedType { if let newValue, let type = newValue.notification.supportedType {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {

View file

@ -31,7 +31,7 @@ enum Tab: Int, Identifiable, Hashable {
} }
@ViewBuilder @ViewBuilder
func makeContentView(popToRootTab: Binding<Tab>) -> some View { func makeContentView(selectedTab: Binding<Tab>, popToRootTab: Binding<Tab>) -> some View {
switch self { switch self {
case .timeline: case .timeline:
TimelineTab(popToRootTab: popToRootTab) TimelineTab(popToRootTab: popToRootTab)
@ -42,9 +42,9 @@ enum Tab: Int, Identifiable, Hashable {
case .federated: case .federated:
TimelineTab(popToRootTab: popToRootTab, timeline: .federated) TimelineTab(popToRootTab: popToRootTab, timeline: .federated)
case .notifications: case .notifications:
NotificationsTab(popToRootTab: popToRootTab, lockedType: nil) NotificationsTab(selectedTab: selectedTab, popToRootTab: popToRootTab, lockedType: nil)
case .mentions: case .mentions:
NotificationsTab(popToRootTab: popToRootTab, lockedType: .mention) NotificationsTab(selectedTab: selectedTab, popToRootTab: popToRootTab, lockedType: .mention)
case .explore: case .explore:
ExploreTab(popToRootTab: popToRootTab) ExploreTab(popToRootTab: popToRootTab)
case .messages: case .messages:
@ -113,14 +113,3 @@ enum Tab: Int, Identifiable, Hashable {
} }
} }
} }
private struct SelectedTab: EnvironmentKey {
static let defaultValue: Tab = .timeline
}
extension EnvironmentValues {
var selectedTab: Tab {
get { self[SelectedTab.self] }
set { self[SelectedTab.self] = newValue }
}
}

View file

@ -0,0 +1,12 @@
import Foundation
public struct Marker: Codable, Sendable {
public struct Content: Codable, Sendable {
public let lastReadId: String
public let version: Int
public let updatedAt: ServerDate
}
public let notifications: Content?
public let home: Content?
}

View file

@ -0,0 +1,22 @@
import Foundation
public enum Markers: Endpoint {
case markers
case markNotifications(lastReadId: String)
case markHome(lastReadId: String)
public func path() -> String {
"markers"
}
public func queryItems() -> [URLQueryItem]? {
switch self {
case let .markNotifications(lastReadId):
[URLQueryItem(name: "notifications[last_read_id]", value: lastReadId)]
case let .markHome(lastReadId):
[URLQueryItem(name: "home[last_read_id]", value: lastReadId)]
default:
nil
}
}
}

View file

@ -90,6 +90,7 @@ import SwiftUI
types: queryTypes, types: queryTypes,
limit: Constants.notificationLimit)) limit: Constants.notificationLimit))
consolidatedNotifications = await notifications.consolidated(selectedType: selectedType) consolidatedNotifications = await notifications.consolidated(selectedType: selectedType)
markAsRead()
nextPageState = notifications.count < Constants.notificationLimit ? .none : .hasNextPage nextPageState = notifications.count < Constants.notificationLimit ? .none : .hasNextPage
} else if let firstId = consolidatedNotifications.first?.id { } else if let firstId = consolidatedNotifications.first?.id {
var newNotifications: [Models.Notification] = await fetchNewPages(minId: firstId, maxPages: 10) var newNotifications: [Models.Notification] = await fetchNewPages(minId: firstId, maxPages: 10)
@ -97,6 +98,7 @@ import SwiftUI
newNotifications = newNotifications.filter { notification in newNotifications = newNotifications.filter { notification in
!consolidatedNotifications.contains(where: { $0.id == notification.id }) !consolidatedNotifications.contains(where: { $0.id == notification.id })
} }
await consolidatedNotifications.insert( await consolidatedNotifications.insert(
contentsOf: newNotifications.consolidated(selectedType: selectedType), contentsOf: newNotifications.consolidated(selectedType: selectedType),
at: 0 at: 0
@ -107,6 +109,8 @@ import SwiftUI
await currentAccount.fetchFollowerRequests() await currentAccount.fetchFollowerRequests()
} }
markAsRead()
withAnimation { withAnimation {
state = .display(notifications: consolidatedNotifications, state = .display(notifications: consolidatedNotifications,
nextPageState: consolidatedNotifications.isEmpty ? .none : nextPageState) nextPageState: consolidatedNotifications.isEmpty ? .none : nextPageState)
@ -162,6 +166,15 @@ import SwiftUI
} }
} }
func markAsRead() {
guard let client, let id = consolidatedNotifications.first?.notifications.first?.id else { return }
Task {
do {
let _: Marker = try await client.post(endpoint: Markers.markNotifications(lastReadId: id))
} catch { }
}
}
func handleEvent(event: any StreamEvent) { func handleEvent(event: any StreamEvent) {
Task { Task {
// Check if the event is a notification, // Check if the event is a notification,