Add application badge with number of new notifications
This commit is contained in:
parent
1af66b189d
commit
db407195f2
|
@ -9,33 +9,34 @@ import SwiftData
|
|||
import EnvironmentKit
|
||||
|
||||
@Model final public class ApplicationSettings {
|
||||
public var currentAccount: String?
|
||||
public var theme: Int32
|
||||
public var tintColor: Int32
|
||||
public var avatarShape: Int32
|
||||
public var activeIcon: String
|
||||
public var lastRefreshTokens: Date
|
||||
public var currentAccount: String? = nil
|
||||
public var theme: Int32 = Int32(Theme.system.rawValue)
|
||||
public var tintColor: Int32 = Int32(TintColor.accentColor2.rawValue)
|
||||
public var avatarShape: Int32 = Int32(AvatarShape.circle.rawValue)
|
||||
public var activeIcon: String = "Default"
|
||||
public var lastRefreshTokens: Date = Date.distantPast
|
||||
|
||||
public var hapticTabSelectionEnabled: Bool
|
||||
public var hapticRefreshEnabled: Bool
|
||||
public var hapticButtonPressEnabled: Bool
|
||||
public var hapticAnimationEnabled: Bool
|
||||
public var hapticNotificationEnabled: Bool
|
||||
public var hapticTabSelectionEnabled: Bool = true
|
||||
public var hapticRefreshEnabled: Bool = true
|
||||
public var hapticButtonPressEnabled: Bool = true
|
||||
public var hapticAnimationEnabled: Bool = true
|
||||
public var hapticNotificationEnabled: Bool = true
|
||||
|
||||
public var showSensitive: Bool
|
||||
public var showPhotoDescription: Bool
|
||||
public var menuPosition: Int32
|
||||
public var showAvatarsOnTimeline: Bool
|
||||
public var showFavouritesOnTimeline: Bool
|
||||
public var showAltIconOnTimeline: Bool
|
||||
public var warnAboutMissingAlt: Bool
|
||||
public var showGridOnUserProfile: Bool
|
||||
public var showReboostedStatuses: Bool
|
||||
public var hideStatusesWithoutAlt: Bool
|
||||
public var showSensitive: Bool = false
|
||||
public var showApplicationBadge: Bool = false
|
||||
public var showPhotoDescription: Bool = false
|
||||
public var menuPosition: Int32 = Int32(MenuPosition.top.rawValue)
|
||||
public var showAvatarsOnTimeline: Bool = false
|
||||
public var showFavouritesOnTimeline: Bool = false
|
||||
public var showAltIconOnTimeline: Bool = false
|
||||
public var warnAboutMissingAlt: Bool = true
|
||||
public var showGridOnUserProfile: Bool = false
|
||||
public var showReboostedStatuses: Bool = false
|
||||
public var hideStatusesWithoutAlt: Bool = false
|
||||
|
||||
public var customNavigationMenuItem1: Int32
|
||||
public var customNavigationMenuItem2: Int32
|
||||
public var customNavigationMenuItem3: Int32
|
||||
public var customNavigationMenuItem1: Int32 = 1
|
||||
public var customNavigationMenuItem2: Int32 = 2
|
||||
public var customNavigationMenuItem3: Int32 = 5
|
||||
|
||||
init(
|
||||
currentAccount: String? = nil,
|
||||
|
@ -50,6 +51,7 @@ import EnvironmentKit
|
|||
hapticAnimationEnabled: Bool = true,
|
||||
hapticNotificationEnabled: Bool = true,
|
||||
showSensitive: Bool = false,
|
||||
showApplicationBadge: Bool = false,
|
||||
showPhotoDescription: Bool = false,
|
||||
menuPosition: Int32 = Int32(MenuPosition.top.rawValue),
|
||||
showAvatarsOnTimeline: Bool = false,
|
||||
|
@ -75,6 +77,7 @@ import EnvironmentKit
|
|||
self.hapticAnimationEnabled = hapticAnimationEnabled
|
||||
self.hapticNotificationEnabled = hapticNotificationEnabled
|
||||
self.showSensitive = showSensitive
|
||||
self.showApplicationBadge = showApplicationBadge
|
||||
self.showPhotoDescription = showPhotoDescription
|
||||
self.menuPosition = menuPosition
|
||||
self.showAvatarsOnTimeline = showAvatarsOnTimeline
|
||||
|
|
|
@ -67,6 +67,7 @@ class ApplicationSettingsHandler {
|
|||
applicationState.showGridOnUserProfile = defaultSettings.showGridOnUserProfile
|
||||
applicationState.showReboostedStatuses = defaultSettings.showReboostedStatuses
|
||||
applicationState.hideStatusesWithoutAlt = defaultSettings.hideStatusesWithoutAlt
|
||||
applicationState.showApplicationBadge = defaultSettings.showApplicationBadge
|
||||
|
||||
if let menuPosition = MenuPosition(rawValue: Int(defaultSettings.menuPosition)) {
|
||||
applicationState.menuPosition = menuPosition
|
||||
|
@ -138,6 +139,12 @@ class ApplicationSettingsHandler {
|
|||
defaultSettings.showSensitive = showSensitive
|
||||
try? modelContext.save()
|
||||
}
|
||||
|
||||
func set(showApplicationBadge: Bool, modelContext: ModelContext) {
|
||||
let defaultSettings = self.get(modelContext: modelContext)
|
||||
defaultSettings.showApplicationBadge = showApplicationBadge
|
||||
try? modelContext.save()
|
||||
}
|
||||
|
||||
func set(showPhotoDescription: Bool, modelContext: ModelContext) {
|
||||
let defaultSettings = self.get(modelContext: modelContext)
|
||||
|
|
|
@ -38,8 +38,8 @@ import ClientKit
|
|||
/// Last notification seen by the user.
|
||||
public var lastSeenNotificationId: String?
|
||||
|
||||
/// Information about new notifications.
|
||||
public var newNotificationsHasBeenAdded = false
|
||||
/// Amount of new notifications.
|
||||
public var amountOfNewNotifications = 0
|
||||
|
||||
/// Last status seen by the user.
|
||||
public var lastSeenStatusId: String?
|
||||
|
@ -119,12 +119,15 @@ import ClientKit
|
|||
/// Hide statuses without ALT text.
|
||||
public var hideStatusesWithoutAlt = false
|
||||
|
||||
/// Should show application badge.
|
||||
public var showApplicationBadge = false
|
||||
|
||||
public func changeApplicationState(accountModel: AccountModel, instance: Instance?, lastSeenStatusId: String?, lastSeenNotificationId: String?) {
|
||||
self.account = accountModel
|
||||
self.lastSeenNotificationId = lastSeenNotificationId
|
||||
self.lastSeenStatusId = lastSeenStatusId
|
||||
self.amountOfNewStatuses = 0
|
||||
self.newNotificationsHasBeenAdded = false
|
||||
self.amountOfNewNotifications = 0
|
||||
|
||||
if let statusesConfiguration = instance?.configuration?.statuses {
|
||||
self.statusMaxCharacters = statusesConfiguration.maxCharacters
|
||||
|
@ -142,7 +145,7 @@ import ClientKit
|
|||
self.lastSeenStatusId = nil
|
||||
self.lastSeenNotificationId = nil
|
||||
self.amountOfNewStatuses = 0
|
||||
self.newNotificationsHasBeenAdded = false
|
||||
self.amountOfNewNotifications = 0
|
||||
|
||||
self.statusMaxCharacters = ApplicationState.defaults.statusMaxCharacters
|
||||
self.statusMaxMediaAttachments = ApplicationState.defaults.statusMaxMediaAttachments
|
||||
|
|
|
@ -22,5 +22,6 @@ public struct AppConstants {
|
|||
public static let accountUri = "\(AppConstants.accountScheme)://\(accountCallbackPart)"
|
||||
|
||||
public static let imagePipelineCacheName = "dev.mczachurski.Vernissage.DataCache"
|
||||
public static let backgroundFetcherName = "dev.mczachurski.Vernissage.NotificationFetcher"
|
||||
public static let coreDataPersistantContainerName = "Vernissage"
|
||||
}
|
||||
|
|
|
@ -4383,6 +4383,22 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"settings.error.notificationEnableFailed" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Error during enabling notifications."
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Błąd podczas włączania powiadomień."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings.navigationBar.title" : {
|
||||
"comment" : "Settings view.",
|
||||
"localizations" : {
|
||||
|
@ -5454,6 +5470,56 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"settings.title.notifications" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Notifications"
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Powiadomienia"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings.title.notificationsDescription" : {
|
||||
"comment" : "Application badge with amount of new notifications will be visible near the app icon.",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Application badge with amount of new notifications will be visible new the app icon."
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Licznik z ilością nowych powiadomień będzie wyświetlany obok ikony aplikacji."
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings.title.notificationsTitle" : {
|
||||
"comment" : "Show application badge",
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Show application badge"
|
||||
}
|
||||
},
|
||||
"pl" : {
|
||||
"stringUnit" : {
|
||||
"state" : "translated",
|
||||
"value" : "Wyświetl ilość powiadomień"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"settings.title.other" : {
|
||||
"localizations" : {
|
||||
"en" : {
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
F87AEB922986C44E00434FB6 /* AuthorizationSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = F87AEB912986C44E00434FB6 /* AuthorizationSession.swift */; };
|
||||
F87AEB972986D16D00434FB6 /* AuthorisationError.swift in Sources */ = {isa = PBXBuildFile; fileRef = F87AEB962986D16D00434FB6 /* AuthorisationError.swift */; };
|
||||
F883402029B62AE900C3E096 /* SearchView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F883401F29B62AE900C3E096 /* SearchView.swift */; };
|
||||
F886BBAC2AE7CF510083152B /* NotificationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F886BBAB2AE7CF510083152B /* NotificationView.swift */; };
|
||||
F88AB05329B3613900345EDE /* PhotoUrl.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88AB05229B3613900345EDE /* PhotoUrl.swift */; };
|
||||
F88AB05529B3626300345EDE /* ImageGrid.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88AB05429B3626300345EDE /* ImageGrid.swift */; };
|
||||
F88AB05829B36B8200345EDE /* AccountsPhotoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F88AB05729B36B8200345EDE /* AccountsPhotoView.swift */; };
|
||||
|
@ -281,6 +282,7 @@
|
|||
F87AEB912986C44E00434FB6 /* AuthorizationSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthorizationSession.swift; sourceTree = "<group>"; };
|
||||
F87AEB962986D16D00434FB6 /* AuthorisationError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthorisationError.swift; sourceTree = "<group>"; };
|
||||
F883401F29B62AE900C3E096 /* SearchView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchView.swift; sourceTree = "<group>"; };
|
||||
F886BBAB2AE7CF510083152B /* NotificationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationView.swift; sourceTree = "<group>"; };
|
||||
F88AB05229B3613900345EDE /* PhotoUrl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoUrl.swift; sourceTree = "<group>"; };
|
||||
F88AB05429B3626300345EDE /* ImageGrid.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImageGrid.swift; sourceTree = "<group>"; };
|
||||
F88AB05729B36B8200345EDE /* AccountsPhotoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountsPhotoView.swift; sourceTree = "<group>"; };
|
||||
|
@ -634,6 +636,7 @@
|
|||
F8B05ACA29B489B100857221 /* HapticsSectionView.swift */,
|
||||
F8B05ACD29B48E2F00857221 /* MediaSettingsView.swift */,
|
||||
F8DF38E529DDB98A0047F1AA /* SocialsSectionView.swift */,
|
||||
F886BBAB2AE7CF510083152B /* NotificationView.swift */,
|
||||
);
|
||||
path = Subviews;
|
||||
sourceTree = "<group>";
|
||||
|
@ -1162,6 +1165,7 @@
|
|||
F89A46DC296EAACE0062125F /* SettingsView.swift in Sources */,
|
||||
F866F6AE29606367002E8F88 /* ApplicationViewMode.swift in Sources */,
|
||||
F8675DD02A1FA40500A89959 /* WaterfallGrid.swift in Sources */,
|
||||
F886BBAC2AE7CF510083152B /* NotificationView.swift in Sources */,
|
||||
F85D4DFE29B78C8400345267 /* HashtagModel.swift in Sources */,
|
||||
F866F6AA29605AFA002E8F88 /* SceneDelegate.swift in Sources */,
|
||||
);
|
||||
|
|
|
@ -15,5 +15,5 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
|||
let sceneConfig: UISceneConfiguration = UISceneConfiguration(name: nil, sessionRole: connectingSceneSession.role)
|
||||
sceneConfig.delegateClass = SceneDelegate.self
|
||||
return sceneConfig
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,10 +2,10 @@
|
|||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>14.0</string>
|
||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||
<array>
|
||||
<string>dev.mczachurski.Vernissage.NotificationFetcher</string>
|
||||
</array>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
@ -25,10 +25,19 @@
|
|||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>ITSAppUsesNonExemptEncryption</key>
|
||||
<false/>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>14.0</string>
|
||||
<key>UIApplicationSceneManifest</key>
|
||||
<dict>
|
||||
<key>UISceneConfigurations</key>
|
||||
<dict/>
|
||||
</dict>
|
||||
<key>UIBackgroundModes</key>
|
||||
<array>
|
||||
<string>fetch</string>
|
||||
<string>processing</string>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
|
|
|
@ -8,6 +8,7 @@ import SwiftUI
|
|||
import PixelfedKit
|
||||
import OAuthSwift
|
||||
import EnvironmentKit
|
||||
import BackgroundTasks
|
||||
|
||||
class SceneDelegate: NSObject, UISceneDelegate {
|
||||
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
|
||||
|
|
|
@ -13,6 +13,7 @@ import Nuke
|
|||
import OSLog
|
||||
import EnvironmentKit
|
||||
import Semaphore
|
||||
import UserNotifications
|
||||
|
||||
/// Service responsible for managing notifications.
|
||||
@MainActor
|
||||
|
@ -46,6 +47,58 @@ public class NotificationsService {
|
|||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
|
|
@ -12,10 +12,12 @@ import EnvironmentKit
|
|||
import WidgetKit
|
||||
import SwiftData
|
||||
import TipKit
|
||||
import BackgroundTasks
|
||||
|
||||
@main
|
||||
struct VernissageApp: App {
|
||||
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||
@Environment(\.scenePhase) private var phase
|
||||
|
||||
@State var applicationState = ApplicationState.shared
|
||||
@State var client = Client.shared
|
||||
|
@ -102,6 +104,17 @@ struct VernissageApp: App {
|
|||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: phase) { oldValue, newValue in
|
||||
switch newValue {
|
||||
case .background: scheduleAppRefresh()
|
||||
default: break
|
||||
}
|
||||
}
|
||||
.backgroundTask(.appRefresh(AppConstants.backgroundFetcherName)) {
|
||||
Task { @MainActor in
|
||||
await self.setBadgeCount()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@MainActor
|
||||
|
@ -127,7 +140,7 @@ struct VernissageApp: App {
|
|||
self.applicationViewMode = .signIn
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
// Create model based on core data entity.
|
||||
let accountModel = currentAccount.toAccountModel()
|
||||
|
||||
|
@ -228,7 +241,21 @@ struct VernissageApp: App {
|
|||
let modelContext = self.modelContainer.mainContext
|
||||
|
||||
if let account = self.applicationState.account {
|
||||
self.applicationState.newNotificationsHasBeenAdded = await NotificationsService.shared.newNotificationsHasBeenAdded(for: account, modelContext: modelContext)
|
||||
self.applicationState.amountOfNewStatuses = await NotificationsService.shared.amountOfNewNotifications(for: account, modelContext: modelContext)
|
||||
try? await NotificationsService.shared.setBadgeCount(self.applicationState.amountOfNewStatuses)
|
||||
} else {
|
||||
try? await NotificationsService.shared.setBadgeCount(0)
|
||||
}
|
||||
}
|
||||
|
||||
private func scheduleAppRefresh() {
|
||||
let request = BGAppRefreshTaskRequest(identifier: AppConstants.backgroundFetcherName)
|
||||
request.earliestBeginDate = .now.addingTimeInterval(3600)
|
||||
try? BGTaskScheduler.shared.submit(request)
|
||||
}
|
||||
|
||||
private func setBadgeCount() async {
|
||||
await self.calculateNewNotificationsInBackground()
|
||||
scheduleAppRefresh()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,9 +90,9 @@ struct MainView: View {
|
|||
Image(systemName: "person.crop.circle")
|
||||
case .notifications:
|
||||
if applicationState.menuPosition == .top {
|
||||
applicationState.newNotificationsHasBeenAdded ? Image(systemName: "bell.badge") : Image(systemName: "bell")
|
||||
applicationState.amountOfNewNotifications > 0 ? Image(systemName: "bell.badge") : Image(systemName: "bell")
|
||||
} else {
|
||||
applicationState.newNotificationsHasBeenAdded
|
||||
applicationState.amountOfNewNotifications > 0
|
||||
? AnyView(
|
||||
Image(systemName: "bell.badge")
|
||||
.symbolRenderingMode(.palette)
|
||||
|
@ -349,7 +349,7 @@ struct MainView: View {
|
|||
|
||||
private func calculateNewNotificationsInBackground() async {
|
||||
if let account = self.applicationState.account {
|
||||
self.applicationState.newNotificationsHasBeenAdded = await NotificationsService.shared.newNotificationsHasBeenAdded(for: account, modelContext: modelContext)
|
||||
self.applicationState.amountOfNewNotifications = await NotificationsService.shared.amountOfNewNotifications(for: account, modelContext: modelContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,7 +98,10 @@ struct NotificationsView: View {
|
|||
}
|
||||
|
||||
try AccountDataHandler.shared.update(lastSeenNotificationId: linkable.data.first?.id, applicationState: self.applicationState, modelContext: modelContext)
|
||||
self.applicationState.newNotificationsHasBeenAdded = false
|
||||
|
||||
// Refresh infomation about viewed notifications.
|
||||
self.applicationState.amountOfNewNotifications = 0
|
||||
try? await NotificationsService.shared.setBadgeCount(0)
|
||||
}
|
||||
} catch {
|
||||
if !Task.isCancelled {
|
||||
|
@ -135,7 +138,10 @@ struct NotificationsView: View {
|
|||
}
|
||||
|
||||
try AccountDataHandler.shared.update(lastSeenNotificationId: linkable.data.first?.id, applicationState: self.applicationState, modelContext: modelContext)
|
||||
self.applicationState.newNotificationsHasBeenAdded = false
|
||||
|
||||
// Refresh infomation about viewed notifications.
|
||||
self.applicationState.amountOfNewNotifications = 0
|
||||
try? await NotificationsService.shared.setBadgeCount(0)
|
||||
|
||||
self.minId = linkable.link?.minId
|
||||
self.notifications.insert(contentsOf: linkable.data, at: 0)
|
||||
|
|
|
@ -39,6 +39,9 @@ struct SettingsView: View {
|
|||
// Avatar shapes.
|
||||
AvatarShapesSectionView()
|
||||
|
||||
// Notifications.
|
||||
NotificationView()
|
||||
|
||||
// Media settings view.
|
||||
MediaSettingsView()
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// https://mczachurski.dev
|
||||
// Copyright © 2023 Marcin Czachurski and the repository contributors.
|
||||
// Licensed under the Apache License 2.0.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import EnvironmentKit
|
||||
import ServicesKit
|
||||
|
||||
struct NotificationView: View {
|
||||
@Environment(ApplicationState.self) var applicationState
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@Environment(\.modelContext) private var modelContext
|
||||
|
||||
var body: some View {
|
||||
@Bindable var applicationState = applicationState
|
||||
|
||||
Section("settings.title.notifications") {
|
||||
|
||||
Toggle(isOn: $applicationState.showApplicationBadge) {
|
||||
VStack(alignment: .leading) {
|
||||
Text("settings.title.notificationsTitle", comment: "Show application badge")
|
||||
Text("settings.title.notificationsDescription", comment: "Application badge with amount of new notifications will be visible near the app icon.")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.customGrayColor)
|
||||
}
|
||||
}
|
||||
.onChange(of: self.applicationState.showApplicationBadge) { oldValue, newValue in
|
||||
Task { @MainActor in
|
||||
do {
|
||||
ApplicationSettingsHandler.shared.set(showApplicationBadge: newValue, modelContext: modelContext)
|
||||
if newValue {
|
||||
let center = UNUserNotificationCenter.current()
|
||||
_ = try await center.requestAuthorization(options: [.alert, .sound, .badge])
|
||||
} else {
|
||||
try await NotificationsService.shared.setBadgeCount(0)
|
||||
}
|
||||
} catch {
|
||||
ErrorService.shared.handle(error, message: "settings.error.notificationEnableFailed", showToastr: false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue