Skip to content

Commit

Permalink
Add History Debug Menu on macOS and display only 1 week of history wh…
Browse files Browse the repository at this point in the history
…en history view is enabled (#3833)

Task/Issue URL: https://app.asana.com/0/72649045549333/1209328755192085

Description:
This change updates History Menu when historyView feature flag is enabled,
to only keep history items for last 7 days, and offer opening Full History View
in order to view all history.
For testing purposes, History Debug Menu was added, where history can be
populated with visits to fake websites.
  • Loading branch information
ayoy authored Feb 5, 2025
1 parent 2b5e47d commit 5f82d5e
Show file tree
Hide file tree
Showing 14 changed files with 155 additions and 22 deletions.
8 changes: 7 additions & 1 deletion DuckDuckGo-macOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -1179,6 +1179,8 @@
3745DE062D536DCF00024FC8 /* HistoryGroupingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3745DE042D536DCA00024FC8 /* HistoryGroupingProvider.swift */; };
3745DE082D536EF900024FC8 /* HistoryGroupingProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3745DE072D536EF000024FC8 /* HistoryGroupingProviderTests.swift */; };
3745DE092D536EF900024FC8 /* HistoryGroupingProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3745DE072D536EF000024FC8 /* HistoryGroupingProviderTests.swift */; };
3745DE0B2D53969300024FC8 /* HistoryDebugMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3745DE0A2D53969000024FC8 /* HistoryDebugMenu.swift */; };
3745DE0C2D53969300024FC8 /* HistoryDebugMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3745DE0A2D53969000024FC8 /* HistoryDebugMenu.swift */; };
37479F152891BC8300302FE2 /* TabCollectionViewModelTests+WithoutPinnedTabsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37479F142891BC8300302FE2 /* TabCollectionViewModelTests+WithoutPinnedTabsManager.swift */; };
374EF08329B751FC003D2E87 /* RecentlyClosedCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374EF08229B751FC003D2E87 /* RecentlyClosedCoordinatorTests.swift */; };
374EF08429B7575B003D2E87 /* RecentlyClosedCoordinatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 374EF08229B751FC003D2E87 /* RecentlyClosedCoordinatorTests.swift */; };
Expand Down Expand Up @@ -3810,6 +3812,7 @@
37445F9B2A1569F00029F789 /* SyncBookmarksAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncBookmarksAdapter.swift; sourceTree = "<group>"; };
3745DE042D536DCA00024FC8 /* HistoryGroupingProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryGroupingProvider.swift; sourceTree = "<group>"; };
3745DE072D536EF000024FC8 /* HistoryGroupingProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryGroupingProviderTests.swift; sourceTree = "<group>"; };
3745DE0A2D53969000024FC8 /* HistoryDebugMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryDebugMenu.swift; sourceTree = "<group>"; };
37479F142891BC8300302FE2 /* TabCollectionViewModelTests+WithoutPinnedTabsManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "TabCollectionViewModelTests+WithoutPinnedTabsManager.swift"; sourceTree = "<group>"; };
374EF08229B751FC003D2E87 /* RecentlyClosedCoordinatorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentlyClosedCoordinatorTests.swift; sourceTree = "<group>"; };
374EFDF22D01C99700B30939 /* ActiveRemoteMessageModel+NewTabPage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ActiveRemoteMessageModel+NewTabPage.swift"; sourceTree = "<group>"; };
Expand Down Expand Up @@ -9238,6 +9241,7 @@
AAE75276263B038A00B973F8 /* Services */ = {
isa = PBXGroup;
children = (
3745DE0A2D53969000024FC8 /* HistoryDebugMenu.swift */,
AAE75278263B046100B973F8 /* History.xcdatamodeld */,
AAE7527B263B056C00B973F8 /* EncryptedHistoryStore.swift */,
85D0327A2B8E3D090041D1FB /* HistoryCoordinatorExtension.swift */,
Expand Down Expand Up @@ -11591,6 +11595,7 @@
37197EAB2942443D00394917 /* WebViewContainerView.swift in Sources */,
84F1C8DF2C774D4200716446 /* NSTableViewExtension.swift in Sources */,
3706FA88293F65D500E42796 /* Logger+Multiple.swift in Sources */,
3745DE0C2D53969300024FC8 /* HistoryDebugMenu.swift in Sources */,
3706FA89293F65D500E42796 /* CrashReportPromptPresenter.swift in Sources */,
3706FA8B293F65D500E42796 /* PreferencesRootView.swift in Sources */,
31521AC12CC013AD00248E6F /* AIChatMenuVisibilityConfigurable.swift in Sources */,
Expand Down Expand Up @@ -13779,6 +13784,7 @@
AAC6881B28626C1900D54247 /* RecentlyClosedWindow.swift in Sources */,
85707F2A276A35FE00DC0649 /* ActionSpeech.swift in Sources */,
B6BE9FAA293F7955006363C6 /* ModalSheetCancellable.swift in Sources */,
3745DE0B2D53969300024FC8 /* HistoryDebugMenu.swift in Sources */,
B6830963274CDEC7004B46BB /* FireproofDomainsStore.swift in Sources */,
F188268D2BBF01C300D9AC4F /* PixelDataModel.xcdatamodeld in Sources */,
7B430EA12A71411A00BAC4A1 /* NetworkProtectionSimulateFailureMenu.swift in Sources */,
Expand Down Expand Up @@ -15678,7 +15684,7 @@
repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 235.2.0;
version = 235.3.0;
};
};
9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/BrowserServicesKit",
"state" : {
"revision" : "744895ff98a5f2213333be0170e495b1e85034d0",
"version" : "235.2.0"
"revision" : "f3a9e71bb3be7dc53a74e2750d4282150e62798e",
"version" : "235.3.0"
}
},
{
Expand Down
113 changes: 113 additions & 0 deletions DuckDuckGo/History/Services/HistoryDebugMenu.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
//
// HistoryDebugMenu.swift
//
// Copyright © 2025 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 AppKit
import History

final class HistoryDebugMenu: NSMenu {

let historyCoordinator: HistoryCoordinating

private let environmentMenu = NSMenu()

init(historyCoordinator: HistoryCoordinating = HistoryCoordinator.shared) {
self.historyCoordinator = historyCoordinator
super.init(title: "")

buildItems {
NSMenuItem(
title: "Add 10 history visits each day (10 domains)",
action: #selector(populateFakeHistory),
target: self,
representedObject: (10, FakeURLsPool.random10Domains)
)
NSMenuItem(
title: "Populate 100 history visits each day (10 domains)",
action: #selector(populateFakeHistory),
target: self,
representedObject: (100, FakeURLsPool.random10Domains)
)
NSMenuItem(
title: "Populate 100 history visits each day (200 domains – SLOW!)",
action: #selector(populateFakeHistory),
target: self,
representedObject: (100, FakeURLsPool.random200Domains)
)
}
}

required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

@objc func populateFakeHistory(_ sender: NSMenuItem) {
guard let (maxVisitsPerDay, pool) = sender.representedObject as? (Int, FakeURLsPool) else {
return
}
Task.detached {
self.populateHistory(maxVisitsPerDay, pool.urls)
}
}

private func populateHistory(_ maxVisitsPerDay: Int, _ urls: [URL]) {
var date = Date()
let endDate = Date.monthAgo

var visitsPerDay = 0

while date > endDate {
guard let url = urls.randomElement() else {
continue
}
let visitDate = Date(timeIntervalSince1970: TimeInterval.random(in: date.startOfDay.timeIntervalSince1970..<date.timeIntervalSince1970))
historyCoordinator.addVisit(of: url, at: visitDate)
visitsPerDay += 1
if visitsPerDay >= maxVisitsPerDay {
date = date.daysAgo(1)
visitsPerDay = 0
}
}
}

enum FakeURLsPool {
case random10Domains
case random200Domains

var urls: [URL] {
switch self {
case .random10Domains:
Self.fakeURLs10Domains
case .random200Domains:
Self.fakeURLs200Domains
}
}

private static let fakeURLs10Domains: [URL] = generateFakeURLs(numberOfDomains: 10)
private static let fakeURLs200Domains: [URL] = generateFakeURLs(numberOfDomains: 200)

private static func generateFakeURLs(numberOfDomains: Int) -> [URL] {
(0..<numberOfDomains).flatMap { _ in
let hostname = UUID().uuidString.lowercased().prefix(8)
return (1...3).map { i in
"https://\(hostname).com/index\(i).html".url!
}
}
}
}

}
32 changes: 22 additions & 10 deletions DuckDuckGo/Menus/HistoryMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,42 +16,44 @@
// limitations under the License.
//

import BrowserServicesKit
import Cocoa
import Combine
import Common
import FeatureFlags
import History
import os.log

final class HistoryMenu: NSMenu {

let historyItem = NSMenuItem(title: "Show History", action: #selector(MainViewController.showHistory), keyEquivalent: "y")
let backMenuItem = NSMenuItem(title: UserText.navigateBack, action: #selector(MainViewController.back), keyEquivalent: "[")
let forwardMenuItem = NSMenuItem(title: UserText.navigateForward, action: #selector(MainViewController.forward), keyEquivalent: "]")

private let recentlyClosedMenuItem = NSMenuItem(title: UserText.mainMenuHistoryRecentlyClosed)
private let reopenLastClosedMenuItem = NSMenuItem(title: UserText.reopenLastClosedTab, action: #selector(AppDelegate.reopenLastClosedTab))
private let reopenAllWindowsFromLastSessionMenuItem = NSMenuItem(title: UserText.mainMenuHistoryReopenAllWindowsFromLastSession,
action: #selector(AppDelegate.reopenAllWindowsFromLastSession))
private let showHistoryMenuItem = NSMenuItem(title: "Show All History…", action: #selector(MainViewController.showHistory), keyEquivalent: "y")
private let showHistorySeparator = NSMenuItem.separator()
private let clearAllHistoryMenuItem = NSMenuItem(title: UserText.mainMenuHistoryClearAllHistory,
action: #selector(MainViewController.clearAllHistory),
keyEquivalent: [.command, .shift, .backspace])
.withAccessibilityIdentifier("HistoryMenu.clearAllHistory")
private let clearAllHistorySeparator = NSMenuItem.separator()

private let historyGroupingProvider: HistoryGroupingProvider
private let featureFlagger: FeatureFlagger
@MainActor
private let reopenMenuItemKeyEquivalentManager = ReopenMenuItemKeyEquivalentManager()

@MainActor
init(historyGroupingProvider: HistoryGroupingProvider = .init(dataSource: HistoryCoordinator.shared)) {
init(historyGroupingProvider: HistoryGroupingProvider = .init(dataSource: HistoryCoordinator.shared), featureFlagger: FeatureFlagger = NSApp.delegateTyped.featureFlagger) {
self.historyGroupingProvider = historyGroupingProvider
self.featureFlagger = featureFlagger

super.init(title: UserText.mainMenuHistory)

self.buildItems {
historyItem
NSMenuItem.separator()

backMenuItem
forwardMenuItem
NSMenuItem.separator()
Expand All @@ -60,6 +62,8 @@ final class HistoryMenu: NSMenu {
recentlyClosedMenuItem
reopenAllWindowsFromLastSessionMenuItem

showHistorySeparator
showHistoryMenuItem
clearAllHistorySeparator
clearAllHistoryMenuItem
}
Expand All @@ -77,22 +81,23 @@ final class HistoryMenu: NSMenu {
override func update() {
super.update()

historyItem.isHidden = !NSApp.delegateTyped.featureFlagger.isFeatureOn(.historyView)
showHistoryMenuItem.isHidden = !featureFlagger.isFeatureOn(.historyView)

updateRecentlyClosedMenu()
updateReopenLastClosedMenuItem()

clearOldVariableMenuItems()
addRecentlyVisited()
addHistoryGroupings()
addClearAllHistoryOnTheBottom()
addClearAllAndShowHistoryOnTheBottom()
}

private func clearOldVariableMenuItems() {
items.removeAll { menuItem in
recentlyVisitedMenuItems.contains(menuItem) ||
historyGroupingsMenuItems.contains(menuItem) ||
menuItem == clearAllHistoryMenuItem
menuItem == clearAllHistoryMenuItem ||
menuItem == showHistoryMenuItem
}
}

Expand Down Expand Up @@ -159,7 +164,7 @@ final class HistoryMenu: NSMenu {
groupings.forEach { grouping in
if grouping.date > Date.weekAgo.startOfDay {
firstWeek.append(grouping)
} else {
} else if !featureFlagger.isFeatureOn(.historyView) {
older.append(grouping)
}
}
Expand Down Expand Up @@ -269,7 +274,14 @@ final class HistoryMenu: NSMenu {

// MARK: - Clear All History

private func addClearAllHistoryOnTheBottom() {
private func addClearAllAndShowHistoryOnTheBottom() {
if featureFlagger.isFeatureOn(.historyView) {
if showHistorySeparator.menu != nil {
removeItem(showHistorySeparator)
}
addItem(showHistorySeparator)
addItem(showHistoryMenuItem)
}
if clearAllHistorySeparator.menu != nil {
removeItem(clearAllHistorySeparator)
}
Expand Down
2 changes: 2 additions & 0 deletions DuckDuckGo/Menus/MainMenu.swift
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,8 @@ final class MainMenu: NSMenu {
NSMenuItem(title: "Shift New Tab daily impression", action: #selector(MainViewController.debugShiftNewTabOpeningDate))
NSMenuItem(title: "Shift \(AppearancePreferences.Constants.dismissNextStepsCardsAfterDays) days", action: #selector(MainViewController.debugShiftNewTabOpeningDateNtimes))
}
NSMenuItem(title: "History")
.submenu(HistoryDebugMenu())
NSMenuItem(title: "Reset Data") {
NSMenuItem(title: "Reset Default Browser Prompt", action: #selector(MainViewController.resetDefaultBrowserPrompt))
NSMenuItem(title: "Reset Default Grammar Checks", action: #selector(MainViewController.resetDefaultGrammarChecks))
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/DataBrokerProtection/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ let package = Package(
targets: ["DataBrokerProtection"])
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "235.2.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "235.3.0"),
.package(path: "../SwiftUIExtensions"),
.package(path: "../AppKitExtensions"),
.package(path: "../XPCHelper"),
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/FeatureFlags/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ let package = Package(
targets: ["FeatureFlags"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "235.2.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "235.3.0"),
],
targets: [
// Targets are the basic building blocks of a package, defining a module or a test suite.
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/HistoryView/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ let package = Package(
targets: ["HistoryView"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "235.2.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "235.3.0"),
.package(path: "../WebKitExtensions"),
.package(path: "../UserScriptActionsManager"),
.package(path: "../Utilities"),
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/NetworkProtectionMac/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ let package = Package(
.library(name: "VPNAppLauncher", targets: ["VPNAppLauncher"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "235.2.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "235.3.0"),
.package(url: "https://github.com/airbnb/lottie-spm", exact: "4.4.3"),
.package(path: "../AppLauncher"),
.package(path: "../UDSHelper"),
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/NewTabPage/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ let package = Package(
targets: ["NewTabPage"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "235.2.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "235.3.0"),
.package(path: "../WebKitExtensions"),
.package(path: "../UserScriptActionsManager"),
.package(path: "../Utilities"),
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/SubscriptionUI/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let package = Package(
targets: ["SubscriptionUI"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "235.2.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "235.3.0"),
.package(path: "../PreferencesUI-macOS"),
.package(path: "../SwiftUIExtensions"),
.package(path: "../FeatureFlags")
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/UserScriptActionsManager/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ let package = Package(
targets: ["UserScriptActionsManager"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "235.2.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "235.3.0"),
],
targets: [
.target(
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/WebKitExtensions/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ let package = Package(
),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "235.2.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "235.3.0"),
.package(path: "../AppKitExtensions")
],
targets: [
Expand Down
2 changes: 1 addition & 1 deletion UnitTests/History/Model/HistoryCoordinatingMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ final class HistoryCoordinatingMock: HistoryCoordinating {

var addVisitCalled = false
var visit: Visit?
func addVisit(of url: URL) -> Visit? {
func addVisit(of url: URL, at date: Date) -> Visit? {
addVisitCalled = true
return visit
}
Expand Down

0 comments on commit 5f82d5e

Please sign in to comment.