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

Commit bd19464

Browse files
Status Menu #3: Move code to NetworkProtection package. (#52)
Task/Issue URL: https://app.asana.com/0/0/1204215685631880/f * Adds options to simulate tunnel and controller errors for testing * The status bar menu is now controller through the main app * Moves several files to NetworkProtection and NetworkProtectionUI: * Removes some files submitted by mistake * Removes a copy of NetworkProtectionUI submitted by mistake * Added back an empty test file to NetworkProtectionUITests
1 parent 9a8590a commit bd19464

File tree

47 files changed

+578
-199
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+578
-199
lines changed

DuckDuckGo.xcodeproj/project.pbxproj

Lines changed: 22 additions & 28 deletions
Large diffs are not rendered by default.

DuckDuckGo/App Delegate/URLEventHandler.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@
1616
// limitations under the License.
1717
//
1818

19+
import ApplicationServices
1920
import Foundation
21+
import NetworkProtection
2022
import os.log
2123

2224
final class URLEventHandler {
@@ -108,10 +110,13 @@ final class URLEventHandler {
108110
/// Handles NetP URLs
109111
///
110112
private static func handleNetworkProtectionURL(_ url: URL) {
111-
if url == networkProtectionShowStatusURL {
113+
switch url {
114+
case AppLauncher.Command.showStatus.launchURL:
112115
Task {
113116
await WindowControllersManager.shared.showNetworkProtectionStatus()
114117
}
118+
default:
119+
return
115120
}
116121
}
117122
#endif

DuckDuckGo/Common/Localizables/UserText+NetworkProtection.swift

Lines changed: 0 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -22,41 +22,7 @@ extension UserText {
2222
static let networkProtectionTunnelName = NSLocalizedString("network.protection.tunnel.name", value: "DuckDuckGo Network Protection", comment: "The name of the NetP VPN that will be visible in the system to the user")
2323
static let networkProtection = NSLocalizedString("network.protection", value: "Network Protection", comment: "Menu item for opening Network Protection")
2424

25-
// MARK: - Connection Status
26-
27-
static let networkProtectionStatusDisconnected = NSLocalizedString("network.protection.status.disconnected", value: "Disconnected", comment: "The label for the NetP VPN when disconnected")
28-
static let networkProtectionStatusDisconnecting = NSLocalizedString("network.protection.status.disconnecting", value: "Disconnecting...", comment: "The label for the NetP VPN when disconnecting")
29-
static let networkProtectionStatusConnected = NSLocalizedString("network.protection.status.connected", value: "Connected", comment: "The label for the NetP VPN when connected")
30-
static let networkProtectionStatusConnecting = NSLocalizedString("network.protection.status.connected", value: "Connecting...", comment: "The label for the NetP VPN when connecting")
31-
static let networkProtectionStatusUnknown = NSLocalizedString("network.protection.status.unknown", value: "Unknown", comment: "When we can't tell the user what the status of the NetP VPN is")
32-
33-
// MARK: - Connection Information
34-
35-
static let networkProtectionServerAddressUnknown = NSLocalizedString("network.protection.server.address.unknown", value: "Unknown", comment: "When we can't tell the user the IP of the NetP server is")
36-
static let networkProtectionServerLocationUnknown = NSLocalizedString("network.protection.server.location.unknown", value: "Unknown", comment: "When we can't tell the user the location of the NetP server")
37-
38-
// MARK: - Status View
39-
40-
static let networkProtectionStatusViewTitle = NSLocalizedString("network.protection.status.view.title", value: "Network Protection", comment: "Title shown in NetworkProtection's status view.")
41-
static let networkProtectionStatusViewFeatureDesc = NSLocalizedString("network.protection.status.view.feature.description", value: "Hide your location from websites and conceal your online activity from Internet providers and others on your network.", comment: "Feature description shown in NetworkProtection's status view.")
42-
static let networkProtectionStatusViewConnLabel = NSLocalizedString("network.protection.status.view.connection.label", value: "Network Protection", comment: "Connection label shown in NetworkProtection's status view.")
43-
static let networkProtectionStatusViewConnDetails = NSLocalizedString("network.protection.status.view.connection.details", value: "Connection Details", comment: "Connection details label shown in NetworkProtection's status view.")
44-
static let networkProtectionStatusViewBetaWarning = NSLocalizedString("network.protection.status.view.beta.warning", value: "DuckDuckGo Network Protection is currently in beta.", comment: "Beta warning message shown in NetworkProtection's status view.")
45-
static let networkProtectionStatusViewLocation = NSLocalizedString("network.protection.status.view.location", value: "Location", comment: "Location label shown in NetworkProtection's status view.")
46-
static let networkProtectionStatusViewIPAddress = NSLocalizedString("network.protection.status.view.ip.address", value: "IP Address", comment: "IP Address label shown in NetworkProtection's status view.")
47-
static let networkProtectionStatusViewFeatureOff = NSLocalizedString("network.protection.status.view.feature.on", value: "Network Protection is OFF", comment: "Text shown in NetworkProtection's status view when NetP is OFF.")
48-
static let networkProtectionStatusViewFeatureOn = NSLocalizedString("network.protection.status.view.feature.on", value: "Network Protection is ON", comment: "Text shown in NetworkProtection's status view when NetP is ON.")
49-
static let networkProtectionStatusViewTimerZero = "00:00:00"
50-
static let networkProtectionStatusViewShareFeedbackPrefix = NSLocalizedString("network.protection.status.view.share.feedback.prefix", value: "Help us improve and ", comment: "Text shown in NetworkProtection's status view before 'share feedback'")
51-
static let networkProtectionStatusViewShareFeedbackSuffix = NSLocalizedString("network.protection.status.view.share.feedback.suffix", value: ".", comment: "Text shown in NetworkProtection's status view after 'share feedback'")
52-
static let networkProtectionStatusViewShareFeedback = NSLocalizedString("network.protection.status.view.share.feedback", value: "share feedback", comment: "Text shown in NetworkProtection's status view in a link that allows users to share feedback")
53-
5425
// MARK: - Navigation Bar
5526

5627
static let networkProtectionButtonTooltip = NSLocalizedString("network.protection.status.button.tooltip", value: "Network Protection", comment: "The tooltip for NetP's nav bar button")
57-
58-
// MARK: - Connection Issues
59-
60-
static let networkProtectionInterruptedReconnecting = NSLocalizedString("network.protection.interrupted.reconnecting", value: "Your Network Protection connection was interrupted. Attempting to reconnect now...", comment: "The warning message shown in NetP's status view when the connection is interrupted and its attempting to reconnect.")
61-
static let networkProtectionInterrupted = NSLocalizedString("network.protection.interrupted", value: "Network Protection was unable to connect at this time. Please try again later.", comment: "The warning message shown in NetP's status view when the connection is interrupted.")
6228
}

DuckDuckGo/Navigation Bar/View/MoreOptionsMenu.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import os.log
2121
import WebKit
2222
import BrowserServicesKit
2323
import SwiftUI
24+
import NetworkProtectionUI
2425

2526
protocol OptionsButtonMenuDelegate: AnyObject {
2627

@@ -94,7 +95,7 @@ final class MoreOptionsMenu: NSMenu {
9495
#if NETP
9596
addItem(withTitle: UserText.networkProtection, action: #selector(showNetworkProtectionStatus(_:)), keyEquivalent: "")
9697
.targetting(self)
97-
.withImage(.init(.vpnIcon))
98+
.withImage(.image(for: .vpnIcon))
9899
#endif
99100

100101
addItem(NSMenuItem.separator())
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
//
2+
// LoginItem.swift
3+
//
4+
// Copyright © 2023 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 ServiceManagement
20+
21+
/// Takes care of enabling and disabling a login item.
22+
///
23+
final class LoginItem {
24+
private static let resetDelay = 200
25+
26+
let agentBundleID: String
27+
28+
init(agentBundleID: String) {
29+
self.agentBundleID = agentBundleID
30+
}
31+
32+
func enable() throws {
33+
if #available(macOS 13.0, *) {
34+
try SMAppService.loginItem(identifier: agentBundleID).register()
35+
} else {
36+
SMLoginItemSetEnabled(agentBundleID as CFString, true)
37+
}
38+
}
39+
40+
func disable() throws {
41+
if #available(macOS 13.0, *) {
42+
try SMAppService.loginItem(identifier: agentBundleID).unregister()
43+
} else {
44+
SMLoginItemSetEnabled(agentBundleID as CFString, false)
45+
}
46+
}
47+
48+
func reset() async throws {
49+
try? disable()
50+
51+
if #available(macOS 13, *) {
52+
try await Task.sleep(for: .milliseconds(Self.resetDelay))
53+
} else {
54+
try await Task.sleep(nanoseconds: UInt64(Self.resetDelay) * NSEC_PER_MSEC)
55+
}
56+
57+
try enable()
58+
}
59+
}

DuckDuckGo/NetworkProtection/NetworkProtectionAgentManager.swift

Lines changed: 0 additions & 68 deletions
This file was deleted.

DuckDuckGo/NetworkProtection/NetworkProtectionIconPublisher.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
import Foundation
2020
import Combine
21+
import NetworkProtectionUI
2122

2223
final class NetworkProtectionIconPublisher {
2324

DuckDuckGo/NetworkProtection/NetworkProtectionNavBarButtonModel.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,15 @@
1919
import AppKit
2020
import Combine
2121
import Foundation
22+
import NetworkProtection
23+
import NetworkProtectionUI
2224

2325
/// Model for managing the NetP button in the Nav Bar.
2426
///
2527
final class NetworkProtectionNavBarButtonModel: NSObject, ObservableObject {
2628

2729
private let networkProtectionStatusReporter: NetworkProtectionStatusReporter
28-
private var status: NetworkProtectionConnectionStatus = .disconnected
30+
private var status: NetworkProtection.ConnectionStatus = .disconnected
2931
private let popovers: NavigationBarPopovers
3032

3133
// MARK: - Subscriptions
@@ -59,7 +61,7 @@ final class NetworkProtectionNavBarButtonModel: NSObject, ObservableObject {
5961
self.popovers = popovers
6062

6163
isHavingConnectivityIssues = networkProtectionStatusReporter.connectivityIssuesPublisher.value
62-
buttonImage = .init(iconPublisher.icon)
64+
buttonImage = .image(for: iconPublisher.icon)
6365

6466
super.init()
6567

@@ -76,7 +78,7 @@ final class NetworkProtectionNavBarButtonModel: NSObject, ObservableObject {
7678

7779
private func setupIconSubscription() {
7880
iconPublisherCancellable = iconPublisher.$icon.sink { [weak self] icon in
79-
self?.buttonImage = .init(icon)
81+
self?.buttonImage = .image(for: icon)
8082
}
8183
}
8284

DuckDuckGo/NetworkProtection/NetworkProtectionProvider.swift

Lines changed: 14 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
//
2-
// NSImage+NetworkProtection.swift
2+
// NetworkProtectionProvider.swift
33
//
44
// Copyright © 2022 DuckDuckGo. All rights reserved.
55
//
@@ -25,17 +25,7 @@ import NetworkExtension
2525
import NetworkProtection
2626
import SystemExtensions
2727

28-
enum NetworkProtectionConnectionStatus {
29-
case notConfigured
30-
case disconnected
31-
case disconnecting
32-
case connected(connectedDate: Date)
33-
case connecting
34-
case reasserting
35-
case unknown
36-
}
37-
38-
typealias NetworkProtectionStatusChangeHandler = (NetworkProtectionConnectionStatus) -> Void
28+
typealias NetworkProtectionStatusChangeHandler = (NetworkProtection.ConnectionStatus) -> Void
3929
typealias NetworkProtectionConfigChangeHandler = () -> Void
4030

4131
protocol NetworkProtectionProvider: NetworkProtection.TunnelController {
@@ -314,16 +304,21 @@ final class DefaultNetworkProtectionProvider: NetworkProtectionProvider {
314304

315305
// MARK: - Ensure things are working
316306

307+
private let loginItem = LoginItem(agentBundleID: "HKE973VLUW.com.duckduckgo.macos.browser.network-protection.notifications")
308+
309+
private func ensureLoginItemsAreEnabled() async throws {
310+
#if DEBUG
311+
try await loginItem.reset()
312+
#else
313+
try loginItem.enable()
314+
#endif
315+
}
316+
317317
#if NETP_SYSTEM_EXTENSION
318318
/// - Returns: `true` if the system extension and the background agent were activated successfully
319319
///
320320
private func ensureSystemExtensionAndAgentAreActivated() async throws -> Bool {
321-
#if DEBUG
322-
try? await NetworkProtectionAgentManager.current.reset()
323-
#else
324-
325-
NetworkProtectionAgentManager.current.enable()
326-
#endif
321+
try await ensureLoginItemsAreEnabled()
327322

328323
if case .willActivateAfterReboot = try await SystemExtensionManager.shared.activate(waitingForUserApprovalHandler: { [weak self] in
329324
self?.controllerErrorStore.lastErrorMessage = "Go to Security & Privacy in System Settings to allow Network Protection to activate"
@@ -454,7 +449,7 @@ final class DefaultNetworkProtectionProvider: NetworkProtectionProvider {
454449
}
455450

456451
#if NETP_SYSTEM_EXTENSION
457-
NetworkProtectionAgentManager.current.disable()
452+
try? LoginItem(agentBundleID: "HKE973VLUW.com.duckduckgo.macos.browser.network-protection.notifications").disable()
458453
#endif
459454
NetworkProtectionSelectedServerUserDefaultsStore().reset()
460455
}

DuckDuckGo/NetworkProtection/NetworkProtectionStatusBarMenu.swift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import Foundation
2020
import Combine
2121
import SwiftUI
2222
import NetworkProtection
23+
import NetworkProtectionUI
2324
import os.log
2425

2526
/// Abstraction of the the Network Protection status bar menu with a simple interface.
@@ -57,14 +58,14 @@ final class NetworkProtectionStatusBarMenu {
5758

5859
let menu = NSMenu(items: [item])
5960
self.statusItem.menu = menu
60-
self.statusItem.button?.image = .init(iconPublisher.icon)
61+
self.statusItem.button?.image = .image(for: iconPublisher.icon)
6162

6263
subscribeToIconUpdates()
6364
}
6465

6566
private func subscribeToIconUpdates() {
6667
iconPublisherCancellable = iconPublisher.$icon.sink { [weak self] icon in
67-
self?.statusItem.button?.image = .init(icon)
68+
self?.statusItem.button?.image = .image(for: icon)
6869
}
6970
}
7071

0 commit comments

Comments
 (0)