Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add NTP Customization Settings onboarding popover #3428

Open
wants to merge 3 commits into
base: dominik/ntp-search-bar
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1083,6 +1083,8 @@
373D9B4829EEAC1B00381FDD /* SyncMetadataDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373D9B4729EEAC1B00381FDD /* SyncMetadataDatabase.swift */; };
373D9B4929EEAC1B00381FDD /* SyncMetadataDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 373D9B4729EEAC1B00381FDD /* SyncMetadataDatabase.swift */; };
373FB4B32B4D6C4B004C88D6 /* PreferencesViews in Frameworks */ = {isa = PBXBuildFile; productRef = 373FB4B22B4D6C4B004C88D6 /* PreferencesViews */; };
374286252CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */; };
374286262CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */; };
37445F992A1566420029F789 /* SyncDataProviders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37445F982A1566420029F789 /* SyncDataProviders.swift */; };
37445F9A2A1566420029F789 /* SyncDataProviders.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37445F982A1566420029F789 /* SyncDataProviders.swift */; };
37445F9C2A1569F00029F789 /* SyncBookmarksAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37445F9B2A1569F00029F789 /* SyncBookmarksAdapter.swift */; };
Expand Down Expand Up @@ -3415,6 +3417,7 @@
373A26962964CF0B0043FC57 /* TestsTargetsBase.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = TestsTargetsBase.xcconfig; sourceTree = "<group>"; };
373B2F802C384DEB0013A94B /* ActiveRemoteMessageModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActiveRemoteMessageModelTests.swift; sourceTree = "<group>"; };
373D9B4729EEAC1B00381FDD /* SyncMetadataDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncMetadataDatabase.swift; sourceTree = "<group>"; };
374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageSettingsVisibilityModelTests.swift; sourceTree = "<group>"; };
37445F982A1566420029F789 /* SyncDataProviders.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncDataProviders.swift; sourceTree = "<group>"; };
37445F9B2A1569F00029F789 /* SyncBookmarksAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncBookmarksAdapter.swift; sourceTree = "<group>"; };
37479F142891BC8300302FE2 /* TabCollectionViewModelTests+WithoutPinnedTabsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TabCollectionViewModelTests+WithoutPinnedTabsManager.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -6503,6 +6506,7 @@
569277C329DEE09D00B633EF /* ContinueSetUpModelTests.swift */,
56D145ED29E6DAD900E3488A /* DataImportProviderTests.swift */,
370270BF2C78EB13002E44E4 /* HomePageSettingsModelTests.swift */,
374286242CC593F900E66323 /* HomePageSettingsVisibilityModelTests.swift */,
37D046A02C7DA9A200AEAA50 /* UserBackgroundImagesManagerTests.swift */,
376731842C7EF97400EB097B /* ColorSchemeLosslessStringConvertibleExtensionTests.swift */,
376731962C7F36AA00EB097B /* UserBackgroundImageTests.swift */,
Expand Down Expand Up @@ -11651,6 +11655,7 @@
3706FE2A293F661700E42796 /* SafariVersionReaderTests.swift in Sources */,
3706FE2B293F661700E42796 /* AtbParserTests.swift in Sources */,
3706FE2C293F661700E42796 /* PermissionStoreMock.swift in Sources */,
374286262CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */,
3706FE2D293F661700E42796 /* ChromiumFaviconsReaderTests.swift in Sources */,
3706FE2E293F661700E42796 /* LocalBookmarkManagerTests.swift in Sources */,
9F180D132B69C665000D695F /* DownloadsTabExtensionMock.swift in Sources */,
Expand Down Expand Up @@ -13298,6 +13303,7 @@
B6AE39F129373AF200C37AA4 /* EmptyAttributionRulesProver.swift in Sources */,
4BB99D1126FE1A84001E4761 /* SafariBookmarksReaderTests.swift in Sources */,
BBC063E82C5A9E4B007BDC18 /* BookmarkManagementDetailViewModelTests.swift in Sources */,
374286252CC5940100E66323 /* HomePageSettingsVisibilityModelTests.swift in Sources */,
1DA860722BE3AE950027B813 /* DockPositionProviderTests.swift in Sources */,
4BBF0925283083EC00EE1418 /* FileSystemDSLTests.swift in Sources */,
4B11060A25903EAC0039B979 /* CoreDataEncryptionTests.swift in Sources */,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "Image.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions DuckDuckGo/Common/Localizables/UserText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1177,6 +1177,8 @@ struct UserText {
static let bookmarksBarPromptAccept = NSLocalizedString("bookmarks.bar.prompt.accept", value: "Show", comment: "Accept button label on bookmarks bar prompt")

