More user interface for push notifications
This commit is contained in:
@ -1,3 +1,4 @@
|
||||
|
||||
import Models
|
||||
import Services
|
||||
import SwiftUI
|
||||
@ -18,19 +19,27 @@ import Views
|
||||
}
|
||||
}
|
||||
|
||||
func tryUpdateToDesired() {
|
||||
print("trying to update to desired state: ", desiredNotificationsEnabled)
|
||||
func tryUpdateToDesired(dataService: DataService) {
|
||||
UserDefaults.standard.set(desiredNotificationsEnabled, forKey: UserDefaultKey.notificationsEnabled.rawValue)
|
||||
|
||||
if desiredNotificationsEnabled {
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) { granted, error in
|
||||
print("notification status: ", granted, "error: ", error)
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) { granted, _ in
|
||||
DispatchQueue.main.async {
|
||||
self.desiredNotificationsEnabled = granted
|
||||
Task {
|
||||
if let savedToken = UserDefaults.standard.string(forKey: UserDefaultKey.firebasePushToken.rawValue) {
|
||||
try? await dataService.syncDeviceToken(deviceTokenOperation: DeviceTokenOperation.addToken(token: savedToken))
|
||||
}
|
||||
NotificationCenter.default.post(name: Notification.Name("ReconfigurePushNotifications"), object: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// UNUserNotificationCenter.current().r
|
||||
UIApplication.shared.openURL(URL(string:"prefs:root=NOTIFICATIONS_ID")!)
|
||||
|
||||
if let tokenID = UserDefaults.standard.string(forKey: UserDefaultKey.deviceTokenID.rawValue) {
|
||||
Task {
|
||||
try? await Services().dataService.syncDeviceToken(deviceTokenOperation: .deleteToken(tokenID: tokenID))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -61,7 +70,7 @@ struct PushNotificationSettingsView: View {
|
||||
Section {
|
||||
Toggle(isOn: $viewModel.desiredNotificationsEnabled, label: { Text("Notifications Enabled") })
|
||||
}.onChange(of: viewModel.desiredNotificationsEnabled) { _ in
|
||||
viewModel.tryUpdateToDesired()
|
||||
viewModel.tryUpdateToDesired(dataService: dataService)
|
||||
}
|
||||
|
||||
Section {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import Foundation
|
||||
import Models
|
||||
import SwiftGraphQL
|
||||
import Utils
|
||||
|
||||
public enum DeviceTokenOperation {
|
||||
case addToken(token: String)
|
||||
@ -26,17 +27,22 @@ public enum DeviceTokenOperation {
|
||||
}
|
||||
|
||||
public extension DataService {
|
||||
func syncDeviceToken(deviceTokenOperation: DeviceTokenOperation) {
|
||||
func syncDeviceToken(deviceTokenOperation: DeviceTokenOperation) async throws -> String? {
|
||||
enum MutationResult {
|
||||
case saved(id: String)
|
||||
case saved(id: String, token: String?)
|
||||
case error(errorCode: Enums.SetDeviceTokenErrorCode)
|
||||
}
|
||||
|
||||
let success = Selection.DeviceToken { (id: try $0.id(), token: try $0.token()) }
|
||||
|
||||
let selection = Selection<MutationResult, Unions.SetDeviceTokenResult> {
|
||||
try $0.on(
|
||||
setDeviceTokenError: .init { .error(errorCode: try $0.errorCodes().first ?? .badRequest) },
|
||||
setDeviceTokenSuccess: .init {
|
||||
.saved(id: try $0.deviceToken(selection: Selection.DeviceToken { try $0.id() }))
|
||||
.saved(
|
||||
id: try $0.deviceToken(selection: success).id,
|
||||
token: try $0.deviceToken(selection: success).token
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
@ -54,6 +60,30 @@ public extension DataService {
|
||||
let path = appEnvironment.graphqlPath
|
||||
let headers = networker.defaultHeaders
|
||||
|
||||
send(mutation, to: path, headers: headers) { _ in }
|
||||
return try await withCheckedThrowingContinuation { continuation in
|
||||
send(mutation, to: path, headers: headers) { result in
|
||||
guard let payload = try? result.get() else {
|
||||
continuation.resume(throwing: BasicError.message(messageText: "network error"))
|
||||
return
|
||||
}
|
||||
|
||||
switch payload.data {
|
||||
case let .saved(id: id, token: token):
|
||||
switch deviceTokenOperation {
|
||||
case .deleteToken(tokenID: _):
|
||||
// When we delete we don't remove the saved token, as we might need to re-use it
|
||||
// in the future
|
||||
UserDefaults.standard.removeObject(forKey: UserDefaultKey.deviceTokenID.rawValue)
|
||||
case .addToken(token: _):
|
||||
UserDefaults.standard.set(id, forKey: UserDefaultKey.deviceTokenID.rawValue)
|
||||
UserDefaults.standard.set(token, forKey: UserDefaultKey.firebasePushToken.rawValue)
|
||||
}
|
||||
|
||||
continuation.resume(returning: id)
|
||||
case let .error(errorCode: errorCode):
|
||||
continuation.resume(throwing: BasicError.message(messageText: errorCode.rawValue))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,4 +25,5 @@ public enum UserDefaultKey: String {
|
||||
case themeName
|
||||
case shouldShowNewFeaturePrimer
|
||||
case notificationsEnabled
|
||||
case deviceTokenID
|
||||
}
|
||||
|
||||
@ -46,7 +46,15 @@ private let logger = Logger(subsystem: "app.omnivore", category: "app-delegate")
|
||||
}
|
||||
|
||||
Services.registerBackgroundFetch()
|
||||
configurePushNotifications()
|
||||
configureFirebase()
|
||||
|
||||
NotificationCenter.default.addObserver(forName: Notification.Name("ReconfigurePushNotifications"), object: nil, queue: OperationQueue.main) { _ in
|
||||
if UserDefaults.standard.bool(forKey: UserDefaultKey.notificationsEnabled.rawValue) {
|
||||
self.registerForNotifications()
|
||||
} else {
|
||||
self.unregisterForNotifications()
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@ -8,9 +8,7 @@ import UIKit
|
||||
import Utils
|
||||
|
||||
extension AppDelegate {
|
||||
func configurePushNotifications() {
|
||||
guard FeatureFlag.enablePushNotifications else { return }
|
||||
|
||||
func configureFirebase() {
|
||||
let keys: FirebaseKeys? = {
|
||||
let isProd = (PublicValet.storedAppEnvironment ?? .initialAppEnvironment) == .prod
|
||||
let firebaseKeys = isProd ? AppKeys.sharedInstance?.firebaseProdKeys : AppKeys.sharedInstance?.firebaseDemoKeys
|
||||
@ -30,9 +28,21 @@ extension AppDelegate {
|
||||
FirebaseApp.configure(options: firebaseOpts)
|
||||
FirebaseConfiguration.shared.setLoggerLevel(.min)
|
||||
|
||||
if UserDefaults.standard.bool(forKey: UserDefaultKey.notificationsEnabled.rawValue) {
|
||||
registerForNotifications()
|
||||
}
|
||||
}
|
||||
|
||||
func registerForNotifications() {
|
||||
Messaging.messaging().delegate = self
|
||||
UNUserNotificationCenter.current().delegate = self
|
||||
UIApplication.shared.registerForRemoteNotifications()
|
||||
Messaging.messaging().delegate = self
|
||||
}
|
||||
|
||||
func unregisterForNotifications() {
|
||||
Messaging.messaging().delegate = nil
|
||||
UNUserNotificationCenter.current().delegate = nil
|
||||
UIApplication.shared.unregisterForRemoteNotifications()
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,12 +97,16 @@ extension AppDelegate: MessagingDelegate {
|
||||
guard let fcmToken = fcmToken else { return }
|
||||
|
||||
let savedToken = UserDefaults.standard.string(forKey: UserDefaultKey.firebasePushToken.rawValue)
|
||||
let deviceTokenID = UserDefaults.standard.string(forKey: UserDefaultKey.deviceTokenID.rawValue)
|
||||
|
||||
if savedToken == fcmToken {
|
||||
// If the deviceTokenID is null, that means we haven't set our token yet, and this is just
|
||||
// a previously saved token.
|
||||
if savedToken == fcmToken, deviceTokenID != nil {
|
||||
return
|
||||
}
|
||||
|
||||
UserDefaults.standard.set(fcmToken, forKey: UserDefaultKey.firebasePushToken.rawValue)
|
||||
Services().dataService.syncDeviceToken(deviceTokenOperation: .addToken(token: fcmToken))
|
||||
Task {
|
||||
try? await Services().dataService.syncDeviceToken(deviceTokenOperation: .addToken(token: fcmToken))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user