Handle badge for push notifications

This commit is contained in:
Thomas Ricouard 2023-01-09 18:52:53 +01:00
parent 8768f28073
commit d59ba03ba3
11 changed files with 36 additions and 12 deletions

View file

@ -1,4 +1,5 @@
import SwiftUI import SwiftUI
import AVFoundation
import Timeline import Timeline
import Network import Network
import KeychainSwift import KeychainSwift
@ -10,7 +11,7 @@ import RevenueCat
@main @main
struct IceCubesApp: App { struct IceCubesApp: App {
public static let defaultServer = "mastodon.social" public static let defaultServer = "mastodon.social"
@UIApplicationDelegateAdaptor private var appDelegate: AppDelegate @UIApplicationDelegateAdaptor private var appDelegate: AppDelegate
@Environment(\.scenePhase) private var scenePhase @Environment(\.scenePhase) private var scenePhase
@ -72,9 +73,7 @@ struct IceCubesApp: App {
private func badgeFor(tab: Tab) -> Int { private func badgeFor(tab: Tab) -> Int {
if tab == .notifications && selectedTab != tab { if tab == .notifications && selectedTab != tab {
return watcher.unreadNotificationsCount return watcher.unreadNotificationsCount + userPreferences.pushNotificationsCount
} else if tab == .messages && selectedTab != tab {
return watcher.unreadMessagesCount
} }
return 0 return 0
} }
@ -131,6 +130,7 @@ struct IceCubesApp: App {
watcher.stopWatching() watcher.stopWatching()
case .active: case .active:
watcher.watch(streams: [.user, .direct]) watcher.watch(streams: [.user, .direct])
UIApplication.shared.applicationIconBadgeNumber = 0
case .inactive: case .inactive:
break break
default: default:
@ -151,6 +151,7 @@ struct IceCubesApp: App {
class AppDelegate: NSObject, UIApplicationDelegate { class AppDelegate: NSObject, UIApplicationDelegate {
func application(_ application: UIApplication, func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
try? AVAudioSession.sharedInstance().setCategory(.ambient, options: .mixWithOthers)
return true return true
} }

View file

@ -36,7 +36,6 @@ struct MessagesTab: View {
} }
.onAppear { .onAppear {
routeurPath.client = client routeurPath.client = client
watcher.unreadMessagesCount = 0
} }
.withSafariRouteur() .withSafariRouteur()
.environmentObject(routeurPath) .environmentObject(routeurPath)

View file

@ -8,6 +8,7 @@ struct NotificationsTab: View {
@EnvironmentObject private var client: Client @EnvironmentObject private var client: Client
@EnvironmentObject private var watcher: StreamWatcher @EnvironmentObject private var watcher: StreamWatcher
@EnvironmentObject private var currentAccount: CurrentAccount @EnvironmentObject private var currentAccount: CurrentAccount
@EnvironmentObject private var userPreferences: UserPreferences
@StateObject private var routeurPath = RouterPath() @StateObject private var routeurPath = RouterPath()
@Binding var popToRootTab: Tab @Binding var popToRootTab: Tab
@ -27,6 +28,7 @@ struct NotificationsTab: View {
.onAppear { .onAppear {
routeurPath.client = client routeurPath.client = client
watcher.unreadNotificationsCount = 0 watcher.unreadNotificationsCount = 0
userPreferences.pushNotificationsCount = 0
} }
.withSafariRouteur() .withSafariRouteur()
.environmentObject(routeurPath) .environmentObject(routeurPath)

View file

@ -17,7 +17,7 @@ struct PushNotificationsView: View {
Form { Form {
Section { Section {
Toggle(isOn: $pushNotifications.isPushEnabled) { Toggle(isOn: $pushNotifications.isPushEnabled) {
Text("Push notification") Text("Push notifications")
} }
} footer: { } footer: {
Text("Receive push notifications on new activities") Text("Receive push notifications on new activities")

View file

@ -6,6 +6,10 @@
<string>development</string> <string>development</string>
<key>com.apple.security.app-sandbox</key> <key>com.apple.security.app-sandbox</key>
<true/> <true/>
<key>com.apple.security.application-groups</key>
<array>
<string>group.icecubesapps</string>
</array>
<key>com.apple.security.files.user-selected.read-only</key> <key>com.apple.security.files.user-selected.read-only</key>
<true/> <true/>
<key>keychain-access-groups</key> <key>keychain-access-groups</key>

View file

@ -2,6 +2,10 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"> <plist version="1.0">
<dict> <dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.icecubesapps</string>
</array>
<key>keychain-access-groups</key> <key>keychain-access-groups</key>
<array> <array>
<string>$(AppIdentifierPrefix)com.thomasricouard.IceCubesApp</string> <string>$(AppIdentifierPrefix)com.thomasricouard.IceCubesApp</string>

View file

@ -54,6 +54,11 @@ class NotificationService: UNNotificationServiceExtension {
bestAttemptContent.userInfo["plaintext"] = plaintextData bestAttemptContent.userInfo["plaintext"] = plaintextData
bestAttemptContent.sound = UNNotificationSound.init(named: UNNotificationSoundName(rawValue: "glass.wav")) bestAttemptContent.sound = UNNotificationSound.init(named: UNNotificationSoundName(rawValue: "glass.wav"))
let preferences = UserPreferences()
preferences.pushNotificationsCount += 1
bestAttemptContent.badge = .init(integerLiteral: preferences.pushNotificationsCount)
if let urlString = notification.icon, if let urlString = notification.icon,
let url = URL(string: urlString) { let url = URL(string: urlString) {
let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("notification-attachments") let temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("notification-attachments")

View file

@ -2,7 +2,7 @@ import Foundation
extension CGFloat { extension CGFloat {
public static let layoutPadding: CGFloat = 20 public static let layoutPadding: CGFloat = 20
public static let dividerPadding: CGFloat = 4 public static let dividerPadding: CGFloat = 2
public static let statusColumnsSpacing: CGFloat = 8 public static let statusColumnsSpacing: CGFloat = 8
public static let maxColumnWidth: CGFloat = 650 public static let maxColumnWidth: CGFloat = 650
} }

View file

@ -48,12 +48,14 @@ public class PushNotificationsService: ObservableObject {
private var keychain: KeychainSwift { private var keychain: KeychainSwift {
let keychain = KeychainSwift() let keychain = KeychainSwift()
keychain.accessGroup = Constants.keychainGroup #if !DEBUG
keychain.accessGroup = Constants.keychainGroup
#endif
return keychain return keychain
} }
public func requestPushNotifications() { public func requestPushNotifications() {
UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound]) { (_, _) in UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .sound, .badge]) { (_, _) in
DispatchQueue.main.async { DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications() UIApplication.shared.registerForRemoteNotifications()
} }

View file

@ -19,7 +19,6 @@ public class StreamWatcher: ObservableObject {
@Published public var events: [any StreamEvent] = [] @Published public var events: [any StreamEvent] = []
@Published public var unreadNotificationsCount: Int = 0 @Published public var unreadNotificationsCount: Int = 0
@Published public var unreadMessagesCount: Int = 0
@Published public var latestEvent: (any StreamEvent)? @Published public var latestEvent: (any StreamEvent)?
public init() { public init() {
@ -81,8 +80,6 @@ public class StreamWatcher: ObservableObject {
self.latestEvent = event self.latestEvent = event
if let event = event as? StreamEventNotification, event.notification.status?.visibility != .direct { if let event = event as? StreamEventNotification, event.notification.status?.visibility != .direct {
self.unreadNotificationsCount += 1 self.unreadNotificationsCount += 1
} else if event is StreamEventConversation {
self.unreadMessagesCount += 1
} }
} }
} }

View file

@ -2,8 +2,18 @@ import SwiftUI
import Foundation import Foundation
public class UserPreferences: ObservableObject { public class UserPreferences: ObservableObject {
private static let sharedDefault = UserDefaults.init(suiteName: "group.icecubesapps")
@AppStorage("remote_local_timeline") public var remoteLocalTimelines: [String] = [] @AppStorage("remote_local_timeline") public var remoteLocalTimelines: [String] = []
@AppStorage("preferred_browser") public var preferredBrowser: PreferredBrowser = .inAppSafari @AppStorage("preferred_browser") public var preferredBrowser: PreferredBrowser = .inAppSafari
public var pushNotificationsCount: Int {
get {
Self.sharedDefault?.integer(forKey: "push_notifications_count") ?? 0
}
set {
Self.sharedDefault?.set(newValue, forKey: "push_notifications_count")
}
}
public init() { } public init() { }
} }