Skip to content

Commit

Permalink
reapply auto clear fix, clear cookies, load page on link click (#2798)
Browse files Browse the repository at this point in the history
  • Loading branch information
brindy authored Apr 28, 2024
1 parent 6b4d1d8 commit 7999941
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 79 deletions.
44 changes: 38 additions & 6 deletions DuckDuckGo/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -263,14 +263,18 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
})
}

let previewsSource = TabPreviewsSource()
let historyManager = makeHistoryManager()
let tabsModel = prepareTabsModel(previewsSource: previewsSource)

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)

main.loadViewIfNeeded()

Expand All @@ -283,7 +287,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}

autoClear = AutoClear(worker: main)

Task {
await autoClear?.clearDataIfEnabled()
}

AppDependencyProvider.shared.voiceSearchHelper.migrateSettingsFlagIfNecessary()

// Task handler registration needs to happen before the end of `didFinishLaunching`, otherwise submitting a task can throw an exception.
Expand Down Expand Up @@ -324,6 +331,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(),
Expand Down Expand Up @@ -619,15 +647,15 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

Task { @MainActor in
await beginAuthentication()
await autoClear?.applicationWillMoveToForeground()
await autoClear?.clearDataIfEnabledAndTimeExpired()
showKeyboardIfSettingOn = true
syncService.scheduler.resumeSyncQueue()
}
}

func applicationDidEnterBackground(_ application: UIApplication) {
displayBlankSnapshotWindow()
autoClear?.applicationDidEnterBackground()
autoClear?.startClearingTimer()
lastBackgroundDate = Date()
AppDependencyProvider.shared.autofillLoginSession.endSession()
suspendSync()
Expand Down Expand Up @@ -677,7 +705,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
}

