138 lines
4.5 KiB
Swift
138 lines
4.5 KiB
Swift
//
|
|
// https://mczachurski.dev
|
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
|
// Licensed under the Apache License 2.0.
|
|
//
|
|
|
|
import SwiftUI
|
|
import PixelfedKit
|
|
import ClientKit
|
|
import ServicesKit
|
|
import EnvironmentKit
|
|
import WidgetsKit
|
|
|
|
struct NotificationsView: View {
|
|
@EnvironmentObject var applicationState: ApplicationState
|
|
@EnvironmentObject var client: Client
|
|
|
|
@State var accountId: String
|
|
@State private var notifications: [PixelfedKit.Notification] = []
|
|
@State private var allItemsLoaded = false
|
|
@State private var state: ViewState = .loading
|
|
|
|
@State private var minId: String?
|
|
@State private var maxId: String?
|
|
|
|
private let defaultPageSize = 40
|
|
|
|
var body: some View {
|
|
self.mainBody()
|
|
.navigationTitle("notifications.navigationBar.title")
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func mainBody() -> some View {
|
|
switch state {
|
|
case .loading:
|
|
LoadingIndicator()
|
|
.task {
|
|
await self.loadNotifications()
|
|
}
|
|
case .loaded:
|
|
if self.notifications.isEmpty {
|
|
NoDataView(imageSystemName: "bell", text: "notifications.title.noNotifications")
|
|
} else {
|
|
self.list()
|
|
}
|
|
case .error(let error):
|
|
ErrorView(error: error) {
|
|
self.state = .loading
|
|
await self.loadMoreNotifications()
|
|
}
|
|
.padding()
|
|
}
|
|
}
|
|
|
|
@ViewBuilder
|
|
private func list() -> some View {
|
|
List {
|
|
ForEach(notifications, id: \.id) { notification in
|
|
NotificationRowView(notification: notification)
|
|
}
|
|
|
|
if allItemsLoaded == false {
|
|
HStack {
|
|
Spacer()
|
|
LoadingIndicator()
|
|
.task {
|
|
await self.loadMoreNotifications()
|
|
}
|
|
Spacer()
|
|
}
|
|
.listRowSeparator(.hidden)
|
|
}
|
|
}
|
|
.listStyle(PlainListStyle())
|
|
.refreshable {
|
|
HapticService.shared.fireHaptic(of: .dataRefresh(intensity: 0.3))
|
|
await self.loadNewNotifications()
|
|
HapticService.shared.fireHaptic(of: .dataRefresh(intensity: 0.7))
|
|
}
|
|
}
|
|
|
|
func loadNotifications() async {
|
|
do {
|
|
if let linkable = try await self.client.notifications?.notifications(maxId: maxId, minId: minId, limit: 5) {
|
|
self.minId = linkable.link?.minId
|
|
self.maxId = linkable.link?.maxId
|
|
self.notifications = linkable.data
|
|
|
|
if linkable.data.isEmpty {
|
|
self.allItemsLoaded = true
|
|
}
|
|
|
|
self.state = .loaded
|
|
}
|
|
} catch {
|
|
if !Task.isCancelled {
|
|
ErrorService.shared.handle(error, message: "notifications.error.loadingNotificationsFailed", showToastr: true)
|
|
self.state = .error(error)
|
|
} else {
|
|
ErrorService.shared.handle(error, message: "notifications.error.loadingNotificationsFailed", showToastr: false)
|
|
}
|
|
}
|
|
}
|
|
|
|
private func loadMoreNotifications() async {
|
|
do {
|
|
if let linkable = try await self.client.notifications?.notifications(maxId: self.maxId, limit: self.defaultPageSize) {
|
|
if linkable.data.isEmpty {
|
|
self.allItemsLoaded = true
|
|
return
|
|
}
|
|
|
|
self.maxId = linkable.link?.maxId
|
|
self.notifications.append(contentsOf: linkable.data)
|
|
}
|
|
} catch {
|
|
ErrorService.shared.handle(error, message: "notifications.error.loadingNotificationsFailed", showToastr: !Task.isCancelled)
|
|
}
|
|
}
|
|
|
|
private func loadNewNotifications() async {
|
|
do {
|
|
if let linkable = try await self.client.notifications?.notifications(minId: self.minId, limit: self.defaultPageSize) {
|
|
if let first = linkable.data.first, self.notifications.contains(where: { notification in notification.id == first.id }) {
|
|
// We have all notifications, we don't have to do anything.
|
|
return
|
|
}
|
|
|
|
self.minId = linkable.link?.minId
|
|
self.notifications.insert(contentsOf: linkable.data, at: 0)
|
|
}
|
|
} catch {
|
|
ErrorService.shared.handle(error, message: "notifications.error.loadingNotificationsFailed", showToastr: !Task.isCancelled)
|
|
}
|
|
}
|
|
}
|