-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathCoder_DesktopApp.swift
139 lines (129 loc) · 4.5 KB
/
Coder_DesktopApp.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import FluidMenuBarExtra
import NetworkExtension
import SwiftUI
import VPNLib
@main
struct DesktopApp: App {
@NSApplicationDelegateAdaptor private var appDelegate: AppDelegate
@State private var hidden: Bool = false
var body: some Scene {
MenuBarExtra("", isInserted: $hidden) {
EmptyView()
}
Window("Sign In", id: Windows.login.rawValue) {
LoginForm()
.environmentObject(appDelegate.state)
}
.windowResizability(.contentSize)
SwiftUI.Settings {
SettingsView<CoderVPNService>()
.environmentObject(appDelegate.vpn)
.environmentObject(appDelegate.state)
}
.windowResizability(.contentSize)
Window("Coder File Sync", id: Windows.fileSync.rawValue) {
FileSyncConfig<CoderVPNService, MutagenDaemon>()
.environmentObject(appDelegate.state)
.environmentObject(appDelegate.fileSyncDaemon)
.environmentObject(appDelegate.vpn)
}
}
}
@MainActor
class AppDelegate: NSObject, NSApplicationDelegate {
private var menuBar: MenuBarController?
let vpn: CoderVPNService
let state: AppState
let fileSyncDaemon: MutagenDaemon
override init() {
vpn = CoderVPNService()
let state = AppState(onChange: vpn.configureTunnelProviderProtocol)
vpn.onStart = {
// We don't need this to have finished before the VPN actually starts
Task { await state.refreshDeploymentConfig() }
}
if state.startVPNOnLaunch {
vpn.startWhenReady = true
}
self.state = state
vpn.installSystemExtension()
#if arch(arm64)
let mutagenBinary = "mutagen-darwin-arm64"
#elseif arch(x86_64)
let mutagenBinary = "mutagen-darwin-amd64"
#endif
let fileSyncDaemon = MutagenDaemon(
mutagenPath: Bundle.main.url(forResource: mutagenBinary, withExtension: nil)
)
Task {
await fileSyncDaemon.tryStart()
}
self.fileSyncDaemon = fileSyncDaemon
}
func applicationDidFinishLaunching(_: Notification) {
menuBar = .init(menuBarExtra: FluidMenuBarExtra(
title: "Coder Desktop",
image: "MenuBarIcon",
onAppear: {
// If the VPN is enabled, it's likely the token isn't expired
guard case .disabled = self.vpn.state, self.state.hasSession else { return }
Task { @MainActor in
await self.state.handleTokenExpiry()
}
}, content: {
VPNMenu<CoderVPNService, MutagenDaemon>().frame(width: 256)
.environmentObject(self.vpn)
.environmentObject(self.state)
.environmentObject(self.fileSyncDaemon)
}
))
// Subscribe to system VPN updates
NotificationCenter.default.addObserver(
self,
selector: #selector(vpnDidUpdate(_:)),
name: .NEVPNStatusDidChange,
object: nil
)
Task {
// If there's no NE config, but the user is logged in, such as
// from a previous install, then we need to reconfigure.
if await !vpn.loadNetworkExtensionConfig() {
state.reconfigure()
}
}
}
deinit {
NotificationCenter.default.removeObserver(self)
}
// This function MUST eventually call `NSApp.reply(toApplicationShouldTerminate: true)`
// or return `.terminateNow`
func applicationShouldTerminate(_: NSApplication) -> NSApplication.TerminateReply {
Task {
async let vpnTask: Void = {
if await self.state.stopVPNOnQuit {
await self.vpn.stop()
}
}()
async let fileSyncTask: Void = self.fileSyncDaemon.stop()
_ = await (vpnTask, fileSyncTask)
NSApp.reply(toApplicationShouldTerminate: true)
}
return .terminateLater
}
func applicationShouldTerminateAfterLastWindowClosed(_: NSApplication) -> Bool {
false
}
}
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()
}