Skip to content
This repository was archived by the owner on Feb 24, 2025. It is now read-only.

Commit 2cdf242

Browse files
authored
Initial implementation of History View (#3851)
Task/Issue URL: https://app.asana.com/0/0/1209328755192086/f Description: This change adds History View behind an internal-only opt-in feature flag. It supports displaying, filtering and searching history, and opening history items.
1 parent a02a1b5 commit 2cdf242

22 files changed

+682
-229
lines changed

DuckDuckGo-macOS.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,6 +1281,8 @@
12811281
37BF3F14286D8A6500BD9014 /* PinnedTabsManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BF3F13286D8A6500BD9014 /* PinnedTabsManager.swift */; };
12821282
37BF3F21286F0A7A00BD9014 /* PinnedTabsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BF3F1E286F0A7A00BD9014 /* PinnedTabsViewModel.swift */; };
12831283
37BF3F22286F0A7A00BD9014 /* PinnedTabsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BF3F1F286F0A7A00BD9014 /* PinnedTabsView.swift */; };
1284+
37C7493A2D55FE710065B48B /* HistoryViewActionsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C749392D55FE690065B48B /* HistoryViewActionsHandler.swift */; };
1285+
37C7493B2D55FE710065B48B /* HistoryViewActionsHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C749392D55FE690065B48B /* HistoryViewActionsHandler.swift */; };
12841286
37C9F78C2CF1C776004D73A1 /* PrivacyStatsTabExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C9F78B2CF1C770004D73A1 /* PrivacyStatsTabExtension.swift */; };
12851287
37C9F78D2CF1C776004D73A1 /* PrivacyStatsTabExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37C9F78B2CF1C770004D73A1 /* PrivacyStatsTabExtension.swift */; };
12861288
37CBCA9A2A8966E60050218F /* SyncSettingsAdapter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37CBCA992A8966E60050218F /* SyncSettingsAdapter.swift */; };
@@ -1332,6 +1334,8 @@
13321334
37DF37072CF38B9F005ED34B /* PrivacyStats in Frameworks */ = {isa = PBXBuildFile; productRef = 37DF37062CF38B9F005ED34B /* PrivacyStats */; };
13331335
37DF37092CF38CD7005ED34B /* PrivacyStatsDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DF37082CF38CD3005ED34B /* PrivacyStatsDatabase.swift */; };
13341336
37DF370A2CF38CD7005ED34B /* PrivacyStatsDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37DF37082CF38CD3005ED34B /* PrivacyStatsDatabase.swift */; };
1337+
37E13B8E2D54B03D002ECD62 /* HistoryViewDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E13B8D2D54B023002ECD62 /* HistoryViewDataProvider.swift */; };
1338+
37E13B8F2D54B03D002ECD62 /* HistoryViewDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E13B8D2D54B023002ECD62 /* HistoryViewDataProvider.swift */; };
13351339
37E2608C2C8A1F6D006EE07F /* UserColorProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E2608B2C8A1F6D006EE07F /* UserColorProviding.swift */; };
13361340
37E2608D2C8A1F6D006EE07F /* UserColorProviding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E2608B2C8A1F6D006EE07F /* UserColorProviding.swift */; };
13371341
37E2608F2C8A3ABE006EE07F /* HomePageSettingsModelNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37E2608E2C8A3ABE006EE07F /* HomePageSettingsModelNavigator.swift */; };
@@ -3883,6 +3887,7 @@
38833887
37BF3F13286D8A6500BD9014 /* PinnedTabsManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PinnedTabsManager.swift; sourceTree = "<group>"; };
38843888
37BF3F1E286F0A7A00BD9014 /* PinnedTabsViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinnedTabsViewModel.swift; sourceTree = "<group>"; };
38853889
37BF3F1F286F0A7A00BD9014 /* PinnedTabsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PinnedTabsView.swift; sourceTree = "<group>"; };
3890+
37C749392D55FE690065B48B /* HistoryViewActionsHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryViewActionsHandler.swift; sourceTree = "<group>"; };
38863891
37C9F78B2CF1C770004D73A1 /* PrivacyStatsTabExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyStatsTabExtension.swift; sourceTree = "<group>"; };
38873892
37CBCA992A8966E60050218F /* SyncSettingsAdapter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SyncSettingsAdapter.swift; sourceTree = "<group>"; };
38883893
37CC53EB27E8A4D10028713D /* PreferencesDataClearingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesDataClearingView.swift; sourceTree = "<group>"; };
@@ -3920,6 +3925,7 @@
39203925
37DD516C296EAEDC00837F27 /* DuckDuckGoAppStore.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = DuckDuckGoAppStore.xcconfig; sourceTree = "<group>"; };
39213926
37DF37082CF38CD3005ED34B /* PrivacyStatsDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrivacyStatsDatabase.swift; sourceTree = "<group>"; };
39223927
37E1116C2C578F1B00583C19 /* DuckDuckGoAppStoreDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DuckDuckGoAppStoreDebug.entitlements; sourceTree = "<group>"; };
3928+
37E13B8D2D54B023002ECD62 /* HistoryViewDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HistoryViewDataProvider.swift; sourceTree = "<group>"; };
39233929
37E2608B2C8A1F6D006EE07F /* UserColorProviding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserColorProviding.swift; sourceTree = "<group>"; };
39243930
37E2608E2C8A3ABE006EE07F /* HomePageSettingsModelNavigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePageSettingsModelNavigator.swift; sourceTree = "<group>"; };
39253931
37E260912C8A3EB4006EE07F /* MockHomePageSettingsModelNavigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockHomePageSettingsModelNavigator.swift; sourceTree = "<group>"; };
@@ -9229,6 +9235,8 @@
92299235
AAE75276263B038A00B973F8 /* Services */ = {
92309236
isa = PBXGroup;
92319237
children = (
9238+
37C749392D55FE690065B48B /* HistoryViewActionsHandler.swift */,
9239+
37E13B8D2D54B023002ECD62 /* HistoryViewDataProvider.swift */,
92329240
3745DE0A2D53969000024FC8 /* HistoryDebugMenu.swift */,
92339241
AAE75278263B046100B973F8 /* History.xcdatamodeld */,
92349242
AAE7527B263B056C00B973F8 /* EncryptedHistoryStore.swift */,
@@ -11771,6 +11779,7 @@
1177111779
3148727B2CC689C200EEF89B /* AIChatToolBarPopUpOnboardingViewModel.swift in Sources */,
1177211780
BBFB72802C48047C0088884C /* SortBookmarksViewModel.swift in Sources */,
1177311781
37197EA72942443D00394917 /* AuthenticationAlert.swift in Sources */,
11782+
37C7493A2D55FE710065B48B /* HistoryViewActionsHandler.swift in Sources */,
1177411783
3706FEC3293F6F0600E42796 /* NativeMessagingCommunicator.swift in Sources */,
1177511784
3706FAFA293F65D500E42796 /* CleanThisHistoryMenuItem.swift in Sources */,
1177611785
1DA6D0FE2A1FF9A100540406 /* HTTPCookie.swift in Sources */,
@@ -12058,6 +12067,7 @@
1205812067
378F44EC29B4C73E00899924 /* ViewExtension.swift in Sources */,
1205912068
3706FB9D293F65D500E42796 /* BookmarkManager.swift in Sources */,
1206012069
3768D83C2C24C0A8004120AE /* RemoteMessageViewModel.swift in Sources */,
12070+
37E13B8F2D54B03D002ECD62 /* HistoryViewDataProvider.swift in Sources */,
1206112071
56A053FD2C1A0AC9007D8FAB /* OnboardingActionsManager.swift in Sources */,
1206212072
B626A76E29928B1600053070 /* TestsClosureNavigationResponder.swift in Sources */,
1206312073
3707C71A294B5D0F00682A9F /* TabExtensions.swift in Sources */,
@@ -13598,6 +13608,7 @@
1359813608
B693954C26F04BEB0015B914 /* FocusRingView.swift in Sources */,
1359913609
4BE41A5E28446EAD00760399 /* BookmarksBarViewModel.swift in Sources */,
1360013610
4B1E6EF127AB5E5D00F51793 /* NSPopUpButtonView.swift in Sources */,
13611+
37E13B8E2D54B03D002ECD62 /* HistoryViewDataProvider.swift in Sources */,
1360113612
372A0FEC2B2379310033BF7F /* SyncMetricsEventsHandler.swift in Sources */,
1360213613
1D5C1AF12CFF58220073ED65 /* Logger+WebExtensions.swift in Sources */,
1360313614
85774B032A71CDD000DE0561 /* BlockMenuItem.swift in Sources */,
@@ -14002,6 +14013,7 @@
1400214013
85378DA2274E7F25007C5CBF /* EmailManagerRequestDelegate.swift in Sources */,
1400314014
1D43EB36292ACE690065E5D6 /* ApplicationVersionReader.swift in Sources */,
1400414015
566B736C2BECC3C600FF1959 /* SyncPausedStateManaging.swift in Sources */,
14016+
37C7493B2D55FE710065B48B /* HistoryViewActionsHandler.swift in Sources */,
1400514017
4BD18F01283F0BC500058124 /* BookmarksBarViewController.swift in Sources */,
1400614018
379DE4BD27EA31AC002CC3DE /* PreferencesAutofillView.swift in Sources */,
1400714019
F1476FC02C1359FB00EAE46A /* SubscriptionUIHandler.swift in Sources */,
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "HistoryFavicon.svg",
5+
"idiom" : "universal"
6+
}
7+
],
8+
"info" : {
9+
"author" : "xcode",
10+
"version" : 1
11+
}
12+
}
Lines changed: 21 additions & 0 deletions
Loading

