Refactor Alerts
This commit is contained in:
@@ -34,7 +34,7 @@ import SwiftUI
|
||||
}
|
||||
} else {
|
||||
content.onAppear {
|
||||
guard !ApplicationLibrary.inPreview else {
|
||||
guard !ApplicationLibrary.inPreview, profile.status.isConnected else {
|
||||
return
|
||||
}
|
||||
Task {
|
||||
@@ -117,7 +117,7 @@ import SwiftUI
|
||||
updateButtonVisibility()
|
||||
}
|
||||
#endif
|
||||
.alertBinding($coordinator.alert)
|
||||
.alert($coordinator.alert)
|
||||
}
|
||||
|
||||
@ViewBuilder private var overviewPage: some View {
|
||||
|
||||
@@ -5,7 +5,7 @@ import SwiftUI
|
||||
public struct ClashModeCard: View {
|
||||
@EnvironmentObject private var commandClient: CommandClient
|
||||
@State private var clashMode: String = ""
|
||||
@State private var alert: Alert?
|
||||
@State private var alert: AlertState?
|
||||
|
||||
public init() {}
|
||||
|
||||
@@ -32,7 +32,7 @@ public struct ClashModeCard: View {
|
||||
.onChangeCompat(of: commandClient.clashMode) { newValue in
|
||||
clashMode = newValue
|
||||
}
|
||||
.alertBinding($alert)
|
||||
.alert($alert)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ public struct ClashModeCard: View {
|
||||
try LibboxNewStandaloneCommandClient()!.setClashMode(newMode)
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = Alert(error)
|
||||
alert = AlertState(error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public struct ProfileCard: View {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
.alertBinding($viewModel.alert)
|
||||
.alert($viewModel.alert)
|
||||
}
|
||||
|
||||
private var headerView: some View {
|
||||
@@ -254,7 +254,7 @@ extension ProfileCard {
|
||||
@Published var showManageProfiles = false
|
||||
@Published var showQRCode = false
|
||||
@Published var isUpdating = false
|
||||
@Published var alert: Alert?
|
||||
@Published var alert: AlertState?
|
||||
@Published var profileToEdit: Profile?
|
||||
|
||||
func updateProfile(_ profile: Profile, environments: ExtensionEnvironments) async {
|
||||
@@ -264,9 +264,9 @@ extension ProfileCard {
|
||||
try await profile.updateRemoteProfile()
|
||||
environments.profileUpdate.send()
|
||||
} catch {
|
||||
alert = Alert(
|
||||
title: Text("Update Failed"),
|
||||
message: Text(error.localizedDescription)
|
||||
alert = AlertState(
|
||||
title: String(localized: "Update Failed"),
|
||||
message: error.localizedDescription
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -368,7 +368,7 @@ extension ProfileCard {
|
||||
}
|
||||
}
|
||||
.disabled(viewModel.isUpdating)
|
||||
.alertBinding($viewModel.alert, $viewModel.isLoading)
|
||||
.alert($viewModel.alert, isLoading: $viewModel.isLoading)
|
||||
.onReceive(environments.profileUpdate) { _ in
|
||||
Task {
|
||||
await viewModel.doReload()
|
||||
|
||||
@@ -9,7 +9,7 @@ public struct ExtensionStatusView: View {
|
||||
@EnvironmentObject private var commandClient: CommandClient
|
||||
|
||||
@State private var columnCount: Int = 4
|
||||
@State private var alert: Alert?
|
||||
@State private var alert: AlertState?
|
||||
|
||||
private let infoFont = Font.system(.caption, design: .monospaced)
|
||||
|
||||
@@ -91,7 +91,7 @@ public struct ExtensionStatusView: View {
|
||||
.frame(alignment: .topLeading)
|
||||
.padding([.top, .leading, .trailing])
|
||||
}
|
||||
.alertBinding($alert)
|
||||
.alert($alert)
|
||||
}
|
||||
|
||||
private func updateColumnCount(_ width: Double) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import SwiftUI
|
||||
|
||||
@MainActor
|
||||
public struct InstallProfileButton: View {
|
||||
@State private var alert: Alert?
|
||||
@State private var alert: AlertState?
|
||||
|
||||
private let callback: () async -> Void
|
||||
public init(_ callback: @escaping (() async -> Void)) {
|
||||
@@ -18,7 +18,7 @@ public struct InstallProfileButton: View {
|
||||
} label: {
|
||||
Label("Install Network Extension", systemImage: "lock.doc.fill")
|
||||
}
|
||||
.alertBinding($alert)
|
||||
.alert($alert)
|
||||
}
|
||||
|
||||
private func installProfile() async {
|
||||
@@ -26,7 +26,7 @@ public struct InstallProfileButton: View {
|
||||
try await ExtensionProfile.install()
|
||||
await callback()
|
||||
} catch {
|
||||
alert = Alert(error)
|
||||
alert = AlertState(error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
@MainActor
|
||||
public struct InstallSystemExtensionButton: View {
|
||||
@State private var alert: Alert?
|
||||
@State private var alert: AlertState?
|
||||
private let callback: () async -> Void
|
||||
public init(_ callback: @escaping () async -> Void) {
|
||||
self.callback = callback
|
||||
@@ -19,19 +19,19 @@
|
||||
} label: {
|
||||
Label("Install System Extension", systemImage: "lock.doc.fill")
|
||||
}
|
||||
.alertBinding($alert)
|
||||
.alert($alert)
|
||||
}
|
||||
|
||||
private func installSystemExtension() async {
|
||||
do {
|
||||
if let result = try await SystemExtension.install() {
|
||||
if result == .willCompleteAfterReboot {
|
||||
alert = Alert(errorMessage: String(localized: "Need Reboot"))
|
||||
alert = AlertState(errorMessage: String(localized: "Need Reboot"))
|
||||
}
|
||||
}
|
||||
await callback()
|
||||
} catch {
|
||||
alert = Alert(error)
|
||||
alert = AlertState(error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public struct StartStopButton: View {
|
||||
private struct ToggleConnectionButton: View {
|
||||
@EnvironmentObject private var environments: ExtensionEnvironments
|
||||
@EnvironmentObject private var profile: ExtensionProfile
|
||||
@State private var alert: Alert?
|
||||
@State private var alert: AlertState?
|
||||
@State private var currentTime = Date()
|
||||
|
||||
private let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect()
|
||||
@@ -105,7 +105,7 @@ public struct StartStopButton: View {
|
||||
.modifier(PrimaryTintModifier())
|
||||
#endif
|
||||
.disabled(!profile.status.isEnabled)
|
||||
.alertBinding($alert)
|
||||
.alert($alert)
|
||||
.onReceive(timer) { _ in
|
||||
currentTime = Date()
|
||||
}
|
||||
@@ -146,7 +146,7 @@ public struct StartStopButton: View {
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = Alert(error)
|
||||
alert = AlertState(error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,15 +48,15 @@ public struct DashboardView: View {
|
||||
private func handleImportProfile() {
|
||||
if let profile = importProfile.wrappedValue {
|
||||
importProfile.wrappedValue = nil
|
||||
coordinator.alert = Alert(
|
||||
title: Text("Import Profile"),
|
||||
message: Text("Are you sure to import profile \(profile.name)?"),
|
||||
primaryButton: .default(Text("Import")) {
|
||||
coordinator.alert = AlertState(
|
||||
title: String(localized: "Import Profile"),
|
||||
message: String(localized: "Are you sure to import profile \(profile.name)?"),
|
||||
primaryButton: .default(String(localized: "Import")) {
|
||||
Task {
|
||||
do {
|
||||
try await profile.importProfile()
|
||||
} catch {
|
||||
coordinator.alert = Alert(error)
|
||||
coordinator.alert = AlertState(error: error)
|
||||
return
|
||||
}
|
||||
environments.profileUpdate.send()
|
||||
@@ -70,10 +70,10 @@ public struct DashboardView: View {
|
||||
private func handleImportRemoteProfile() {
|
||||
if let remoteProfile = importRemoteProfile.wrappedValue {
|
||||
importRemoteProfile.wrappedValue = nil
|
||||
coordinator.alert = Alert(
|
||||
title: Text("Import Remote Profile"),
|
||||
message: Text("Are you sure to import remote profile \(remoteProfile.name)? You will connect to \(remoteProfile.host) to download the configuration."),
|
||||
primaryButton: .default(Text("Import")) {
|
||||
coordinator.alert = AlertState(
|
||||
title: String(localized: "Import Remote Profile"),
|
||||
message: String(localized: "Are you sure to import remote profile \(remoteProfile.name)? You will connect to \(remoteProfile.host) to download the configuration."),
|
||||
primaryButton: .default(String(localized: "Import")) {
|
||||
importRemoteProfileRequest = .init(name: remoteProfile.name, url: remoteProfile.url)
|
||||
},
|
||||
secondaryButton: .cancel()
|
||||
@@ -117,7 +117,7 @@ public struct DashboardView: View {
|
||||
} else if let profile = environments.extensionProfile {
|
||||
activeDashboardView
|
||||
.environmentObject(profile)
|
||||
.alertBinding($coordinator.alert)
|
||||
.alert($coordinator.alert)
|
||||
.onChangeCompat(of: profile.status) { status in
|
||||
coordinator.handleStatusChange(status, profile: profile)
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public final class DashboardViewModel: BaseViewModel {
|
||||
public var onEmptyProfilesChange: ((Bool) -> Void)?
|
||||
private var openURL: ((URL) -> Void)?
|
||||
|
||||
public override init() {
|
||||
override public init() {
|
||||
super.init()
|
||||
isLoading = true
|
||||
}
|
||||
@@ -65,7 +65,7 @@ public final class DashboardViewModel: BaseViewModel {
|
||||
await SharedPreferences.selectedProfileID.set(selectedProfileID)
|
||||
}
|
||||
} catch {
|
||||
alert = Alert(error)
|
||||
alert = AlertState(error: error)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ public final class DashboardViewModel: BaseViewModel {
|
||||
systemProxyAvailable = status.available
|
||||
systemProxyEnabled = status.enabled
|
||||
} catch {
|
||||
alert = Alert(error)
|
||||
alert = AlertState(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ public final class DashboardViewModel: BaseViewModel {
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = Alert(error)
|
||||
alert = AlertState(error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -123,34 +123,29 @@ public final class DashboardViewModel: BaseViewModel {
|
||||
guard reports.hasNext() else { return }
|
||||
|
||||
let report = reports.next()!
|
||||
let continueChain: () -> Void = { [weak self] in
|
||||
_ = Task.detached {
|
||||
try? await Task.sleep(nanoseconds: 300 * NSEC_PER_MSEC)
|
||||
await self?.loopShowDeprecateNotes(reports)
|
||||
}
|
||||
}
|
||||
|
||||
if report.migrationLink.isEmpty {
|
||||
alert = Alert(
|
||||
title: Text("Deprecated Warning"),
|
||||
message: Text(report.message()),
|
||||
dismissButton: .cancel(Text("Ok")) {
|
||||
Task.detached { [weak self] in
|
||||
try await Task.sleep(nanoseconds: 300 * NSEC_PER_MSEC)
|
||||
await self?.loopShowDeprecateNotes(reports)
|
||||
}
|
||||
}
|
||||
alert = AlertState(
|
||||
title: String(localized: "Deprecated Warning"),
|
||||
message: report.message(),
|
||||
dismissButton: .cancel(String(localized: "Ok"))
|
||||
)
|
||||
alert?.onDismiss = continueChain
|
||||
} else {
|
||||
alert = Alert(
|
||||
title: Text("Deprecated Warning"),
|
||||
message: Text(report.message()),
|
||||
primaryButton: .default(Text("Documentation")) {
|
||||
alert = AlertState(
|
||||
title: String(localized: "Deprecated Warning"),
|
||||
message: report.message(),
|
||||
primaryButton: .default(String(localized: "Documentation")) {
|
||||
self.openURL?(URL(string: report.migrationLink)!)
|
||||
Task.detached { [weak self] in
|
||||
try await Task.sleep(nanoseconds: 300 * NSEC_PER_MSEC)
|
||||
await self?.loopShowDeprecateNotes(reports)
|
||||
}
|
||||
},
|
||||
secondaryButton: .cancel(Text("Ok")) {
|
||||
Task.detached { [weak self] in
|
||||
try await Task.sleep(nanoseconds: 300 * NSEC_PER_MSEC)
|
||||
await self?.loopShowDeprecateNotes(reports)
|
||||
}
|
||||
}
|
||||
secondaryButton: .cancel(String(localized: "Ok")),
|
||||
onDismiss: continueChain
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -165,10 +160,10 @@ public final class DashboardViewModel: BaseViewModel {
|
||||
#if os(macOS)
|
||||
if myError.domain == "Library.FullDiskAccessPermissionRequired" {
|
||||
await MainActor.run {
|
||||
alert = Alert(
|
||||
title: Text("Full Disk Access permission is required"),
|
||||
message: Text("Please grant the permission for **SFMExtension**, then we can continue."),
|
||||
primaryButton: .default(Text("Authorize"), action: openFDASettings),
|
||||
alert = AlertState(
|
||||
title: String(localized: "Full Disk Access permission is required"),
|
||||
message: String(localized: "Please grant the permission for **SFMExtension**, then we can continue."),
|
||||
primaryButton: .default(String(localized: "Authorize"), action: openFDASettings),
|
||||
secondaryButton: .cancel()
|
||||
)
|
||||
}
|
||||
@@ -176,7 +171,7 @@ public final class DashboardViewModel: BaseViewModel {
|
||||
}
|
||||
#endif
|
||||
await MainActor.run {
|
||||
alert = Alert(title: Text("Service Error"), message: Text(myError.localizedDescription))
|
||||
alert = AlertState(title: String(localized: "Service Error"), message: myError.localizedDescription)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ public struct OverviewView: View {
|
||||
.onChangeCompat(of: cardConfigurationVersion) { _ in
|
||||
Task { await configuration.reload() }
|
||||
}
|
||||
.alertBinding($coordinator.alert)
|
||||
.alert($coordinator.alert)
|
||||
.disabled(!ApplicationLibrary.inPreview && (!profile.status.isSwitchable || coordinator.reasserting))
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ public final class OverviewViewModel: BaseViewModel {
|
||||
do {
|
||||
try await serviceReload()
|
||||
} catch {
|
||||
alert = Alert(error)
|
||||
alert = AlertState(error: error)
|
||||
}
|
||||
}
|
||||
reasserting = false
|
||||
@@ -46,7 +46,7 @@ public final class OverviewViewModel: BaseViewModel {
|
||||
await MainActor.run { reasserting = false }
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run { alert = Alert(error) }
|
||||
await MainActor.run { alert = AlertState(error: error) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user