Make profile "Connect" item a connection toggle

Requires adding multiple delegates to LightVPNManager.
This commit is contained in:
Davide De Rosa 2022-08-19 09:29:34 +02:00
parent 4d56ed6fca
commit 0fee726951
8 changed files with 127 additions and 20 deletions

View File

@ -40,14 +40,14 @@ class DefaultLightVPNManager: LightVPNManager {
vpnManager.currentState.vpnStatus.asLightVPNStatus
}
weak var delegate: LightVPNManagerDelegate?
private var delegates: [String: LightVPNManagerDelegate] = [:]
init() {
vpnManager.currentState.$isEnabled
.removeDuplicates()
.receive(on: DispatchQueue.main)
.sink {
self.delegate?.didUpdateState(
self.didUpdateState(
isEnabled: $0,
vpnStatus: self.vpnManager.currentState.vpnStatus.asLightVPNStatus
)
@ -57,7 +57,7 @@ class DefaultLightVPNManager: LightVPNManager {
.removeDuplicates()
.receive(on: DispatchQueue.main)
.sink {
self.delegate?.didUpdateState(
self.didUpdateState(
isEnabled: self.vpnManager.currentState.isEnabled,
vpnStatus: $0.asLightVPNStatus
)
@ -77,6 +77,13 @@ class DefaultLightVPNManager: LightVPNManager {
try? await vpnManager.connect(with: profileId, toServer: serverId)
}
}
@MainActor
func disconnect() {
Task {
await vpnManager.disable()
}
}
@MainActor
func toggle() {
@ -95,6 +102,22 @@ class DefaultLightVPNManager: LightVPNManager {
await vpnManager.reconnect()
}
}
func addDelegate(_ delegate: LightVPNManagerDelegate, withIdentifier identifier: String) {
delegates[identifier] = delegate
}
func removeDelegate(withIdentifier identifier: String) {
delegates.removeValue(forKey: identifier)
}
}
extension DefaultLightVPNManager: LightVPNManagerDelegate {
func didUpdateState(isEnabled: Bool, vpnStatus: LightVPNStatus) {
delegates.values.forEach {
$0.didUpdateState(isEnabled: isEnabled, vpnStatus: vpnStatus)
}
}
}
private extension VPNStatus {

View File

@ -46,11 +46,15 @@ public protocol LightVPNManager {
func connect(with profileId: UUID, to serverId: String)
func disconnect()
func toggle()
func reconnect()
var delegate: LightVPNManagerDelegate? { get set }
func addDelegate(_ delegate: LightVPNManagerDelegate, withIdentifier identifier: String)
func removeDelegate(withIdentifier identifier: String)
}
@objc

View File

@ -30,14 +30,40 @@ extension HostProfileItem {
let profile: LightProfile
private let vpnManager: LightVPNManager
private var didUpdate: ((LightVPNStatus) -> Void)?
init(_ profile: LightProfile, vpnManager: LightVPNManager) {
self.profile = profile
self.vpnManager = vpnManager
vpnManager.addDelegate(self, withIdentifier: profile.id.uuidString)
}
deinit {
vpnManager.removeDelegate(withIdentifier: profile.id.uuidString)
}
@objc func connectTo() {
vpnManager.connect(with: profile.id)
}
@objc func disconnect() {
vpnManager.disconnect()
}
func subscribe(_ block: @escaping (LightVPNStatus) -> Void) {
didUpdate = block
}
}
}
extension HostProfileItem.ViewModel: LightVPNManagerDelegate {
func didUpdateState(isEnabled: Bool, vpnStatus: LightVPNStatus) {
guard profile.isActive else {
didUpdate?(.disconnected)
return
}
didUpdate?(vpnStatus)
}
}

View File

@ -47,16 +47,23 @@ struct HostProfileItem: Item {
private func submenu() -> NSMenu {
let menu = NSMenu()
menu.autoenablesItems = false
let item = NSMenuItem(
title: L10n.Global.Strings.connect,
action: #selector(viewModel.connectTo),
keyEquivalent: ""
)
item.target = viewModel
item.representedObject = viewModel
let toggleItem = NSMenuItem()
toggleItem.target = viewModel
toggleItem.representedObject = viewModel
menu.addItem(item)
viewModel.subscribe {
if $0 == .disconnected {
toggleItem.title = L10n.Global.Strings.connect
toggleItem.action = #selector(viewModel.connectTo)
} else {
toggleItem.title = L10n.Global.Strings.disconnect
toggleItem.action = #selector(viewModel.disconnect)
}
}
menu.addItem(toggleItem)
return menu
}
}

View File

@ -45,10 +45,14 @@ extension PassepartoutMenu {
self.profileManager = profileManager
self.vpnManager = vpnManager
vpnManager.delegate = self
vpnManager.addDelegate(self, withIdentifier: "PassepartoutMenu")
setStatus(vpnManager.vpnStatus)
}
deinit {
vpnManager.removeDelegate(withIdentifier: "PassepartoutMenu")
}
func install(systemMenu: SystemMenu) {
statusItem.menu = systemMenu.asMenu
}

View File

@ -33,10 +33,18 @@ extension ProviderProfileItem {
private let vpnManager: LightVPNManager
private var didUpdate: ((LightVPNStatus) -> Void)?
init(_ profile: LightProfile, providerManager: LightProviderManager, vpnManager: LightVPNManager) {
self.profile = profile
self.providerManager = providerManager
self.vpnManager = vpnManager
vpnManager.addDelegate(self, withIdentifier: profile.id.uuidString)
}
deinit {
vpnManager.removeDelegate(withIdentifier: profile.id.uuidString)
}
private var providerName: String {
@ -55,15 +63,33 @@ extension ProviderProfileItem {
}
func isActiveCategory(_ category: LightProviderCategory) -> Bool {
return category.name == profile.providerServer?.categoryName
category.name == profile.providerServer?.categoryName
}
func connectTo() {
@objc func connectTo() {
vpnManager.connect(with: profile.id)
}
@objc func disconnect() {
vpnManager.disconnect()
}
func downloadIfNeeded() {
providerManager.downloadIfNeeded(providerName, vpnProtocol: vpnProtocol)
}
func subscribe(_ block: @escaping (LightVPNStatus) -> Void) {
didUpdate = block
}
}
}
extension ProviderProfileItem.ViewModel: LightVPNManagerDelegate {
func didUpdateState(isEnabled: Bool, vpnStatus: LightVPNStatus) {
guard profile.isActive else {
didUpdate?(.disconnected)
return
}
didUpdate?(vpnStatus)
}
}

View File

@ -50,6 +50,8 @@ struct ProviderProfileItem: Item {
private func submenu() -> NSMenu {
let menu = NSMenu()
menu.autoenablesItems = false
let categories = viewModel.categories
guard !categories.isEmpty else {
let downloadItem = TextItem(L10n.Global.Strings.download) {
@ -59,10 +61,21 @@ struct ProviderProfileItem: Item {
return menu
}
let connectItem = TextItem(L10n.Global.Strings.connect) {
viewModel.connectTo()
let toggleItem = NSMenuItem()
toggleItem.target = viewModel
toggleItem.representedObject = viewModel
viewModel.subscribe {
if $0 == .disconnected {
toggleItem.title = L10n.Global.Strings.connect
toggleItem.action = #selector(viewModel.connectTo)
} else {
toggleItem.title = L10n.Global.Strings.disconnect
toggleItem.action = #selector(viewModel.disconnect)
}
}
menu.addItem(connectItem.asMenuItem(withParent: menu))
menu.addItem(toggleItem)
menu.addItem(.separator())
if categories.count > 1 {

View File

@ -47,7 +47,11 @@ extension VPNItemGroup {
self.toggleTitleBlock = toggleTitleBlock
self.reconnectTitleBlock = reconnectTitleBlock
vpnManager.delegate = self
vpnManager.addDelegate(self, withIdentifier: "VPNItemGroup")
}
deinit {
vpnManager.removeDelegate(withIdentifier: "VPNItemGroup")
}
var toggleTitle: String {