Task { @MainActor in
await autoClear?.applicationWillMoveToForeground()
// Autoclear should have happened by now
showKeyboardIfSettingOn = false

if !handleAppDeepLink(app, mainViewController, url) {
Expand Down Expand Up @@ -794,7 +822,11 @@ class AppDelegate: UIResponder, UIApplicationDelegate {

Task { @MainActor in

await autoClear?.applicationWillMoveToForeground()
if appIsLaunching {
await autoClear?.clearDataIfEnabled()
} else {
await autoClear?.clearDataIfEnabledAndTimeExpired()
}

if shortcutItem.type == ShortcutKey.clipboard, let query = UIPasteboard.general.string {
mainViewController?.clearNavigationStack()
Expand Down
17 changes: 9 additions & 8 deletions DuckDuckGo/AutoClear.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,18 +33,19 @@ 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
private func clearData() async {
func clearDataIfEnabled() async {
guard let settings = AutoClearSettingsModel(settings: appSettings) else { return }

if settings.action.contains(.clearTabs) {
Expand All @@ -59,7 +60,7 @@ class AutoClear {
}

/// Note: function is parametrised because of tests.
func applicationDidEnterBackground(_ time: TimeInterval = Date().timeIntervalSince1970) {
func startClearingTimer(_ time: TimeInterval = Date().timeIntervalSince1970) {
timestamp = time
}

Expand All @@ -81,13 +82,13 @@ class AutoClear {
}

@MainActor
func applicationWillMoveToForeground() async {
func clearDataIfEnabledAndTimeExpired() async {
guard isClearingEnabled,
let timestamp = timestamp,
shouldClearData(elapsedTime: Date().timeIntervalSince1970 - timestamp) else { return }

worker.clearNavigationStack()
await clearData()
self.timestamp = nil
worker.clearNavigationStack()
await clearDataIfEnabled()
}
}
69 changes: 27 additions & 42 deletions DuckDuckGo/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -81,11 +81,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

let bookmarksDatabase: CoreDataDatabase
private weak var bookmarksDatabaseCleaner: BookmarkDatabaseCleaner?
private var favoritesViewModel: FavoritesListInteracting
Expand Down Expand Up @@ -132,7 +134,7 @@ class MainViewController: UIViewController {

lazy var tabSwitcherTransition = TabSwitcherTransitionDelegate()
var currentTab: TabViewController? {
return tabManager?.current(createIfNeeded: false)
return tabManager.current(createIfNeeded: false)
}

var searchBarRect: CGRect {
Expand Down Expand Up @@ -165,7 +167,9 @@ class MainViewController: UIViewController {
historyManager: HistoryManager,
syncService: DDGSyncing,
syncDataProviders: SyncDataProviders,
appSettings: AppSettings
appSettings: AppSettings,
previewsSource: TabPreviewsSource,
tabsModel: TabsModel
) {
self.bookmarksDatabase = bookmarksDatabase
self.bookmarksDatabaseCleaner = bookmarksDatabaseCleaner
Expand All @@ -176,8 +180,18 @@ class MainViewController: UIViewController {
self.bookmarksCachingSearch = BookmarksCachingSearch(bookmarksStore: CoreDataBookmarksSearchStore(bookmarksStore: bookmarksDatabase))
self.appSettings = appSettings

super.init(nibName: nil, bundle: nil)
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()
}

Expand Down Expand Up @@ -230,7 +244,6 @@ class MainViewController: UIViewController {
initTabButton()
initMenuButton()
initBookmarksButton()
configureTabManager()
loadInitialView()
previewsSource.prepare()
addLaunchTabNotificationObserver()
Expand Down Expand Up @@ -683,40 +696,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 }
Expand Down Expand Up @@ -865,6 +844,7 @@ class MainViewController: UIViewController {
selectTab(existing)
return
} else if reuseExisting, let existing = tabManager.firstHomeTab() {
doRefreshAfterClear = false
tabManager.selectTab(existing)
loadUrl(url, fromExternalLink: fromExternalLink)
} else {
Expand Down Expand Up @@ -2040,7 +2020,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
Expand Down Expand Up @@ -2289,7 +2269,7 @@ extension MainViewController: TabSwitcherButtonDelegate {
}

func showTabSwitcher() {
guard currentTab ?? tabManager?.current(createIfNeeded: true) != nil else {
guard currentTab ?? tabManager.current(createIfNeeded: true) != nil else {
fatalError("Unable to get current tab")
}

Expand Down Expand Up @@ -2346,6 +2326,10 @@ extension MainViewController: AutoClearWorker {
}

func refreshUIAfterClear() {
guard doRefreshAfterClear else {
doRefreshAfterClear = true
return
}
showBars()
attachHomeScreen()
tabsBarController?.refresh(tabsModel: tabManager.model)
Expand All @@ -2358,6 +2342,7 @@ extension MainViewController: AutoClearWorker {
refreshUIAfterClear()
}

@MainActor
func forgetData() async {
guard !clearInProgress else {
assertionFailure("Shouldn't get called multiple times")
Expand Down
12 changes: 3 additions & 9 deletions DuckDuckGo/TabManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -45,20 +46,13 @@ 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 {
let controller = buildController(forTab: tab, inheritedAttribution: nil)
tabControllerCache.append(controller)
}

registerForNotifications()
}

Expand Down
33 changes: 19 additions & 14 deletions DuckDuckGoTests/AutoClearTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,31 +49,36 @@ 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

await logic.applicationWillMoveToForeground()
logic.applicationDidEnterBackground()

await logic.clearDataIfEnabledAndTimeExpired()
logic.startClearingTimer()

XCTAssertEqual(worker.clearNavigationStackInvocationCount, 0)
XCTAssertEqual(worker.forgetDataInvocationCount, 0)

await logic.clearDataIfEnabledAndTimeExpired()

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,
Expand All @@ -85,14 +90,14 @@ class AutoClearTests: XCTestCase {
for (timing, delay) in cases {
appSettings.autoClearTiming = timing

logic.applicationDidEnterBackground(Date().timeIntervalSince1970 - delay + 1)
await logic.applicationWillMoveToForeground()
logic.startClearingTimer(Date().timeIntervalSince1970 - delay + 1)
await logic.clearDataIfEnabledAndTimeExpired()

XCTAssertEqual(worker.clearNavigationStackInvocationCount, iterationCount)
XCTAssertEqual(worker.forgetDataInvocationCount, iterationCount)

logic.applicationDidEnterBackground(Date().timeIntervalSince1970 - delay - 1)
await logic.applicationWillMoveToForeground()
logic.startClearingTimer(Date().timeIntervalSince1970 - delay - 1)
await logic.clearDataIfEnabledAndTimeExpired()

iterationCount += 1
XCTAssertEqual(worker.clearNavigationStackInvocationCount, iterationCount)
Expand Down

0 comments on commit 7999941

Please sign in to comment.