Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: animate menu bar icon with vpn state#72

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
ethanndickson merged 2 commits intomainfromethan/animate-menubar-icon
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes fromall 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 deletionsCoder Desktop/Coder Desktop/Coder_DesktopApp.swift
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import FluidMenuBarExtra
import NetworkExtension
import SwiftUI

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

@MainActor
class AppDelegate: NSObject, NSApplicationDelegate {
private varmenuBarExtra: FluidMenuBarExtra?
private varmenuBar: 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 deletionsCoder Desktop/Coder Desktop/MenuBarIconController.swift
View file
Open in desktop
Original file line numberDiff line numberDiff 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 deletionsCoder Desktop/Coder Desktop/VPNService.swift
View file
Open in desktop
Original file line numberDiff line numberDiff 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 DownExpand 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 deletionsCoder Desktop/project.yml
View file
Open in desktop
Original file line numberDiff line numberDiff 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

[8]ページ先頭

©2009-2025 Movatter.jp