metatext-app-ios-iphone-ipad/ViewModels/Sources/ViewModels/View Models/RootViewModel.swift

163 lines
6.2 KiB
Swift

// Copyright © 2020 Metabolist. All rights reserved.
import Combine
import Foundation
import Mastodon
import ServiceLayer
public final class RootViewModel: ObservableObject {
@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?
private let environment: AppEnvironment
private let allIdentitiesService: AllIdentitiesService
private let userNotificationService: UserNotificationService
private var cancellables = Set<AnyCancellable>()
public init(environment: AppEnvironment) throws {
self.environment = environment
allIdentitiesService = try AllIdentitiesService(environment: environment)
userNotificationService = UserNotificationService(environment: environment)
allIdentitiesService.immediateMostRecentlyUsedIdentityIdPublisher()
.replaceError(with: nil)
.assign(to: &$mostRecentlyUsedIdentityId)
identitySelected(id: mostRecentlyUsedIdentityId, immediate: true)
allIdentitiesService.identitiesCreated
.sink { [weak self] in self?.identitySelected(id: $0) }
.store(in: &cancellables)
userNotificationService.events
.sink { [weak self] in self?.handle(event: $0) }
.store(in: &cancellables)
}
}
public extension RootViewModel {
func identitySelected(id: Identity.Id?) {
identitySelected(id: id, immediate: false)
}
func deleteIdentity(id: Identity.Id) {
allIdentitiesService.deleteIdentity(id: id)
.sink { _ in } receiveValue: { _ in }
.store(in: &cancellables)
}
func addIdentityViewModel() -> AddIdentityViewModel {
AddIdentityViewModel(
allIdentitiesService: allIdentitiesService,
instanceURLService: InstanceURLService(environment: environment))
}
func newStatusViewModel(
identityContext: IdentityContext,
inReplyTo: StatusViewModel? = nil,
redraft: Status? = nil) -> NewStatusViewModel {
NewStatusViewModel(
allIdentitiesService: allIdentitiesService,
identityContext: identityContext,
environment: environment,
inReplyTo: inReplyTo,
redraft: redraft,
extensionContext: nil)
}
}
private extension RootViewModel {
func identitySelected(id: Identity.Id?, immediate: Bool) {
navigationViewModel?.presentingSecondaryNavigation = false
guard
let id = id,
let identityService = try? allIdentitiesService.identityService(id: id) else {
navigationViewModel = nil
return
}
let identityPublisher = identityService.identityPublisher(immediate: immediate)
.catch { [weak self] _ -> Empty<Identity, Never> in
DispatchQueue.main.async {
if self?.navigationViewModel?.identityContext.identity.id == id {
self?.identitySelected(id: self?.mostRecentlyUsedIdentityId, immediate: false)
}
}
return Empty()
}
.share()
identityPublisher
.first()
.map { [weak self] in
guard let self = self else { return nil }
let identityContext = IdentityContext(
identity: $0,
publisher: identityPublisher.eraseToAnyPublisher(),
service: identityService,
environment: self.environment)
identityContext.service.updateLastUse()
.sink { _ in } receiveValue: { _ in }
.store(in: &self.cancellables)
if identityContext.identity.authenticated,
!identityContext.identity.pending,
let registerForRemoteNotifications = self.registerForRemoteNotifications {
self.userNotificationService.isAuthorized(request: true)
.filter { $0 }
.zip(registerForRemoteNotifications())
.filter { identityContext.identity.lastRegisteredDeviceToken != $1 }
.map { ($1, identityContext.identity.pushSubscriptionAlerts) }
.flatMap(identityContext.service.createPushSubscription(deviceToken:alerts:))
.sink { _ in } receiveValue: { _ in }
.store(in: &self.cancellables)
}
return NavigationViewModel(identityContext: identityContext)
}
.assign(to: &$navigationViewModel)
}
func handle(event: UserNotificationService.Event) {
switch event {
case let .willPresentNotification(_, completionHandler):
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:
break
}
}
func handle(pushNotification: PushNotification, identityId: Identity.Id) {
// TODO
}
}