Skip to content
This repository was archived by the owner on Feb 24, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
cd4e0e4
Update header
quanganhdo Mar 27, 2024
3a8ebc9
Status badge
quanganhdo Apr 2, 2024
8955662
Merge branch 'main' into anh/netp/screen-improvements
quanganhdo Apr 2, 2024
5926f89
Merge branch 'main' of github.com:duckduckgo/macos-browser into anh/n…
quanganhdo Apr 2, 2024
bf32d19
New assets
quanganhdo Apr 3, 2024
81fcbd4
Add Lottie to VPN target
quanganhdo Apr 3, 2024
cbbd23d
Play lottie animation with intro
quanganhdo Apr 3, 2024
6257eea
Move assets
quanganhdo Apr 3, 2024
2df0228
Use correct size
quanganhdo Apr 3, 2024
ff913e8
Skip intro if not user initiated
quanganhdo Apr 3, 2024
18a73c3
Add Lottie to VPN App Store
quanganhdo Apr 3, 2024
29e84f2
Merge branch 'main' into anh/netp/screen-improvements
quanganhdo Apr 4, 2024
0c9b716
Address warnings
quanganhdo Apr 4, 2024
ea471c5
Bad workaround for popover positioning issue
quanganhdo Apr 5, 2024
c0f64a1
Remove unreliable check
quanganhdo Apr 5, 2024
7206235
Fix typo
quanganhdo Apr 5, 2024
a594d25
Merge branch 'main' into anh/netp/screen-improvements
quanganhdo Apr 8, 2024
f78be2b
Add Lottie to notification target
quanganhdo Apr 8, 2024
1eac1ff
Merge branch 'main' into anh/netp/screen-improvements
quanganhdo Apr 9, 2024
16887a8
Merge branch 'main' into anh/netp/screen-improvements
quanganhdo Apr 9, 2024
7ac9e80
Merge branch 'main' into anh/netp/screen-improvements
quanganhdo Apr 10, 2024
40907ad
Merge branch 'main' into anh/netp/screen-improvements
quanganhdo Apr 12, 2024
2864ba9
VPN screen improvements #2: Location (#2590)
quanganhdo Apr 16, 2024
c7ecdbb
VPN screen improvements #3: Data volume + Menu items (#2611)
quanganhdo Apr 19, 2024
36576f7
Update BSK
quanganhdo Apr 19, 2024
d845726
Merge branch 'main' into anh/netp/screen-improvements
quanganhdo Apr 19, 2024
97b95ec
Merge branch 'main' into anh/netp/screen-improvements
quanganhdo Apr 19, 2024
6cf6f5d
Fix Swiftlint warnings
quanganhdo Apr 19, 2024
e05cc7c
Fix botched merge
quanganhdo Apr 19, 2024
082ac88
Merge branch 'main' into anh/netp/screen-improvements
quanganhdo Apr 19, 2024
bb90b30
Fix broken tests
quanganhdo Apr 19, 2024
ea57352
Fix tests
quanganhdo Apr 19, 2024
1e39ac1
Update BSK
quanganhdo Apr 19, 2024
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
80 changes: 79 additions & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion DuckDuckGo/Application/URLEventHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ final class URLEventHandler {
case AppLaunchCommand.shareFeedback.launchURL:
WindowControllersManager.shared.showShareFeedbackModal()
case AppLaunchCommand.justOpen.launchURL:
WindowControllersManager.shared.showNewWindow()
WindowControllersManager.shared.showMainWindow()
case AppLaunchCommand.showVPNLocations.launchURL:
WindowControllersManager.shared.showPreferencesTab(withSelectedPane: .vpn)
WindowControllersManager.shared.showLocationPickerSheet()
Expand Down
15 changes: 12 additions & 3 deletions DuckDuckGo/Common/Localizables/UserText+NetworkProtection.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ extension UserText {
static let networkProtectionInviteSuccessMessage = "DuckDuckGo's VPN secures all of your device's Internet traffic anytime, anywhere."

// MARK: - Navigation Bar Status View
// "network.protection.navbar.status.view.share.feedback" - Menu item for 'Send VPN Feedback' in the VPN status view that's shown in the navigation bar
static let networkProtectionNavBarStatusViewShareFeedback = "Send VPN Feedback…"
// "network.protection.navbar.status.view.share.feedback" - Menu item for 'Share VPN Feedback' in the VPN status view that's shown in the navigation bar
static let networkProtectionNavBarStatusViewShareFeedback = "Share VPN Feedback…"
// "network.protection.status.menu.vpn.settings" - The status menu 'VPN Settings' menu item
static let networkProtectionNavBarStatusMenuVPNSettings = "VPN Settings…"
// "network.protection.status.menu.faq" - The status menu 'FAQ' menu item
Expand Down Expand Up @@ -299,7 +299,7 @@ extension UserText {
// "vpn.location.description.nearest" - Nearest city setting description
static let vpnLocationNearest = "Nearest"
// "vpn.location.description.nearest.available" - Nearest available location setting description
static let vpnLocationNearestAvailable = "Nearest available"
static let vpnLocationNearestAvailable = "Nearest Location"
// "vpn.location.nearest.available.title" - Subtitle underneath the nearest available vpn location preference text.
static let vpnLocationNearestAvailableSubtitle = "Automatically connect to the nearest server we can find."

Expand Down Expand Up @@ -330,6 +330,15 @@ extension UserText {
static let uninstallVPNAlertTitle = "Are you sure you want to uninstall the VPN?"
// "vpn.uninstall.alert.informative.text" - Informative text for the alert that comes up when the user decides to uninstall our VPN
static let uninstallVPNInformativeText = "Uninstalling the DuckDuckGo VPN will disconnect the VPN and remove it from your device."

// MARK: - VPN Screen
// "network.protection.vpn.location.nearest" - Description of the location type in the VPN status view
static let netPVPNLocationNearest = "(Nearest)"

// "network.protection.vpn.location.subtitle.formatted.city.and.country" - Subtitle for the preferred location item that formats a city and country. E.g Chicago, United States
static func netPVPNSettingsLocationSubtitleFormattedCityAndCountry(city: String, country: String) -> String {
return "\(city), \(country)"
}
}

#if DBP
Expand Down
3 changes: 2 additions & 1 deletion DuckDuckGo/MainWindow/MainViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ final class MainViewController: NSViewController {
serverInfoObserver: ipcClient.ipcServerInfoObserver,
connectionErrorObserver: ipcClient.ipcConnectionErrorObserver,
connectivityIssuesObserver: connectivityIssuesObserver,
controllerErrorMessageObserver: controllerErrorMessageObserver
controllerErrorMessageObserver: controllerErrorMessageObserver,
dataVolumeObserver: ipcClient.ipcDataVolumeObserver
)
}()

Expand Down
6 changes: 6 additions & 0 deletions DuckDuckGo/NavigationBar/View/NetPPopoverManagerMock.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ final class IPCClientMock: NetworkProtectionIPCClient {
}
var ipcControllerErrorMessageObserver: any NetworkProtection.ControllerErrorMesssageObserver = ControllerErrorMesssageObserverMock()

final class DataVolumeObserverMock: NetworkProtection.DataVolumeObserver {
var publisher: AnyPublisher<DataVolume, Never> = PassthroughSubject().eraseToAnyPublisher()
var recentValue: DataVolume = .init()
}
var ipcDataVolumeObserver: any NetworkProtection.DataVolumeObserver = DataVolumeObserverMock()

func start(completion: @escaping (Error?) -> Void) {
completion(nil)
}
Expand Down

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ protocol NetworkProtectionIPCClient {
var ipcStatusObserver: ConnectionStatusObserver { get }
var ipcServerInfoObserver: ConnectionServerInfoObserver { get }
var ipcConnectionErrorObserver: ConnectionErrorObserver { get }
var ipcDataVolumeObserver: DataVolumeObserver { get }

func start(completion: @escaping (Error?) -> Void)
func stop(completion: @escaping (Error?) -> Void)
Expand All @@ -38,6 +39,7 @@ extension TunnelControllerIPCClient: NetworkProtectionIPCClient {
public var ipcStatusObserver: any NetworkProtection.ConnectionStatusObserver { connectionStatusObserver }
public var ipcServerInfoObserver: any NetworkProtection.ConnectionServerInfoObserver { serverInfoObserver }
public var ipcConnectionErrorObserver: any NetworkProtection.ConnectionErrorObserver { connectionErrorObserver }
public var ipcDataVolumeObserver: any NetworkProtection.DataVolumeObserver { dataVolumeObserver }
}

final class NetworkProtectionNavBarPopoverManager: NetPPopoverManager {
Expand All @@ -57,7 +59,7 @@ final class NetworkProtectionNavBarPopoverManager: NetPPopoverManager {

// swiftlint:disable:next function_body_length
func show(positionedBelow view: NSView, withDelegate delegate: NSPopoverDelegate) {
let popover = networkProtectionPopover ?? {
let popover = {

let controller = NetworkProtectionIPCTunnelController(ipcClient: ipcClient)

Expand All @@ -66,7 +68,8 @@ final class NetworkProtectionNavBarPopoverManager: NetPPopoverManager {
serverInfoObserver: ipcClient.ipcServerInfoObserver,
connectionErrorObserver: ipcClient.ipcConnectionErrorObserver,
connectivityIssuesObserver: ConnectivityIssueObserverThroughDistributedNotifications(),
controllerErrorMessageObserver: ControllerErrorMesssageObserverThroughDistributedNotifications()
controllerErrorMessageObserver: ControllerErrorMesssageObserverThroughDistributedNotifications(),
dataVolumeObserver: ipcClient.ipcDataVolumeObserver
)

let onboardingStatusPublisher = UserDefaults.netP.networkProtectionOnboardingStatusPublisher
Expand Down Expand Up @@ -111,6 +114,7 @@ final class NetworkProtectionNavBarPopoverManager: NetPPopoverManager {
agentLoginItem: LoginItem.vpnMenu,
isMenuBarStatusView: false,
userDefaults: .netP,
locationFormatter: DefaultVPNLocationFormatter(),
uninstallHandler: { [weak self] in
_ = await self?.networkProtectionFeatureDisabler.disable(keepAuthToken: false, uninstallSystemExtension: true)
})
Expand All @@ -132,6 +136,7 @@ final class NetworkProtectionNavBarPopoverManager: NetPPopoverManager {
func toggle(positionedBelow view: NSView, withDelegate delegate: NSPopoverDelegate) {
if let networkProtectionPopover, networkProtectionPopover.isShown {
networkProtectionPopover.close()
self.networkProtectionPopover = nil
} else {
let featureVisibility = DefaultNetworkProtectionVisibility()

Expand All @@ -145,5 +150,6 @@ final class NetworkProtectionNavBarPopoverManager: NetPPopoverManager {

func close() {
networkProtectionPopover?.close()
networkProtectionPopover = nil
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
//
// DefaultVPNLocationFormatter.swift
//
// Copyright © 2024 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 Foundation
import SwiftUI
import NetworkProtection

struct DefaultVPNLocationFormatter: VPNLocationFormatting {
func emoji(for country: String?,
preferredLocation someLocation: VPNSettings.SelectedLocation) -> String? {
if let country {
return NetworkProtectionVPNCountryLabelsModel(country: country, useFullCountryName: true).emoji
}

let preferredLocation = VPNLocationModel(selectedLocation: someLocation)
switch preferredLocation.icon {
case .defaultIcon:
return nil
case .emoji(let emoji):
return emoji
}
}

func string(from location: String?,
preferredLocation someLocation: VPNSettings.SelectedLocation) -> String {
let preferredLocation = VPNLocationModel(selectedLocation: someLocation)

if let location {
return preferredLocation.isNearest ? "\(location) (Nearest)" : location
}

return preferredLocation.title
}

@available(macOS 12, *)
func string(from location: String?,
preferredLocation someLocation: VPNSettings.SelectedLocation,
locationTextColor: Color,
preferredLocationTextColor: Color) -> AttributedString {
let preferredLocation = VPNLocationModel(selectedLocation: someLocation)

if let location {
var attributedString = AttributedString(
preferredLocation.isNearest ? "\(location) \(UserText.netPVPNLocationNearest)" : location
)
attributedString.foregroundColor = locationTextColor
if let range = attributedString.range(of: UserText.netPVPNLocationNearest) {
attributedString[range].foregroundColor = preferredLocationTextColor
}
return attributedString
}

var attributedString = AttributedString(preferredLocation.title)
attributedString.foregroundColor = locationTextColor
return attributedString
}
}

final class VPNLocationModel: ObservableObject {
enum LocationIcon {
case defaultIcon
case emoji(String)
}

let title: String
let icon: LocationIcon
let isNearest: Bool

init(selectedLocation: VPNSettings.SelectedLocation) {
switch selectedLocation {
case .nearest:
title = UserText.vpnLocationNearestAvailable
icon = .defaultIcon
isNearest = true
case .location(let location):
let countryLabelsModel = NetworkProtectionVPNCountryLabelsModel(country: location.country, useFullCountryName: true)
if let city = location.city {
title = UserText.netPVPNSettingsLocationSubtitleFormattedCityAndCountry(
city: city,
country: countryLabelsModel.title
)
} else {
title = countryLabelsModel.title
}
icon = .emoji(countryLabelsModel.emoji)
isNearest = false
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,13 @@ struct NetworkProtectionVPNCountryLabelsModel {
let emoji: String
let title: String

init(country: String) {
self.title = Locale.current.localizedString(forRegionCode: country) ?? country.capitalized
init(country: String, useFullCountryName: Bool = true) {
if useFullCountryName {
self.title = Locale.current.localizedString(forRegionCode: country) ?? country.capitalized
} else {
self.title = country.localizedUppercase
}

self.emoji = Self.flag(country: country)
}

Expand Down
3 changes: 2 additions & 1 deletion DuckDuckGo/VPNFeedbackForm/VPNMetadataCollector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,8 @@ final class DefaultVPNMetadataCollector: VPNMetadataCollector {
serverInfoObserver: ipcClient.serverInfoObserver,
connectionErrorObserver: ipcClient.connectionErrorObserver,
connectivityIssuesObserver: ConnectivityIssueObserverThroughDistributedNotifications(),
controllerErrorMessageObserver: ControllerErrorMesssageObserverThroughDistributedNotifications()
controllerErrorMessageObserver: ControllerErrorMesssageObserverThroughDistributedNotifications(),
dataVolumeObserver: ipcClient.dataVolumeObserver
)

// Force refresh just in case. A refresh is requested when the IPC client is created, but distributed notifications don't guarantee delivery
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGo/Windows/View/WindowControllersManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ extension WindowControllersManager {
}
}

func showNewWindow() {
func showMainWindow() {
guard WindowControllersManager.shared.lastKeyMainWindowController == nil else { return }
let tabCollection = TabCollection(tabs: [])
let tabCollectionViewModel = TabCollectionViewModel(tabCollection: tabCollection)
Expand Down
4 changes: 2 additions & 2 deletions DuckDuckGoDBPBackgroundAgent/UserText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,6 @@ import Foundation
final class UserText {
// MARK: - Status Menu

static let networkProtectionStatusMenuShareFeedback = NSLocalizedString("network.protection.status.menu.share.feedback", value: "Send VPN Feedback...", comment: "The status menu 'Send VPN Feedback' menu item")
static let networkProtectionStatusMenuOpenDuckDuckGo = NSLocalizedString("network.protection.status.menu.open.duckduckgo", value: "Open DuckDuckGo...", comment: "The status menu 'Open DuckDuckGo' menu item")
static let networkProtectionStatusMenuShareFeedback = NSLocalizedString("network.protection.status.menu.share.feedback", value: "Share VPN Feedback", comment: "The status menu 'Share VPN Feedback' menu item")
static let networkProtectionStatusMenuOpenDuckDuckGo = NSLocalizedString("network.protection.status.menu.open.duckduckgo", value: "Open DuckDuckGo", comment: "The status menu 'Open DuckDuckGo' menu item")
}
15 changes: 11 additions & 4 deletions DuckDuckGoVPN/DuckDuckGoVPNAppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -184,12 +184,18 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate {
platformNotificationCenter: NSWorkspace.shared.notificationCenter,
platformDidWakeNotification: NSWorkspace.didWakeNotification)

let dataVolumeObserver = DataVolumeObserverThroughSession(
tunnelSessionProvider: tunnelController,
platformNotificationCenter: NSWorkspace.shared.notificationCenter,
platformDidWakeNotification: NSWorkspace.didWakeNotification)

return DefaultNetworkProtectionStatusReporter(
statusObserver: statusObserver,
serverInfoObserver: serverInfoObserver,
connectionErrorObserver: errorObserver,
connectivityIssuesObserver: DisabledConnectivityIssueObserver(),
controllerErrorMessageObserver: ControllerErrorMesssageObserverThroughDistributedNotifications()
controllerErrorMessageObserver: ControllerErrorMesssageObserverThroughDistributedNotifications(),
dataVolumeObserver: dataVolumeObserver
)
}()

Expand Down Expand Up @@ -242,17 +248,18 @@ final class DuckDuckGoVPNAppDelegate: NSObject, NSApplicationDelegate {
StatusBarMenu.MenuItem(name: UserText.networkProtectionStatusMenuFAQ, action: { [weak self] in
await self?.appLauncher.launchApp(withCommand: .showFAQ)
}),
StatusBarMenu.MenuItem(name: UserText.networkProtectionStatusMenuShareFeedback, action: { [weak self] in
await self?.appLauncher.launchApp(withCommand: .shareFeedback)
}),
StatusBarMenu.MenuItem(name: UserText.networkProtectionStatusMenuOpenDuckDuckGo, action: { [weak self] in
await self?.appLauncher.launchApp(withCommand: .justOpen)
}),
StatusBarMenu.MenuItem(name: UserText.networkProtectionStatusMenuShareFeedback, action: { [weak self] in
await self?.appLauncher.launchApp(withCommand: .shareFeedback)
})
]
},
agentLoginItem: nil,
isMenuBarStatusView: true,
userDefaults: .netP,
locationFormatter: DefaultVPNLocationFormatter(),
uninstallHandler: { [weak self] in
guard let self else { return }
await self.vpnUninstaller.uninstall(includingSystemExtension: true)
Expand Down
10 changes: 10 additions & 0 deletions DuckDuckGoVPN/TunnelControllerIPCService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ final class TunnelControllerIPCService {
subscribeToErrorChanges()
subscribeToStatusUpdates()
subscribeToServerChanges()
subscribeToDataVolumeUpdates()

server.serverDelegate = self
}
Expand Down Expand Up @@ -84,6 +85,15 @@ final class TunnelControllerIPCService {
}
.store(in: &cancellables)
}

private func subscribeToDataVolumeUpdates() {
statusReporter.dataVolumeObserver.publisher
.subscribe(on: DispatchQueue.main)
.sink { [weak self] dataVolume in
self?.server.dataVolumeUpdated(dataVolume)
}
.store(in: &cancellables)
}
}

// MARK: - Requests from the client
Expand Down
2 changes: 1 addition & 1 deletion DuckDuckGoVPN/UserText.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,5 @@ final class UserText {
static let networkProtectionStatusMenuVPNSettings = NSLocalizedString("network.protection.status.menu.vpn.settings", value: "VPN Settings…", comment: "The status menu 'VPN Settings' menu item")
static let networkProtectionStatusMenuFAQ = NSLocalizedString("network.protection.status.menu.faq", value: "Frequently Asked Questions…", comment: "The status menu 'FAQ' menu item")
static let networkProtectionStatusMenuOpenDuckDuckGo = NSLocalizedString("network.protection.status.menu.vpn.open-duckduckgo", value: "Open DuckDuckGo…", comment: "The status menu 'Open DuckDuckGo' menu item")
static let networkProtectionStatusMenuShareFeedback = NSLocalizedString("network.protection.status.menu.share.feedback", value: "Send VPN Feedback…", comment: "The status menu 'Send VPN Feedback' menu item")
static let networkProtectionStatusMenuShareFeedback = NSLocalizedString("network.protection.status.menu.share.feedback", value: "Share VPN Feedback…", comment: "The status menu 'Share VPN Feedback' menu item")
}
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: "137.0.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "138.0.0"),
.package(path: "../PixelKit"),
.package(path: "../SwiftUIExtensions"),
.package(path: "../XPCHelper"),
Expand Down
2 changes: 1 addition & 1 deletion LocalPackages/NetworkProtectionMac/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ let package = Package(
.library(name: "NetworkProtectionUI", targets: ["NetworkProtectionUI"]),
],
dependencies: [
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "137.0.0"),
.package(url: "https://github.com/duckduckgo/BrowserServicesKit", exact: "138.0.0"),
.package(path: "../XPCHelper"),
.package(path: "../SwiftUIExtensions"),
.package(path: "../LoginItems"),
Expand Down
Loading