2022-12-30 18:20:54 +01:00
|
|
|
//
|
|
|
|
// https://mczachurski.dev
|
2023-01-18 18:41:42 +01:00
|
|
|
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
2022-12-30 18:20:54 +01:00
|
|
|
// Licensed under the MIT License.
|
|
|
|
//
|
2023-01-18 18:41:42 +01:00
|
|
|
|
2022-12-30 18:20:54 +01:00
|
|
|
import SwiftUI
|
2023-01-18 18:41:42 +01:00
|
|
|
import MastodonKit
|
2022-12-30 18:20:54 +01:00
|
|
|
|
|
|
|
struct NotificationsView: View {
|
2023-01-18 18:41:42 +01:00
|
|
|
@EnvironmentObject var applicationState: ApplicationState
|
|
|
|
|
|
|
|
@State var accountId: String
|
|
|
|
@State private var notifications: [MastodonKit.Notification] = []
|
|
|
|
@State private var allItemsLoaded = false
|
|
|
|
@State private var firstLoadFinished = false
|
|
|
|
|
|
|
|
@State private var minId: String?
|
|
|
|
@State private var maxId: String?
|
|
|
|
|
2023-01-20 13:47:38 +01:00
|
|
|
private let defaultPageSize = 20
|
2023-01-18 18:41:42 +01:00
|
|
|
|
2022-12-30 18:20:54 +01:00
|
|
|
var body: some View {
|
2023-01-18 18:41:42 +01:00
|
|
|
List {
|
|
|
|
ForEach(notifications, id: \.id) { notification in
|
2023-01-24 21:13:50 +01:00
|
|
|
NotificationRow(notification: notification)
|
2023-01-18 18:41:42 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
if allItemsLoaded == false && firstLoadFinished == true {
|
|
|
|
HStack {
|
|
|
|
Spacer()
|
|
|
|
LoadingIndicator()
|
|
|
|
.task {
|
|
|
|
await self.loadMoreNotifications()
|
|
|
|
}
|
|
|
|
Spacer()
|
|
|
|
}
|
|
|
|
.listRowSeparator(.hidden)
|
|
|
|
}
|
|
|
|
}.overlay {
|
|
|
|
if firstLoadFinished == false {
|
|
|
|
LoadingIndicator()
|
|
|
|
} else {
|
|
|
|
if self.notifications.isEmpty {
|
|
|
|
VStack {
|
2023-01-23 11:42:28 +01:00
|
|
|
Image(systemName: "bell")
|
2023-01-18 18:41:42 +01:00
|
|
|
.font(.largeTitle)
|
|
|
|
.padding(.bottom, 4)
|
2023-01-23 11:42:28 +01:00
|
|
|
Text("Unfortunately, there is nothing here.")
|
2023-01-18 18:41:42 +01:00
|
|
|
.font(.title3)
|
|
|
|
}.foregroundColor(.lightGrayColor)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.navigationBarTitle("Notifications")
|
|
|
|
.listStyle(PlainListStyle())
|
|
|
|
.refreshable {
|
|
|
|
await self.loadNewNotifications()
|
|
|
|
}
|
|
|
|
.task {
|
|
|
|
if self.notifications.isEmpty == false {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
await self.loadNotifications()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func loadNotifications() async {
|
|
|
|
do {
|
2023-01-22 21:44:07 +01:00
|
|
|
let linkable = try await NotificationService.shared.notifications(
|
2023-01-18 18:41:42 +01:00
|
|
|
forAccountId: self.accountId,
|
|
|
|
andContext: self.applicationState.accountData,
|
|
|
|
maxId: maxId,
|
|
|
|
minId: minId,
|
|
|
|
limit: 5)
|
|
|
|
|
|
|
|
self.minId = linkable.link?.minId
|
|
|
|
self.maxId = linkable.link?.maxId
|
|
|
|
self.notifications = linkable.data
|
|
|
|
|
|
|
|
if linkable.data.isEmpty || linkable.data.count < 5 {
|
|
|
|
self.allItemsLoaded = true
|
|
|
|
}
|
|
|
|
|
|
|
|
self.firstLoadFinished = true
|
|
|
|
} catch {
|
2023-01-20 13:47:38 +01:00
|
|
|
ErrorService.shared.handle(error, message: "Error during download notifications from server.", showToastr: !Task.isCancelled)
|
2023-01-18 18:41:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func loadMoreNotifications() async {
|
|
|
|
do {
|
2023-01-22 21:44:07 +01:00
|
|
|
let linkable = try await NotificationService.shared.notifications(
|
2023-01-18 18:41:42 +01:00
|
|
|
forAccountId: self.accountId,
|
|
|
|
andContext: self.applicationState.accountData,
|
|
|
|
maxId: self.maxId,
|
|
|
|
limit: self.defaultPageSize)
|
|
|
|
|
|
|
|
self.maxId = linkable.link?.maxId
|
|
|
|
self.notifications.append(contentsOf: linkable.data)
|
|
|
|
|
|
|
|
if linkable.data.isEmpty || linkable.data.count < self.defaultPageSize {
|
|
|
|
self.allItemsLoaded = true
|
|
|
|
}
|
|
|
|
} catch {
|
2023-01-20 13:47:38 +01:00
|
|
|
ErrorService.shared.handle(error, message: "Error during download notifications from server.", showToastr: !Task.isCancelled)
|
2023-01-18 18:41:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func loadNewNotifications() async {
|
|
|
|
do {
|
2023-01-22 21:44:07 +01:00
|
|
|
let linkable = try await NotificationService.shared.notifications(
|
2023-01-18 18:41:42 +01:00
|
|
|
forAccountId: self.accountId,
|
|
|
|
andContext: self.applicationState.accountData,
|
|
|
|
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
|
2023-01-22 21:44:07 +01:00
|
|
|
self.notifications.insert(contentsOf: linkable.data, at: 0)
|
2023-01-18 18:41:42 +01:00
|
|
|
} catch {
|
2023-01-20 13:47:38 +01:00
|
|
|
ErrorService.shared.handle(error, message: "Error during download notifications from server.", showToastr: !Task.isCancelled)
|
2023-01-18 18:41:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func downloadAllImages(notifications: [MastodonKit.Notification]) async {
|
|
|
|
// Download all avatars into cache.
|
|
|
|
let accounts = notifications.map({ notification in notification.account })
|
|
|
|
await self.downloadAvatars(accounts: accounts)
|
|
|
|
|
|
|
|
// Download all images into cache.
|
|
|
|
var images: [(id: String, url: URL)] = []
|
|
|
|
for notification in notifications {
|
|
|
|
if let mediaAttachment = notification.status?.mediaAttachments {
|
|
|
|
images.append(contentsOf:
|
|
|
|
mediaAttachment
|
|
|
|
.filter({ attachment in
|
2023-01-22 13:49:19 +01:00
|
|
|
attachment.type == MediaAttachment.MediaAttachmentType.image
|
2023-01-18 18:41:42 +01:00
|
|
|
})
|
|
|
|
.map({
|
|
|
|
attachment in (id: attachment.id, url: attachment.url)
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
await self.downloadImages(images: images)
|
|
|
|
}
|
|
|
|
|
|
|
|
private func downloadAvatars(accounts: [Account]) async {
|
|
|
|
await withTaskGroup(of: Void.self) { group in
|
|
|
|
for account in accounts {
|
2023-01-24 20:38:21 +01:00
|
|
|
group.addTask { await CacheImageService.shared.downloadImage(url: account.avatar) }
|
2023-01-18 18:41:42 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private func downloadImages(images: [(id: String, url: URL)]) async {
|
|
|
|
await withTaskGroup(of: Void.self) { group in
|
|
|
|
for image in images {
|
2023-01-24 20:38:21 +01:00
|
|
|
group.addTask { await CacheImageService.shared.downloadImage(url: image.url) }
|
2023-01-18 18:41:42 +01:00
|
|
|
}
|
|
|
|
}
|
2022-12-30 18:20:54 +01:00
|
|
|
}
|
|
|
|
}
|