// MARK: Home Page Settings
static let homePageSettingsOnboardingTitle = NSLocalizedString("home.page.settings.onboarding.title", value: "Add extra personality to your new tab page", comment: "Home Page Settings Onboarding message title")
static let homePageSettingsOnboardingMessage = NSLocalizedString("home.page.settings.onboarding.message", value: "Customize the background, theme, and even what content you see. Give it a try!", comment: "Home Page Settings Onboarding message")
static let homePageSettingsTitle = NSLocalizedString("home.page.settings.header", value: "Customize", comment: "Home Page Settings title")
static let goToSettings = NSLocalizedString("home.page.settings.go.to.settings", value: "Go to Settings", comment: "Settings button caption")
static let background = NSLocalizedString("home.page.settings.background", value: "Background", comment: "Section title in Home Page Settings to customization Home Page background")
Expand Down
1 change: 1 addition & 0 deletions DuckDuckGo/Common/Utilities/UserDefaultsWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ public struct UserDefaultsWrapper<T> {
case homePageIsRecentActivityVisible = "home.page.is.recent.activity.visible"
case homePageIsSearchBarVisible = "home.page.is.search.bar.visible"
case homePageIsFirstSession = "home.page.is.first.session"
case homePageDidShowSettingsOnboarding = "home.page.did.show.settings.onboarding"
case homePageUserBackgroundImages = "home.page.user.background.images"
case homePageCustomBackground = "home.page.custom.background"
case homePageLastPickedCustomColor = "home.page.last.picked.custom.color"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,37 @@ import PixelKit
import SwiftUI
import SwiftUIExtensions

protocol SettingsVisibilityModelPersistor {
var didShowSettingsOnboarding: Bool { get set }
}

final class UserDefaultsSettingsVisibilityModelPersistor: SettingsVisibilityModelPersistor {
@UserDefaultsWrapper(key: .homePageDidShowSettingsOnboarding, defaultValue: false)
var didShowSettingsOnboarding: Bool
}

extension HomePage.Models {
/**
* This tiny model is used by HomePageViewController to expose a setting to control settings visibility
* This tiny model is used by HomePageViewController to expose a setting to control settings visibility,
* as well as to keep track of the settings onboarding popover.
*/
final class SettingsVisibilityModel: ObservableObject {
@Published var isSettingsVisible: Bool = false

var didShowSettingsOnboarding: Bool {
get {
persistor.didShowSettingsOnboarding
}
set {
persistor.didShowSettingsOnboarding = newValue
}
}

init(persistor: SettingsVisibilityModelPersistor = UserDefaultsSettingsVisibilityModelPersistor()) {
self.persistor = persistor
}

private var persistor: SettingsVisibilityModelPersistor
}

final class SettingsModel: ObservableObject {
Expand Down Expand Up @@ -64,6 +89,7 @@ extension HomePage.Models {
let showAddImageFailedAlert: () -> Void
let navigator: HomePageSettingsModelNavigator

@Published var settingsButtonWidth: CGFloat = .infinity
@Published private(set) var availableUserBackgroundImages: [UserBackgroundImage] = []

private var availableCustomImagesCancellable: AnyCancellable?
Expand Down
32 changes: 18 additions & 14 deletions DuckDuckGo/HomePage/View/HomePageView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ extension HomePage.Views {
static let targetWidth: CGFloat = 508
static let minWindowWidth: CGFloat = 660
static let settingsPanelWidth: CGFloat = 236
static let customizeButtonPadding: CGFloat = 14
let isBurner: Bool

@EnvironmentObject var model: AppearancePreferences
Expand Down Expand Up @@ -114,7 +115,7 @@ extension HomePage.Views {
Spacer()
HStack {
Spacer(minLength: Self.targetWidth + (geometry.size.width - Self.targetWidth)/2)
SettingsButtonView(isSettingsVisible: $settingsVisibilityModel.isSettingsVisible)
SettingsButtonView()
.padding([.bottom, .trailing], 14)
}
}
Expand Down Expand Up @@ -236,25 +237,28 @@ extension HomePage.Views {
}

struct SettingsButtonView: View {
let defaultColor: Color = .homeFavoritesBackground
let onHoverColor: Color = .buttonMouseOver
let onSelectedColor: Color = .buttonMouseDown
let iconSize = 16.02
let targetSize = 28.0
let buttonWidthWithoutTitle = 52.0
static let defaultColor: Color = .homeFavoritesBackground
static let onHoverColor: Color = .buttonMouseOver
static let iconSize = 16.0
static let height = 28.0
static let buttonWidthWithoutTitle = 46.0

@State var isHovering: Bool = false
@Binding var isSettingsVisible: Bool
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has been removed in favor of using settingsVisibilityModel.


@State private var textWidth: CGFloat = .infinity
@State private var textWidth: CGFloat = .infinity {
didSet {
settingsModel.settingsButtonWidth = textWidth + Self.buttonWidthWithoutTitle
}
}
@EnvironmentObject var settingsModel: HomePage.Models.SettingsModel
@EnvironmentObject var settingsVisibilityModel: HomePage.Models.SettingsVisibilityModel

private var buttonBackgroundColor: Color {
isHovering ? onHoverColor : defaultColor
isHovering ? Self.onHoverColor : Self.defaultColor
}

private func isCompact(with geometry: GeometryProxy) -> Bool {
geometry.size.width < textWidth + buttonWidthWithoutTitle
geometry.size.width < settingsModel.settingsButtonWidth
}

var body: some View {
Expand All @@ -276,21 +280,21 @@ extension HomePage.Views {
HStack(spacing: 6) {
Image(.optionsMainView)
.resizable()
.frame(width: iconSize, height: iconSize)
.frame(width: Self.iconSize, height: Self.iconSize)
.scaledToFit()
if !isCompact(with: geometry) {
Text(UserText.homePageSettingsTitle)
.font(.system(size: 13))
.background(WidthGetter())
}
}
.frame(height: targetSize)
.frame(height: Self.height)
.padding(.horizontal, isCompact(with: geometry) ? 6 : 12)
}
.fixedSize()
.link(onHoverChanged: nil) {
withAnimation {
isSettingsVisible.toggle()
settingsVisibilityModel.isSettingsVisible.toggle()
}
}
.onHover { isHovering in
Expand Down
42 changes: 42 additions & 0 deletions DuckDuckGo/HomePage/View/HomePageViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,16 @@ final class HomePageViewController: NSViewController {
super.viewDidAppear()
refreshModels()
addressBarModel.addressBarTextField?.makeMeFirstResponder()

showSettingsOnboardingIfNeeded()
}

override func viewWillDisappear() {
super.viewWillDisappear()

historyCancellable = nil

presentedViewControllers?.forEach { $0.dismiss() }
}

func refreshModelsOnAppBecomingActive() {
Expand Down Expand Up @@ -271,6 +275,44 @@ final class HomePageViewController: NSViewController {
.show(in: view.window)
}

private func showSettingsOnboardingIfNeeded() {
if !settingsVisibilityModel.didShowSettingsOnboarding {
DispatchQueue.main.async {
let bounds = self.view.bounds
let settingsButtonWidth = Application.appDelegate.homePageSettingsModel.settingsButtonWidth

let rect = NSRect(
x: bounds.maxX - HomePage.Views.RootView.customizeButtonPadding - settingsButtonWidth,
y: bounds.maxY - HomePage.Views.RootView.customizeButtonPadding - HomePage.Views.RootView.SettingsButtonView.height,
width: settingsButtonWidth,
height: HomePage.Views.RootView.SettingsButtonView.height)

let viewController = PopoverMessageViewController(
title: UserText.homePageSettingsOnboardingTitle,
message: UserText.homePageSettingsOnboardingMessage,
image: .settingsOnboardingPopover,
shouldShowCloseButton: true,
presentMultiline: true,
autoDismissDuration: nil,
onClick: { [weak self] in
self?.settingsVisibilityModel.isSettingsVisible = true
}
)
viewController.show(onParent: self, rect: rect, of: self.view, preferredEdge: .minY)
self.settingsVisibilityModel.didShowSettingsOnboarding = true

// Hide the popover as soon as settings is shown ('Customize' button is clicked).
self.settingsVisibilityModel.$isSettingsVisible
.filter { $0 }
.prefix(1)
.sink { [weak viewController] _ in
viewController?.dismiss()
}
.store(in: &self.cancellables)
}
}
}

private var burningDataCancellable: AnyCancellable?
private func subscribeToBurningData() {
burningDataCancellable = fireViewModel.fire.$burningData
Expand Down
24 changes: 24 additions & 0 deletions DuckDuckGo/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -27292,6 +27292,30 @@
}
}
},
"home.page.settings.onboarding.message" : {
"comment" : "Home Page Settings Onboarding message",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Customize the background, theme, and even what content you see. Give it a try!"
}
}
}
},
"home.page.settings.onboarding.title" : {
"comment" : "Home Page Settings Onboarding message title",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Add extra personality to your new tab page"
}
}
}
},
"home.page.settings.sections" : {
"comment" : "Section title in Home Page Settings to adjust Home Page sections visibility",
"extractionState" : "extracted_with_value",
Expand Down
1 change: 1 addition & 0 deletions DuckDuckGo/Menus/MainMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,7 @@ final class MainMenu: NSMenu {
NSMenuItem(title: "Reset CPM Experiment Cohort", action: #selector(AppDelegate.resetCpmCohort))
NSMenuItem(title: "Reset Duck Player Preferences", action: #selector(MainViewController.resetDuckPlayerPreferences))
NSMenuItem(title: "Reset Onboarding", action: #selector(MainViewController.resetOnboarding(_:)))
NSMenuItem(title: "Reset Home Page Settings Onboarding", action: #selector(MainViewController.resetHomePageSettingsOnboarding(_:)))
NSMenuItem(title: "Reset Contextual Onboarding", action: #selector(MainViewController.resetContextualOnboarding(_:)))
NSMenuItem(title: "Reset Sync Promo prompts", action: #selector(MainViewController.resetSyncPromoPrompts))

Expand Down
4 changes: 4 additions & 0 deletions DuckDuckGo/Menus/MainMenuActions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,10 @@ extension MainViewController {
UserDefaults.standard.set(false, forKey: UserDefaultsWrapper<Bool>.Key.onboardingFinished.rawValue)
}

@objc func resetHomePageSettingsOnboarding(_ sender: Any?) {
UserDefaults.standard.set(false, forKey: UserDefaultsWrapper<Any>.Key.homePageDidShowSettingsOnboarding.rawValue)
}

@objc func resetContextualOnboarding(_ sender: Any?) {
Application.appDelegate.onboardingStateMachine.state = .notStarted
}
Expand Down
Loading
Loading