Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
24 changes: 21 additions & 3 deletions Coder Desktop/Coder Desktop/Coder_DesktopApp.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import FluidMenuBarExtra
import NetworkExtension
import SwiftUI

@main
Expand Down Expand Up @@ -26,7 +27,7 @@ struct DesktopApp: App {

@MainActor
class AppDelegate: NSObject, NSApplicationDelegate {
private var menuBarExtra: FluidMenuBarExtra?
private var menuBar: MenuBarController?
let vpn: CoderVPNService
let state: AppState

Expand All @@ -36,11 +37,18 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}

func applicationDidFinishLaunching(_: Notification) {
menuBarExtra = FluidMenuBarExtra(title: "Coder Desktop", image: "MenuBarIcon") {
menuBar = .init(menuBarExtra: FluidMenuBarExtra(title: "Coder Desktop", image: "MenuBarIcon") {
VPNMenu<CoderVPNService>().frame(width: 256)
.environmentObject(self.vpn)
.environmentObject(self.state)
}
})
// Subscribe to system VPN updates
NotificationCenter.default.addObserver(
self,
selector: #selector(vpnDidUpdate(_:)),
name: .NEVPNStatusDidChange,
object: nil
)
}

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

extension AppDelegate {
@objc private func vpnDidUpdate(_ notification: Notification) {
guard let connection = notification.object as? NETunnelProviderSession else {
return
}
vpn.vpnDidUpdate(connection)
menuBar?.vpnDidUpdate(connection)
}
}

@MainActor
func appActivate() {
NSApp.activate()
Expand Down
57 changes: 57 additions & 0 deletions Coder Desktop/Coder Desktop/MenuBarIconController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import FluidMenuBarExtra
import NetworkExtension
import SwiftUI

@MainActor
class MenuBarController {
let menuBarExtra: FluidMenuBarExtra
private let onImage = NSImage(named: "MenuBarIcon")!
private let offOpacity = CGFloat(0.3)
private let onOpacity = CGFloat(1.0)

private var animationTask: Task<Void, Never>?

init(menuBarExtra: FluidMenuBarExtra) {
self.menuBarExtra = menuBarExtra
}

func vpnDidUpdate(_ connection: NETunnelProviderSession) {
switch connection.status {
case .connected:
stopAnimation()
menuBarExtra.setOpacity(onOpacity)
case .connecting, .reasserting, .disconnecting:
startAnimation()
case .invalid, .disconnected:
stopAnimation()
menuBarExtra.setOpacity(offOpacity)
@unknown default:
stopAnimation()
menuBarExtra.setOpacity(offOpacity)
}
}

func startAnimation() {
if animationTask != nil { return }
animationTask = Task {
defer { animationTask = nil }
let totalFrames = 60
let cycleDurationMs: UInt64 = 2000
let frameDurationMs = cycleDurationMs / UInt64(totalFrames - 1)
repeat {
for frame in 0 ..< totalFrames {
if Task.isCancelled { break }
let progress = Double(frame) / Double(totalFrames - 1)
let alpha = 0.3 + 0.7 * (0.5 - 0.5 * cos(2 * Double.pi * progress))
menuBarExtra.setOpacity(CGFloat(alpha))
try? await Task.sleep(for: .milliseconds(frameDurationMs))
}
} while !Task.isCancelled
}
}

func stopAnimation() {
animationTask?.cancel()
animationTask = nil
}
}
14 changes: 1 addition & 13 deletions Coder Desktop/Coder Desktop/VPNService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,6 @@ final class CoderVPNService: NSObject, VPNService {
Task {
await loadNetworkExtensionConfig()
}
NotificationCenter.default.addObserver(
self,
selector: #selector(vpnDidUpdate(_:)),
name: .NEVPNStatusDidChange,
object: nil
)
}

deinit {
Expand Down Expand Up @@ -159,13 +153,7 @@ final class CoderVPNService: NSObject, VPNService {
}

extension CoderVPNService {
// The number of NETunnelProviderSession states makes the excessive branching
// necessary.
// swiftlint:disable:next cyclomatic_complexity
@objc private func vpnDidUpdate(_ notification: Notification) {
guard let connection = notification.object as? NETunnelProviderSession else {
return
}
public func vpnDidUpdate(_ connection: NETunnelProviderSession) {
switch (tunnelState, connection.status) {
// Any -> Disconnected: Update UI w/ error if present
case (_, .disconnected):
Expand Down
6 changes: 4 additions & 2 deletions Coder Desktop/project.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,10 @@ packages:
url: https://github.com/SimplyDanny/SwiftLintPlugins
from: 0.57.1
FluidMenuBarExtra:
url: https://github.com/lfroms/fluid-menu-bar-extra
from: 1.1.0
# Forked so we can dynamically update the menu bar icon.
# The upstream repo has a purposefully limited API
url: https://github.com/coder/fluid-menu-bar-extra
revision: 020be37
KeychainAccess:
url: https://github.com/kishikawakatsumi/KeychainAccess
branch: e0c7eebc5a4465a3c4680764f26b7a61f567cdaf
Expand Down
Loading
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