Skip to content

Commit

Permalink
Extract NTP-related persistence from AppUserDefaults
Browse files Browse the repository at this point in the history
  • Loading branch information
dus7 committed Sep 10, 2024
1 parent 8bd95f4 commit 5a129fb
Show file tree
Hide file tree
Showing 17 changed files with 148 additions and 84 deletions.
12 changes: 12 additions & 0 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@
6FD8E5202C5BA23200345670 /* NewTabPageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FD8E51F2C5BA23200345670 /* NewTabPageViewModel.swift */; };
6FD8E5222C5BA5C400345670 /* NewTabPageIntroMessageSetup.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FD8E5212C5BA5C400345670 /* NewTabPageIntroMessageSetup.swift */; };
6FDA1FB32B59584400AC962A /* AddressDisplayHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FDA1FB22B59584400AC962A /* AddressDisplayHelper.swift */; };
6FDC63FF2C906DD300DB71B3 /* NewTabPageStorage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FDC63FE2C906DD300DB71B3 /* NewTabPageStorage.swift */; };
6FE018402C25CB3F001F680D /* FavoritesSectionHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FE0183F2C25CB3F001F680D /* FavoritesSectionHeader.swift */; };
6FE095D82BD90AFB00490FF8 /* UniversalOmniBarState.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FE095D72BD90AFB00490FF8 /* UniversalOmniBarState.swift */; };
6FE127382C20492500EB5724 /* NewTabPage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FE127372C20492500EB5724 /* NewTabPage.swift */; };
Expand Down Expand Up @@ -1588,6 +1589,7 @@
6FD8E51F2C5BA23200345670 /* NewTabPageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageViewModel.swift; sourceTree = "<group>"; };
6FD8E5212C5BA5C400345670 /* NewTabPageIntroMessageSetup.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageIntroMessageSetup.swift; sourceTree = "<group>"; };
6FDA1FB22B59584400AC962A /* AddressDisplayHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddressDisplayHelper.swift; sourceTree = "<group>"; };
6FDC63FE2C906DD300DB71B3 /* NewTabPageStorage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageStorage.swift; sourceTree = "<group>"; };
6FE0183F2C25CB3F001F680D /* FavoritesSectionHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoritesSectionHeader.swift; sourceTree = "<group>"; };
6FE095D72BD90AFB00490FF8 /* UniversalOmniBarState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniversalOmniBarState.swift; sourceTree = "<group>"; };
6FE127372C20492500EB5724 /* NewTabPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPage.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3868,9 +3870,18 @@
name = IntroMessage;
sourceTree = "<group>";
};
6FDC63FD2C906DBB00DB71B3 /* Storage */ = {
isa = PBXGroup;
children = (
6FDC63FE2C906DD300DB71B3 /* NewTabPageStorage.swift */,
);
name = Storage;
sourceTree = "<group>";
};
6FE127362C20436A00EB5724 /* HomeRedesign */ = {
isa = PBXGroup;
children = (
6FDC63FD2C906DBB00DB71B3 /* Storage */,
6FE1273B2C204C0D00EB5724 /* Subviews */,
6F03CAF82C32C3AA004179A8 /* Messages */,
6FE127372C20492500EB5724 /* NewTabPage.swift */,
Expand Down Expand Up @@ -7348,6 +7359,7 @@
85F2FFCF2211F8E5006BB258 /* TabSwitcherViewController+KeyCommands.swift in Sources */,
3157B43327F497E90042D3D7 /* SaveLoginView.swift in Sources */,
F17922E01E71BB59006E3D97 /* AutocompleteViewControllerDelegate.swift in Sources */,
6FDC63FF2C906DD300DB71B3 /* NewTabPageStorage.swift in Sources */,
BDE91CDC2C62AA3A0005CB74 /* DefaultMetadataCollector.swift in Sources */,
D664C7C82B289AA200CBFA76 /* SubscriptionFlowView.swift in Sources */,
EE458D142ABB652900FC651A /* NetworkProtectionDebugUtilities.swift in Sources */,
Expand Down
6 changes: 0 additions & 6 deletions DuckDuckGo/AppSettings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,14 +82,8 @@ protocol AppSettings: AnyObject, AppDebugSettings {

var duckPlayerMode: DuckPlayerMode { get set }
var duckPlayerAskModeOverlayHidden: Bool { get set }

var newTabPageShortcutsSettings: Data? { get set }
var newTabPageSectionsSettings: Data? { get set }
var newTabPageIntroMessageEnabled: Bool? { get set }
var newTabPageIntroMessageSeenCount: Int { get set }
}

protocol AppDebugSettings {
var newTabPageSectionsEnabled: Bool { get set }
var onboardingHighlightsEnabled: Bool { get set }
}
15 changes: 0 additions & 15 deletions DuckDuckGo/AppUserDefaults.swift
Original file line number Diff line number Diff line change
Expand Up @@ -383,9 +383,6 @@ public class AppUserDefaults: AppSettings {
userDefaults?.setValue(newValue.rawValue, forKey: Keys.crashCollectionOptInStatus)
}
}

@UserDefaultsWrapper(key: .debugNewTabPageSectionsEnabledKey, defaultValue: false)
var newTabPageSectionsEnabled: Bool

var duckPlayerMode: DuckPlayerMode {
get {
Expand Down Expand Up @@ -418,18 +415,6 @@ public class AppUserDefaults: AppSettings {
}
}

@UserDefaultsWrapper(key: .newTabPageShortcutsSettings, defaultValue: nil)
var newTabPageShortcutsSettings: Data?

@UserDefaultsWrapper(key: .newTabPageSectionsSettings, defaultValue: nil)
var newTabPageSectionsSettings: Data?

@UserDefaultsWrapper(key: .newTabPageIntroMessageEnabled, defaultValue: nil)
var newTabPageIntroMessageEnabled: Bool?

@UserDefaultsWrapper(key: .newTabPageIntroMessageSeenCount, defaultValue: 0)
var newTabPageIntroMessageSeenCount: Int

@UserDefaultsWrapper(key: .debugOnboardingHighlightsEnabledKey, defaultValue: false)
var onboardingHighlightsEnabled: Bool
}
Expand Down
10 changes: 5 additions & 5 deletions DuckDuckGo/NewTabPageIntroMessageSetup.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,24 +21,24 @@ import BrowserServicesKit
import Core

struct NewTabPageIntroMessageSetup {
let appSettings: AppSettings
let storage: NewTabPageIntroDataStoring
let statistics: StatisticsStore
let newTabPageManager: NewTabPageManaging

init(appSettings: AppSettings = AppDependencyProvider.shared.appSettings,
init(storage: NewTabPageIntroDataStoring = NewTabPageIntroDataUserDefaultsStorage(),
statistics: StatisticsStore = StatisticsUserDefaults(),
newTabPageManager: NewTabPageManaging = NewTabPageManager()) {
self.appSettings = appSettings
self.storage = storage
self.statistics = statistics
self.newTabPageManager = newTabPageManager
}

func perform() {

let isNotSetUp = appSettings.newTabPageIntroMessageEnabled == nil
let isNotSetUp = storage.newTabPageIntroMessageEnabled == nil
guard newTabPageManager.isAvailableInPublicRelease && isNotSetUp else { return }

// For new users we **don't** want intro message
appSettings.newTabPageIntroMessageEnabled = statistics.installDate != nil
storage.newTabPageIntroMessageEnabled = statistics.installDate != nil
}
}
19 changes: 14 additions & 5 deletions DuckDuckGo/NewTabPageManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,24 @@ protocol NewTabPageDebugging: NewTabPageManaging {
var isFeatureFlagEnabled: Bool { get }
}

protocol NewTabPageLocalFlagStoring: AnyObject {
var newTabPageSectionsEnabled: Bool { get set }
}

final class NewTabPageLocalFlagUserDefaultsStorage: NewTabPageLocalFlagStoring {
@UserDefaultsWrapper(key: .debugNewTabPageSectionsEnabledKey, defaultValue: false)
var newTabPageSectionsEnabled: Bool
}

final class NewTabPageManager: NewTabPageManaging, NewTabPageDebugging {

var appDefaults: AppDebugSettings
let localFlagStorage: NewTabPageLocalFlagStoring
let featureFlagger: FeatureFlagger

init(appDefaults: AppDebugSettings = AppDependencyProvider.shared.appSettings,
init(localFlagStorage: NewTabPageLocalFlagStoring = NewTabPageLocalFlagUserDefaultsStorage(),
featureFlager: FeatureFlagger = AppDependencyProvider.shared.featureFlagger) {

self.appDefaults = appDefaults
self.localFlagStorage = localFlagStorage
self.featureFlagger = featureFlager
}

Expand All @@ -64,10 +73,10 @@ final class NewTabPageManager: NewTabPageManaging, NewTabPageDebugging {

var isLocalFlagEnabled: Bool {
get {
appDefaults.newTabPageSectionsEnabled
localFlagStorage.newTabPageSectionsEnabled
}
set {
appDefaults.newTabPageSectionsEnabled = newValue
localFlagStorage.newTabPageSectionsEnabled = newValue
}
}

Expand Down
22 changes: 11 additions & 11 deletions DuckDuckGo/NewTabPageSectionsDebugView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import SwiftUI
struct NewTabPageSectionsDebugView: View {

private var newTabPageDebugging: NewTabPageDebugging
private var appSettings: AppSettings
private let introDataStorage: NewTabPageIntroDataStoring

@State private var isFeatureEnabled: Bool
@State private var introMessageCount: Int
Expand All @@ -34,24 +34,24 @@ struct NewTabPageSectionsDebugView: View {
} set: {
newTabPageDebugging.isLocalFlagEnabled = $0
isFeatureEnabled = newTabPageDebugging.isNewTabPageSectionsEnabled
isIntroMessageInitialized = appSettings.newTabPageIntroMessageEnabled != nil
isIntroMessageInitialized = introDataStorage.newTabPageIntroMessageEnabled != nil
}
}

private var introMessageEnabled: Binding<Bool> {
Binding {
appSettings.newTabPageIntroMessageEnabled ?? false
introDataStorage.newTabPageIntroMessageEnabled ?? false
} set: {
appSettings.newTabPageIntroMessageEnabled = $0
isIntroMessageInitialized = appSettings.newTabPageIntroMessageEnabled != nil
introDataStorage.newTabPageIntroMessageEnabled = $0
isIntroMessageInitialized = introDataStorage.newTabPageIntroMessageEnabled != nil
}
}

private var introMessageCountBinding: Binding<Int> {
Binding {
appSettings.newTabPageIntroMessageSeenCount
introDataStorage.newTabPageIntroMessageSeenCount
} set: {
appSettings.newTabPageIntroMessageSeenCount = $0
introDataStorage.newTabPageIntroMessageSeenCount = $0
introMessageCount = $0
}
}
Expand All @@ -61,9 +61,9 @@ struct NewTabPageSectionsDebugView: View {
newTabPageDebugging = manager
isFeatureEnabled = manager.isNewTabPageSectionsEnabled

appSettings = AppDependencyProvider.shared.appSettings
introMessageCount = appSettings.newTabPageIntroMessageSeenCount
isIntroMessageInitialized = appSettings.newTabPageIntroMessageEnabled != nil
introDataStorage = NewTabPageIntroDataUserDefaultsStorage()
introMessageCount = introDataStorage.newTabPageIntroMessageSeenCount
isIntroMessageInitialized = introDataStorage.newTabPageIntroMessageEnabled != nil
}

var body: some View {
Expand Down Expand Up @@ -134,7 +134,7 @@ struct NewTabPageSectionsDebugView: View {
})

Button("Reset intro message", action: {
appSettings.newTabPageIntroMessageEnabled = nil
introDataStorage.newTabPageIntroMessageEnabled = nil
introMessageCountBinding.wrappedValue = 0
isIntroMessageInitialized = false
})
Expand Down
3 changes: 2 additions & 1 deletion DuckDuckGo/NewTabPageSectionsSettingsStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ typealias NewTabPageSectionsSettingsStorage = NewTabPageSettingsPersistentStorag

extension NewTabPageSettingsPersistentStorage<NewTabPageSection> {
convenience init() {
self.init(keyPath: \.newTabPageSectionsSettings,
self.init(persistence: NewTabPageSettingsDataUserDefaultsStorage(),
keyPath: \.newTabPageSectionsSettings,
defaultOrder: NewTabPageSection.allCases,
defaultEnabledItems: NewTabPageSection.allCases)
}
Expand Down
14 changes: 7 additions & 7 deletions DuckDuckGo/NewTabPageSettingsPersistentStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,14 @@ final class NewTabPageSettingsPersistentStorage<Item: NewTabPageSettingsStorageI
private(set) var itemsOrder: [Item]
private var enabledItems: Set<Item>

private var appSettings: AppSettings
private let keyPath: WritableKeyPath<AppSettings, Data?>
private var persistence: NewTabPageSettingsDataStoring
private let keyPath: WritableKeyPath<NewTabPageSettingsDataStoring, Data?>

init(appSettings: AppSettings = AppDependencyProvider.shared.appSettings,
keyPath: WritableKeyPath<AppSettings, Data?>,
init(persistence: NewTabPageSettingsDataStoring,
keyPath: WritableKeyPath<NewTabPageSettingsDataStoring, Data?>,
defaultOrder: [Item],
defaultEnabledItems: [Item]) {
self.appSettings = appSettings
self.persistence = persistence
self.keyPath = keyPath
self.itemsOrder = defaultOrder
self.enabledItems = Set(defaultEnabledItems)
Expand All @@ -62,12 +62,12 @@ final class NewTabPageSettingsPersistentStorage<Item: NewTabPageSettingsStorageI
func save() {
let newSettings = NewTabPageItemSettings(itemsOrder: itemsOrder, enabledItems: enabledItems)
if let data = try? JSONEncoder().encode(newSettings) {
appSettings[keyPath: keyPath] = data
persistence[keyPath: keyPath] = data
}
}

private func load() {
if let settingsData = appSettings[keyPath: keyPath],
if let settingsData = persistence[keyPath: keyPath],
let settings = try? JSONDecoder().decode(NewTabPageItemSettings<Item>.self, from: settingsData) {
itemsOrder = settings.itemsOrder
enabledItems = settings.enabledItems
Expand Down
3 changes: 2 additions & 1 deletion DuckDuckGo/NewTabPageShortcutsSettingsStorage.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ typealias NewTabPageShortcutsSettingsStorage = NewTabPageSettingsPersistentStora

extension NewTabPageSettingsPersistentStorage<NewTabPageShortcut> {
convenience init() {
self.init(keyPath: \.newTabPageShortcutsSettings,
self.init(persistence: NewTabPageSettingsDataUserDefaultsStorage(),
keyPath: \.newTabPageShortcutsSettings,
defaultOrder: NewTabPageShortcut.allCases,
defaultEnabledItems: NewTabPageShortcut.enabledByDefault)
}
Expand Down
47 changes: 47 additions & 0 deletions DuckDuckGo/NewTabPageStorage.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// NewTabPageStorage.swift
// DuckDuckGo
//
// Copyright © 2024 DuckDuckGo. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation
import Core

protocol NewTabPageIntroDataStoring: AnyObject {
var newTabPageIntroMessageEnabled: Bool? { get set }
var newTabPageIntroMessageSeenCount: Int { get set }
}

protocol NewTabPageSettingsDataStoring: AnyObject {
var newTabPageShortcutsSettings: Data? { get set }
var newTabPageSectionsSettings: Data? { get set }
}

final class NewTabPageIntroDataUserDefaultsStorage: NewTabPageIntroDataStoring {
@UserDefaultsWrapper(key: .newTabPageIntroMessageEnabled, defaultValue: nil)
var newTabPageIntroMessageEnabled: Bool?

@UserDefaultsWrapper(key: .newTabPageIntroMessageSeenCount, defaultValue: 0)
var newTabPageIntroMessageSeenCount: Int
}

final class NewTabPageSettingsDataUserDefaultsStorage: NewTabPageSettingsDataStoring {
@UserDefaultsWrapper(key: .newTabPageShortcutsSettings, defaultValue: nil)
var newTabPageShortcutsSettings: Data?

@UserDefaultsWrapper(key: .newTabPageSectionsSettings, defaultValue: nil)
var newTabPageSectionsSettings: Data?
}
7 changes: 6 additions & 1 deletion DuckDuckGo/NewTabPageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,12 @@ private struct CustomizeButtonPrefKey: PreferenceKey {
addFavoriteViewModel: .init(favoritesCreating: NullMenuBookmarksInteracting()),
shortcutsModel: ShortcutsModel(),
shortcutsSettingsModel: NewTabPageShortcutsSettingsModel(),
sectionsSettingsModel: NewTabPageSectionsSettingsModel(storage: .init(keyPath: \.newTabPageSectionsSettings, defaultOrder: NewTabPageSection.allCases, defaultEnabledItems: []))
sectionsSettingsModel: NewTabPageSectionsSettingsModel(
storage: .init(persistence: NewTabPageSettingsDataUserDefaultsStorage(),
keyPath: \.newTabPageSectionsSettings,
defaultOrder: NewTabPageSection.allCases,
defaultEnabledItems: [])
)
)
}

Expand Down
16 changes: 8 additions & 8 deletions DuckDuckGo/NewTabPageViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,32 +26,32 @@ final class NewTabPageViewModel: ObservableObject {
@Published private(set) var isOnboarding: Bool
@Published var isShowingSettings: Bool

private let appSettings: AppSettings
private var introDataStorage: NewTabPageIntroDataStoring
private let pixelFiring: PixelFiring.Type

init(appSettings: AppSettings = AppDependencyProvider.shared.appSettings,
init(introDataStorage: NewTabPageIntroDataStoring = NewTabPageIntroDataUserDefaultsStorage(),
pixelFiring: PixelFiring.Type = Pixel.self) {
self.appSettings = appSettings
self.introDataStorage = introDataStorage
self.pixelFiring = pixelFiring

isIntroMessageVisible = appSettings.newTabPageIntroMessageEnabled ?? false
isIntroMessageVisible = introDataStorage.newTabPageIntroMessageEnabled ?? false
isOnboarding = false
isShowingSettings = false
}

func introMessageDisplayed() {
pixelFiring.fire(.newTabPageMessageDisplayed, withAdditionalParameters: [:])

appSettings.newTabPageIntroMessageSeenCount += 1
if appSettings.newTabPageIntroMessageSeenCount >= 3 {
appSettings.newTabPageIntroMessageEnabled = false
introDataStorage.newTabPageIntroMessageSeenCount += 1
if introDataStorage.newTabPageIntroMessageSeenCount >= 3 {
introDataStorage.newTabPageIntroMessageEnabled = false
}
}

func dismissIntroMessage() {
pixelFiring.fire(.newTabPageMessageDismissed, withAdditionalParameters: [:])

appSettings.newTabPageIntroMessageEnabled = false
introDataStorage.newTabPageIntroMessageEnabled = false
isIntroMessageVisible = false
}

Expand Down
Loading

0 comments on commit 5a129fb

Please sign in to comment.