Vernissage/Vernissage/Services/NotificationsService.swift

107 lines
4.0 KiB
Swift

//
// https://mczachurski.dev
// Copyright © 2023 Marcin Czachurski and the repository contributors.
// Licensed under the Apache License 2.0.
//
import Foundation
import SwiftData
import PixelfedKit
import ClientKit
import ServicesKit
import Nuke
import OSLog
import EnvironmentKit
import Semaphore
import UserNotifications
/// Service responsible for managing notifications.
@MainActor
public class NotificationsService {
public static let shared = NotificationsService()
private init() { }
private let semaphore = AsyncSemaphore(value: 1)
public func newNotificationsHasBeenAdded(for account: AccountModel, modelContext: ModelContext) async -> Bool {
await semaphore.wait()
defer { semaphore.signal() }
guard let accessToken = account.accessToken else {
return false
}
// Get maximimum downloaded stauts id.
guard let lastSeenNotificationId = self.getLastSeenNotificationId(accountId: account.id, modelContext: modelContext) else {
return false
}
let client = PixelfedClient(baseURL: account.serverUrl).getAuthenticated(token: accessToken)
do {
let linkableNotifications = try await client.notifications(minId: lastSeenNotificationId, limit: 5)
return linkableNotifications.data.first(where: { $0.id != lastSeenNotificationId }) != nil
} catch {
ErrorService.shared.handle(error, message: "notifications.error.loadingNotificationsFailed")
return false
}
}
public func amountOfNewNotifications(for account: AccountModel, modelContext: ModelContext) async -> Int {
await semaphore.wait()
defer { semaphore.signal() }
guard let accessToken = account.accessToken else {
return 0
}
// Get maximimum downloaded stauts id.
guard let lastSeenNotificationId = self.getLastSeenNotificationId(accountId: account.id, modelContext: modelContext) else {
return 0
}
let client = PixelfedClient(baseURL: account.serverUrl).getAuthenticated(token: accessToken)
var notifications: [PixelfedKit.Notification] = []
var newestNotificationId = lastSeenNotificationId
// There can be more then 80 newest notifications, that's why we have to sometimes send more then one request.
while true {
do {
let downloadedNotifications = try await client.notifications(minId: newestNotificationId, limit: 80)
guard let firstNotification = downloadedNotifications.data.first else {
break
}
let visibleNotifications = downloadedNotifications.data.filter({ $0.id != lastSeenNotificationId })
notifications.append(contentsOf: visibleNotifications)
newestNotificationId = firstNotification.id
} catch {
ErrorService.shared.handle(error, message: "global.error.errorDuringDownloadingNewStatuses")
break
}
}
// Return number of new notifications not visible yet on the timeline.
return notifications.count
}
/// Function sets application badge counts when notifications (and badge) are enabled.
public func setBadgeCount(_ count: Int) async throws {
let center = UNUserNotificationCenter.current()
let settings = await center.notificationSettings()
guard (settings.authorizationStatus == .authorized) || (settings.authorizationStatus == .provisional) else { return }
if settings.badgeSetting == .enabled {
try await center.setBadgeCount(count)
}
}
private func getLastSeenNotificationId(accountId: String, modelContext: ModelContext) -> String? {
let accountData = AccountDataHandler.shared.getAccountData(accountId: accountId, modelContext: modelContext)
return accountData?.lastSeenNotificationId
}
}