Merge branch 'feature/TF-1261_LastSyncDate' into release/v5.3.5

# Conflicts:
#	TwoFAS/TwoFAS/Root/Modules/Main/Interactor/MainModuleInteractor.swift
#	TwoFAS/TwoFAS/Root/Modules/Main/Presenter/MainPresenter.swift
This commit is contained in:
Zbigniew Cisiński 2024-03-04 23:28:29 +01:00
commit 9b192591c8
14 changed files with 138 additions and 12 deletions

View File

@ -22,6 +22,8 @@ import Foundation
public extension Notification.Name {
static let servicesWereUpdated = Notification.Name("servicesWereUpdatedNotification")
static let sectionsWereUpdated = Notification.Name("sectionsWereUpdatedNotification")
static let syncCompletedSuccessfuly = Notification.Name("syncCompletedSuccessfuly")
static let clearSyncCompletedSuccessfuly = Notification.Name("clearSyncCompletedSuccessfuly")
}
public extension Notification {

View File

@ -42,6 +42,10 @@ public protocol CloudBackupStateInteracting: AnyObject {
func clearBackup()
func synchronizeBackup()
var successSyncDate: Date? { get }
func saveSuccessSyncDate()
func clearSavesuccessSync()
}
/// Use one instance per use case
@ -80,6 +84,10 @@ extension CloudBackupStateInteractor: CloudBackupStateInteracting {
var isBackupEnabled: Bool { isEnabled }
var isBackupAvailable: Bool { isAvailable }
var successSyncDate: Date? {
mainRepository.successSyncDate
}
func startMonitoring() {
Log("CloudBackupStateInteractor - start monitoring, listenerID: \(listenerID)", module: .interactor)
saveStates()
@ -138,6 +146,16 @@ extension CloudBackupStateInteractor: CloudBackupStateInteracting {
Log("CloudBackupStateInteractor - synchronizeBackup", module: .interactor)
mainRepository.synchronizeBackup()
}
func saveSuccessSyncDate() {
Log("CloudBackupStateInteractor - saveSuccessSync", module: .interactor)
mainRepository.saveSuccessSyncDate(Date())
}
func clearSavesuccessSync() {
Log("CloudBackupStateInteractor - clearSavesuccessSync", module: .interactor)
mainRepository.saveSuccessSyncDate(nil)
}
}
private extension CloudBackupStateInteractor {

View File

@ -122,6 +122,7 @@ protocol MainRepository: AnyObject {
// MARK: - Cloud
var secretSyncError: ((String) -> Void)? { get set }
var isCloudBackupConnected: Bool { get }
var successSyncDate: Date? { get }
var cloudCurrentState: CloudState { get }
func registerForCloudStateChanges(_ listener: @escaping CloudStateListener, id: CloudStateListenerID)
func unregisterForCloudStageChanges(with id: CloudStateListenerID)
@ -133,6 +134,7 @@ protocol MainRepository: AnyObject {
userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
)
func saveSuccessSyncDate(_ date: Date?)
// MARK: - Import
var fileURL: URL? { get set }

View File

@ -44,6 +44,10 @@ public enum CloudState: Equatable {
}
extension MainRepositoryImpl {
var successSyncDate: Date? {
userDefaultsRepository.successSyncDate
}
var secretSyncError: ((String) -> Void)? {
get {
cloudHandler.secretSyncError
@ -89,6 +93,10 @@ extension MainRepositoryImpl {
) {
SyncInstance.didReceiveRemoteNotification(userInfo: userInfo, fetchCompletionHandler: completionHandler)
}
func saveSuccessSyncDate(_ date: Date?) {
userDefaultsRepository.saveSuccessSyncDate(date)
}
}
private extension MainRepositoryImpl {

View File

@ -97,4 +97,7 @@ protocol UserDefaultsRepository: AnyObject {
var exchangeToken: String? { get }
func setExchangeToken(_ key: String)
func clearExchangeToken()
var successSyncDate: Date? { get }
func saveSuccessSyncDate(_ date: Date?)
}

View File

@ -51,6 +51,7 @@ final class UserDefaultsRepositoryImpl: UserDefaultsRepository {
case mainMenuPortraitCollapsed
case mainMenuLandscapeCollapsed
case dateOfFirstRun
case syncSuccessDate
}
private let userDefaults = UserDefaults()
private let sharedDefaults = UserDefaults(suiteName: Config.suiteName)!
@ -279,6 +280,16 @@ final class UserDefaultsRepositoryImpl: UserDefaultsRepository {
userDefaults.bool(forKey: Keys.introductionWasShown.rawValue)
}
// MARK: - Sync success
var successSyncDate: Date? {
userDefaults.object(forKey: Keys.syncSuccessDate.rawValue) as? Date
}
func saveSuccessSyncDate(_ date: Date?) {
userDefaults.set(date, forKey: Keys.syncSuccessDate.rawValue)
userDefaults.synchronize()
}
// MARK: - View Path
func clearViewPath() {

View File

@ -85,6 +85,8 @@ final class CloudHandler: CloudHandlerType {
private let itemHandlerMigrationProxy: ItemHandlerMigrationProxy
private let cloudKit: CloudKit
private let notificationCenter = NotificationCenter.default
private var isClearing = false
private var listeners: [String: CloudHandlerStateListener] = [:]
@ -313,6 +315,7 @@ final class CloudHandler: CloudHandlerType {
private func setDisabled() {
Log("Cloud Handler - Set Disabled", module: .cloudSync)
ConstStorage.cloudEnabled = false
notificationCenter.post(name: .clearSyncCompletedSuccessfuly, object: nil)
}
private var isEnabled: Bool { ConstStorage.cloudEnabled }
@ -344,6 +347,8 @@ final class CloudHandler: CloudHandlerType {
if isClearing {
clearBackup()
} else {
notificationCenter.post(name: .syncCompletedSuccessfuly, object: nil)
}
}

View File

@ -32,6 +32,8 @@ protocol MainModuleInteracting: AnyObject {
func clearImportedFileURL()
func savePIN(_ PIN: String, ofType pinType: PINType)
func saveSuccessSync()
func clearSavesuccessSync()
// MARK: - New app version
func checkForNewAppVersion(completion: @escaping (URL?) -> Void)
@ -111,6 +113,14 @@ extension MainModuleInteractor: MainModuleInteracting {
func savePIN(_ PIN: String, ofType pinType: PINType) {
protectionInteractor.savePIN(PIN, typeOfPIN: pinType)
}
func saveSuccessSync() {
cloudBackupStateInteractor.saveSuccessSyncDate()
}
func clearSavesuccessSync() {
cloudBackupStateInteractor.clearSavesuccessSync()
}
// MARK: - New app version

View File

@ -84,6 +84,14 @@ final class MainPresenter {
viewIsVisible()
}
func handleSyncCompletedSuccessfuly() {
interactor.saveSuccessSync()
}
func handleClearSyncCompletedSuccessfuly() {
interactor.clearSavesuccessSync()
}
func handleSavePIN(_ PIN: String, pinType: PINType) {
interactor.savePIN(PIN, ofType: pinType)
}

View File

@ -30,6 +30,7 @@ final class MainViewController: UIViewController {
var presenter: MainPresenter!
private let settingsEventController = SettingsEventController()
private let notificationCenter = NotificationCenter.default
var splitView: MainSplitViewController?
@ -48,66 +49,78 @@ final class MainViewController: UIViewController {
}
deinit {
NotificationCenter.default.removeObserver(self)
notificationCenter.removeObserver(self)
}
}
extension MainViewController {
private func setupEvents() {
NotificationCenter.default.addObserver(
notificationCenter.addObserver(
self,
selector: #selector(refreshAuthList),
name: .pushNotificationRefreshAuthList,
object: nil
)
NotificationCenter.default.addObserver(
notificationCenter.addObserver(
self,
selector: #selector(refreshAuthList),
name: UIApplication.didBecomeActiveNotification,
object: nil
)
NotificationCenter.default.addObserver(
notificationCenter.addObserver(
self,
selector: #selector(clearAuthList),
name: UIApplication.willResignActiveNotification,
object: nil
)
NotificationCenter.default.addObserver(
notificationCenter.addObserver(
self,
selector: #selector(authorizeFromApp),
name: .pushNotificationAuthorizeFromApp,
object: nil
)
NotificationCenter.default.addObserver(
notificationCenter.addObserver(
self,
selector: #selector(switchToSetupPIN),
name: .switchToSetupPIN,
object: nil
)
NotificationCenter.default.addObserver(
notificationCenter.addObserver(
self,
selector: #selector(switchToBrowserExtension),
name: .switchToBrowserExtension,
object: nil
)
NotificationCenter.default.addObserver(
notificationCenter.addObserver(
self,
selector: #selector(fileAwaitsOpening),
name: .fileAwaitsOpening,
object: nil
)
NotificationCenter.default.addObserver(
notificationCenter.addObserver(
self,
selector: #selector(tokensVisible),
name: .tokensScreenIsVisible,
object: nil
)
NotificationCenter.default.addObserver(
notificationCenter.addObserver(
self,
selector: #selector(tokensVisible),
name: .userLoggedIn,
object: nil
)
notificationCenter.addObserver(
self,
selector: #selector(syncCompletedSuccessfuly),
name: .syncCompletedSuccessfuly,
object: nil
)
notificationCenter.addObserver(
self,
selector: #selector(clearSyncCompletedSuccessfuly),
name: .clearSyncCompletedSuccessfuly,
object: nil
)
}
@objc
@ -151,6 +164,16 @@ extension MainViewController {
private func tokensVisible() {
presenter.handleViewIsVisible()
}
@objc
private func syncCompletedSuccessfuly() {
presenter.handleSyncCompletedSuccessfuly()
}
@objc
private func clearSyncCompletedSuccessfuly() {
presenter.handleClearSyncCompletedSuccessfuly()
}
}
extension MainViewController: MainViewControlling {

View File

@ -35,6 +35,8 @@ protocol BackupMenuModuleInteracting: AnyObject {
func toggleBackup()
func clearBackup()
var syncSuccessDate: Date? { get }
}
final class BackupMenuModuleInteractor {
@ -102,6 +104,10 @@ extension BackupMenuModuleInteractor: BackupMenuModuleInteracting {
func clearBackup() {
cloudBackup.clearBackup()
}
var syncSuccessDate: Date? {
cloudBackup.successSyncDate
}
}
private extension BackupMenuModuleInteractor {

View File

@ -21,6 +21,15 @@ import Foundation
extension BackupMenuPresenter {
func buildMenu() -> [BackupMenuSection] {
var footer = T.Backup.sectionDescription
let dateStr: String = {
if let date = interactor.syncSuccessDate {
return dateFormatter.string(from: date)
}
return "-"
}()
footer.append("\n\n\(T.backupSettingsSyncTitle): \(dateStr)")
let cloudBackup = BackupMenuSection(
title: T.Backup.cloudBackup,
cells: [
@ -34,7 +43,7 @@ extension BackupMenuPresenter {
)
)
],
footer: T.Backup.sectionDescription
footer: footer
)
let exportEnabled = interactor.exportEnabled && interactor.isBackupAllowed

View File

@ -23,6 +23,12 @@ final class BackupMenuPresenter {
weak var view: BackupMenuViewControlling?
private let flowController: BackupMenuFlowControlling
var dateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
dateFormatter.timeStyle = .short
return dateFormatter
}()
let interactor: BackupMenuModuleInteracting
init(flowController: BackupMenuFlowControlling, interactor: BackupMenuModuleInteracting) {
@ -30,7 +36,7 @@ final class BackupMenuPresenter {
self.interactor = interactor
interactor.reload = { [weak self] in self?.reload() }
}
func viewWillAppear() {
interactor.startMonitoring()
reload()
@ -74,6 +80,10 @@ final class BackupMenuPresenter {
func handleBecomeActive() {
reload()
}
func handleSyncSuccessDateUpdate() {
reload()
}
}
private extension BackupMenuPresenter {

View File

@ -98,6 +98,12 @@ final class BackupMenuViewController: UIViewController {
name: .refreshTabContent,
object: nil
)
NotificationCenter.default.addObserver(
self,
selector: #selector(updateSyncSuccDate),
name: .syncCompletedSuccessfuly,
object: nil
)
presenter.viewWillAppear()
}
@ -113,6 +119,11 @@ final class BackupMenuViewController: UIViewController {
presenter.handleBecomeActive()
}
@objc
private func updateSyncSuccDate() {
presenter.handleSyncSuccessDateUpdate()
}
private func setupConstraints() {
switch traitCollection.horizontalSizeClass {
case .regular: