Skip to content

Commit

Permalink
Bookmarks shortcut panel (#2245)
Browse files Browse the repository at this point in the history
Task/Issue URL: https://app.asana.com/0/0/1206670741563549/f

**Description**:
1. “Three dots hover” menu button for Bookmarks and Folders in the bookmarks shortcut panel.
2. Add “Star" icon for favorite bookmarks.
3. Enhance Contextual Menu with "Edit…” for both folders and bookmarks.
4. Present Add/Edit Bookmarks/Folder dialogs.
  • Loading branch information
alessandroboron committed Mar 7, 2024
1 parent 8edd20d commit 493e03d
Show file tree
Hide file tree
Showing 14 changed files with 377 additions and 50 deletions.
8 changes: 8 additions & 0 deletions DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -2433,6 +2433,9 @@
9F56CFAD2B84326C00BB7F11 /* AddEditBookmarkDialogViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F56CFAC2B84326C00BB7F11 /* AddEditBookmarkDialogViewModel.swift */; };
9F56CFAE2B84326C00BB7F11 /* AddEditBookmarkDialogViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F56CFAC2B84326C00BB7F11 /* AddEditBookmarkDialogViewModel.swift */; };
9F56CFAF2B84326C00BB7F11 /* AddEditBookmarkDialogViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F56CFAC2B84326C00BB7F11 /* AddEditBookmarkDialogViewModel.swift */; };
9F56CFB12B843F6C00BB7F11 /* BookmarksDialogViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F56CFB02B843F6C00BB7F11 /* BookmarksDialogViewFactory.swift */; };
9F56CFB22B843F6C00BB7F11 /* BookmarksDialogViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F56CFB02B843F6C00BB7F11 /* BookmarksDialogViewFactory.swift */; };
9F56CFB32B843F6C00BB7F11 /* BookmarksDialogViewFactory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F56CFB02B843F6C00BB7F11 /* BookmarksDialogViewFactory.swift */; };
9F982F0D2B8224BF00231028 /* AddEditBookmarkFolderDialogViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F982F0C2B8224BE00231028 /* AddEditBookmarkFolderDialogViewModel.swift */; };
9F982F0E2B8224BF00231028 /* AddEditBookmarkFolderDialogViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F982F0C2B8224BE00231028 /* AddEditBookmarkFolderDialogViewModel.swift */; };
9F982F0F2B8224BF00231028 /* AddEditBookmarkFolderDialogViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9F982F0C2B8224BE00231028 /* AddEditBookmarkFolderDialogViewModel.swift */; };
Expand Down Expand Up @@ -4051,6 +4054,7 @@
9F514F902B7D88AD001832A9 /* AddEditBookmarkFolderDialogView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddEditBookmarkFolderDialogView.swift; sourceTree = "<group>"; };
9F56CFA82B82DC4300BB7F11 /* AddEditBookmarkFolderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddEditBookmarkFolderView.swift; sourceTree = "<group>"; };
9F56CFAC2B84326C00BB7F11 /* AddEditBookmarkDialogViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddEditBookmarkDialogViewModel.swift; sourceTree = "<group>"; };
9F56CFB02B843F6C00BB7F11 /* BookmarksDialogViewFactory.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarksDialogViewFactory.swift; sourceTree = "<group>"; };
9F982F0C2B8224BE00231028 /* AddEditBookmarkFolderDialogViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddEditBookmarkFolderDialogViewModel.swift; sourceTree = "<group>"; };
9F982F112B82268F00231028 /* AddEditBookmarkFolderDialogViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddEditBookmarkFolderDialogViewModelTests.swift; sourceTree = "<group>"; };
9FA173D92B79BD8A00EE4E6E /* BookmarkDialogContainerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BookmarkDialogContainerView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -6758,6 +6762,7 @@
9F514F902B7D88AD001832A9 /* AddEditBookmarkFolderDialogView.swift */,
9F56CFA82B82DC4300BB7F11 /* AddEditBookmarkFolderView.swift */,
9FEE98642B846870002E44E8 /* AddEditBookmarkView.swift */,
9F56CFB02B843F6C00BB7F11 /* BookmarksDialogViewFactory.swift */,
);
path = Dialog;
sourceTree = "<group>";
Expand Down Expand Up @@ -10350,6 +10355,7 @@
3706FC01293F65D500E42796 /* ChromiumBookmarksReader.swift in Sources */,
3706FC02293F65D500E42796 /* Downloads.xcdatamodeld in Sources */,
B60C6F7829B0E286007BFAA8 /* SearchNonexistentDomainNavigationResponder.swift in Sources */,
9F56CFB22B843F6C00BB7F11 /* BookmarksDialogViewFactory.swift in Sources */,
3707C720294B5D2900682A9F /* WKWebsiteDataStoreExtension.swift in Sources */,
3706FC03293F65D500E42796 /* TabPreviewViewController.swift in Sources */,
4B9754EC2984300100D7B834 /* EmailManagerExtension.swift in Sources */,
Expand Down Expand Up @@ -11320,6 +11326,7 @@
4B957A542AC7AE700062CA31 /* VisitMenuItem.swift in Sources */,
4B957A552AC7AE700062CA31 /* EncryptionKeyStore.swift in Sources */,
4B957A562AC7AE700062CA31 /* TabExtensionsBuilder.swift in Sources */,
9F56CFB32B843F6C00BB7F11 /* BookmarksDialogViewFactory.swift in Sources */,
1E2AE4C82ACB216B00684E0A /* HoverTrackingArea.swift in Sources */,
4B957A582AC7AE700062CA31 /* PasswordManagementIdentityItemView.swift in Sources */,
4B957A592AC7AE700062CA31 /* ProgressExtension.swift in Sources */,
Expand Down Expand Up @@ -12134,6 +12141,7 @@
4B6785472AA8DE68008A5004 /* NetworkProtectionFeatureDisabler.swift in Sources */,
4B0526642B1D55D80054955A /* VPNFeedbackCategory.swift in Sources */,
4B9292D42667123700AD2C21 /* BookmarkListViewController.swift in Sources */,
9F56CFB12B843F6C00BB7F11 /* BookmarksDialogViewFactory.swift in Sources */,
4B723E0D26B0006100E14D75 /* SecureVaultLoginImporter.swift in Sources */,
B645D8F629FA95440024461F /* WKProcessPoolExtension.swift in Sources */,
4B9292D32667123700AD2C21 /* AddBookmarkModalView.swift in Sources */,
Expand Down
12 changes: 12 additions & 0 deletions DuckDuckGo/Bookmarks/Model/BookmarkOutlineViewDataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ final class BookmarkOutlineViewDataSource: NSObject, NSOutlineViewDataSource, NS

private let contentMode: ContentMode
private let bookmarkManager: BookmarkManager
private let onMenuRequestedAction: ((BookmarkOutlineCellView) -> Void)?
private let presentFaviconsFetcherOnboarding: (() -> Void)?

private var favoritesPseudoFolder = PseudoFolder.favorites
Expand All @@ -43,11 +44,13 @@ final class BookmarkOutlineViewDataSource: NSObject, NSOutlineViewDataSource, NS
contentMode: ContentMode,
bookmarkManager: BookmarkManager,
treeController: BookmarkTreeController,
onMenuRequestedAction: ((BookmarkOutlineCellView) -> Void)? = nil,
presentFaviconsFetcherOnboarding: (() -> Void)? = nil
) {
self.contentMode = contentMode
self.bookmarkManager = bookmarkManager
self.treeController = treeController
self.onMenuRequestedAction = onMenuRequestedAction
self.presentFaviconsFetcherOnboarding = presentFaviconsFetcherOnboarding

super.init()
Expand Down Expand Up @@ -123,6 +126,7 @@ final class BookmarkOutlineViewDataSource: NSObject, NSOutlineViewDataSource, NS
}
let cell = outlineView.makeView(withIdentifier: .init(BookmarkOutlineCellView.className()), owner: self) as? BookmarkOutlineCellView
?? BookmarkOutlineCellView(identifier: .init(BookmarkOutlineCellView.className()))
cell.delegate = self

if let bookmark = node.representedObject as? Bookmark {
cell.update(from: bookmark)
Expand Down Expand Up @@ -329,3 +333,11 @@ final class BookmarkOutlineViewDataSource: NSObject, NSOutlineViewDataSource, NS
}

}

// MARK: - BookmarkOutlineCellViewDelegate

extension BookmarkOutlineViewDataSource: BookmarkOutlineCellViewDelegate {
func outlineCellViewRequestedMenu(_ cell: BookmarkOutlineCellView) {
onMenuRequestedAction?(cell)
}
}
34 changes: 30 additions & 4 deletions DuckDuckGo/Bookmarks/Services/ContextualMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ struct ContextualMenu {

// Not all contexts support an editing option for bookmarks. The option is displayed by default, but `includeBookmarkEditMenu` can disable it.
static func menu(for objects: [Any]?, includeBookmarkEditMenu: Bool = true) -> NSMenu? {
menu(for: objects, target: nil, includeBookmarkEditMenu: includeBookmarkEditMenu)
}

/// Creates an instance of NSMenu for the specified Objects and target.
/// - Parameters:
/// - objects: The objects to create the menu for
/// - target: The target to associate to the `NSMenuItem`
/// - includeBookmarkEditMenu: True if menu should return edit bookmark option. False otherwise. Default is true
/// - Returns: An instance of NSMenu or nil if `objects` is not a `Bookmark` or a `Folder`.
static func menu(for objects: [Any]?, target: AnyObject?, includeBookmarkEditMenu: Bool = true) -> NSMenu? {

guard let objects = objects, objects.count > 0 else {
return menuForNoSelection()
}
Expand All @@ -33,13 +44,23 @@ struct ContextualMenu {
let node = objects.first as? BookmarkNode
let object = node?.representedObject ?? objects.first as? BaseBookmarkEntity

let menu: NSMenu?

if let bookmark = object as? Bookmark {
return menu(for: bookmark, includeBookmarkEditMenu: includeBookmarkEditMenu)
menu = self.menu(for: bookmark, includeBookmarkEditMenu: includeBookmarkEditMenu)
} else if let folder = object as? BookmarkFolder {
return menu(for: folder)
// When the user edits a folder we need to show the parent in the folder picker. Folders directly child of PseudoFolder `Bookmarks` have nil parent because their parent is not an instance of `BookmarkFolder`
let parent = node?.parent?.representedObject as? BookmarkFolder
menu = self.menu(for: folder, parent: parent)
} else {
return nil
menu = nil
}

menu?.items.forEach { item in
item.target = target
}

return menu
}

// MARK: - Single Item Menu Creation
Expand Down Expand Up @@ -75,10 +96,11 @@ struct ContextualMenu {
return menu
}

private static func menu(for folder: BookmarkFolder) -> NSMenu {
private static func menu(for folder: BookmarkFolder, parent: BookmarkFolder?) -> NSMenu {
let menu = NSMenu(title: "")

menu.addItem(renameFolderMenuItem(folder: folder))
menu.addItem(editFolderMenuItem(folder: folder, parent: parent))
menu.addItem(deleteFolderMenuItem(folder: folder))
menu.addItem(NSMenuItem.separator())

Expand All @@ -97,6 +119,10 @@ struct ContextualMenu {
return menuItem(UserText.renameFolder, #selector(FolderMenuItemSelectors.renameFolder(_:)), folder)
}

static func editFolderMenuItem(folder: BookmarkFolder, parent: BookmarkFolder?) -> NSMenuItem {
menuItem(UserText.editBookmark, #selector(FolderMenuItemSelectors.editFolder(_:)), (folder, parent))
}

static func deleteFolderMenuItem(folder: BookmarkFolder) -> NSMenuItem {
return menuItem(UserText.deleteFolder, #selector(FolderMenuItemSelectors.deleteFolder(_:)), folder)
}
Expand Down
1 change: 1 addition & 0 deletions DuckDuckGo/Bookmarks/Services/MenuItemSelectors.swift
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import AppKit

func newFolder(_ sender: NSMenuItem)
func renameFolder(_ sender: NSMenuItem)
func editFolder(_ sender: NSMenuItem)
func deleteFolder(_ sender: NSMenuItem)
func openInNewTabs(_ sender: NSMenuItem)

Expand Down
62 changes: 50 additions & 12 deletions DuckDuckGo/Bookmarks/View/BookmarkListViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@ final class BookmarkListViewController: NSViewController {
contentMode: .bookmarksAndFolders,
bookmarkManager: bookmarkManager,
treeController: treeController,
onMenuRequestedAction: { [weak self] cell in
self?.showContextMenu(for: cell)
},
presentFaviconsFetcherOnboarding: { [weak self] in
guard let self, let window = self.view.window else {
return
Expand Down Expand Up @@ -334,18 +337,13 @@ final class BookmarkListViewController: NSViewController {
}

@objc func newBookmarkButtonClicked(_ sender: AnyObject) {
delegate?.popover(shouldPreventClosure: true)
AddBookmarkModalView(model: AddBookmarkModalViewModel(currentTabWebsite: currentTabWebsite) { [weak delegate] _ in
delegate?.popover(shouldPreventClosure: false)
}).show(in: parent?.view.window)
let view = BookmarksDialogViewFactory.makeAddBookmarkView(currentTab: currentTabWebsite)
showDialog(view: view)
}

@objc func newFolderButtonClicked(_ sender: AnyObject) {
delegate?.popover(shouldPreventClosure: true)
AddBookmarkFolderModalView()
.show(in: parent?.view.window) { [weak delegate] in
delegate?.popover(shouldPreventClosure: false)
}
let view = BookmarksDialogViewFactory.makeAddBookmarkFolderView(parentFolder: nil)
showDialog(view: view)
}

@objc func openManagementInterface(_ sender: NSButton) {
Expand Down Expand Up @@ -425,6 +423,30 @@ final class BookmarkListViewController: NSViewController {
outlineView.selectRowIndexes(indexes, byExtendingSelection: false)
}

private func showContextMenu(for cell: BookmarkOutlineCellView) {
let row = outlineView.row(for: cell)
guard
let item = outlineView.item(atRow: row),
let contextMenu = ContextualMenu.menu(for: [item])
else {
return
}

contextMenu.popUpAtMouseLocation(in: view)
}

}

private extension BookmarkListViewController {

func showDialog(view: any ModalView) {
delegate?.popover(shouldPreventClosure: true)

view.show(in: parent?.view.window) { [weak delegate] in
delegate?.popover(shouldPreventClosure: false)
}
}

}

// MARK: - Menu Item Selectors
Expand All @@ -439,11 +461,11 @@ extension BookmarkListViewController: NSMenuDelegate {
}

if outlineView.selectedRowIndexes.contains(row) {
return ContextualMenu.menu(for: outlineView.selectedItems, includeBookmarkEditMenu: false)
return ContextualMenu.menu(for: outlineView.selectedItems)
}

if let item = outlineView.item(atRow: row) {
return ContextualMenu.menu(for: [item], includeBookmarkEditMenu: false)
return ContextualMenu.menu(for: [item])
} else {
return nil
}
Expand Down Expand Up @@ -498,7 +520,13 @@ extension BookmarkListViewController: BookmarkMenuItemSelectors {
}

func editBookmark(_ sender: NSMenuItem) {
// Unsupported in the list view for the initial release.
guard let bookmark = sender.representedObject as? Bookmark else {
assertionFailure("Failed to retrieve Bookmark from Edit Bookmark context menu item")
return
}

let view = BookmarksDialogViewFactory.makeEditBookmarkView(bookmark: bookmark)
showDialog(view: view)
}

func copyBookmark(_ sender: NSMenuItem) {
Expand Down Expand Up @@ -549,6 +577,16 @@ extension BookmarkListViewController: FolderMenuItemSelectors {
}
}

func editFolder(_ sender: NSMenuItem) {
guard let (folder, parent) = sender.representedObject as? (BookmarkFolder, BookmarkFolder?) else {
assertionFailure("Failed to retrieve Bookmark from Edit Folder context menu item")
return
}

let view = BookmarksDialogViewFactory.makeEditBookmarkFolderView(folder: folder, parentFolder: parent)
showDialog(view: view)
}

func deleteFolder(_ sender: NSMenuItem) {
guard let folder = sender.representedObject as? BookmarkFolder else {
assertionFailure("Failed to retrieve Bookmark from Delete Folder context menu item")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -647,14 +647,8 @@ extension BookmarkManagementDetailViewController: BookmarkTableCellViewDelegate
return
}

if let contextMenu = ContextualMenu.menu(for: [bookmark]), let cursorLocation = self.view.window?.mouseLocationOutsideOfEventStream {
let convertedLocation = self.view.convert(cursorLocation, from: nil)
contextMenu.items.forEach { item in
item.target = self
}

contextMenu.popUp(positioning: nil, at: convertedLocation, in: self.view)
}
guard let contextMenu = ContextualMenu.menu(for: [bookmark], target: self) else { return }
contextMenu.popUpAtMouseLocation(in: view)
}

func bookmarkTableCellViewToggledFavorite(cell: BookmarkTableCellView) {
Expand Down Expand Up @@ -748,6 +742,10 @@ extension BookmarkManagementDetailViewController: FolderMenuItemSelectors {
.show(in: view.window)
}

func editFolder(_ sender: NSMenuItem) {
// https://app.asana.com/0/0/1206531304671952/f
}

func deleteFolder(_ sender: NSMenuItem) {
guard let folder = sender.representedObject as? BookmarkFolder else {
assertionFailure("Failed to retrieve Bookmark from Delete Folder context menu item")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,10 @@ extension BookmarkManagementSidebarViewController: FolderMenuItemSelectors {
.show(in: view.window)
}

func editFolder(_ sender: NSMenuItem) {
// https://app.asana.com/0/0/1206531304671948/f
}

func deleteFolder(_ sender: NSMenuItem) {
guard let folder = sender.representedObject as? BookmarkFolder else {
assertionFailure("Failed to retrieve Folder from Delete Folder context menu item")
Expand Down
Loading

0 comments on commit 493e03d

Please sign in to comment.