Skip to content

Commit 3442516

Browse files
1 parent f33afbd commit 3442516

File tree

4 files changed

+83
-18
lines changed

4 files changed

+83
-18
lines changed

Coder Desktop/Coder Desktop/Coder_DesktopApp.swift

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import FluidMenuBarExtra
2+
import NetworkExtension
23
import SwiftUI
34

45
@main
@@ -26,7 +27,7 @@ struct DesktopApp: App {
2627

2728
@MainActor
2829
class AppDelegate: NSObject, NSApplicationDelegate {
29-
private var menuBarExtra: FluidMenuBarExtra?
30+
private var menuBar: MenuBarController?
3031
let vpn: CoderVPNService
3132
let state: AppState
3233

@@ -36,11 +37,18 @@ class AppDelegate: NSObject, NSApplicationDelegate {
3637
}
3738

3839
func applicationDidFinishLaunching(_: Notification) {
39-
menuBarExtra = FluidMenuBarExtra(title: "Coder Desktop", image: "MenuBarIcon") {
40+
menuBar = .init(menuBarExtra: FluidMenuBarExtra(title: "Coder Desktop", image: "MenuBarIcon") {
4041
VPNMenu<CoderVPNService>().frame(width: 256)
4142
.environmentObject(self.vpn)
4243
.environmentObject(self.state)
43-
}
44+
})
45+
// Subscribe to system VPN updates
46+
NotificationCenter.default.addObserver(
47+
self,
48+
selector: #selector(vpnDidUpdate(_:)),
49+
name: .NEVPNStatusDidChange,
50+
object: nil
51+
)
4452
}
4553

4654
// This function MUST eventually call `NSApp.reply(toApplicationShouldTerminate: true)`
@@ -59,6 +67,16 @@ class AppDelegate: NSObject, NSApplicationDelegate {
5967
}
6068
}
6169

70+
extension AppDelegate {
71+
@objc private func vpnDidUpdate(_ notification: Notification) {
72+
guard let connection = notification.object as? NETunnelProviderSession else {
73+
return
74+
}
75+
vpn.vpnDidUpdate(connection)
76+
menuBar?.vpnDidUpdate(connection)
77+
}
78+
}
79+
6280
@MainActor
6381
func appActivate() {
6482
NSApp.activate()
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import FluidMenuBarExtra
2+
import NetworkExtension
3+
import SwiftUI
4+
5+
@MainActor
6+
class MenuBarController {
7+
let menuBarExtra: FluidMenuBarExtra
8+
private let onImage = NSImage(named: "MenuBarIcon")!
9+
private let offOpacity = CGFloat(0.3)
10+
private let onOpacity = CGFloat(1.0)
11+
12+
private var animationTask: Task<Void, Never>?
13+
14+
init(menuBarExtra: FluidMenuBarExtra) {
15+
self.menuBarExtra = menuBarExtra
16+
}
17+
18+
func vpnDidUpdate(_ connection: NETunnelProviderSession) {
19+
switch connection.status {
20+
case .connected:
21+
stopAnimation()
22+
menuBarExtra.setOpacity(onOpacity)
23+
case .connecting, .reasserting, .disconnecting:
24+
startAnimation()
25+
case .invalid, .disconnected:
26+
stopAnimation()
27+
menuBarExtra.setOpacity(offOpacity)
28+
@unknown default:
29+
stopAnimation()
30+
menuBarExtra.setOpacity(offOpacity)
31+
}
32+
}
33+
34+
func startAnimation() {
35+
if animationTask != nil { return }
36+
animationTask = Task {
37+
defer { animationTask = nil }
38+
let totalFrames = 60
39+
let cycleDurationMs: UInt64 = 2000
40+
let frameDurationMs = cycleDurationMs / UInt64(totalFrames - 1)
41+
repeat {
42+
for frame in 0 ..< totalFrames {
43+
if Task.isCancelled { break }
44+
let progress = Double(frame) / Double(totalFrames - 1)
45+
let alpha = 0.3 + 0.7 * (0.5 - 0.5 * cos(2 * Double.pi * progress))
46+
menuBarExtra.setOpacity(CGFloat(alpha))
47+
try? await Task.sleep(for: .milliseconds(frameDurationMs))
48+
}
49+
} while !Task.isCancelled
50+
}
51+
}
52+
53+
func stopAnimation() {
54+
animationTask?.cancel()
55+
animationTask = nil
56+
}
57+
}

Coder Desktop/Coder Desktop/VPNService.swift

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,6 @@ final class CoderVPNService: NSObject, VPNService {
7070
Task {
7171
await loadNetworkExtensionConfig()
7272
}
73-
NotificationCenter.default.addObserver(
74-
self,
75-
selector: #selector(vpnDidUpdate(_:)),
76-
name: .NEVPNStatusDidChange,
77-
object: nil
78-
)
7973
}
8074

8175
deinit {
@@ -159,13 +153,7 @@ final class CoderVPNService: NSObject, VPNService {
159153
}
160154

161155
extension CoderVPNService {
162-
// The number of NETunnelProviderSession states makes the excessive branching
163-
// necessary.
164-
// swiftlint:disable:next cyclomatic_complexity
165-
@objc private func vpnDidUpdate(_ notification: Notification) {
166-
guard let connection = notification.object as? NETunnelProviderSession else {
167-
return
168-
}
156+
public func vpnDidUpdate(_ connection: NETunnelProviderSession) {
169157
switch (tunnelState, connection.status) {
170158
// Any -> Disconnected: Update UI w/ error if present
171159
case (_, .disconnected):

Coder Desktop/project.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,10 @@ packages:
8989
url: https://github.com/SimplyDanny/SwiftLintPlugins
9090
from: 0.57.1
9191
FluidMenuBarExtra:
92-
url: https://github.com/lfroms/fluid-menu-bar-extra
93-
from: 1.1.0
92+
# Forked so we can dynamically update the menu bar icon.
93+
# The upstream repo has a purposefully limited API
94+
url: https://github.com/coder/fluid-menu-bar-extra
95+
revision: 020be37
9496
KeychainAccess:
9597
url: https://github.com/kishikawakatsumi/KeychainAccess
9698
branch: e0c7eebc5a4465a3c4680764f26b7a61f567cdaf

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy