Notificatio navigation wip

This commit is contained in:
Justin Mazzocchi 2021-02-04 00:30:37 -08:00
parent aea6030d43
commit 03b17d21ee
No known key found for this signature in database
GPG key ID: E223E6937AAFB01C
5 changed files with 48 additions and 27 deletions

View file

@ -39,6 +39,7 @@ final class NotificationService: UNNotificationServiceExtension {
return return
} }
bestAttemptContent.userInfo[PushNotificationParsingService.pushNotificationUserInfoKey] = decryptedJSON
bestAttemptContent.title = pushNotification.title bestAttemptContent.title = pushNotification.title
bestAttemptContent.body = XMLUnescaper(string: pushNotification.body).unescape() bestAttemptContent.body = XMLUnescaper(string: pushNotification.body).unescape()

View file

@ -19,6 +19,9 @@ public struct PushNotificationParsingService {
} }
public extension PushNotificationParsingService { public extension PushNotificationParsingService {
static let identityIdUserInfoKey = "i"
static let pushNotificationUserInfoKey = "com.metabolist.metatext.push-notification-user-info-key"
func extractAndDecrypt(userInfo: [AnyHashable: Any]) throws -> (Data, Identity.Id) { func extractAndDecrypt(userInfo: [AnyHashable: Any]) throws -> (Data, Identity.Id) {
guard let identityIdString = userInfo[Self.identityIdUserInfoKey] as? String, guard let identityIdString = userInfo[Self.identityIdUserInfoKey] as? String,
let identityId = Identity.Id(uuidString: identityIdString), let identityId = Identity.Id(uuidString: identityIdString),
@ -48,7 +51,6 @@ public extension PushNotificationParsingService {
} }
private extension PushNotificationParsingService { private extension PushNotificationParsingService {
static let identityIdUserInfoKey = "i"
static let encryptedMessageUserInfoKey = "m" static let encryptedMessageUserInfoKey = "m"
static let saltUserInfoKey = "s" static let saltUserInfoKey = "s"
static let serverPublicKeyUserInfoKey = "k" static let serverPublicKeyUserInfoKey = "k"

View file

@ -9,28 +9,27 @@ import ViewModels
@main @main
struct MetatextApp: App { struct MetatextApp: App {
@UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate @UIApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
private let environment = AppEnvironment.live( // swiftlint:disable:next force_try
userNotificationCenter: .current(), private let viewModel = try! RootViewModel(environment: Self.environment)
reduceMotion: { UIAccessibility.isReduceMotionEnabled })
init() { init() {
try? AVAudioSession.sharedInstance().setCategory(.ambient, mode: .default) try? AVAudioSession.sharedInstance().setCategory(.ambient, mode: .default)
try? ImageCacheConfiguration(environment: environment).configure() try? ImageCacheConfiguration(environment: Self.environment).configure()
} }
var body: some Scene { var body: some Scene {
WindowGroup { viewModel.registerForRemoteNotifications = appDelegate.registerForRemoteNotifications
RootView(
// swiftlint:disable force_try return WindowGroup {
viewModel: try! RootViewModel( RootView(viewModel: viewModel)
environment: environment,
registerForRemoteNotifications: appDelegate.registerForRemoteNotifications))
// swiftlint:enable force_try
} }
} }
} }
private extension MetatextApp { private extension MetatextApp {
static let environment = AppEnvironment.live(
userNotificationCenter: .current(),
reduceMotion: { UIAccessibility.isReduceMotionEnabled })
static let imageCacheName = "Images" static let imageCacheName = "Images"
static let imageCacheDirectoryURL = FileManager.default.containerURL( static let imageCacheDirectoryURL = FileManager.default.containerURL(
forSecurityApplicationGroupIdentifier: AppEnvironment.appGroup)? forSecurityApplicationGroupIdentifier: AppEnvironment.appGroup)?

View file

@ -70,7 +70,7 @@ public extension Instance {
} }
public extension RootViewModel { public extension RootViewModel {
static let preview = try! RootViewModel(environment: environment) { Empty().eraseToAnyPublisher() } static let preview = try! RootViewModel(environment: environment)
} }
public extension IdentityContext { public extension IdentityContext {

View file

@ -7,20 +7,30 @@ import ServiceLayer
public final class RootViewModel: ObservableObject { public final class RootViewModel: ObservableObject {
@Published public private(set) var navigationViewModel: NavigationViewModel? @Published public private(set) var navigationViewModel: NavigationViewModel?
public var registerForRemoteNotifications: (() -> AnyPublisher<Data, Error>)? {
didSet {
guard let registerForRemoteNotifications = registerForRemoteNotifications else { return }
userNotificationService.isAuthorized(request: false)
.filter { $0 }
.zip(registerForRemoteNotifications())
.map { $1 }
.flatMap(allIdentitiesService.updatePushSubscriptions(deviceToken:))
.sink { _ in } receiveValue: { _ in }
.store(in: &cancellables)
}
}
@Published private var mostRecentlyUsedIdentityId: Identity.Id? @Published private var mostRecentlyUsedIdentityId: Identity.Id?
private let environment: AppEnvironment private let environment: AppEnvironment
private let allIdentitiesService: AllIdentitiesService private let allIdentitiesService: AllIdentitiesService
private let userNotificationService: UserNotificationService private let userNotificationService: UserNotificationService
private let registerForRemoteNotifications: () -> AnyPublisher<Data, Error>
private var cancellables = Set<AnyCancellable>() private var cancellables = Set<AnyCancellable>()
public init(environment: AppEnvironment, public init(environment: AppEnvironment) throws {
registerForRemoteNotifications: @escaping () -> AnyPublisher<Data, Error>) throws {
self.environment = environment self.environment = environment
allIdentitiesService = try AllIdentitiesService(environment: environment) allIdentitiesService = try AllIdentitiesService(environment: environment)
userNotificationService = UserNotificationService(environment: environment) userNotificationService = UserNotificationService(environment: environment)
self.registerForRemoteNotifications = registerForRemoteNotifications
allIdentitiesService.immediateMostRecentlyUsedIdentityIdPublisher() allIdentitiesService.immediateMostRecentlyUsedIdentityIdPublisher()
.replaceError(with: nil) .replaceError(with: nil)
@ -32,14 +42,6 @@ public final class RootViewModel: ObservableObject {
.sink { [weak self] in self?.identitySelected(id: $0) } .sink { [weak self] in self?.identitySelected(id: $0) }
.store(in: &cancellables) .store(in: &cancellables)
userNotificationService.isAuthorized(request: false)
.filter { $0 }
.zip(registerForRemoteNotifications())
.map { $1 }
.flatMap(allIdentitiesService.updatePushSubscriptions(deviceToken:))
.sink { _ in } receiveValue: { _ in }
.store(in: &cancellables)
userNotificationService.events userNotificationService.events
.sink { [weak self] in self?.handle(event: $0) } .sink { [weak self] in self?.handle(event: $0) }
.store(in: &cancellables) .store(in: &cancellables)
@ -116,10 +118,12 @@ private extension RootViewModel {
.sink { _ in } receiveValue: { _ in } .sink { _ in } receiveValue: { _ in }
.store(in: &self.cancellables) .store(in: &self.cancellables)
if identityContext.identity.authenticated && !identityContext.identity.pending { if identityContext.identity.authenticated,
!identityContext.identity.pending,
let registerForRemoteNotifications = self.registerForRemoteNotifications {
self.userNotificationService.isAuthorized(request: true) self.userNotificationService.isAuthorized(request: true)
.filter { $0 } .filter { $0 }
.zip(self.registerForRemoteNotifications()) .zip(registerForRemoteNotifications())
.filter { identityContext.identity.lastRegisteredDeviceToken != $1 } .filter { identityContext.identity.lastRegisteredDeviceToken != $1 }
.map { ($1, identityContext.identity.pushSubscriptionAlerts) } .map { ($1, identityContext.identity.pushSubscriptionAlerts) }
.flatMap(identityContext.service.createPushSubscription(deviceToken:alerts:)) .flatMap(identityContext.service.createPushSubscription(deviceToken:alerts:))
@ -136,8 +140,23 @@ private extension RootViewModel {
switch event { switch event {
case let .willPresentNotification(_, completionHandler): case let .willPresentNotification(_, completionHandler):
completionHandler(.banner) completionHandler(.banner)
case let .didReceiveResponse(response, completionHandler):
let userInfo = response.notification.request.content.userInfo
if let identityIdString = userInfo[PushNotificationParsingService.identityIdUserInfoKey] as? String,
let identityId = Identity.Id(uuidString: identityIdString),
let pushNotificationJSON = userInfo[PushNotificationParsingService.pushNotificationUserInfoKey] as? Data,
let pushNotification = try? MastodonDecoder().decode(PushNotification.self, from: pushNotificationJSON) {
handle(pushNotification: pushNotification, identityId: identityId)
}
completionHandler()
default: default:
break break
} }
} }
func handle(pushNotification: PushNotification, identityId: Identity.Id) {
// TODO
}
} }