mirror of
https://github.com/outbackdingo/firezone.git
synced 2026-01-27 10:18:54 +00:00
fix(apple): show "Loading Resources..." instead of "No Resources" while loading (#6358)
Closes #6356 --------- Signed-off-by: Reactor Scram <ReactorScram@users.noreply.github.com> Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
This commit is contained in:
@@ -82,7 +82,7 @@ public class TunnelManager {
|
||||
|
||||
// Cache resources on this side of the IPC barrier so we can
|
||||
// return them to callers when they haven't changed.
|
||||
private var resourcesListCache: [Resource] = []
|
||||
private var resourcesListCache: ResourceList = ResourceList.loading
|
||||
|
||||
// Persists our tunnel settings
|
||||
private var manager: NETunnelProviderManager?
|
||||
@@ -272,7 +272,7 @@ public class TunnelManager {
|
||||
updateDisabledResources()
|
||||
}
|
||||
|
||||
func fetchResources(callback: @escaping ([Resource]) -> Void) {
|
||||
func fetchResources(callback: @escaping (ResourceList) -> Void) {
|
||||
guard session().status == .connected else { return }
|
||||
|
||||
do {
|
||||
@@ -281,7 +281,7 @@ public class TunnelManager {
|
||||
self.resourceListHash = Data(SHA256.hash(data: data))
|
||||
let decoder = JSONDecoder()
|
||||
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
||||
self.resourcesListCache = (try? decoder.decode([Resource].self, from: data)) ?? []
|
||||
self.resourcesListCache = ResourceList.loaded(try! decoder.decode([Resource].self, from: data))
|
||||
}
|
||||
|
||||
callback(self.resourcesListCache)
|
||||
@@ -323,7 +323,7 @@ public class TunnelManager {
|
||||
if session.status == .disconnected {
|
||||
// Reset resource list on disconnect
|
||||
resourceListHash = Data()
|
||||
resourcesListCache = []
|
||||
resourcesListCache = ResourceList.loading
|
||||
}
|
||||
|
||||
await statusChangeHandler?(session.status)
|
||||
|
||||
@@ -8,6 +8,20 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
public enum ResourceList {
|
||||
case loading
|
||||
case loaded([Resource])
|
||||
|
||||
public func asArray() -> [Resource] {
|
||||
switch self {
|
||||
case .loading:
|
||||
[]
|
||||
case .loaded(let x):
|
||||
x
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct Resource: Decodable, Identifiable, Equatable {
|
||||
public let id: String
|
||||
public var name: String
|
||||
|
||||
@@ -142,7 +142,7 @@ public final class Store: ObservableObject {
|
||||
|
||||
// Network Extensions don't have a 2-way binding up to the GUI process,
|
||||
// so we need to periodically ask the tunnel process for them.
|
||||
func beginUpdatingResources(callback: @escaping ([Resource]) -> Void) {
|
||||
func beginUpdatingResources(callback: @escaping (ResourceList) -> Void) {
|
||||
Log.app.log("\(#function)")
|
||||
|
||||
tunnelManager.fetchResources(callback: callback)
|
||||
|
||||
@@ -18,7 +18,6 @@ import SwiftUI
|
||||
// https://developer.apple.com/documentation/swiftui/menubarextra
|
||||
public final class MenuBar: NSObject, ObservableObject {
|
||||
private var statusItem: NSStatusItem
|
||||
private var resources: [Resource] = []
|
||||
|
||||
// Wish these could be `[String]` but diffing between different types is tricky
|
||||
private var lastShownFavorites: [Resource] = []
|
||||
@@ -73,20 +72,18 @@ public final class MenuBar: NSObject, ObservableObject {
|
||||
|
||||
if status == .connected {
|
||||
model.store.beginUpdatingResources { newResources in
|
||||
// Handle resource changes
|
||||
self.populateResourceMenus(newResources)
|
||||
self.handleTunnelStatusOrResourcesChanged(status: status, resources: newResources)
|
||||
self.resources = newResources
|
||||
// Handle resource changes
|
||||
self.populateResourceMenus(newResources.asArray())
|
||||
self.handleTunnelStatusOrResourcesChanged(status: status, resources: newResources)
|
||||
}
|
||||
} else {
|
||||
model.store.endUpdatingResources()
|
||||
populateResourceMenus([])
|
||||
resources = []
|
||||
}
|
||||
|
||||
// Handle status changes
|
||||
self.updateStatusItemIcon(status: status)
|
||||
self.handleTunnelStatusOrResourcesChanged(status: status, resources: resources)
|
||||
self.handleTunnelStatusOrResourcesChanged(status: status, resources: model.resources)
|
||||
|
||||
}).store(in: &cancellables)
|
||||
}
|
||||
@@ -325,7 +322,7 @@ public final class MenuBar: NSObject, ObservableObject {
|
||||
(connectingAnimationImageIndex + 1) % connectingAnimationImages.count
|
||||
}
|
||||
|
||||
private func handleTunnelStatusOrResourcesChanged(status: NEVPNStatus, resources: [Resource]?) {
|
||||
private func handleTunnelStatusOrResourcesChanged(status: NEVPNStatus, resources: ResourceList) {
|
||||
// Update "Sign In" / "Sign Out" menu items
|
||||
switch status {
|
||||
case .invalid:
|
||||
@@ -405,13 +402,16 @@ public final class MenuBar: NSObject, ObservableObject {
|
||||
}()
|
||||
}
|
||||
|
||||
private func resourceMenuTitle(_ resources: [Resource]?) -> String {
|
||||
guard let resources = resources else { return "Loading Resources..." }
|
||||
|
||||
if resources.isEmpty {
|
||||
return "No Resources"
|
||||
} else {
|
||||
return "Resources"
|
||||
private func resourceMenuTitle(_ resources: ResourceList) -> String {
|
||||
switch resources {
|
||||
case .loading:
|
||||
return "Loading Resources..."
|
||||
case .loaded(let x):
|
||||
if x.isEmpty {
|
||||
return "No Resources"
|
||||
} else {
|
||||
return "Resources"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -654,7 +654,7 @@ public final class MenuBar: NSObject, ObservableObject {
|
||||
// When the user clicks to add or remove a favorite, the menu will close anyway, so just recreate the whole menu.
|
||||
// This avoids complex logic when changing in and out of the "nothing is favorited" special case
|
||||
self.populateResourceMenus([])
|
||||
self.populateResourceMenus(resources)
|
||||
self.populateResourceMenus(model.resources.asArray())
|
||||
}
|
||||
|
||||
private func copyToClipboard(_ string: String) {
|
||||
|
||||
@@ -12,7 +12,7 @@ import SwiftUI
|
||||
@MainActor
|
||||
public final class SessionViewModel: ObservableObject {
|
||||
@Published private(set) var actorName: String? = nil
|
||||
@Published private(set) var resources: [Resource]? = nil
|
||||
@Published private(set) var resources: ResourceList = ResourceList.loading
|
||||
@Published private(set) var status: NEVPNStatus? = nil
|
||||
|
||||
|
||||
@@ -67,7 +67,8 @@ struct SessionView: View {
|
||||
var body: some View {
|
||||
switch model.status {
|
||||
case .connected:
|
||||
if let resources = model.resources {
|
||||
switch model.resources {
|
||||
case .loaded(let resources):
|
||||
if resources.isEmpty {
|
||||
Text("No Resources. Contact your admin to be granted access.")
|
||||
} else {
|
||||
@@ -91,10 +92,9 @@ struct SessionView: View {
|
||||
.navigationTitle("All Resources")
|
||||
}
|
||||
}
|
||||
|
||||
.listStyle(GroupedListStyle())
|
||||
}
|
||||
} else {
|
||||
case .loading:
|
||||
Text("Loading Resources...")
|
||||
}
|
||||
case .connecting:
|
||||
|
||||
Reference in New Issue
Block a user