diff --git a/DuckDuckGo/AppDelegate.swift b/DuckDuckGo/AppDelegate.swift index 42def30fc4..fb1e82dd9b 100644 --- a/DuckDuckGo/AppDelegate.swift +++ b/DuckDuckGo/AppDelegate.swift @@ -266,7 +266,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate { }) } + let previewsSource = TabPreviewsSource() let historyManager = makeHistoryManager() + let tabsModel = prepareTabsModel(previewsSource: previewsSource) #if APP_TRACKING_PROTECTION let main = MainViewController(bookmarksDatabase: bookmarksDatabase, @@ -275,14 +277,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate { historyManager: historyManager, syncService: syncService, syncDataProviders: syncDataProviders, - appSettings: AppDependencyProvider.shared.appSettings) + appSettings: AppDependencyProvider.shared.appSettings, + previewsSource: previewsSource, + tabsModel: tabsModel) #else let main = MainViewController(bookmarksDatabase: bookmarksDatabase, bookmarksDatabaseCleaner: syncDataProviders.bookmarksAdapter.databaseCleaner, historyManager: historyManager, syncService: syncService, syncDataProviders: syncDataProviders, - appSettings: AppDependencyProvider.shared.appSettings) + appSettings: AppDependencyProvider.shared.appSettings, + previewsSource: previewsSource, + tabsModel: tabsModel) #endif main.loadViewIfNeeded() @@ -345,6 +351,27 @@ class AppDelegate: UIResponder, UIApplicationDelegate { return true } + private func prepareTabsModel(previewsSource: TabPreviewsSource = TabPreviewsSource(), + appSettings: AppSettings = AppDependencyProvider.shared.appSettings, + isDesktop: Bool = UIDevice.current.userInterfaceIdiom == .pad) -> TabsModel { + let isPadDevice = UIDevice.current.userInterfaceIdiom == .pad + let tabsModel: TabsModel + if AutoClearSettingsModel(settings: appSettings) != nil { + tabsModel = TabsModel(desktop: isPadDevice) + tabsModel.save() + previewsSource.removeAllPreviews() + } else { + if let storedModel = TabsModel.get() { + // Save new model in case of migration + storedModel.save() + tabsModel = storedModel + } else { + tabsModel = TabsModel(desktop: isPadDevice) + } + } + return tabsModel + } + private func makeHistoryManager() -> HistoryManager { let historyManager = HistoryManager(privacyConfigManager: ContentBlocking.shared.privacyConfigurationManager, variantManager: DefaultVariantManager(), @@ -731,7 +758,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } Task { @MainActor in - await autoClear?.applicationWillMoveToForeground() + // Autoclear should have happened by now showKeyboardIfSettingOn = false if !handleAppDeepLink(app, mainViewController, url) { diff --git a/DuckDuckGo/AutoClear.swift b/DuckDuckGo/AutoClear.swift index eca57dce95..bda1c46614 100644 --- a/DuckDuckGo/AutoClear.swift +++ b/DuckDuckGo/AutoClear.swift @@ -33,14 +33,15 @@ class AutoClear { private let worker: AutoClearWorker private var timestamp: TimeInterval? - private lazy var appSettings = AppDependencyProvider.shared.appSettings - + private let appSettings: AppSettings + var isClearingEnabled: Bool { return AutoClearSettingsModel(settings: appSettings) != nil } - init(worker: AutoClearWorker) { + init(worker: AutoClearWorker, appSettings: AppSettings = AppDependencyProvider.shared.appSettings) { self.worker = worker + self.appSettings = appSettings } @MainActor @@ -86,8 +87,8 @@ class AutoClear { let timestamp = timestamp, shouldClearData(elapsedTime: Date().timeIntervalSince1970 - timestamp) else { return } + self.timestamp = nil worker.clearNavigationStack() await clearData() - self.timestamp = nil } } diff --git a/DuckDuckGo/MainViewController.swift b/DuckDuckGo/MainViewController.swift index 92c0277371..efe8920d17 100644 --- a/DuckDuckGo/MainViewController.swift +++ b/DuckDuckGo/MainViewController.swift @@ -84,11 +84,13 @@ class MainViewController: UIViewController { var tabsBarController: TabsBarViewController? var suggestionTrayController: SuggestionTrayViewController? - var tabManager: TabManager! - let previewsSource = TabPreviewsSource() + let tabManager: TabManager + let previewsSource: TabPreviewsSource let appSettings: AppSettings private var launchTabObserver: LaunchTabNotification.Observer? + var doRefreshAfterClear = true + #if APP_TRACKING_PROTECTION private let appTrackingProtectionDatabase: CoreDataDatabase #endif @@ -138,7 +140,7 @@ class MainViewController: UIViewController { lazy var tabSwitcherTransition = TabSwitcherTransitionDelegate() var currentTab: TabViewController? { - return tabManager?.current(createIfNeeded: false) + return tabManager.current(createIfNeeded: false) } var searchBarRect: CGRect { @@ -173,7 +175,9 @@ class MainViewController: UIViewController { historyManager: HistoryManager, syncService: DDGSyncing, syncDataProviders: SyncDataProviders, - appSettings: AppSettings = AppUserDefaults() + appSettings: AppSettings = AppUserDefaults(), + previewsSource: TabPreviewsSource, + tabsModel: TabsModel ) { self.appTrackingProtectionDatabase = appTrackingProtectionDatabase self.bookmarksDatabase = bookmarksDatabase @@ -184,9 +188,17 @@ class MainViewController: UIViewController { self.favoritesViewModel = FavoritesListViewModel(bookmarksDatabase: bookmarksDatabase, favoritesDisplayMode: appSettings.favoritesDisplayMode) self.bookmarksCachingSearch = BookmarksCachingSearch(bookmarksStore: CoreDataBookmarksSearchStore(bookmarksStore: bookmarksDatabase)) self.appSettings = appSettings - + self.previewsSource = previewsSource + + self.tabManager = TabManager(model: tabsModel, + previewsSource: previewsSource, + bookmarksDatabase: bookmarksDatabase, + historyManager: historyManager, + syncService: syncService) + super.init(nibName: nil, bundle: nil) - + + tabManager.delegate = self bindFavoritesDisplayMode() bindSyncService() } @@ -197,7 +209,9 @@ class MainViewController: UIViewController { historyManager: HistoryManager, syncService: DDGSyncing, syncDataProviders: SyncDataProviders, - appSettings: AppSettings + appSettings: AppSettings, + previewsSource: TabPreviewsSource, + tabsModel: TabsModel ) { self.bookmarksDatabase = bookmarksDatabase self.bookmarksDatabaseCleaner = bookmarksDatabaseCleaner @@ -207,9 +221,18 @@ class MainViewController: UIViewController { self.favoritesViewModel = FavoritesListViewModel(bookmarksDatabase: bookmarksDatabase, favoritesDisplayMode: appSettings.favoritesDisplayMode) self.bookmarksCachingSearch = BookmarksCachingSearch(bookmarksStore: CoreDataBookmarksSearchStore(bookmarksStore: bookmarksDatabase)) self.appSettings = appSettings - + self.previewsSource = previewsSource + + self.tabManager = TabManager(model: tabsModel, + previewsSource: previewsSource, + bookmarksDatabase: bookmarksDatabase, + historyManager: historyManager, + syncService: syncService) + + super.init(nibName: nil, bundle: nil) + tabManager.delegate = self bindSyncService() } #endif @@ -263,7 +286,6 @@ class MainViewController: UIViewController { initTabButton() initMenuButton() initBookmarksButton() - configureTabManager() loadInitialView() previewsSource.prepare() addLaunchTabNotificationObserver() @@ -692,40 +714,6 @@ class MainViewController: UIViewController { dismissOmniBar() } - private func configureTabManager() { - - let isPadDevice = UIDevice.current.userInterfaceIdiom == .pad - - let tabsModel: TabsModel - if let settings = AutoClearSettingsModel(settings: appSettings) { - // This needs to be refactored so that tabs model is injected and cleared before view did load, - // but for now, ensure this happens in the right order by clearing data here too, if needed. - tabsModel = TabsModel(desktop: isPadDevice) - tabsModel.save() - previewsSource.removeAllPreviews() - - if settings.action.contains(.clearData) { - Task { @MainActor in - await forgetData() - } - } - } else { - if let storedModel = TabsModel.get() { - // Save new model in case of migration - storedModel.save() - tabsModel = storedModel - } else { - tabsModel = TabsModel(desktop: isPadDevice) - } - } - tabManager = TabManager(model: tabsModel, - previewsSource: previewsSource, - bookmarksDatabase: bookmarksDatabase, - historyManager: historyManager, - syncService: syncService, - delegate: self) - } - private func addLaunchTabNotificationObserver() { launchTabObserver = LaunchTabNotification.addObserver(handler: { [weak self] urlString in guard let self = self else { return } @@ -883,6 +871,7 @@ class MainViewController: UIViewController { selectTab(existing) return } else if reuseExisting, let existing = tabManager.firstHomeTab() { + doRefreshAfterClear = false tabManager.selectTab(existing) loadUrl(url, fromExternalLink: fromExternalLink) } else { @@ -1999,7 +1988,7 @@ extension MainViewController: TabDelegate { if currentTab == tab { refreshControls() } - tabManager?.save() + tabManager.save() tabsBarController?.refresh(tabsModel: tabManager.model) // note: model in swipeTabsCoordinator doesn't need to be updated here // https://app.asana.com/0/414235014887631/1206847376910045/f @@ -2246,7 +2235,7 @@ extension MainViewController: TabSwitcherButtonDelegate { } func showTabSwitcher() { - guard let currentTab = currentTab ?? tabManager?.current(createIfNeeded: true) else { + guard let currentTab = currentTab ?? tabManager.current(createIfNeeded: true) else { fatalError("Unable to get current tab") } @@ -2308,6 +2297,10 @@ extension MainViewController: AutoClearWorker { } func refreshUIAfterClear() { + guard doRefreshAfterClear else { + doRefreshAfterClear = true + return + } showBars() attachHomeScreen() tabsBarController?.refresh(tabsModel: tabManager.model) @@ -2320,6 +2313,7 @@ extension MainViewController: AutoClearWorker { refreshUIAfterClear() } + @MainActor func forgetData() async { guard !clearInProgress else { assertionFailure("Shouldn't get called multiple times") diff --git a/DuckDuckGo/TabManager.swift b/DuckDuckGo/TabManager.swift index a6bf166cf2..63cc6b3f48 100644 --- a/DuckDuckGo/TabManager.swift +++ b/DuckDuckGo/TabManager.swift @@ -35,6 +35,7 @@ class TabManager { private let historyManager: HistoryManager private let syncService: DDGSyncing private var previewsSource: TabPreviewsSource + weak var delegate: TabDelegate? @UserDefaultsWrapper(key: .faviconTabsCacheNeedsCleanup, defaultValue: true) @@ -45,14 +46,12 @@ class TabManager { previewsSource: TabPreviewsSource, bookmarksDatabase: CoreDataDatabase, historyManager: HistoryManager, - syncService: DDGSyncing, - delegate: TabDelegate) { + syncService: DDGSyncing) { self.model = model self.previewsSource = previewsSource self.bookmarksDatabase = bookmarksDatabase self.historyManager = historyManager self.syncService = syncService - self.delegate = delegate let index = model.currentIndex let tab = model.tabs[index] if tab.link != nil { diff --git a/DuckDuckGoTests/AutoClearTests.swift b/DuckDuckGoTests/AutoClearTests.swift index 2edb1e52ee..45e66212ba 100644 --- a/DuckDuckGoTests/AutoClearTests.swift +++ b/DuckDuckGoTests/AutoClearTests.swift @@ -49,19 +49,20 @@ class AutoClearTests: XCTestCase { private var worker: MockWorker! private var logic: AutoClear! + private var appSettings: AppSettingsMock! + + override func setUp() async throws { + try await super.setUp() - override func setUp() { - super.setUp() - worker = MockWorker() - logic = AutoClear(worker: worker) + appSettings = AppSettingsMock() + logic = AutoClear(worker: worker, appSettings: appSettings) } // Note: applicationDidLaunch based clearing has moved to "configureTabManager" function of // MainViewController to ensure that tabs are removed before the data is cleared. func testWhenTimingIsSetToTerminationThenOnlyRestartClearsData() async { - let appSettings = AppUserDefaults() appSettings.autoClearAction = .clearData appSettings.autoClearTiming = .termination @@ -70,10 +71,14 @@ class AutoClearTests: XCTestCase { XCTAssertEqual(worker.clearNavigationStackInvocationCount, 0) XCTAssertEqual(worker.forgetDataInvocationCount, 0) + + await logic.applicationWillMoveToForeground() + + XCTAssertEqual(worker.clearNavigationStackInvocationCount, 0) + XCTAssertEqual(worker.forgetDataInvocationCount, 0) } func testWhenDesiredTimingIsSetThenDataIsClearedOnceTimeHasElapsed() async { - let appSettings = AppUserDefaults() appSettings.autoClearAction = .clearData let cases: [AutoClearSettingsModel.Timing: TimeInterval] = [.delay5min: 5 * 60,