File tree 4 files changed +46
-11
lines changed
4 files changed +46
-11
lines changed Original file line number Diff line number Diff line change @@ -40,11 +40,21 @@ class AppDelegate: NSObject, NSApplicationDelegate {
40
40
}
41
41
42
42
func applicationDidFinishLaunching( _: Notification ) {
43
- menuBar = . init( menuBarExtra: FluidMenuBarExtra ( title: " Coder Desktop " , image: " MenuBarIcon " ) {
44
- VPNMenu < CoderVPNService > ( ) . frame ( width: 256 )
45
- . environmentObject ( self . vpn)
46
- . environmentObject ( self . state)
47
- } )
43
+ menuBar = . init( menuBarExtra: FluidMenuBarExtra (
44
+ title: " Coder Desktop " ,
45
+ image: " MenuBarIcon " ,
46
+ onAppear: {
47
+ // If the VPN is enabled, it's likely the token isn't expired
48
+ guard case . disabled = self . vpn. state, self . state. hasSession else { return }
49
+ Task { @MainActor in
50
+ await self . state. handleTokenExpiry ( )
51
+ }
52
+ } , content: {
53
+ VPNMenu < CoderVPNService > ( ) . frame ( width: 256 )
54
+ . environmentObject ( self . vpn)
55
+ . environmentObject ( self . state)
56
+ }
57
+ ) )
48
58
// Subscribe to system VPN updates
49
59
NotificationCenter . default. addObserver (
50
60
self ,
Original file line number Diff line number Diff line change @@ -2,10 +2,12 @@ import CoderSDK
2
2
import Foundation
3
3
import KeychainAccess
4
4
import NetworkExtension
5
+ import os
5
6
import SwiftUI
6
7
7
8
@MainActor
8
9
class AppState : ObservableObject {
10
+ private let logger = Logger ( subsystem: Bundle . main. bundleIdentifier!, category: " AppState " )
9
11
let appId = Bundle . main. bundleIdentifier!
10
12
11
13
// Stored in UserDefaults
@@ -95,6 +97,9 @@ class AppState: ObservableObject {
95
97
)
96
98
if hasSession {
97
99
_sessionToken = Published ( initialValue: keychainGet ( for: Keys . sessionToken) )
100
+ if sessionToken == nil || sessionToken!. isEmpty == true {
101
+ clearSession ( )
102
+ }
98
103
}
99
104
}
100
105
@@ -105,6 +110,24 @@ class AppState: ObservableObject {
105
110
reconfigure ( )
106
111
}
107
112
113
+ public func handleTokenExpiry( ) async {
114
+ if hasSession {
115
+ let client = Client ( url: baseAccessURL!, token: sessionToken!)
116
+ do {
117
+ _ = try await client. user ( " me " )
118
+ } catch let ClientError . api( apiErr) {
119
+ // Expired token
120
+ if apiErr. statusCode == 401 {
121
+ clearSession ( )
122
+ }
123
+ } catch {
124
+ // Some other failure, we'll show an error if they try and do something
125
+ logger. error ( " failed to check token validity: \( error) " )
126
+ return
127
+ }
128
+ }
129
+ }
130
+
108
131
public func clearSession( ) {
109
132
hasSession = false
110
133
sessionToken = nil
Original file line number Diff line number Diff line change @@ -104,10 +104,10 @@ public struct Client {
104
104
}
105
105
106
106
public struct APIError : Decodable , Sendable {
107
- let response : Response
108
- let statusCode : Int
109
- let method : String
110
- let url : URL
107
+ public let response : Response
108
+ public let statusCode : Int
109
+ public let method : String
110
+ public let url : URL
111
111
112
112
var description : String {
113
113
var components = [ " \( method) \( url. absoluteString) \n Unexpected status code \( statusCode) : \n \( response. message) " ]
Original file line number Diff line number Diff line change @@ -92,10 +92,12 @@ packages:
92
92
url : https://github.com/SimplyDanny/SwiftLintPlugins
93
93
from : 0.57.1
94
94
FluidMenuBarExtra :
95
- # Forked so we can dynamically update the menu bar icon.
95
+ # Forked to:
96
+ # - Dynamically update the menu bar icon
97
+ # - Set onAppear/disappear handlers.
96
98
# The upstream repo has a purposefully limited API
97
99
url : https://github.com/coder/fluid-menu-bar-extra
98
- revision : 020be37
100
+ revision : 96a861a
99
101
KeychainAccess :
100
102
url : https://github.com/kishikawakatsumi/KeychainAccess
101
103
branch : e0c7eebc5a4465a3c4680764f26b7a61f567cdaf
You can’t perform that action at this time.
0 commit comments