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

Display Next Steps cards on HTML New Tab Page #3621

Merged
merged 13 commits into from
Dec 5, 2024
Merged
26 changes: 26 additions & 0 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1107,6 +1107,10 @@
372217822B33380700B8E9C2 /* TestUtils in Frameworks */ = {isa = PBXBuildFile; productRef = 372217812B33380700B8E9C2 /* TestUtils */; };
3723A94F2CE7400D00A0C59A /* NewTabPageRMFClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3723A94E2CE73FDD00A0C59A /* NewTabPageRMFClient.swift */; };
3723A9502CE7400D00A0C59A /* NewTabPageRMFClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3723A94E2CE73FDD00A0C59A /* NewTabPageRMFClient.swift */; };
3724ED8B2CFE6A290043626A /* NewTabPageNextStepsCardsProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3724ED8A2CFE6A120043626A /* NewTabPageNextStepsCardsProviding.swift */; };
3724ED8C2CFE6A290043626A /* NewTabPageNextStepsCardsProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3724ED8A2CFE6A120043626A /* NewTabPageNextStepsCardsProviding.swift */; };
3724ED8E2CFE6B3F0043626A /* NewTabPageNextStepsCardsClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3724ED8D2CFE6B330043626A /* NewTabPageNextStepsCardsClientTests.swift */; };
3724ED8F2CFE6B3F0043626A /* NewTabPageNextStepsCardsClientTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3724ED8D2CFE6B330043626A /* NewTabPageNextStepsCardsClientTests.swift */; };
37269EFB2B332F9E005E8E46 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 37269EFA2B332F9E005E8E46 /* Common */; };
37269EFD2B332FAC005E8E46 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 37269EFC2B332FAC005E8E46 /* Common */; };
37269EFF2B332FBB005E8E46 /* Common in Frameworks */ = {isa = PBXBuildFile; productRef = 37269EFE2B332FBB005E8E46 /* Common */; };
Expand Down Expand Up @@ -1248,6 +1252,8 @@
37BF3F22286F0A7A00BD9014 /* PinnedTabsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BF3F1F286F0A7A00BD9014 /* PinnedTabsView.swift */; };
37C9F78C2CF1C776004D73A1 /* PrivacyStatsTabExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C9F78B2CF1C770004D73A1 /* PrivacyStatsTabExtension.swift */; };
37C9F78D2CF1C776004D73A1 /* PrivacyStatsTabExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C9F78B2CF1C770004D73A1 /* PrivacyStatsTabExtension.swift */; };
37CB368B2CFA5AF100E5E5FA /* NewTabPageNextStepsCardsClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CB368A2CFA5AEC00E5E5FA /* NewTabPageNextStepsCardsClient.swift */; };
37CB368C2CFA5AF100E5E5FA /* NewTabPageNextStepsCardsClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CB368A2CFA5AEC00E5E5FA /* NewTabPageNextStepsCardsClient.swift */; };
37CBCA9A2A8966E60050218F /* SyncSettingsAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CBCA992A8966E60050218F /* SyncSettingsAdapter.swift */; };
37CBCA9B2A8966E60050218F /* SyncSettingsAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CBCA992A8966E60050218F /* SyncSettingsAdapter.swift */; };
37CC53EC27E8A4D10028713D /* PreferencesDataClearingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CC53EB27E8A4D10028713D /* PreferencesDataClearingView.swift */; };
Expand Down Expand Up @@ -3664,6 +3670,8 @@
37219B392CBFD4F300C9D7A8 /* NewTabPageSearchBoxExperiment+Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NewTabPageSearchBoxExperiment+Logger.swift"; sourceTree = "<group>"; };
37219B3C2CC27DB300C9D7A8 /* NewTabPageSearchBoxExperimentTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageSearchBoxExperimentTests.swift; sourceTree = "<group>"; };
3723A94E2CE73FDD00A0C59A /* NewTabPageRMFClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageRMFClient.swift; sourceTree = "<group>"; };
3724ED8A2CFE6A120043626A /* NewTabPageNextStepsCardsProviding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageNextStepsCardsProviding.swift; sourceTree = "<group>"; };
3724ED8D2CFE6B330043626A /* NewTabPageNextStepsCardsClientTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageNextStepsCardsClientTests.swift; sourceTree = "<group>"; };
372A0FEB2B2379310033BF7F /* SyncMetricsEventsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncMetricsEventsHandler.swift; sourceTree = "<group>"; };
372BC2A02A4AFA47001D8FD5 /* SyncCredentialsAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncCredentialsAdapter.swift; sourceTree = "<group>"; };
372ED7C12CDD4815002287EC /* NewTabPageUserContentController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageUserContentController.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -3760,6 +3768,7 @@
37BF3F1E286F0A7A00BD9014 /* PinnedTabsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinnedTabsViewModel.swift; sourceTree = "<group>"; };
37BF3F1F286F0A7A00BD9014 /* PinnedTabsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinnedTabsView.swift; sourceTree = "<group>"; };
37C9F78B2CF1C770004D73A1 /* PrivacyStatsTabExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyStatsTabExtension.swift; sourceTree = "<group>"; };
37CB368A2CFA5AEC00E5E5FA /* NewTabPageNextStepsCardsClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewTabPageNextStepsCardsClient.swift; sourceTree = "<group>"; };
37CBCA992A8966E60050218F /* SyncSettingsAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncSettingsAdapter.swift; sourceTree = "<group>"; };
37CC53EB27E8A4D10028713D /* PreferencesDataClearingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesDataClearingView.swift; sourceTree = "<group>"; };
37CC53F327E8D4620028713D /* NSPathControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSPathControlView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -5855,6 +5864,7 @@
children = (
37F8E2322CEE363D002F0141 /* NewTabPageContextMenuPresenting.swift */,
3723A94E2CE73FDD00A0C59A /* NewTabPageRMFClient.swift */,
37CB36892CFA5ADA00E5E5FA /* NextStepsCards */,
37F1E32D2CEF2DDD00130142 /* Favorites */,
37DF370B2CF3CEF5005ED34B /* PrivacyStats */,
379B5AEB2CEA26C600B9F5D7 /* NewTabPageConfigurationClient.swift */,
Expand All @@ -5873,6 +5883,7 @@
3767880E2CECD5A200F59D83 /* NewTabPageUserScriptTests.swift */,
3767880B2CECCB6C00F59D83 /* NewTabPageActionsManagerTests.swift */,
376788112CECF03000F59D83 /* NewTabPageRMFClientTests.swift */,
3724ED8D2CFE6B330043626A /* NewTabPageNextStepsCardsClientTests.swift */,
37F1E32E2CEF4A2C00130142 /* NewTabPageFavoritesClientTests.swift */,
378D62562CEF801A0056BBD8 /* NewTabPageFavoritesModelTests.swift */,
37FC2A1A2CF903A70048E226 /* NewTabPagePrivacyStatsClientTests.swift */,
Expand Down Expand Up @@ -6037,6 +6048,15 @@
path = PrivacyStats;
sourceTree = "<group>";
};
37CB36892CFA5ADA00E5E5FA /* NextStepsCards */ = {
isa = PBXGroup;
children = (
3724ED8A2CFE6A120043626A /* NewTabPageNextStepsCardsProviding.swift */,
37CB368A2CFA5AEC00E5E5FA /* NewTabPageNextStepsCardsClient.swift */,
);
path = NextStepsCards;
sourceTree = "<group>";
};
37CD54C027F2FDD100F1F7B9 /* Model */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -11698,6 +11718,7 @@
3706FB80293F65D500E42796 /* NSAlert+ActiveDownloadsTermination.swift in Sources */,
B677FC552B064A9C0099EB04 /* DataImportViewModel.swift in Sources */,
D64A5FF92AEA5C2B00B6D6E7 /* HomeButtonMenuFactory.swift in Sources */,
3724ED8B2CFE6A290043626A /* NewTabPageNextStepsCardsProviding.swift in Sources */,
4BE3A6C22C16BEB1003FC378 /* VPNRedditSessionWorkaround.swift in Sources */,
3707C717294B5D0F00682A9F /* FindInPageTabExtension.swift in Sources */,
3706FB81293F65D500E42796 /* IndexPathExtension.swift in Sources */,
Expand Down Expand Up @@ -12158,6 +12179,7 @@
3706FC77293F65D500E42796 /* PageObserverUserScript.swift in Sources */,
4BF0E5132AD25A2600FFEC9E /* DuckDuckGoUserAgent.swift in Sources */,
3706FC78293F65D500E42796 /* SecureVaultReporter.swift in Sources */,
37CB368C2CFA5AF100E5E5FA /* NewTabPageNextStepsCardsClient.swift in Sources */,
3706FC79293F65D500E42796 /* NSImageExtensions.swift in Sources */,
3706FEBD293F6EFF00E42796 /* BWCommand.swift in Sources */,
C172E7302C9329D300521D9A /* FlippedView.swift in Sources */,
Expand Down Expand Up @@ -12585,6 +12607,7 @@
CD33012A2C887B1C009AA127 /* URLTokenValidatorTests.swift in Sources */,
9F0660742BECC71200B8EEF1 /* SubscriptionAttributionPixelHandlerTests.swift in Sources */,
9FBD847B2BB3EC3300220859 /* MockAttributionOriginProvider.swift in Sources */,
3724ED8F2CFE6B3F0043626A /* NewTabPageNextStepsCardsClientTests.swift in Sources */,
56A214B02CB583BF00E5BC0E /* TrackerMessageProviderTests.swift in Sources */,
3706FE82293F661700E42796 /* MockStatisticsStore.swift in Sources */,
9FBD84712BB3DD8400220859 /* MockAttributionsPixelHandler.swift in Sources */,
Expand Down Expand Up @@ -13072,6 +13095,7 @@
B62B483E2ADE48DE000DECE5 /* ArrayBuilder.swift in Sources */,
4B92929B26670D2A00AD2C21 /* BookmarkOutlineViewDataSource.swift in Sources */,
56D145EB29E6C99B00E3488A /* DataImportStatusProviding.swift in Sources */,
37CB368B2CFA5AF100E5E5FA /* NewTabPageNextStepsCardsClient.swift in Sources */,
843965122C6F2FFE004C8899 /* NSDragOperationExtension.swift in Sources */,
31D5375C291D944100407A95 /* PasswordManagementBitwardenItemView.swift in Sources */,
1D9297BF2C1B062900A38521 /* ApplicationUpdateDetector.swift in Sources */,
Expand All @@ -13089,6 +13113,7 @@
B6E3E5542BBFCEE300A41922 /* NoDownloadsCellView.swift in Sources */,
3768D8382C24BFF5004120AE /* RemoteMessageView.swift in Sources */,
4BBDEE9428FC14760092FAA6 /* ConnectBitwardenViewController.swift in Sources */,
3724ED8C2CFE6A290043626A /* NewTabPageNextStepsCardsProviding.swift in Sources */,
1DDF076428F815AD00EDFBE3 /* BWManager.swift in Sources */,
9833912F27AAA3CE00DAF119 /* AppTrackerDataSetProvider.swift in Sources */,
B65211252B29A42C00B30633 /* BookmarkStoreMock.swift in Sources */,
Expand Down Expand Up @@ -14105,6 +14130,7 @@
B6106BAF26A7C6180013B453 /* PermissionStoreMock.swift in Sources */,
4B98D27A28D95F1A003C2B6F /* ChromiumFaviconsReaderTests.swift in Sources */,
9F0660732BECC71200B8EEF1 /* SubscriptionAttributionPixelHandlerTests.swift in Sources */,
3724ED8E2CFE6B3F0043626A /* NewTabPageNextStepsCardsClientTests.swift in Sources */,
BDCB66D82C7CE1A600E8ABC9 /* VPNFeedbackFormViewModelTests.swift in Sources */,
567A23E12C89B1EE0010F66C /* BrowserTabViewControllerOnboardingTests.swift in Sources */,
986189E62A7CFB3E001B4519 /* LocalBookmarkStoreSavingTests.swift in Sources */,
Expand Down
54 changes: 44 additions & 10 deletions DuckDuckGo/HomePage/Model/HomePageContinueSetUpModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import AppKit
import BrowserServicesKit
import Combine
import Common
import Foundation
import PixelKit
Expand All @@ -26,6 +27,20 @@ import Subscription
import NetworkProtection
import NetworkProtectionUI

