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

Commit

Permalink
Merge branch 'main' into alex/zoom-improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
SabrinaTardio committed Feb 7, 2025
2 parents 2c7e751 + bb86771 commit a8fd5a3
Show file tree
Hide file tree
Showing 85 changed files with 2,477 additions and 242 deletions.
2 changes: 1 addition & 1 deletion Configuration/BuildNumber.xcconfig
Original file line number Diff line number Diff line change
@@ -1 +1 @@
CURRENT_PROJECT_VERSION = 354
CURRENT_PROJECT_VERSION = 359
4 changes: 3 additions & 1 deletion DuckDuckGo-macOS.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -12095,6 +12095,7 @@
9F6434622BEC82B700D2D8A0 /* AttributionPixelHandler.swift in Sources */,
843AD3DC2CD389CC00163067 /* XMLNodeExtension.swift in Sources */,
7B969B3E2D52A81D004AE4E8 /* VPNUIPresenting.swift in Sources */,
3706FC32293F65D500E42796 /* MoreOptionsMenu.swift in Sources */,
3706FC34293F65D500E42796 /* PermissionAuthorizationViewController.swift in Sources */,
3706FC35293F65D500E42796 /* BookmarkNode.swift in Sources */,
B6ABD0CB2BC03F610000EB69 /* SecurityScopedFileURLController.swift in Sources */,
Expand Down Expand Up @@ -13258,6 +13259,7 @@
4B9DB01D2A983B24000927DB /* Waitlist.swift in Sources */,
BB9BDD4A2D09BAA80069E9EF /* TabBarRemoteMessageViewModel.swift in Sources */,
3745DE062D536DCF00024FC8 /* HistoryGroupingProvider.swift in Sources */,
AAA0CC33252F181A0079BC96 /* NavigationButtonMenuDelegate.swift in Sources */,
1DA84D2F2C11989D0011C80F /* Update.swift in Sources */,
AAC30A2A268E239100D2D9CD /* CrashReport.swift in Sources */,
1D6A492029CF7A490011DF74 /* NSPopoverExtension.swift in Sources */,
Expand Down Expand Up @@ -15395,7 +15397,7 @@
repositoryURL = "https://github.com/duckduckgo/BrowserServicesKit";
requirement = {
kind = exactVersion;
version = 236.0.0;
version = 236.0.1;
};
};
9FF521422BAA8FF300B9819B /* XCRemoteSwiftPackageReference "lottie-spm" */ = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,17 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/BrowserServicesKit",
"state" : {
"revision" : "744895ff98a5f2213333be0170e495b1e85034d0",
"version" : "235.2.0"
"revision" : "ab64a6616c7b726a55b6c67c0da421c636db1224",
"version" : "236.0.1"
}
},
{
"identity" : "content-scope-scripts",
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/content-scope-scripts",
"state" : {
"revision" : "4a6bca2433aec573369546291bb6c53addef0a5f",
"version" : "7.15.0"
"revision" : "7a37fdc86198b0447e9ff17dbf494171bfaddc33",
"version" : "7.16.0"
}
},
{
Expand Down Expand Up @@ -113,8 +113,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/duckduckgo/privacy-dashboard",
"state" : {
"revision" : "c52bd5d851b1f8f0482e82b8720852670f525497",
"version" : "8.1.0"
"revision" : "099f7ed5faac946e4d80746703aaaf87fdfbee09",
"version" : "8.3.0"
}
},
{
Expand Down Expand Up @@ -158,8 +158,8 @@
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-crypto.git",
"state" : {
"revision" : "ff0f781cf7c6a22d52957e50b104f5768b50c779",
"version" : "3.10.0"
"revision" : "f2f3774fd116a305136b6866e5e7cb7dff39d8f2",
"version" : "3.10.1"
}
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@
<Test
Identifier = "AutoconsentIntegrationTests/testWhenAutoconsentDisabled_promptIsDisplayed()">
</Test>
<Test
Identifier = "ConfigurationManagerIntegrationTests/testTdsAreFetchedFromURLBasedOnPrivacyConfigExperiment()">
</Test>
<Test
Identifier = "CoreDataEncryptionTests/testSavingIncorrectValueTypes()">
</Test>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@
<Test
Identifier = "AutoconsentIntegrationTests/testWhenAutoconsentDisabled_promptIsDisplayed()">
</Test>
<Test
Identifier = "ConfigurationManagerIntegrationTests/testTdsAreFetchedFromURLBasedOnPrivacyConfigExperiment()">
</Test>
<Test
Identifier = "CoreDataEncryptionTests/testSavingIncorrectValueTypes()">
</Test>
Expand Down
15 changes: 15 additions & 0 deletions DuckDuckGo/Assets.xcassets/Images/Help-16.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "Help-16.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "Support-16.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.
37 changes: 36 additions & 1 deletion DuckDuckGo/Common/Localizables/UserText+NetworkProtection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,48 @@ extension UserText {

static let networkProtectionInviteSuccessMessage = NSLocalizedString("network.protection.invite.success.title", value: "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere.", comment: "Message for the VPN invite success view")

// MARK: - Navigation Bar Status View
// MARK: - VPN Status View submenu (legacy)

static let networkProtectionNavBarStatusViewSendFeedback = NSLocalizedString("network.protection.navbar.status.view.send.feedback", value: "Send Feedback…", comment: "Menu item for 'Send Feedback' in the VPN status view that's shown in the navigation bar")

static let networkProtectionNavBarStatusViewVPNSettings = NSLocalizedString("network.protection.navbar.status.view.vpn.settings", value: "VPN Settings…", comment: "The status menu 'VPN Settings' menu item")

static let networkProtectionNavBarStatusViewFAQ = NSLocalizedString("network.protection.navbar.status.view.faq", value: "FAQs and Support…", comment: "The status menu 'FAQ' menu item")

// MARK: - VPN Status View submenu

static let vpnStatusViewVPNSettingsMenuItemTitle = NSLocalizedString(
"vpn.status-view.vpn-settings.menu-item.title",
value: "VPN Settings",
comment: "The VPN status view's 'VPN Settings' menu item for our main app. The number shown is how many Apps are excluded.")

static func vpnStatusViewExcludedAppsMenuItemTitle(_ count: Int) -> String {
let message = NSLocalizedString(
"vpn.status-view.excluded-apps.menu-item.title",
value: "Excluded Apps (%d)",
comment: "The VPN status view's 'Excluded Apps' menu item for our main app. The number shown is how many Apps are excluded.")

return String(format: message, count)
}

static func vpnStatusViewExcludedDomainsMenuItemTitle(_ count: Int) -> String {
let message = NSLocalizedString(
"vpn.status-view.excluded-domains.menu-item.title",
value: "Excluded Websites (%d)",
comment: "The VPN status view's 'Excluded Websites' menu item for our main app. The number shown is how many websites are excluded.")

return String(format: message, count)
}

static let vpnStatusViewSendFeedbackMenuItemTitle = NSLocalizedString(
"vpn.status-view.send-feedback.menu-item.title",
value: "Send Feedback",
comment: "The VPN status view's 'Send Feedback' menu item for our main app")

static let vpnStatusViewFAQMenuItemTitle = NSLocalizedString(
"vpn.status-view.faq.menu-item.title",
value: "FAQs and Support",
comment: "The VPN status view's 'FAQ' menu item for our main app")
}

extension UserText {
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/Common/Localizables/UserText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ struct UserText {

static let downloadsOpenPopupOnCompletion = NSLocalizedString("downloads.open.on.completion", value: "Automatically open the Downloads panel when downloads complete", comment: "Checkbox to open a Download Manager popover when downloads are completed")

static let maliciousSiteDetectionHeader = NSLocalizedString("phishing-detection.enabled.header", value: "Malicious Site Protection", comment: "Header for phishing site protection section in the settings page")
static let maliciousSiteDetectionHeader = NSLocalizedString("phishing-detection.enabled.header", value: "Site Safety Warnings", comment: "Header for phishing site protection section in the settings page")
static let maliciousSiteDetectionIsEnabled = NSLocalizedString("phishing-detection.enabled.checkbox", value: "Warn me on sites flagged for phishing or malware.", comment: "Checkbox that enables or disables the phishing and malware detection feature in the browser")
static let maliciousDetectionEnabledWarning = NSLocalizedString("phishing-detection.enabled.warning", value: "Disabling this feature can put your personal information at risk.", comment: "A description box to warn users away from disabling the phishing and malware protection feature")

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!
}
}
}
}

}
89 changes: 89 additions & 0 deletions DuckDuckGo/History/Services/HistoryGroupingProvider.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
//
// HistoryGroupingProvider.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 BrowserServicesKit
import Common
import FeatureFlags
import Foundation
import History
import os.log