DuckDuckGo/Common/Extensions/ArrayExtension.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ extension Array {
2626
}
2727
}
2828

29+
func chunk(with limit: Int, offset: Int) -> [Element] {
30+
guard !isEmpty, offset < count else {
31+
return []
32+
}
33+
let endIndex = Swift.min(offset + limit, count)
34+
return Array(self[offset ..< endIndex])
35+
}
36+
2937
/// Map collection insertion indexes for a filtered collection into a non-filtered collection
3038
/// Used to skip `stub` or `pendingDeletion` object indices in a full (non-filtered) database
3139
/// items collection and use insertion indexes of a filtered collection, the one that‘s displaying non-stub, non-deleted items.

DuckDuckGo/History/Services/HistoryDebugMenu.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,13 +37,13 @@ final class HistoryDebugMenu: NSMenu {
3737
representedObject: (10, FakeURLsPool.random10Domains)
3838
)
3939
NSMenuItem(
40-
title: "Populate 100 history visits each day (10 domains)",
40+
title: "Add 100 history visits each day (10 domains)",
4141
action: #selector(populateFakeHistory),
4242
target: self,
4343
representedObject: (100, FakeURLsPool.random10Domains)
4444
)
4545
NSMenuItem(
46-
title: "Populate 100 history visits each day (200 domains – SLOW!)",
46+
title: "Add 100 history visits each day (200 domains – SLOW!)",
4747
action: #selector(populateFakeHistory),
4848
target: self,
4949
representedObject: (100, FakeURLsPool.random200Domains)
@@ -75,7 +75,9 @@ final class HistoryDebugMenu: NSMenu {
7575
continue
7676
}
7777
let visitDate = Date(timeIntervalSince1970: TimeInterval.random(in: date.startOfDay.timeIntervalSince1970..<date.timeIntervalSince1970))
78+
let title = url.host?.split(separator: ".").first.flatMap(String.init) ?? "Test"
7879
historyCoordinator.addVisit(of: url, at: visitDate)
80+
historyCoordinator.updateTitleIfNeeded(title: title, url: url)
7981
visitsPerDay += 1
8082
if visitsPerDay >= maxVisitsPerDay {
8183
date = date.daysAgo(1)

DuckDuckGo/History/Services/HistoryGroupingProvider.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ protocol HistoryGroupingDataSource: AnyObject {
3030
var history: BrowsingHistory? { get }
3131
}
3232

33+
struct HistoryGrouping {
34+
let date: Date
35+
let visits: [Visit]
36+
}
37+
3338
extension HistoryCoordinator: HistoryGroupingDataSource {}
3439

3540
/**
@@ -59,10 +64,10 @@ final class HistoryGroupingProvider {
5964
/**
6065
* Returns history visits bucketed per day.
6166
*/
62-
func getVisitGroupings() -> [HistoryMenu.HistoryGrouping] {
67+
func getVisitGroupings() -> [HistoryGrouping] {
6368
Dictionary(grouping: getSortedArrayOfVisits(), by: \.date.startOfDay)
6469
.map { date, sortedVisits in
65-
HistoryMenu.HistoryGrouping(date: date, visits: removeDuplicatesIfNeeded(from: sortedVisits))
70+
HistoryGrouping(date: date, visits: removeDuplicatesIfNeeded(from: sortedVisits))
6671
}
6772
.sorted { $0.date > $1.date }
6873
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//
2+
// HistoryViewActionsHandler.swift
3+
//
4+
// Copyright © 2025 DuckDuckGo. All rights reserved.
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
19+
import HistoryView
20+
21+
final class HistoryViewActionsHandler: HistoryView.ActionsHandling {
22+
23+
@MainActor
24+
func open(_ url: URL) {
25+
guard let tabCollectionViewModel else {
26+
return
27+
}
28+
29+
if NSApplication.shared.isCommandPressed && NSApplication.shared.isOptionPressed {
30+
WindowsManager.openNewWindow(with: url, source: .bookmark, isBurner: tabCollectionViewModel.isBurner)
31+
} else if NSApplication.shared.isCommandPressed && NSApplication.shared.isShiftPressed {
32+
tabCollectionViewModel.insertOrAppendNewTab(.contentFromURL(url, source: .bookmark), selected: true)
33+
} else if NSApplication.shared.isCommandPressed {
34+
tabCollectionViewModel.insertOrAppendNewTab(.contentFromURL(url, source: .bookmark), selected: false)
35+
} else {
36+
tabCollectionViewModel.selectedTabViewModel?.tab.setContent(.contentFromURL(url, source: .historyEntry))
37+
}
38+
}
39+
40+
@MainActor
41+
private var tabCollectionViewModel: TabCollectionViewModel? {
42+
WindowControllersManager.shared.lastKeyMainWindowController?.mainViewController.tabCollectionViewModel
43+
}
44+
}

0 commit comments

Comments
 (0)