protocol ContinueSetUpModelTabOpening {
@MainActor
func openTab(_ tab: Tab)
}

struct TabCollectionViewModelTabOpener: ContinueSetUpModelTabOpening {
let tabCollectionViewModel: TabCollectionViewModel

@MainActor
func openTab(_ tab: Tab) {
tabCollectionViewModel.insertOrAppend(tab: tab, selected: true)
}
}

extension HomePage.Models {

static let newHomePageTabOpen = Notification.Name("newHomePageAppOpen")
Expand All @@ -49,7 +64,7 @@ extension HomePage.Models {
private let defaultBrowserProvider: DefaultBrowserProvider
private let dockCustomizer: DockCustomization
private let dataImportProvider: DataImportStatusProviding
private let tabCollectionViewModel: TabCollectionViewModel
private let tabOpener: ContinueSetUpModelTabOpening
private let emailManager: EmailManager
private let duckPlayerPreferences: DuckPlayerPreferencesPersistor
private let subscriptionManager: SubscriptionManager
Expand All @@ -58,9 +73,13 @@ extension HomePage.Models {
var shouldShowAllFeatures: Bool {
didSet {
updateVisibleMatrix()
shouldShowAllFeaturesSubject.send(shouldShowAllFeatures)
}
}

let shouldShowAllFeaturesPublisher: AnyPublisher<Bool, Never>
private let shouldShowAllFeaturesSubject = PassthroughSubject<Bool, Never>()

struct Settings {
@UserDefaultsWrapper(key: .homePageShowMakeDefault, defaultValue: true)
var shouldShowMakeDefaultSetting: Bool
Expand Down Expand Up @@ -102,36 +121,43 @@ extension HomePage.Models {

lazy var listOfFeatures = settings.isFirstSession ? firstRunFeatures : randomisedFeatures

private var featuresMatrix: [[FeatureType]] = [[]] {
@Published var featuresMatrix: [[FeatureType]] = [[]] {
didSet {
updateVisibleMatrix()
}
}

@Published var visibleFeaturesMatrix: [[FeatureType]] = [[]]

init(defaultBrowserProvider: DefaultBrowserProvider,
dockCustomizer: DockCustomization,
dataImportProvider: DataImportStatusProviding,
tabCollectionViewModel: TabCollectionViewModel,
init(defaultBrowserProvider: DefaultBrowserProvider = SystemDefaultBrowserProvider(),
dockCustomizer: DockCustomization = DockCustomizer(),
dataImportProvider: DataImportStatusProviding = BookmarksAndPasswordsImportStatusProvider(),
tabOpener: ContinueSetUpModelTabOpening,
emailManager: EmailManager = EmailManager(),
duckPlayerPreferences: DuckPlayerPreferencesPersistor,
duckPlayerPreferences: DuckPlayerPreferencesPersistor = DuckPlayerPreferencesUserDefaultsPersistor(),
privacyConfigurationManager: PrivacyConfigurationManaging = AppPrivacyFeatures.shared.contentBlocking.privacyConfigurationManager,
subscriptionManager: SubscriptionManager = Application.appDelegate.subscriptionManager) {

self.defaultBrowserProvider = defaultBrowserProvider
self.dockCustomizer = dockCustomizer
self.dataImportProvider = dataImportProvider
self.tabCollectionViewModel = tabCollectionViewModel
self.tabOpener = tabOpener
self.emailManager = emailManager
self.duckPlayerPreferences = duckPlayerPreferences
self.privacyConfigurationManager = privacyConfigurationManager
self.subscriptionManager = subscriptionManager
self.settings = .init()

shouldShowAllFeaturesPublisher = shouldShowAllFeaturesSubject.removeDuplicates().eraseToAnyPublisher()

refreshFeaturesMatrix()

NotificationCenter.default.addObserver(self, selector: #selector(newTabOpenNotification(_:)), name: HomePage.Models.newHomePageTabOpen, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(windowDidBecomeKey(_:)), name: NSWindow.didBecomeKeyNotification, object: nil)

// HTML NTP doesn't refresh on appear so we have to connect to the appear signal
// (the notification in this case) to trigger a refresh.
NotificationCenter.default.addObserver(self, selector: #selector(refreshFeaturesForHTMLNewTabPage(_:)), name: .newTabPageWebViewDidAppear, object: nil)
}

@MainActor func performAction(for featureType: FeatureType) {
Expand Down Expand Up @@ -166,14 +192,14 @@ extension HomePage.Models {
private func performDuckPlayerAction() {
if let videoUrl = URL(string: duckPlayerURL) {
let tab = Tab(content: .url(videoUrl, source: .link), shouldLoadInBackground: true)
tabCollectionViewModel.append(tab: tab)
tabOpener.openTab(tab)
}
}

@MainActor
private func performEmailProtectionAction() {
let tab = Tab(content: .url(EmailUrls().emailProtectionLink, source: .ui), shouldLoadInBackground: true)
tabCollectionViewModel.append(tab: tab)
tabOpener.openTab(tab)
}

func performDockAction() {
Expand Down Expand Up @@ -243,6 +269,14 @@ extension HomePage.Models {
}

@objc private func windowDidBecomeKey(_ notification: Notification) {
// Async dispatch allows default browser setting to propagate
// after being changed in the system dialog
DispatchQueue.main.async {
self.refreshFeaturesMatrix()
}
}

@objc private func refreshFeaturesForHTMLNewTabPage(_ notification: Notification) {
refreshFeaturesMatrix()
}

Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/HomePage/View/ContinueSetUpView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ extension HomePage.Views {
defaultBrowserProvider: SystemDefaultBrowserProvider(),
dockCustomizer: DockCustomizer(),
dataImportProvider: BookmarksAndPasswordsImportStatusProvider(),
tabCollectionViewModel: TabCollectionViewModel(),
tabOpener: TabCollectionViewModelTabOpener(tabCollectionViewModel: TabCollectionViewModel()),
duckPlayerPreferences: DuckPlayerPreferencesUserDefaultsPersistor()
))
}
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ extension HomePage.Views.BackgroundCategoryView {
defaultBrowserProvider: SystemDefaultBrowserProvider(),
dockCustomizer: DockCustomizer(),
dataImportProvider: BookmarksAndPasswordsImportStatusProvider(),
tabCollectionViewModel: TabCollectionViewModel(),
tabOpener: TabCollectionViewModelTabOpener(tabCollectionViewModel: TabCollectionViewModel()),
duckPlayerPreferences: DuckPlayerPreferencesUserDefaultsPersistor()
))
.environmentObject(HomePage.Models.FavoritesModel(
Expand Down Expand Up @@ -276,7 +276,7 @@ extension HomePage.Views.BackgroundCategoryView {
defaultBrowserProvider: SystemDefaultBrowserProvider(),
dockCustomizer: DockCustomizer(),
dataImportProvider: BookmarksAndPasswordsImportStatusProvider(),
tabCollectionViewModel: TabCollectionViewModel(),
tabOpener: TabCollectionViewModelTabOpener(tabCollectionViewModel: TabCollectionViewModel()),
duckPlayerPreferences: DuckPlayerPreferencesUserDefaultsPersistor()
))
.environmentObject(HomePage.Models.FavoritesModel(
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/HomePage/View/HomePageViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ final class HomePageViewController: NSViewController {
defaultBrowserProvider: SystemDefaultBrowserProvider(),
dockCustomizer: DockCustomizer(),
dataImportProvider: BookmarksAndPasswordsImportStatusProvider(),
tabCollectionViewModel: tabCollectionViewModel,
tabOpener: TabCollectionViewModelTabOpener(tabCollectionViewModel: tabCollectionViewModel),
duckPlayerPreferences: DuckPlayerPreferencesUserDefaultsPersistor()
)
}
Expand Down
8 changes: 8 additions & 0 deletions DuckDuckGo/NewTabPage/NewTabPageActionsManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,16 @@ extension NewTabPageActionsManager {
self.init(scriptClients: [
NewTabPageConfigurationClient(appearancePreferences: appearancePreferences),
NewTabPageRMFClient(remoteMessageProvider: activeRemoteMessageModel, openURLHandler: openURLHandler),
NewTabPageNextStepsCardsClient(model: HomePage.Models.ContinueSetUpModel(tabOpener: NewTabPageTabOpener())),
NewTabPageFavoritesClient(favoritesModel: NewTabPageFavoritesModel()),
NewTabPagePrivacyStatsClient(model: privacyStatsModel)
])
}
}

struct NewTabPageTabOpener: ContinueSetUpModelTabOpening {
@MainActor
func openTab(_ tab: Tab) {
WindowControllersManager.shared.lastKeyMainWindowController?.mainViewController.tabCollectionViewModel.insertOrAppend(tab: tab, selected: true)
}
}
Loading
Loading