refactor: improve alert error context and copy action
This commit is contained in:
@@ -7,24 +7,24 @@ open class BaseViewModel: ObservableObject {
|
||||
|
||||
public init() {}
|
||||
|
||||
public func showError(_ error: Error) {
|
||||
alert = AlertState(error: error)
|
||||
public func showError(_ error: Error, action: String) {
|
||||
alert = AlertState(action: action, error: error)
|
||||
}
|
||||
|
||||
public func execute(_ operation: () async throws -> Void) async {
|
||||
public func execute(_ operation: () async throws -> Void, action: String) async {
|
||||
do {
|
||||
try await operation()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: action, error: error)
|
||||
}
|
||||
}
|
||||
|
||||
public func executeOnBackground(_ operation: @escaping @Sendable () async throws -> Void) async {
|
||||
public func executeOnBackground(_ operation: @escaping @Sendable () async throws -> Void, action: String) async {
|
||||
do {
|
||||
try await operation()
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: action, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ public struct GlobalChecksModifier: ViewModifier {
|
||||
try await profile.importProfile()
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "import profile", error: error)
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -174,7 +174,7 @@ public struct GlobalChecksModifier: ViewModifier {
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "check deprecated notes", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ public struct ShareButtonCompat<Label: View>: View {
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "prepare share file", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -117,7 +117,7 @@ public struct ShareButtonCompat<Label: View>: View {
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "prepare share file", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public class ConnectionListViewModel: BaseViewModel {
|
||||
do {
|
||||
try LibboxNewStandaloneCommandClient()!.closeConnections()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "close all connections", error: error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ public struct ConnectionView: View {
|
||||
try await LibboxNewStandaloneCommandClient()!.closeConnection(connection.id)
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "close connection", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,7 +195,7 @@ public struct ClashModeCard: View {
|
||||
try LibboxNewStandaloneCommandClient()!.setClashMode(newMode)
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "set clash mode", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ public struct ProfileCard: View {
|
||||
) { result in
|
||||
viewModel.exportDocument = nil
|
||||
if case let .failure(error) = result {
|
||||
viewModel.alert = AlertState(error: error)
|
||||
viewModel.alert = AlertState(action: "export profile", error: error)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -371,7 +371,7 @@ public struct ProfileCard: View {
|
||||
)
|
||||
#endif
|
||||
} catch {
|
||||
viewModel.alert = AlertState(error: error)
|
||||
viewModel.alert = AlertState(action: "share profile", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -394,7 +394,7 @@ public struct ProfileCard: View {
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
viewModel.alert = AlertState(error: error)
|
||||
viewModel.alert = AlertState(action: "export profile", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -427,7 +427,7 @@ public struct ProfileCard: View {
|
||||
do {
|
||||
viewModel.qrsShareData = try await profile.origin.encodedContentDataAsync()
|
||||
} catch {
|
||||
viewModel.alert = AlertState(error: error)
|
||||
viewModel.alert = AlertState(action: "prepare QRS share", error: error)
|
||||
viewModel.showQRSShare = false
|
||||
}
|
||||
}
|
||||
@@ -536,7 +536,7 @@ extension ProfileCard {
|
||||
try await profile.updateRemoteProfile()
|
||||
environments.profileUpdate.send()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "update remote profile", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,7 +402,7 @@ struct ProfilePickerSheet: View {
|
||||
try await profile.origin.updateRemoteProfile()
|
||||
environments.profileUpdate.send()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "update remote profile", error: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,7 +742,7 @@ private struct ProfilePickerRow: View {
|
||||
) { result in
|
||||
exportDocument = nil
|
||||
if case let .failure(error) = result {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "export profile", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -789,7 +789,7 @@ private struct ProfilePickerRow: View {
|
||||
) { result in
|
||||
exportDocument = nil
|
||||
if case let .failure(error) = result {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "export profile", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -919,7 +919,7 @@ private struct ProfilePickerRow: View {
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "prepare QRS share", error: error)
|
||||
showQRSShare = false
|
||||
}
|
||||
}
|
||||
@@ -1015,7 +1015,7 @@ private struct ProfilePickerRow: View {
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "export profile", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1067,7 +1067,7 @@ private struct ProfilePickerRow: View {
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "share profile", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1091,7 +1091,7 @@ private struct ProfilePickerRow: View {
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "export profile", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1282,7 +1282,7 @@ private struct ProfilePickerRow: View {
|
||||
) { result in
|
||||
exportDocument = nil
|
||||
if case let .failure(error) = result {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "export profile", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1336,7 +1336,7 @@ private struct ProfilePickerRow: View {
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "prepare QRS share", error: error)
|
||||
showQRSShare = false
|
||||
}
|
||||
}
|
||||
@@ -1405,7 +1405,7 @@ private struct ProfilePickerRow: View {
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "export profile", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public struct InstallProfileButton: View {
|
||||
try await ExtensionProfile.install()
|
||||
await callback()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "install network extension", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
}
|
||||
await callback()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "install system extension", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,7 +165,8 @@ public struct StartStopButton: View {
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
isStarting = false
|
||||
alert = AlertState(error: error)
|
||||
let action = isEnabled ? "start service" : "stop service"
|
||||
alert = AlertState(action: action, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public final class DashboardViewModel: BaseViewModel {
|
||||
await SharedPreferences.selectedProfileID.set(selectedProfileID)
|
||||
}
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "load profile list", error: error)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,7 @@ extension ExtensionProfile {
|
||||
)
|
||||
}
|
||||
#endif
|
||||
return AlertState(error: nsError)
|
||||
return AlertState(action: "fetch last disconnect error", error: nsError)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ public final class OverviewViewModel: BaseViewModel {
|
||||
do {
|
||||
try await profile.reloadService()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "reload service", error: error)
|
||||
}
|
||||
}
|
||||
reasserting = false
|
||||
@@ -32,7 +32,7 @@ public final class OverviewViewModel: BaseViewModel {
|
||||
await MainActor.run { reasserting = false }
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run { alert = AlertState(error: error) }
|
||||
await MainActor.run { alert = AlertState(action: "update system proxy settings", error: error) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ public class GroupListViewModel: BaseViewModel {
|
||||
try await LibboxNewStandaloneCommandClient()!.selectOutbound(groupTag, outboundTag: outboundTag)
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "select outbound", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,7 +109,7 @@ public class GroupListViewModel: BaseViewModel {
|
||||
try await LibboxNewStandaloneCommandClient()!.setGroupExpand(tag, isExpand: isExpand)
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "update group expansion", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,7 +125,7 @@ public class GroupListViewModel: BaseViewModel {
|
||||
try await LibboxNewStandaloneCommandClient()!.urlTest(tag)
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "run URL test", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,7 +399,7 @@ private struct LogContentInnerView: View {
|
||||
cleanup()
|
||||
logFileURL = nil
|
||||
if case let .failure(error) = result {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "export log file", error: error)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showShareSheet) {
|
||||
|
||||
@@ -135,7 +135,7 @@ public class LogDataModel: ObservableObject {
|
||||
try text.write(to: fileURL, atomically: true, encoding: .utf8)
|
||||
logFileURL = fileURL
|
||||
} catch {
|
||||
viewModel?.alert = AlertState(error: error)
|
||||
viewModel?.alert = AlertState(action: "prepare log file", error: error)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -85,7 +85,7 @@ public final class EditProfileContentViewModel: BaseViewModel {
|
||||
do {
|
||||
try await loadContentBackground()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "load profile content", error: error)
|
||||
}
|
||||
isLoading = false
|
||||
}
|
||||
@@ -111,7 +111,7 @@ public final class EditProfileContentViewModel: BaseViewModel {
|
||||
do {
|
||||
try await saveContentBackground(profile)
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "save profile content", error: error)
|
||||
return
|
||||
}
|
||||
isChanged = false
|
||||
|
||||
@@ -21,7 +21,7 @@ public final class EditProfileViewModel: BaseViewModel {
|
||||
try await profile.updateRemoteProfile()
|
||||
environments.profileUpdate.send()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "update remote profile", error: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public final class EditProfileViewModel: BaseViewModel {
|
||||
do {
|
||||
try await ProfileManager.delete(profile)
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "delete profile", error: error)
|
||||
return
|
||||
}
|
||||
environments.profileUpdate.send()
|
||||
@@ -46,7 +46,7 @@ public final class EditProfileViewModel: BaseViewModel {
|
||||
#endif
|
||||
try await profile.onProfileUpdated()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "save profile", error: error)
|
||||
return
|
||||
}
|
||||
isChanged = false
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
case let .failed(error):
|
||||
DispatchQueue.main.async { [self] in
|
||||
reset()
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "connect to import source", error: error)
|
||||
}
|
||||
default: break
|
||||
}
|
||||
@@ -47,7 +47,7 @@
|
||||
do {
|
||||
try await loopMessages(environments: environments)
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "import profile from device", error: error)
|
||||
reset()
|
||||
}
|
||||
}
|
||||
@@ -121,7 +121,7 @@
|
||||
try await socket.write(request.encode())
|
||||
} catch {
|
||||
isImporting = false
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "request profile content from device", error: error)
|
||||
reset()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,14 +223,14 @@ public struct NewProfileMenuView: View {
|
||||
environments.profileUpdate.send()
|
||||
dismiss()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "import profile", error: error)
|
||||
}
|
||||
}
|
||||
},
|
||||
secondaryButton: .cancel()
|
||||
)
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "read imported profile file", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -250,7 +250,7 @@ public struct NewProfileMenuView: View {
|
||||
var error: NSError?
|
||||
let remoteProfile = LibboxParseRemoteProfileImportLink(string, &error)
|
||||
if let error {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "parse QR code profile link", error: error)
|
||||
return
|
||||
}
|
||||
guard let remoteProfile else {
|
||||
@@ -274,14 +274,14 @@ public struct NewProfileMenuView: View {
|
||||
environments.profileUpdate.send()
|
||||
dismiss()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "import profile", error: error)
|
||||
}
|
||||
}
|
||||
},
|
||||
secondaryButton: .cancel()
|
||||
)
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "decode QRS profile data", error: error)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -185,7 +185,7 @@ public struct NewProfileView: View {
|
||||
viewModel.fileURL = urls[0]
|
||||
}
|
||||
} catch {
|
||||
viewModel.alert = AlertState(error: error)
|
||||
viewModel.alert = AlertState(action: "read imported profile file", error: error)
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -213,7 +213,7 @@ public struct NewProfileView: View {
|
||||
viewModel.fileURL = urls[0]
|
||||
}
|
||||
} catch {
|
||||
viewModel.alert = AlertState(error: error)
|
||||
viewModel.alert = AlertState(action: "read imported profile file", error: error)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +72,7 @@ public final class NewProfileViewModel: BaseViewModel {
|
||||
do {
|
||||
createdProfile = try await createProfileBackground()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "create profile", error: error)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
)
|
||||
#endif
|
||||
default:
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "scan QR code", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,7 +204,7 @@ public struct CoreView: View {
|
||||
try await environments.extensionProfile!.stop()
|
||||
await destroyWorkingDirectory()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "stop service before destroying working directory", error: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ public struct CoreView: View {
|
||||
#endif
|
||||
isLoading = true
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "destroy working directory", error: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,7 +281,7 @@ public struct CoreView: View {
|
||||
try await manager.signalEnumerator(for: .workingSet)
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "notify Files app about working directory changes", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -300,7 +300,7 @@ public struct CoreView: View {
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "open working directory in Files", error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,14 +109,14 @@ public struct AppView: View {
|
||||
try HelperServiceManager.registerRootHelper()
|
||||
refreshHelperStatus()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "update helper service", error: error)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Label("Update", systemImage: "arrow.down.doc.fill")
|
||||
}
|
||||
FormButton(role: .destructive) {
|
||||
performHelperAction {
|
||||
performHelperAction(actionName: "uninstall helper service") {
|
||||
try HelperServiceManager.unregisterRootHelper()
|
||||
}
|
||||
} label: {
|
||||
@@ -130,7 +130,7 @@ public struct AppView: View {
|
||||
}
|
||||
} else {
|
||||
FormButton {
|
||||
performHelperAction {
|
||||
performHelperAction(actionName: "install helper service") {
|
||||
try HelperServiceManager.registerRootHelper()
|
||||
}
|
||||
} label: {
|
||||
@@ -210,7 +210,7 @@ public struct AppView: View {
|
||||
try SMAppService.mainApp.unregister()
|
||||
}
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "update login items", error: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,7 +231,7 @@ public struct AppView: View {
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "update system extension", error: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,16 +252,16 @@ public struct AppView: View {
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "uninstall system extension", error: error)
|
||||
}
|
||||
}
|
||||
|
||||
private func performHelperAction(_ action: () throws -> Void) {
|
||||
private func performHelperAction(actionName: String, _ action: () throws -> Void) {
|
||||
do {
|
||||
try action()
|
||||
refreshHelperStatus()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: actionName, error: error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -264,7 +264,7 @@ public struct OnDemandRulesView: View {
|
||||
await updateService()
|
||||
isLoading = true
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "reset on-demand rules", error: error)
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
@@ -289,7 +289,7 @@ public struct OnDemandRulesView: View {
|
||||
let enabled = mode != .disabled
|
||||
try await profile.updateOnDemand(enabled: enabled, useDefaultRules: mode == .alwaysOn)
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "update on-demand rules", error: error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -111,7 +111,7 @@ struct PacketTunnelView: View {
|
||||
do {
|
||||
try await profile.restart()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "restart service", error: error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ public struct ProfileOverrideView: View {
|
||||
do {
|
||||
try await profile.reloadService()
|
||||
} catch {
|
||||
alert = AlertState(error: error)
|
||||
alert = AlertState(action: "reload service", error: error)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user