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

Implement VPN App Exclusions management view #3800

Merged
merged 33 commits into from
Feb 3, 2025
Merged
Changes from 1 commit
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
d651390
Adds the VPN App Exclusions remote feature flag
diegoreymendez Jan 15, 2025
2a99818
WIP
diegoreymendez Jan 15, 2025
50ae50e
Creates PreferencesUI package
diegoreymendez Jan 23, 2025
69d8f07
Rolls back some unintentional string changes
diegoreymendez Jan 23, 2025
c0f0d5a
Rolls back some changes
diegoreymendez Jan 23, 2025
60b2d30
Merges the latest from main
diegoreymendez Jan 23, 2025
7d27544
Fixes a swiftlint warning
diegoreymendez Jan 23, 2025
375f060
Fixes a bad merge
diegoreymendez Jan 23, 2025
22e0456
Renaming for clarity
diegoreymendez Jan 23, 2025
1c26266
Initial implementation for new exclusions in VPN settings complete
diegoreymendez Jan 25, 2025
1a6acb5
Swiftlint fixes
diegoreymendez Jan 25, 2025
dedaf62
Merges the latest from main
diegoreymendez Jan 25, 2025
8f24a92
Fixes a swiftlint error
diegoreymendez Jan 25, 2025
4e4e2d1
Merges the latest from develop
diegoreymendez Jan 28, 2025
2f453a3
Adds translations
diegoreymendez Jan 29, 2025
7f48ad6
Merges the latest from main
diegoreymendez Jan 29, 2025
e38b913
WIP
diegoreymendez Jan 29, 2025
3d77bf4
Merge branch 'diego/vpn-app-exclusions-settings-pane-changes' into di…
diegoreymendez Jan 29, 2025
3b5e770
WIP
diegoreymendez Jan 29, 2025
b6bbe27
Implements app exclusions management view
diegoreymendez Jan 29, 2025
de4e6fa
Localized a string that wasn't localized by mistake
diegoreymendez Jan 29, 2025
12ae307
Addresses some review feedback
diegoreymendez Jan 29, 2025
e11dcf3
Fixes the rounded corner of the SubfeatureView
diegoreymendez Jan 29, 2025
ba57596
Renames a file
diegoreymendez Jan 29, 2025
72dbb3f
Merge branch 'diego/vpn-app-exclusions-settings-pane-changes' into di…
diegoreymendez Jan 29, 2025
3a2d571
Merges the latest from main
diegoreymendez Jan 29, 2025
9d001b2
Implements the app-exclusions management view
diegoreymendez Jan 30, 2025
195a546
Rolls back an unintentional change and pushes an improvement
diegoreymendez Jan 31, 2025
860b1e3
Fixes the file name in a header
diegoreymendez Feb 3, 2025
8050810
Fixes the message in an assertion failure
diegoreymendez Feb 3, 2025
aee7ccd
Adds app exclusion translations
diegoreymendez Feb 3, 2025
263857e
Removes empty files
diegoreymendez Feb 3, 2025
62d13ae
Exclusion counts in VPN settings now update dynamically
diegoreymendez Feb 3, 2025
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
Prev Previous commit
Next Next commit
Initial implementation for new exclusions in VPN settings complete
diegoreymendez committed Jan 25, 2025

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit 1c26266cdfd6f98296c4c65488a47108c5b79850
2 changes: 1 addition & 1 deletion DuckDuckGo.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
@@ -3435,7 +3435,7 @@
/* Begin PBXCopyFilesBuildPhase section */
4B2D065D2A11D2AE00DE1F49 /* Embed Login Items */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
buildActionMask = 12;
dstPath = Contents/Library/LoginItems;
dstSubfolderSpec = 1;
files = (
15 changes: 15 additions & 0 deletions DuckDuckGo/Assets.xcassets/Images/Globe-16.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "Globe-16.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"template-rendering-intent" : "template"
}
}
Binary file not shown.
15 changes: 15 additions & 0 deletions DuckDuckGo/Assets.xcassets/Images/Window-16.imageset/Contents.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"images" : [
{
"filename" : "Window-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
@@ -179,6 +179,8 @@ extension UserText {

static let vpnExcludedSitesTitle = NSLocalizedString("vpn.excluded.sites.title", value: "Excluded Websites", comment: "Excluded Sites title in VPN settings")

static let vpnExcludedAppsTitle = NSLocalizedString("vpn.excluded.apps.title", value: "Excluded Apps", comment: "Excluded Apps title in VPN settings")

static let vpnGeneralTitle = NSLocalizedString("vpn.general.title", value: "General", comment: "General section title in VPN settings")

static let vpnShortcutsSettingsTitle = NSLocalizedString("vpn.shortcuts.settings.title", value: "Shortcuts", comment: "Shortcuts section title in VPN settings")
@@ -214,6 +216,12 @@ extension UserText {
return String(format: message, count)
}

// MARK: - Exclusions

static let vpnSettingsExclusionsDescription = NSLocalizedString("vpn.setting.exclusions.description", value: "Some websites and apps are not compatible with VPNs. Exclude these sites and apps to use them while connected to the VPN.", comment: "The description shown for the exclusions section in VPN settings")

static let vpnSettingsManageExclusionsButtonTitle = NSLocalizedString("vpn.setting.exclusions.manage.button.title", value: "Manage...", comment: "Title for the button to manage exclusions")

// MARK: - Excluded Domains

static let vpnExcludedDomainsDescription = NSLocalizedString("vpn.setting.excluded.domains.description", value: "Excluded websites will bypass the VPN.", comment: "Excluded Sites description")
48 changes: 48 additions & 0 deletions DuckDuckGo/Localizable.xcstrings
Original file line number Diff line number Diff line change
@@ -68885,6 +68885,18 @@
}
}
},
"vpn.excluded.apps.title" : {
"comment" : "Excluded Apps title in VPN settings",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Excluded Apps"
}
}
}
},
"vpn.excluded.domains.add.domain" : {
"comment" : "Add Domain button for the excluded sites view",
"extractionState" : "extracted_with_value",
@@ -69065,6 +69077,18 @@
}
}
},
"vpn.exclusions.title" : {
"comment" : "Exclusions section title in VPN settings",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Exclusions"
}
}
}
},
"vpn.feedback-form.button.cancel" : {
"comment" : "Title for the Cancel button of the VPN feedback form",
"extractionState" : "extracted_with_value",
@@ -71585,6 +71609,30 @@
}
}
},
"vpn.setting.exclusions.description" : {
"comment" : "The description shown for the exclusions section in VPN settings",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Some websites and apps are not compatible with VPNs. Exclude these sites and apps to use them while connected to the VPN."
}
}
}
},
"vpn.setting.exclusions.manage.button.title" : {
"comment" : "Title for the button to manage exclusions",
"extractionState" : "extracted_with_value",
"localizations" : {
"en" : {
"stringUnit" : {
"state" : "new",
"value" : "Manage..."
}
}
}
},
"vpn.setting.title.connect.on.login" : {
"comment" : "Connect on Login setting title",
"extractionState" : "extracted_with_value",
10 changes: 10 additions & 0 deletions DuckDuckGo/Preferences/Model/VPNPreferencesModel.swift
Original file line number Diff line number Diff line change
@@ -86,6 +86,16 @@ final class VPNPreferencesModel: ObservableObject {
proxySettings.proxyAvailable
}

var excludedDomainsCount: Int {
proxySettings.excludedDomains.count
}

var excludedAppsCount: Int {
proxySettings.appRoutingRules.filter { (bundleId, rule) in
rule == .exclude
}.count
}

@Published var notifyStatusChanges: Bool {
didSet {
settings.notifyStatusChanges = notifyStatusChanges
25 changes: 16 additions & 9 deletions DuckDuckGo/Preferences/View/PreferencesVPNView.swift
Original file line number Diff line number Diff line change
@@ -118,23 +118,24 @@ extension Preferences {

PreferencePaneSection {
TextMenuItemHeader(UserText.vpnExclusionsTitle)
TextMenuItemCaption(UserText.vpnSettingsExclusionsDescription)

SubfeatureGroup {
SubfeatureView(iconName: "VPN-Icon",
SubfeatureView(icon: Image(.globe16),
title: UserText.vpnExcludedSitesTitle,
description: "None",
buttonName: "Manage...",
buttonAction: { /* no-op */ },
description: exclusionCountString(value: model.excludedDomainsCount),
buttonName: UserText.vpnSettingsManageExclusionsButtonTitle,
buttonAction: { model.manageExcludedSites() },
enabled: true)

Divider()
.foregroundColor(Color.secondary)

SubfeatureView(iconName: "VPN-Icon",
title: "Excluded Apps",
description: "None",
buttonName: "Manage...",
buttonAction: { /* no-op */ },
SubfeatureView(icon: Image(.window16),
title: UserText.vpnExcludedAppsTitle,
description: exclusionCountString(value: model.excludedAppsCount),
buttonName: UserText.vpnSettingsManageExclusionsButtonTitle,
buttonAction: { /* will be implemented in follow-up PR */ },
enabled: true)
}
}
@@ -203,6 +204,12 @@ extension Preferences {
}
}
}

/// Resolves the text to be used for exclusion counts
///
private func exclusionCountString(value: Int) -> String {
value > 0 ? String(value) : "None"
}
}
}

3 changes: 3 additions & 0 deletions LocalPackages/PreferencesUI/Package.swift
Original file line number Diff line number Diff line change
@@ -15,6 +15,9 @@ let package = Package(
targets: [
.target(
name: "PreferencesUI",
dependencies: [
.product(name: "SwiftUIExtensions", package: "SwiftUIExtensions")
],
swiftSettings: [
.define("DEBUG", .when(configuration: .debug))
]
Original file line number Diff line number Diff line change
@@ -20,15 +20,16 @@ import SwiftUI
import SwiftUIExtensions

public struct SubfeatureView: View {
public var iconName: String
public var icon: Image
public var title: String
public var description: String
public var buttonName: String?
public var buttonAction: (() -> Void)?
public var enabled: Bool

public init(iconName: String, title: String, description: String, buttonName: String? = nil, buttonAction: (() -> Void)? = nil, enabled: Bool = true) {
self.iconName = iconName
public init(icon: Image, title: String, description: String, buttonName: String? = nil, buttonAction: (() -> Void)? = nil, enabled: Bool = true) {

self.icon = icon
self.title = title
self.description = description
self.buttonName = buttonName
@@ -40,7 +41,7 @@ public struct SubfeatureView: View {
VStack(alignment: .center) {
VStack {
HStack(alignment: .center, spacing: 8) {
Image(iconName, bundle: .module)
icon
.padding(4)
.background(Color(.badgeBackground))
.cornerRadius(4)