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

Implement static prompts for add to dock #3790

Merged
merged 22 commits into from
Jan 31, 2025
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
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-macOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2876,6 +2876,8 @@
B6FA893F269C424500588ECD /* PrivacyDashboardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6FA893E269C424500588ECD /* PrivacyDashboardViewController.swift */; };
B6FA8941269C425400588ECD /* PrivacyDashboardPopover.swift in Sources */ = {isa = PBXBuildFile; fileRef = B6FA8940269C425400588ECD /* PrivacyDashboardPopover.swift */; };
BB0346F52CEB80B400D23E05 /* DownloadsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB0346F42CEB80B400D23E05 /* DownloadsTests.swift */; };
BB1A43902D4968F2000807C7 /* MenuItemWithNotificationDot.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1A438F2D4968F2000807C7 /* MenuItemWithNotificationDot.swift */; };
BB1A43912D4968F2000807C7 /* MenuItemWithNotificationDot.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB1A438F2D4968F2000807C7 /* MenuItemWithNotificationDot.swift */; };
BB3229052D08644400DA92E9 /* TabBarRemoteMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB3229042D08643700DA92E9 /* TabBarRemoteMessageView.swift */; };
BB3229062D08644400DA92E9 /* TabBarRemoteMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB3229042D08643700DA92E9 /* TabBarRemoteMessageView.swift */; };
BB4339DB2C7F9606005D7ED7 /* PinnedTabsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = BB4339DA2C7F9606005D7ED7 /* PinnedTabsTests.swift */; };
Expand Down Expand Up @@ -4876,6 +4878,7 @@
B6FA893E269C424500588ECD /* PrivacyDashboardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyDashboardViewController.swift; sourceTree = "<group>"; };
B6FA8940269C425400588ECD /* PrivacyDashboardPopover.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyDashboardPopover.swift; sourceTree = "<group>"; };
BB0346F42CEB80B400D23E05 /* DownloadsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DownloadsTests.swift; sourceTree = "<group>"; };
BB1A438F2D4968F2000807C7 /* MenuItemWithNotificationDot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MenuItemWithNotificationDot.swift; sourceTree = "<group>"; };
BB3229042D08643700DA92E9 /* TabBarRemoteMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarRemoteMessageView.swift; sourceTree = "<group>"; };
BB4339DA2C7F9606005D7ED7 /* PinnedTabsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedTabsTests.swift; sourceTree = "<group>"; };
BB470EBA2C5A66D6002EE91D /* BookmarkManagementDetailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkManagementDetailViewModel.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -8428,6 +8431,7 @@
AA86491624D8339A001BABEE /* View */ = {
isa = PBXGroup;
children = (
BB1A438F2D4968F2000807C7 /* MenuItemWithNotificationDot.swift */,
BBB9314C2D1F0F1700D50AC1 /* ShowToolbarsOnFullScreenMenuCoordinator.swift */,
AA7EB6EE27E880EA00036718 /* Animations */,
AAC5E4F025D6BF10007F5990 /* AddressBarButton.swift */,
Expand Down Expand Up @@ -11966,6 +11970,7 @@
B62B483A2ADE46FC000DECE5 /* Application.swift in Sources */,
3706FBEE293F65D500E42796 /* MainView.swift in Sources */,
3706FBEF293F65D500E42796 /* EmailUrlExtensions.swift in Sources */,
BB1A43912D4968F2000807C7 /* MenuItemWithNotificationDot.swift in Sources */,
3706FBF0293F65D500E42796 /* PasswordManagementItemModel.swift in Sources */,
3706FBF2293F65D500E42796 /* FindInPageModel.swift in Sources */,
1D9A4E5B2B43213B00F449E2 /* TabSnapshotExtension.swift in Sources */,
Expand Down Expand Up @@ -13170,6 +13175,7 @@
37878E562CA3330300CC9EB5 /* HomePageAddressBarModel.swift in Sources */,
85480FCF25D1AA22009424E3 /* ConfigurationStore.swift in Sources */,
AA3D531B27A2F57E00074EC1 /* Feedback.swift in Sources */,
BB1A43902D4968F2000807C7 /* MenuItemWithNotificationDot.swift in Sources */,
4B0A63E8289DB58E00378EF7 /* FirefoxFaviconsReader.swift in Sources */,
1E7E2E9029029A2A00C01B54 /* ContentBlockingRulesUpdateObserver.swift in Sources */,
4B8AC93926B48A5100879451 /* FirefoxLoginReader.swift in Sources */,
Expand Down
2 changes: 2 additions & 0 deletions DuckDuckGo/Application/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {

#if SPARKLE
var updateController: UpdateController!
var dockCustomization: DockCustomization!
#endif

@UserDefaultsWrapper(key: .firstLaunchDate, defaultValue: Date.monthAgo)
Expand Down Expand Up @@ -349,6 +350,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
#if SPARKLE
if NSApp.runType != .uiTests {
updateController = UpdateController(internalUserDecider: internalUserDecider)
dockCustomization = DockCustomizer()
stateRestorationManager.subscribeToAutomaticAppRelaunching(using: updateController.willRelaunchAppPublisher)
}
#endif
Expand Down
27 changes: 26 additions & 1 deletion DuckDuckGo/Application/DockCustomizer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,39 @@
//

import Foundation
import Combine
import Common
import os.log
import Persistence

protocol DockCustomization {
var isAddedToDock: Bool { get }
var didShowFeatureFromMoreOptionsMenu: Bool { get set }
var didShowPublisher: AnyPublisher<Bool, Never> { get }

@discardableResult
func addToDock() -> Bool
}

final class DockCustomizer: DockCustomization {
enum Keys {
static let wasAddToDockFeatureShown = "more-options-menu.was-add-to-dock-shown"
}

private let positionProvider: DockPositionProviding
private let keyValueStore: KeyValueStoring

@Published private var didShowFeatureFromMoreOptionsMenuPrivate: Bool = false
var didShowPublisher: AnyPublisher<Bool, Never> {
$didShowFeatureFromMoreOptionsMenuPrivate.eraseToAnyPublisher()
}

init(positionProvider: DockPositionProviding = DockPositionProvider()) {
init(positionProvider: DockPositionProviding = DockPositionProvider(),
keyValueStore: KeyValueStoring = UserDefaults.standard) {
self.positionProvider = positionProvider
self.keyValueStore = keyValueStore

didShowFeatureFromMoreOptionsMenuPrivate = keyValueStore.object(forKey: Keys.wasAddToDockFeatureShown) as? Bool ?? false
}

private var dockPlistURL: URL = URL(fileURLWithPath: NSString(string: "~/Library/Preferences/com.apple.dock.plist").expandingTildeInPath)
Expand All @@ -53,6 +70,14 @@ final class DockCustomizer: DockCustomization {
return persistentApps.contains(where: { ($0["tile-data"] as? [String: AnyObject])?["bundle-identifier"] as? String == bundleIdentifier })
}

var didShowFeatureFromMoreOptionsMenu: Bool {
get { return didShowFeatureFromMoreOptionsMenuPrivate }
set {
didShowFeatureFromMoreOptionsMenuPrivate = newValue
keyValueStore.set(newValue, forKey: Keys.wasAddToDockFeatureShown)
}
}

// Adds a dictionary representing the application, either by using an existing
// one from 'recent-apps' or creating a new one if the application isn't recently used.
// It then inserts this dictionary into the 'persistent-apps' list at a position
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xDE",
"green" : "0x79",
"red" : "0x5F"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0xA1",
"green" : "0x44",
"red" : "0x2D"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"colors" : [
{
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x3C",
"green" : "0xBA",
"red" : "0x4C"
}
},
"idiom" : "universal"
},
{
"appearances" : [
{
"appearance" : "luminosity",
"value" : "dark"
}
],
"color" : {
"color-space" : "srgb",
"components" : {
"alpha" : "1.000",
"blue" : "0x6D",
"green" : "0xD6",
"red" : "0x7B"
}
},
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "add-to-home.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"images" : [
{
"filename" : "Check-Circle-16.svg",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"preserves-vector-representation" : true,
"template-rendering-intent" : "template"
}
}
1 change: 1 addition & 0 deletions DuckDuckGo/Common/Localizables/UserText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,7 @@ struct UserText {
static let isAddedToDock = NSLocalizedString("preferences.is-added-to-dock", value: "DuckDuckGo is added to the Dock.", comment: "Indicates that the browser is added to the macOS system Dock")
static let isNotAddedToDock = NSLocalizedString("preferences.not-added-to-dock", value: "DuckDuckGo is not added to the Dock.", comment: "Indicate that the browser is not added to macOS system Dock")
static let addToDock = NSLocalizedString("preferences.add-to-dock", value: "Add to Dock…", comment: "Action button to add the app to the Dock")
static let addDuckDuckGoToDock = NSLocalizedString("preferences.add-to-dock", value: "Add DuckDuckGo To Dock…", comment: "Action button to add the app to the Dock")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not sure if this has been reviewed already, but usually the word to in macOS menus seems to be lowercase. Just dropping a note that we may want to change this:
Screenshot 2025-01-28 at 15 15 44

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good call, copy was approved, but I can bring this to their attention.

static let onStartup = NSLocalizedString("preferences.on-startup", value: "On Startup", comment: "Name of the preferences section related to app startup")
static let reopenAllWindowsFromLastSession = NSLocalizedString("preferences.reopen-windows", value: "Reopen all windows from last session", comment: "Option to control session restoration")
static let showHomePage = NSLocalizedString("preferences.show-home", value: "Open a new window", comment: "Option to control session startup")
Expand Down
18 changes: 9 additions & 9 deletions DuckDuckGo/Localizable.xcstrings
Original file line number Diff line number Diff line change
Expand Up @@ -56685,55 +56685,55 @@
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Zum Dock hinzufügen …"
"value" : "DuckDuckGo zum Dock hinzufügen…"
}
},
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Add to Dock…"
"value" : "Add DuckDuckGo To Dock…"
}
},
"es" : {
"stringUnit" : {
"state" : "translated",
"value" : "Añadir al Dock…"
"value" : "Añadir DuckDuckGo al Dock…"
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Ajouter au Dock…"
"value" : "Ajouter DuckDuckGo au Dock…"
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
"value" : "Aggiungi al dock…"
"value" : "Aggiungi DuckDuckGo al Dock…"
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
"value" : "App toevoegen aan je dock…"
"value" : "Voeg DuckDuckGo toe aan Dock…"
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Dodaj do Docka…"
"value" : "Dodaj DuckDuckGo do Dock…"
}
},
"pt" : {
"stringUnit" : {
"state" : "translated",
"value" : "Adicionar à Dock…"
"value" : "Adicionar DuckDuckGo ao Dock…"
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
"value" : "Добавить на док-панель…"
"value" : "Добавить DuckDuckGo в док-панель…"
}
}
}
Expand Down
11 changes: 11 additions & 0 deletions DuckDuckGo/Menus/MainMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,11 +103,13 @@ final class MainMenu: NSMenu {

let helpMenu = NSMenu(title: UserText.mainMenuHelp)
let aboutMenuItem = NSMenuItem(title: UserText.about, action: #selector(AppDelegate.showAbout))
let addToDockMenuItem = NSMenuItem(title: UserText.addDuckDuckGoToDock, action: #selector(AppDelegate.addToDock))
let setAsDefaultMenuItem = NSMenuItem(title: UserText.setAsDefaultBrowser + "…", action: #selector(AppDelegate.setAsDefault))
let releaseNotesMenuItem = NSMenuItem(title: UserText.releaseNotesMenuItem, action: #selector(AppDelegate.showReleaseNotes))
let whatIsNewMenuItem = NSMenuItem(title: UserText.whatsNewMenuItem, action: #selector(AppDelegate.showWhatIsNew))
let sendFeedbackMenuItem = NSMenuItem(title: UserText.sendFeedback, action: #selector(AppDelegate.openFeedback))

private let dockCustomizer: DockCustomization
private let defaultBrowserPreferences: DefaultBrowserPreferences
private let aiChatMenuConfig: AIChatMenuVisibilityConfigurable

Expand All @@ -117,9 +119,11 @@ final class MainMenu: NSMenu {
init(featureFlagger: FeatureFlagger,
bookmarkManager: BookmarkManager,
faviconManager: FaviconManagement,
dockCustomizer: DockCustomization = DockCustomizer(),
defaultBrowserPreferences: DefaultBrowserPreferences = .shared,
aiChatMenuConfig: AIChatMenuVisibilityConfigurable) {

self.dockCustomizer = dockCustomizer
self.defaultBrowserPreferences = defaultBrowserPreferences
self.aiChatMenuConfig = aiChatMenuConfig
super.init(title: UserText.duckDuckGo)
Expand Down Expand Up @@ -149,6 +153,7 @@ final class MainMenu: NSMenu {
NSMenuItem.separator()

preferencesMenuItem
addToDockMenuItem
setAsDefaultMenuItem

NSMenuItem.separator()
Expand Down Expand Up @@ -438,6 +443,11 @@ final class MainMenu: NSMenu {
override func update() {
super.update()

#if SPARKLE
addToDockMenuItem.isHidden = dockCustomizer.isAddedToDock
#else
addToDockMenuItem.isHidden = true
#endif
setAsDefaultMenuItem.isHidden = defaultBrowserPreferences.isDefault

// To be safe, hide the NetP shortcut menu item by default.
Expand Down Expand Up @@ -674,6 +684,7 @@ final class MainMenu: NSMenu {
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))
NSMenuItem(title: "Reset Add To Dock more options menu notification", action: #selector(MainViewController.resetAddToDockFeatureNotification))

}.withAccessibilityIdentifier("MainMenu.resetData")
NSMenuItem(title: "UI Triggers") {
Expand Down
13 changes: 13 additions & 0 deletions DuckDuckGo/Menus/MainMenuActions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ extension AppDelegate {
WindowControllersManager.shared.showTab(with: .settings(pane: .about))
}

@MainActor
@objc func addToDock(_ sender: Any?) {
DockCustomizer().addToDock()
PixelKit.fire(GeneralPixel.userAddedToDockFromMainMenu)
}

@MainActor
@objc func setAsDefault(_ sender: Any?) {
PixelKit.fire(GeneralPixel.defaultRequestedFromMainMenu)
Expand Down Expand Up @@ -906,6 +912,13 @@ extension MainViewController {
SyncPromoManager().resetPromos()
}

@objc func resetAddToDockFeatureNotification(_ sender: Any?) {
#if SPARKLE
guard var dockCustomizer = Application.appDelegate.dockCustomization else { return }
dockCustomizer.didShowFeatureFromMoreOptionsMenu = false
#endif
}

@objc func resetTipKit(_ sender: Any?) {
TipKitDebugOptionsUIActionHandler().resetTipKitTapped()
}
Expand Down
Loading
Loading