diff --git a/ApplicationLibrary/Views/Dashboard/ActiveDashboardView.swift b/ApplicationLibrary/Views/Dashboard/ActiveDashboardView.swift index b35a0c6..3f7c749 100644 --- a/ApplicationLibrary/Views/Dashboard/ActiveDashboardView.swift +++ b/ApplicationLibrary/Views/Dashboard/ActiveDashboardView.swift @@ -7,7 +7,7 @@ import SwiftUI @Environment(\.scenePhase) private var scenePhase @EnvironmentObject private var environments: ExtensionEnvironments @EnvironmentObject private var profile: ExtensionProfile - @StateObject private var coordinator = DashboardViewModel() + @ObservedObject private var coordinator: DashboardViewModel @State private var cardConfigurationVersion = 0 #if os(iOS) || os(tvOS) @State private var showCardManagement = false @@ -18,20 +18,25 @@ import SwiftUI private let externalCardConfigurationVersion: Int? - public init(externalCardConfigurationVersion: Int? = nil) { + public init(coordinator: DashboardViewModel, externalCardConfigurationVersion: Int? = nil) { + _coordinator = ObservedObject(wrappedValue: coordinator) self.externalCardConfigurationVersion = externalCardConfigurationVersion } public var body: some View { + viewContent + } + + @ViewBuilder private var viewContent: some View { if coordinator.isLoading { - ProgressView().onAppear { - coordinator.onEmptyProfilesChange = { - environments.emptyProfiles = $0 + ProgressView() + #if os(iOS) || os(tvOS) + .onAppear { + Task { + await coordinator.reload() + } } - Task { - await coordinator.reload() - } - } + #endif } else { content.onAppear { guard !ApplicationLibrary.inPreview, profile.status.isConnected else { @@ -40,8 +45,7 @@ import SwiftUI Task { await coordinator.reloadSystemProxy() } - }.onChangeCompat(of: profile.status) { - status in + }.onChangeCompat(of: profile.status) { status in guard !ApplicationLibrary.inPreview, status == .connected else { return } @@ -77,25 +81,21 @@ import SwiftUI } else { environments.connect() } - }.onChangeCompat(of: scenePhase) { - phase in + }.onChangeCompat(of: scenePhase) { phase in guard phase == .active else { return } environments.connect() - }.onChangeCompat(of: profile.status) { - status in + }.onChangeCompat(of: profile.status) { status in guard status.isConnected else { return } environments.connect() - }.onReceive(environments.profileUpdate) { - _ in + }.onReceive(environments.profileUpdate) { _ in Task { await coordinator.reload() } - }.onReceive(environments.selectedProfileUpdate) { - _ in + }.onReceive(environments.selectedProfileUpdate) { _ in Task { await coordinator.updateSelectedProfile() if profile.status.isConnected { @@ -104,20 +104,16 @@ import SwiftUI } } #if os(iOS) || os(tvOS) - .onReceive(environments.commandClient.$groups) { - _ in + .onReceive(environments.commandClient.$groups) { _ in updateButtonVisibility() - }.onReceive(environments.commandClient.$connections) { - _ in + }.onReceive(environments.commandClient.$connections) { _ in updateButtonVisibility() - }.onReceive(profile.$status) { - _ in + }.onReceive(profile.$status) { _ in updateButtonVisibility() }.onAppear { updateButtonVisibility() } #endif - .alert($coordinator.alert) } @ViewBuilder private var overviewPage: some View { diff --git a/ApplicationLibrary/Views/Dashboard/DashboardView.swift b/ApplicationLibrary/Views/Dashboard/DashboardView.swift index 0265238..f5171d9 100644 --- a/ApplicationLibrary/Views/Dashboard/DashboardView.swift +++ b/ApplicationLibrary/Views/Dashboard/DashboardView.swift @@ -22,6 +22,7 @@ public struct DashboardView: View { content .onAppear { coordinator.setOpenURL { openURL($0) } + coordinator.setEnvironments(environments) #if os(macOS) Task { await coordinator.reload() } #endif @@ -133,9 +134,9 @@ public struct DashboardView: View { @ViewBuilder private var activeDashboardView: some View { #if os(macOS) - ActiveDashboardView(externalCardConfigurationVersion: cardConfigurationVersion) + ActiveDashboardView(coordinator: coordinator, externalCardConfigurationVersion: cardConfigurationVersion) #else - ActiveDashboardView() + ActiveDashboardView(coordinator: coordinator) #endif } } diff --git a/ApplicationLibrary/Views/Dashboard/DashboardViewModel.swift b/ApplicationLibrary/Views/Dashboard/DashboardViewModel.swift index 5231f10..1d8cfbe 100644 --- a/ApplicationLibrary/Views/Dashboard/DashboardViewModel.swift +++ b/ApplicationLibrary/Views/Dashboard/DashboardViewModel.swift @@ -17,9 +17,13 @@ public final class DashboardViewModel: BaseViewModel { @Published public var systemExtensionInstalled = true #endif - public var onEmptyProfilesChange: ((Bool) -> Void)? + private weak var environments: ExtensionEnvironments? private var openURL: ((URL) -> Void)? + public func setEnvironments(_ environments: ExtensionEnvironments) { + self.environments = environments + } + override public init() { super.init() isLoading = true @@ -55,7 +59,7 @@ public final class DashboardViewModel: BaseViewModel { do { profileList = try await ProfileManager.list().map { ProfilePreview($0) } guard !profileList.isEmpty else { - onEmptyProfilesChange?(true) + environments?.emptyProfiles = true return } @@ -69,7 +73,7 @@ public final class DashboardViewModel: BaseViewModel { return } } - onEmptyProfilesChange?(profileList.isEmpty) + environments?.emptyProfiles = profileList.isEmpty } public func reloadSystemProxy() async { diff --git a/ApplicationLibrary/Views/Log/LogViewModel.swift b/ApplicationLibrary/Views/Log/LogViewModel.swift index afe08cf..cce1550 100644 --- a/ApplicationLibrary/Views/Log/LogViewModel.swift +++ b/ApplicationLibrary/Views/Log/LogViewModel.swift @@ -10,13 +10,12 @@ import SwiftUI #endif @MainActor -public class LogViewModel: ObservableObject { +public class LogViewModel: BaseViewModel { @Published public var selectedLogLevel: Int? @Published public var isPaused = false @Published public var searchText = "" @Published public var isSearching = false @Published public var filteredLogs: [LogEntry] = [] - @Published public var alert: AlertState? @Published public var showFileExporter = false @Published public var logFileURL: URL? @@ -42,6 +41,7 @@ public class LogViewModel: ObservableObject { public init(commandClient: CommandClient) { self.commandClient = commandClient + super.init() let debouncedSearchText = $searchText .debounce(for: .milliseconds(300), scheduler: DispatchQueue.main) diff --git a/ApplicationLibrary/Views/Setting/ServiceLogViewModel.swift b/ApplicationLibrary/Views/Setting/ServiceLogViewModel.swift index df8f6d5..6642171 100644 --- a/ApplicationLibrary/Views/Setting/ServiceLogViewModel.swift +++ b/ApplicationLibrary/Views/Setting/ServiceLogViewModel.swift @@ -3,10 +3,13 @@ import Library import SwiftUI @MainActor -final class ServiceLogViewModel: ObservableObject { - @Published var isLoading = true +final class ServiceLogViewModel: BaseViewModel { @Published var content = "" - @Published var alert: AlertState? + + override init() { + super.init() + isLoading = true + } var isEmpty: Bool { content.isEmpty diff --git a/ApplicationLibrary/Views/Setting/SettingViewModel.swift b/ApplicationLibrary/Views/Setting/SettingViewModel.swift index 9ff5898..286ddae 100644 --- a/ApplicationLibrary/Views/Setting/SettingViewModel.swift +++ b/ApplicationLibrary/Views/Setting/SettingViewModel.swift @@ -2,10 +2,14 @@ import Library import SwiftUI @MainActor -final class SettingViewModel: ObservableObject { - @Published var isLoading = true +final class SettingViewModel: BaseViewModel { @Published var taiwanFlagAvailable = false + override init() { + super.init() + isLoading = true + } + nonisolated func checkTaiwanFlagAvailability() async { let available: Bool if ApplicationLibrary.inPreview { diff --git a/MacLibrary/MainViewModel.swift b/MacLibrary/MainViewModel.swift index d21c862..168e5e8 100644 --- a/MacLibrary/MainViewModel.swift +++ b/MacLibrary/MainViewModel.swift @@ -5,13 +5,10 @@ import Library import SwiftUI @MainActor -public class MainViewModel: ObservableObject { +public class MainViewModel: BaseViewModel { @Published public var selection = NavigationPage.dashboard @Published public var importProfile: LibboxProfileContent? @Published public var importRemoteProfile: LibboxImportRemoteProfile? - @Published public var alert: AlertState? - - public init() {} public func onAppear(environments: ExtensionEnvironments) { environments.postReload()