/**
* This protocol describes a data source (history provider) for `HistoryGroupingProvider`.
*/
protocol HistoryGroupingDataSource: AnyObject {
var history: BrowsingHistory? { get }
}

extension HistoryCoordinator: HistoryGroupingDataSource {}

/**
* This class is responsible for grouping history visits for History Menu.
*
* When `historyView` feature flag is enabled, visits are deduplicated
* to only have the latest visit per URL per day.
*/
final class HistoryGroupingProvider {
private let featureFlagger: FeatureFlagger
private(set) weak var dataSource: HistoryGroupingDataSource?

init(dataSource: HistoryGroupingDataSource, featureFlagger: FeatureFlagger = NSApp.delegateTyped.featureFlagger) {
self.featureFlagger = featureFlagger
self.dataSource = dataSource
}

/**
* Returns visits for a given day.
*/
func getRecentVisits(maxCount: Int) -> [Visit] {
removeDuplicatesIfNeeded(from: getSortedArrayOfVisits())
.prefix(maxCount)
.filter { Calendar.current.isDateInToday($0.date) }
}

/**
* Returns history visits bucketed per day.
*/
func getVisitGroupings() -> [HistoryMenu.HistoryGrouping] {
Dictionary(grouping: getSortedArrayOfVisits(), by: \.date.startOfDay)
.map { date, sortedVisits in
HistoryMenu.HistoryGrouping(date: date, visits: removeDuplicatesIfNeeded(from: sortedVisits))
}
.sorted { $0.date > $1.date }
}

private func removeDuplicatesIfNeeded(from sortedVisits: [Visit]) -> [Visit] {
// History View introduces visits deduplication to declutter history.
// We don't want to release it before History View, so we're
// only deduplicating visits if the feature flag is on.
guard featureFlagger.isFeatureOn(.historyView) else {
return sortedVisits
}
// It's so simple because visits array is sorted by timestamp, so removing duplicates would
// remove items with older timestamps (as proven by unit tests).
return sortedVisits.removingDuplicates(byKey: \.historyEntry?.url)
}

private func getSortedArrayOfVisits() -> [Visit] {
guard let history = dataSource?.history else {
Logger.general.error("HistoryCoordinator: No history available")
return []
}
return history.flatMap(\.visits).sorted { $0.date > $1.date }
}
}
2 changes: 1 addition & 1 deletion DuckDuckGo/HomePage/View/HomePageViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ final class HomePageViewController: NSViewController {

func createRecentlyVisitedModel() -> HomePage.Models.RecentlyVisitedModel {
return .init { [weak self] url in
PixelKit.fire(GeneralPixel.privacyFeedHistoryLinkOpened, frequency: .dailyAndCount)
PixelKit.fire(NewTabPagePixel.privacyFeedHistoryLinkOpened, frequency: .dailyAndCount)
self?.openUrl(url)
}
}
Expand Down
Loading

0 comments on commit a8fd5a3

Please sign in to comment.