diff --git a/TwoFAS/Data/Interactors/LocalNotificationFetchInteractor.swift b/TwoFAS/Data/Interactors/LocalNotificationFetchInteractor.swift index 79a0df92..90f741c8 100644 --- a/TwoFAS/Data/Interactors/LocalNotificationFetchInteractor.swift +++ b/TwoFAS/Data/Interactors/LocalNotificationFetchInteractor.swift @@ -18,6 +18,7 @@ // import Foundation +import Common public struct LocalNotification: Hashable { public enum NotificationKind: String, Decodable, Hashable { @@ -66,16 +67,20 @@ final class LocalNotificationFetchInteractor { extension LocalNotificationFetchInteractor: LocalNotificationFetchInteracting { func getNotification(completion: @escaping (LocalNotification?) -> Void) { + Log("Local Notification Fetch - fetching", module: .interactor) if mainRepository.localNotificationsHandled { + Log("Local Notification Fetch - handled, ready", module: .interactor) fetched = true completion(currentNotification()) } else { + Log("Local Notification Fetch - awaiting", module: .interactor) notificationCallback = completion } } func markNotificationAsRead() { - setWasRead(true) + Log("Local Notification Fetch - mark as read", module: .interactor) + setWasRead(true) } } @@ -84,6 +89,7 @@ private extension LocalNotificationFetchInteractor { private func notificationsHandled() { guard !fetched else { return } fetched = true + Log("Local Notification Fetch - handled after awaiting", module: .interactor) notificationCallback?(currentNotification()) } @@ -104,10 +110,16 @@ private extension LocalNotificationFetchInteractor { } func currentNotification() -> LocalNotification? { - guard let publishedID, let notificationPublishedDate else { return nil } + guard let publishedID, let notificationPublishedDate else { + Log("Local Notification Fetch - no notification", module: .interactor) + return nil + } + + let kind = LocalNotification.NotificationKind.kindFromCycle(cycle) + Log("Local Notification Fetch - we have notification of kind: \(kind)", module: .interactor) return LocalNotification( id: publishedID, - kind: .kindFromCycle(cycle), + kind: kind, publishedAt: notificationPublishedDate, wasRead: wasRead ) diff --git a/TwoFAS/Data/Interactors/LocalNotificationStateInteractor.swift b/TwoFAS/Data/Interactors/LocalNotificationStateInteractor.swift index 2cafe1a4..25b9296a 100644 --- a/TwoFAS/Data/Interactors/LocalNotificationStateInteractor.swift +++ b/TwoFAS/Data/Interactors/LocalNotificationStateInteractor.swift @@ -59,6 +59,7 @@ final class LocalNotificationStateInteractor { self?.awaitsBackupStateChange?() self?.awaitsBackupStateChange = nil } + cloudBackup.startMonitoring() } } @@ -71,6 +72,7 @@ extension LocalNotificationStateInteractor: LocalNotificationStateInteracting { if runCount >= 2 && cycle == -2 { startNotification(-1) + return } guard isTimeForNext else { @@ -82,6 +84,8 @@ extension LocalNotificationStateInteractor: LocalNotificationStateInteracting { let next = nextCycle() + Log("Local Notification State - next cycle: \(next)", module: .interactor) + switch next { case 0: canDisplayBackup { [weak self] value in if value { @@ -89,33 +93,35 @@ extension LocalNotificationStateInteractor: LocalNotificationStateInteracting { } else { self?.setInactiveNotification(next) } + self?.markLocalNotificationsAsHandled() } case 1: if canDisplayBrowserExtension() { startNotification(next) } else { setInactiveNotification(next) } + markLocalNotificationsAsHandled() case 2: if canDisplayDonation() { startNotification(3) } else { setInactiveNotification(next) } + markLocalNotificationsAsHandled() default: break } - - markLocalNotificationsAsHandled() } } private extension LocalNotificationStateInteractor { func markLocalNotificationsAsHandled() { + Log("Local Notification State - notification handled", module: .interactor) mainRepository.markLocalNotificationsAsHandled() notificationCenter.post(name: .localNotificationsHandled, object: nil) } var isTimeForNext: Bool { guard let publicationDate = mainRepository.localNotificationPublicationDate else { - return false + return true } let days = publicationDate.days(from: .now) return days >= cycleDays @@ -130,10 +136,12 @@ private extension LocalNotificationStateInteractor { } func saveCycle(_ cycle: Int) { + Log("Local Notification State - setting cycle: \(cycle)", module: .interactor) mainRepository.saveLocalNotificationCycle(cycle) } func increaseRunCount() { + Log("Local Notification State - increaseRunCount", module: .interactor) mainRepository.saveRunCount(runCount + 1) } @@ -146,12 +154,14 @@ private extension LocalNotificationStateInteractor { } func startNotification(_ cycle: Int) { + Log("Local Notification State - start notification for cycle: \(cycle)", module: .interactor) saveNotificationPublicationDate() saveCycle(cycle) setIsPublished(true) } func setInactiveNotification(_ cycle: Int) { + Log("Local Notification State - set inactive notification for cycle: \(cycle)", module: .interactor) saveNotificationPublicationDate() saveCycle(cycle) setIsPublished(false) @@ -162,6 +172,7 @@ private extension LocalNotificationStateInteractor { } func clearNotification() { + Log("Local Notification State - clear notifications", module: .interactor) setIsPublished(false) clearNotificationPublicationDate() setWasRead(false) @@ -210,7 +221,7 @@ private extension LocalNotificationStateInteractor { } func isBackupPossible() -> Bool { - !cloudBackup.isBackupEnabled && cloudBackup.isBackupAvailable && hasServices && !mdmInteractor.isBackupBlocked + !cloudBackup.isBackupEnabled && hasServices && !mdmInteractor.isBackupBlocked } func canDisplayBrowserExtension() -> Bool { diff --git a/TwoFAS/TwoFAS/Interactors/ModuleInteractorFactory.swift b/TwoFAS/TwoFAS/Interactors/ModuleInteractorFactory.swift index ecf4edb2..5a289acf 100644 --- a/TwoFAS/TwoFAS/Interactors/ModuleInteractorFactory.swift +++ b/TwoFAS/TwoFAS/Interactors/ModuleInteractorFactory.swift @@ -32,7 +32,8 @@ final class ModuleInteractorFactory { registerDeviceInteractor: InteractorFactory.shared.registerDeviceInteractor(), appStateInteractor: InteractorFactory.shared.appStateInteractor(), notificationInteractor: InteractorFactory.shared.notificationInteractor(), - widgetsInteractor: InteractorFactory.shared.widgetsInteractor() + widgetsInteractor: InteractorFactory.shared.widgetsInteractor(), + localNotificationStateInteractor: InteractorFactory.shared.localNotificationStateInteractor() ) } @@ -343,8 +344,7 @@ final class ModuleInteractorFactory { appInfoInteractor: InteractorFactory.shared.appInfoInteractor(), rootInteractor: InteractorFactory.shared.rootInteractor(), mdmInteractor: InteractorFactory.shared.mdmInteractor(), - protectionInteractor: InteractorFactory.shared.protectionInteractor(), - localNotificationStateInteractor: InteractorFactory.shared.localNotificationStateInteractor() + protectionInteractor: InteractorFactory.shared.protectionInteractor() ) } diff --git a/TwoFAS/TwoFAS/Root/Interactor/RootModuleInteractor.swift b/TwoFAS/TwoFAS/Root/Interactor/RootModuleInteractor.swift index 57c3d1a9..583390a2 100644 --- a/TwoFAS/TwoFAS/Root/Interactor/RootModuleInteractor.swift +++ b/TwoFAS/TwoFAS/Root/Interactor/RootModuleInteractor.swift @@ -44,7 +44,7 @@ protocol RootModuleInteracting: AnyObject { ) func lockScreenActive() - func lockScreenInactive() + func lockScreenInactive() } final class RootModuleInteractor { @@ -57,6 +57,7 @@ final class RootModuleInteractor { private let appStateInteractor: AppStateInteracting private let notificationInteractor: NotificationInteracting private let widgetsInteractor: WidgetsInteracting + private let localNotificationStateInteractor: LocalNotificationStateInteracting init( rootInteractor: RootInteracting, @@ -65,7 +66,8 @@ final class RootModuleInteractor { registerDeviceInteractor: RegisterDeviceInteracting, appStateInteractor: AppStateInteracting, notificationInteractor: NotificationInteracting, - widgetsInteractor: WidgetsInteracting + widgetsInteractor: WidgetsInteracting, + localNotificationStateInteractor: LocalNotificationStateInteracting ) { self.rootInteractor = rootInteractor self.linkInteractor = linkInteractor @@ -74,6 +76,7 @@ final class RootModuleInteractor { self.appStateInteractor = appStateInteractor self.notificationInteractor = notificationInteractor self.widgetsInteractor = widgetsInteractor + self.localNotificationStateInteractor = localNotificationStateInteractor rootInteractor.storageError = { [weak self] error in self?.storageError?(error) @@ -94,7 +97,7 @@ extension RootModuleInteractor: RootModuleInteracting { rootInteractor.initializeApp() registerDeviceInteractor.initialize() } - + func lockApplicationIfNeeded(presentLoginImmediately: @escaping () -> Void) { rootInteractor.lockApplicationIfNeeded( presentLoginImmediately: presentLoginImmediately @@ -120,6 +123,7 @@ extension RootModuleInteractor: RootModuleInteracting { didCopyToken() } rootInteractor.applicationDidBecomeActive() + localNotificationStateInteractor.activate() } func shouldHandleURL(url: URL) -> Bool { diff --git a/TwoFAS/TwoFAS/Root/Modules/Main/Flow/MainFlowController.swift b/TwoFAS/TwoFAS/Root/Modules/Main/Flow/MainFlowController.swift index 5b59b425..9dd503a3 100644 --- a/TwoFAS/TwoFAS/Root/Modules/Main/Flow/MainFlowController.swift +++ b/TwoFAS/TwoFAS/Root/Modules/Main/Flow/MainFlowController.swift @@ -207,6 +207,10 @@ extension MainFlowController: MainSplitFlowControllerParent { func navigationSwitchedToSettingsExternalImport() { viewController.presenter.handleSwitchToExternalImport() } + + func navigationSwitchedToSettingsBackup() { + viewController.presenter.handleSwitchToBackup() + } } extension MainFlowController: NewPINNavigationFlowControllerParent { diff --git a/TwoFAS/TwoFAS/Root/Modules/Main/Interactor/MainModuleInteractor.swift b/TwoFAS/TwoFAS/Root/Modules/Main/Interactor/MainModuleInteractor.swift index a8effb33..fb9c3883 100644 --- a/TwoFAS/TwoFAS/Root/Modules/Main/Interactor/MainModuleInteractor.swift +++ b/TwoFAS/TwoFAS/Root/Modules/Main/Interactor/MainModuleInteractor.swift @@ -30,7 +30,6 @@ protocol MainModuleInteracting: AnyObject { func initialize() func checkForImport() -> URL? func clearImportedFileURL() - func refreshLocalNotifications() func savePIN(_ PIN: String, ofType pinType: PINType) func saveSuccessSync() @@ -65,7 +64,6 @@ final class MainModuleInteractor { private let rootInteractor: RootInteracting private let mdmInteractor: MDMInteracting private let protectionInteractor: ProtectionInteracting - private let localNotificationStateInteractor: LocalNotificationStateInteracting init( logUploadingInteractor: LogUploadingInteracting, @@ -77,8 +75,7 @@ final class MainModuleInteractor { appInfoInteractor: AppInfoInteracting, rootInteractor: RootInteracting, mdmInteractor: MDMInteracting, - protectionInteractor: ProtectionInteracting, - localNotificationStateInteractor: LocalNotificationStateInteracting + protectionInteractor: ProtectionInteracting ) { self.logUploadingInteractor = logUploadingInteractor self.cloudBackupStateInteractor = cloudBackupStateInteractor @@ -89,7 +86,6 @@ final class MainModuleInteractor { self.rootInteractor = rootInteractor self.mdmInteractor = mdmInteractor self.protectionInteractor = protectionInteractor - self.localNotificationStateInteractor = localNotificationStateInteractor cloudBackupStateInteractor.secretSyncError = { [weak self] in self?.secretSyncError?($0) } } @@ -102,10 +98,6 @@ extension MainModuleInteractor: MainModuleInteracting { appInfoInteractor.markDateOfFirstRunIfNeeded() } - func refreshLocalNotifications() { - localNotificationStateInteractor.activate() - } - func checkForImport() -> URL? { fileInteractor.url } diff --git a/TwoFAS/TwoFAS/Root/Modules/Main/Presenter/MainPresenter.swift b/TwoFAS/TwoFAS/Root/Modules/Main/Presenter/MainPresenter.swift index 4e6984ab..67595c87 100644 --- a/TwoFAS/TwoFAS/Root/Modules/Main/Presenter/MainPresenter.swift +++ b/TwoFAS/TwoFAS/Root/Modules/Main/Presenter/MainPresenter.swift @@ -37,7 +37,6 @@ final class MainPresenter { func viewDidLoad() { interactor.initialize() - interactor.refreshLocalNotifications() flowController.toSetupSplit() } @@ -45,10 +44,6 @@ final class MainPresenter { viewIsVisible() } - func handleAppDidBecomeActive() { - interactor.refreshLocalNotifications() - } - func handleSwitchToSetupPIN() { view?.navigateToViewPath(.settings(option: .security)) } @@ -61,6 +56,10 @@ final class MainPresenter { view?.navigateToViewPath(.settings(option: .externalImport)) } + func handleSwitchToBackup() { + view?.navigateToViewPath(.settings(option: .backup)) + } + func handleSwitchedToSettings() { view?.settingsTabActive() } diff --git a/TwoFAS/TwoFAS/Root/Modules/Main/View/MainViewController.swift b/TwoFAS/TwoFAS/Root/Modules/Main/View/MainViewController.swift index 125d923d..70ad9915 100644 --- a/TwoFAS/TwoFAS/Root/Modules/Main/View/MainViewController.swift +++ b/TwoFAS/TwoFAS/Root/Modules/Main/View/MainViewController.swift @@ -63,7 +63,7 @@ extension MainViewController { ) notificationCenter.addObserver( self, - selector: #selector(appDidBecomeActive), + selector: #selector(refreshAuthList), name: UIApplication.didBecomeActiveNotification, object: nil ) @@ -128,12 +128,6 @@ extension MainViewController { presenter.handleClearAuthList() } - @objc - private func appDidBecomeActive() { - presenter.handleAppDidBecomeActive() - presenter.handleRefreshAuthList() - } - @objc private func refreshAuthList() { presenter.handleRefreshAuthList() diff --git a/TwoFAS/TwoFAS/Root/Modules/MainSplit/Flow/MainSplitFlowController.swift b/TwoFAS/TwoFAS/Root/Modules/MainSplit/Flow/MainSplitFlowController.swift index 968de04e..f39331fb 100644 --- a/TwoFAS/TwoFAS/Root/Modules/MainSplit/Flow/MainSplitFlowController.swift +++ b/TwoFAS/TwoFAS/Root/Modules/MainSplit/Flow/MainSplitFlowController.swift @@ -24,6 +24,7 @@ protocol MainSplitFlowControllerParent: AnyObject { func navigationSwitchedToTokens() func navigationSwitchedToSettings() func navigationSwitchedToSettingsExternalImport() + func navigationSwitchedToSettingsBackup() } protocol MainSplitFlowControlling: AnyObject { @@ -175,6 +176,10 @@ extension MainSplitFlowController: TokensPlainFlowControllerParent { func tokensSwitchToSettingsExternalImport() { parent?.navigationSwitchedToSettingsExternalImport() } + + func tokensSwitchToSettingsBackup() { + parent?.navigationSwitchedToSettingsBackup() + } } extension MainSplitFlowController: SettingsFlowControllerParent { diff --git a/TwoFAS/TwoFAS/Root/Modules/News/Flow/NewsNavigationFlowController.swift b/TwoFAS/TwoFAS/Root/Modules/News/Flow/NewsNavigationFlowController.swift index 0417c4d1..eb8409e0 100644 --- a/TwoFAS/TwoFAS/Root/Modules/News/Flow/NewsNavigationFlowController.swift +++ b/TwoFAS/TwoFAS/Root/Modules/News/Flow/NewsNavigationFlowController.swift @@ -22,6 +22,7 @@ import Common protocol NewsNavigationFlowControllerParent: AnyObject { func newsClose() + func newsToBackup() } final class NewsNavigationFlowController: NavigationFlowController { @@ -48,4 +49,8 @@ extension NewsNavigationFlowController: NewsPlainFlowControllerParent { func newsClose() { parent?.newsClose() } + + func newsToBackup() { + parent?.newsToBackup() + } } diff --git a/TwoFAS/TwoFAS/Root/Modules/News/Flow/NewsPlainFlowController.swift b/TwoFAS/TwoFAS/Root/Modules/News/Flow/NewsPlainFlowController.swift index 902a56c0..f2a8957c 100644 --- a/TwoFAS/TwoFAS/Root/Modules/News/Flow/NewsPlainFlowController.swift +++ b/TwoFAS/TwoFAS/Root/Modules/News/Flow/NewsPlainFlowController.swift @@ -22,6 +22,7 @@ import Common protocol NewsPlainFlowControllerParent: AnyObject { func newsClose() + func newsToBackup() } protocol NewsPlainFlowControlling: AnyObject { @@ -69,8 +70,7 @@ extension NewsPlainFlowController: NewsPlainFlowControlling { func toInternalLink(_ internalLink: ListNewsEntry.InternalLink) { switch internalLink { case .backup: - print("to backup!") - // to backup! + parent?.newsToBackup() } } } diff --git a/TwoFAS/TwoFAS/Root/Modules/Tokens/Flow/TokensPlainFlowController.swift b/TwoFAS/TwoFAS/Root/Modules/Tokens/Flow/TokensPlainFlowController.swift index db343fdf..54662b42 100644 --- a/TwoFAS/TwoFAS/Root/Modules/Tokens/Flow/TokensPlainFlowController.swift +++ b/TwoFAS/TwoFAS/Root/Modules/Tokens/Flow/TokensPlainFlowController.swift @@ -25,6 +25,7 @@ import SwiftUI protocol TokensPlainFlowControllerParent: AnyObject { func tokensSwitchToTokensTab() func tokensSwitchToSettingsExternalImport() + func tokensSwitchToSettingsBackup() } protocol TokensPlainFlowControlling: AnyObject { @@ -654,6 +655,13 @@ extension TokensPlainFlowController: NewsNavigationFlowControllerParent { viewController.presenter.handleRefreshNewsStatus() dismiss() } + + func newsToBackup() { + viewController.presenter.handleRefreshNewsStatus() + dismiss { [weak self] in + self?.parent?.tokensSwitchToSettingsBackup() + } + } } extension TokensPlainFlowController: GuideSelectorNavigationFlowControllerParent { diff --git a/TwoFAS/TwoFAS/Root/Modules/Tokens/Presenter/TokensPresenter.swift b/TwoFAS/TwoFAS/Root/Modules/Tokens/Presenter/TokensPresenter.swift index 41a0a231..5fe5553f 100644 --- a/TwoFAS/TwoFAS/Root/Modules/Tokens/Presenter/TokensPresenter.swift +++ b/TwoFAS/TwoFAS/Root/Modules/Tokens/Presenter/TokensPresenter.swift @@ -87,7 +87,7 @@ extension TokensPresenter { interactor.sync() appActiveActions() interactor.fetchNews { [weak self] in - self?.updateAddNewsIcon() + self?.updateNewsIcon() } } @@ -446,7 +446,7 @@ extension TokensPresenter { } func handleRefreshNewsStatus() { - updateAddNewsIcon() + updateNewsIcon() } } @@ -532,7 +532,7 @@ private extension TokensPresenter { }() if interactor.hasServices { - updateAddNewsIcon() + updateNewsIcon() view?.showList() if Set(currentServices) != Set(newServices) || changeRequriesTokenRefresh { @@ -553,7 +553,7 @@ private extension TokensPresenter { if !isSearching && currentState == .edit { setCurrentState(.normal) } - updateAddNewsIcon() + updateNewsIcon() interactor.stopCounters() updateEditStateButton() @@ -568,14 +568,6 @@ private extension TokensPresenter { changeRequriesTokenRefresh = false } - func updateAddNewsIcon() { - if interactor.hasServices { - view?.updateAddIcon(using: mapButtonStateFor(currentState, isFirst: false)) - } else { - view?.updateAddIcon(using: mapButtonStateFor(currentState, isFirst: !isSearching)) - } - } - func mapButtonStateFor(_ currentState: State, isFirst: Bool) -> TokensViewControllerAddState { switch (currentState, isFirst) { case (.edit, _): @@ -602,6 +594,20 @@ private extension TokensPresenter { return .none } } + + func updateNewsIcon() { + interactor.fetchNews { [weak self] in + self?.updateAddServiceIcon() + } + } + + private func updateAddServiceIcon() { + if interactor.hasServices { + view?.updateAddIcon(using: mapButtonStateFor(currentState, isFirst: false)) + } else { + view?.updateAddIcon(using: mapButtonStateFor(currentState, isFirst: !isSearching)) + } + } } private extension TokensPresenter.State {