Refactor Alerts
This commit is contained in:
@@ -1,47 +0,0 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
public extension Alert {
|
||||
init(_ error: Error, _ dismissAction: (() -> Void)? = nil) {
|
||||
self.init(
|
||||
errorMessage: error.localizedDescription,
|
||||
dismissAction
|
||||
)
|
||||
}
|
||||
|
||||
init(errorMessage: String, _ dismissAction: (() -> Void)? = nil) {
|
||||
self.init(
|
||||
title: Text("Error"),
|
||||
message: Text(errorMessage),
|
||||
dismissButton: .default(Text("Ok")) {
|
||||
dismissAction?()
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
public extension View {
|
||||
func alertBinding(_ binding: Binding<Alert?>) -> some View {
|
||||
alert(isPresented: Binding(get: {
|
||||
binding.wrappedValue != nil
|
||||
}, set: { newValue, _ in
|
||||
if !newValue {
|
||||
binding.wrappedValue = nil
|
||||
}
|
||||
})) {
|
||||
binding.wrappedValue!
|
||||
}
|
||||
}
|
||||
|
||||
func alertBinding(_ binding: Binding<Alert?>, _ isLoading: Binding<Bool>) -> some View {
|
||||
alert(isPresented: Binding(get: {
|
||||
binding.wrappedValue != nil
|
||||
}, set: { newValue, _ in
|
||||
if !newValue, !isLoading.wrappedValue {
|
||||
binding.wrappedValue = nil
|
||||
}
|
||||
})) {
|
||||
binding.wrappedValue!
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
import SwiftUI
|
||||
|
||||
public struct AlertState: Equatable {
|
||||
public var title: String
|
||||
public var message: String
|
||||
public var primaryButton: ButtonState?
|
||||
public var secondaryButton: ButtonState?
|
||||
public var onDismiss: (() -> Void)?
|
||||
|
||||
public struct ButtonState: Equatable {
|
||||
public var label: String
|
||||
public var role: ButtonRole?
|
||||
public var action: (() -> Void)?
|
||||
|
||||
public init(label: String, role: ButtonRole? = nil, action: (() -> Void)? = nil) {
|
||||
self.label = label
|
||||
self.role = role
|
||||
self.action = action
|
||||
}
|
||||
|
||||
public static func == (lhs: ButtonState, rhs: ButtonState) -> Bool {
|
||||
lhs.label == rhs.label && lhs.role == rhs.role
|
||||
}
|
||||
|
||||
public static func `default`(_ label: String, action: (() -> Void)? = nil) -> ButtonState {
|
||||
ButtonState(label: label, action: action)
|
||||
}
|
||||
|
||||
public static func cancel(_ label: String = String(localized: "Cancel"), action: (() -> Void)? = nil) -> ButtonState {
|
||||
ButtonState(label: label, role: .cancel, action: action)
|
||||
}
|
||||
|
||||
public static func destructive(_ label: String, action: (() -> Void)? = nil) -> ButtonState {
|
||||
ButtonState(label: label, role: .destructive, action: action)
|
||||
}
|
||||
}
|
||||
|
||||
public init(error: Error, dismiss: (() -> Void)? = nil) {
|
||||
self.init(errorMessage: error.localizedDescription, dismiss: dismiss)
|
||||
}
|
||||
|
||||
public init(errorMessage: String, dismiss: (() -> Void)? = nil) {
|
||||
title = String(localized: "Error")
|
||||
message = errorMessage
|
||||
primaryButton = .default(String(localized: "Ok"), action: dismiss)
|
||||
secondaryButton = nil
|
||||
onDismiss = nil
|
||||
}
|
||||
|
||||
public init(title: String, message: String, dismissButton: ButtonState? = nil) {
|
||||
self.title = title
|
||||
self.message = message
|
||||
primaryButton = dismissButton ?? .default(String(localized: "Ok"))
|
||||
secondaryButton = nil
|
||||
onDismiss = nil
|
||||
}
|
||||
|
||||
public init(title: String, message: String, primaryButton: ButtonState, secondaryButton: ButtonState) {
|
||||
self.title = title
|
||||
self.message = message
|
||||
self.primaryButton = primaryButton
|
||||
self.secondaryButton = secondaryButton
|
||||
onDismiss = nil
|
||||
}
|
||||
|
||||
public init(title: String, message: String, primaryButton: ButtonState, secondaryButton: ButtonState, onDismiss: @escaping () -> Void) {
|
||||
self.title = title
|
||||
self.message = message
|
||||
self.primaryButton = primaryButton
|
||||
self.secondaryButton = secondaryButton
|
||||
self.onDismiss = onDismiss
|
||||
}
|
||||
|
||||
public static func == (lhs: AlertState, rhs: AlertState) -> Bool {
|
||||
lhs.title == rhs.title && lhs.message == rhs.message &&
|
||||
lhs.primaryButton == rhs.primaryButton && lhs.secondaryButton == rhs.secondaryButton
|
||||
}
|
||||
}
|
||||
|
||||
public extension View {
|
||||
@ViewBuilder
|
||||
func alert(_ binding: Binding<AlertState?>) -> some View {
|
||||
alert(
|
||||
binding.wrappedValue?.title ?? "",
|
||||
isPresented: Binding(
|
||||
get: { binding.wrappedValue != nil },
|
||||
set: { newValue, _ in
|
||||
if !newValue {
|
||||
binding.wrappedValue?.onDismiss?()
|
||||
binding.wrappedValue = nil
|
||||
}
|
||||
}
|
||||
),
|
||||
presenting: binding.wrappedValue
|
||||
) { alertState in
|
||||
if let secondary = alertState.secondaryButton {
|
||||
Button(role: alertState.primaryButton?.role) {
|
||||
alertState.primaryButton?.action?()
|
||||
} label: {
|
||||
Text(alertState.primaryButton?.label ?? "Ok")
|
||||
}
|
||||
Button(role: secondary.role) {
|
||||
secondary.action?()
|
||||
} label: {
|
||||
Text(secondary.label)
|
||||
}
|
||||
} else if let primary = alertState.primaryButton {
|
||||
Button(role: primary.role) {
|
||||
primary.action?()
|
||||
} label: {
|
||||
Text(primary.label)
|
||||
}
|
||||
}
|
||||
} message: { alertState in
|
||||
Text(alertState.message)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder
|
||||
func alert(_ binding: Binding<AlertState?>, isLoading: Binding<Bool>) -> some View {
|
||||
alert(
|
||||
binding.wrappedValue?.title ?? "",
|
||||
isPresented: Binding(
|
||||
get: { binding.wrappedValue != nil },
|
||||
set: { newValue, _ in
|
||||
if !newValue, !isLoading.wrappedValue {
|
||||
binding.wrappedValue?.onDismiss?()
|
||||
binding.wrappedValue = nil
|
||||
}
|
||||
}
|
||||
),
|
||||
presenting: binding.wrappedValue
|
||||
) { alertState in
|
||||
if let secondary = alertState.secondaryButton {
|
||||
Button(role: alertState.primaryButton?.role) {
|
||||
alertState.primaryButton?.action?()
|
||||
} label: {
|
||||
Text(alertState.primaryButton?.label ?? "Ok")
|
||||
}
|
||||
Button(role: secondary.role) {
|
||||
secondary.action?()
|
||||
} label: {
|
||||
Text(secondary.label)
|
||||
}
|
||||
} else if let primary = alertState.primaryButton {
|
||||
Button(role: primary.role) {
|
||||
primary.action?()
|
||||
} label: {
|
||||
Text(primary.label)
|
||||
}
|
||||
}
|
||||
} message: { alertState in
|
||||
Text(alertState.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,20 +2,20 @@ import SwiftUI
|
||||
|
||||
@MainActor
|
||||
open class BaseViewModel: ObservableObject {
|
||||
@Published public var alert: Alert?
|
||||
@Published public var alert: AlertState?
|
||||
@Published public var isLoading = false
|
||||
|
||||
public init() {}
|
||||
|
||||
public func showError(_ error: Error) {
|
||||
alert = Alert(error)
|
||||
alert = AlertState(error: error)
|
||||
}
|
||||
|
||||
public func execute(_ operation: () async throws -> Void) async {
|
||||
do {
|
||||
try await operation()
|
||||
} catch {
|
||||
alert = Alert(error)
|
||||
alert = AlertState(error: error)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ open class BaseViewModel: ObservableObject {
|
||||
try await operation()
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = Alert(error)
|
||||
alert = AlertState(error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@ import SwiftUI
|
||||
|
||||
@MainActor
|
||||
public struct ProfileShareButton<Label>: View where Label: View {
|
||||
private let alert: Binding<Alert?>
|
||||
private let alert: Binding<AlertState?>
|
||||
private let profile: Profile
|
||||
private let label: () -> Label
|
||||
|
||||
public init(_ alert: Binding<Alert?>, _ profile: Profile, label: @escaping () -> Label) {
|
||||
public init(_ alert: Binding<AlertState?>, _ profile: Profile, label: @escaping () -> Label) {
|
||||
self.alert = alert
|
||||
self.profile = profile
|
||||
self.label = label
|
||||
@@ -44,13 +44,13 @@ public struct ShareButtonCompat<Label>: View where Label: View {
|
||||
private let label: () -> Label
|
||||
private let itemURL: () throws -> URL
|
||||
|
||||
@Binding private var alert: Alert?
|
||||
@Binding private var alert: AlertState?
|
||||
|
||||
#if os(macOS)
|
||||
@State private var sharePresented = false
|
||||
#endif
|
||||
|
||||
public init(_ alert: Binding<Alert?>, @ViewBuilder label: @escaping () -> Label, itemURL: @escaping () throws -> URL) {
|
||||
public init(_ alert: Binding<AlertState?>, @ViewBuilder label: @escaping () -> Label, itemURL: @escaping () throws -> URL) {
|
||||
_alert = alert
|
||||
self.label = label
|
||||
self.itemURL = itemURL
|
||||
@@ -83,7 +83,7 @@ public struct ShareButtonCompat<Label>: View where Label: View {
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
alert = Alert(error)
|
||||
alert = AlertState(error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -109,10 +109,10 @@ public struct ShareButtonCompat<Label>: View where Label: View {
|
||||
#if os(macOS)
|
||||
private struct SharingServicePicker: NSViewRepresentable {
|
||||
@Binding private var isPresented: Bool
|
||||
@Binding private var alert: Alert?
|
||||
@Binding private var alert: AlertState?
|
||||
private let item: () throws -> URL
|
||||
|
||||
init(_ isPresented: Binding<Bool>, _ alert: Binding<Alert?>, _ item: @escaping () throws -> URL) {
|
||||
init(_ isPresented: Binding<Bool>, _ alert: Binding<AlertState?>, _ item: @escaping () throws -> URL) {
|
||||
_isPresented = isPresented
|
||||
_alert = alert
|
||||
self.item = item
|
||||
@@ -132,7 +132,7 @@ public struct ShareButtonCompat<Label>: View where Label: View {
|
||||
picker.show(relativeTo: .zero, of: nsView, preferredEdge: .minY)
|
||||
}
|
||||
} catch {
|
||||
alert = Alert(error)
|
||||
alert = AlertState(error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user