160 lines
4.1 KiB
Swift
160 lines
4.1 KiB
Swift
import SharedModels
|
|
import SwiftUI
|
|
|
|
public struct FlaggedMessageView: View {
|
|
|
|
@Environment(\.flaggedMessageViewStyle) private var style
|
|
|
|
let message: String?
|
|
let status: Flagged.CheckResult.Status
|
|
|
|
public init(message: String?, status: Flagged.CheckResult.Status) {
|
|
self.message = message
|
|
self.status = status
|
|
}
|
|
|
|
public var body: some View {
|
|
style.makeBody(
|
|
configuration: .init(message: message, status: status)
|
|
)
|
|
}
|
|
}
|
|
|
|
extension FlaggedMessageView {
|
|
|
|
public init(flagged: Flagged) {
|
|
self.init(message: flagged.message, status: flagged.status)
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
@preconcurrency
|
|
public protocol FlaggedMessageViewStyle {
|
|
associatedtype Body: View
|
|
typealias Configuration = FlaggedMessageViewStyleConfiguration
|
|
|
|
@ViewBuilder
|
|
func makeBody(configuration: Self.Configuration) -> Self.Body
|
|
}
|
|
|
|
public struct FlaggedMessageViewStyleConfiguration {
|
|
public let message: String?
|
|
public let status: Flagged.CheckResult.Status
|
|
|
|
public init(message: String?, status: Flagged.CheckResult.Status) {
|
|
self.message = message
|
|
self.status = status
|
|
}
|
|
}
|
|
|
|
public struct AnyFlaggedMessageViewStyle: FlaggedMessageViewStyle {
|
|
private let _makeBody: (Configuration) -> AnyView
|
|
|
|
internal init(makeBody: @escaping (Configuration) -> AnyView) {
|
|
self._makeBody = makeBody
|
|
}
|
|
|
|
public init<S: FlaggedMessageViewStyle>(_ style: S) {
|
|
self.init { configuration in
|
|
AnyView(style.makeBody(configuration: configuration))
|
|
}
|
|
}
|
|
|
|
public func makeBody(configuration: Configuration) -> some View {
|
|
_makeBody(configuration)
|
|
}
|
|
}
|
|
|
|
public struct HorizontalFlaggedMessageViewStyle: FlaggedMessageViewStyle {
|
|
|
|
@ViewBuilder
|
|
public func makeBody(configuration: Configuration) -> some View {
|
|
if configuration.message != nil {
|
|
HStack {
|
|
FlaggedStatusLabel(status: configuration.status)
|
|
FlaggedMessageLabel(message: configuration.message)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct VerticalFlaggedMessageViewStyle: FlaggedMessageViewStyle {
|
|
|
|
@ViewBuilder
|
|
public func makeBody(configuration: Configuration) -> some View {
|
|
if configuration.message != nil {
|
|
VStack(alignment: .leading) {
|
|
FlaggedStatusLabel(status: configuration.status)
|
|
FlaggedMessageLabel(message: configuration.message)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public struct AutomaticFlaggedMessageViewStyle: FlaggedMessageViewStyle {
|
|
|
|
let horizontalSizeClass: UserInterfaceSizeClass?
|
|
|
|
@ViewBuilder
|
|
public func makeBody(configuration: Configuration) -> some View {
|
|
if horizontalSizeClass == .compact {
|
|
VerticalFlaggedMessageViewStyle().makeBody(configuration: configuration)
|
|
} else {
|
|
HorizontalFlaggedMessageViewStyle().makeBody(configuration: configuration)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension FlaggedMessageViewStyle where Self == VerticalFlaggedMessageViewStyle {
|
|
public static var vertical: Self { .init() }
|
|
}
|
|
|
|
extension FlaggedMessageViewStyle where Self == HorizontalFlaggedMessageViewStyle {
|
|
public static var horizontal: Self { .init() }
|
|
}
|
|
|
|
extension FlaggedMessageViewStyle where Self == AutomaticFlaggedMessageViewStyle {
|
|
public static func automatic(horizontalSizeClass: UserInterfaceSizeClass? = nil) -> Self {
|
|
.init(horizontalSizeClass: horizontalSizeClass)
|
|
}
|
|
}
|
|
|
|
@MainActor
|
|
private struct FlaggedMessageViewStyleKey: @preconcurrency EnvironmentKey {
|
|
|
|
static let defaultValue = AnyFlaggedMessageViewStyle(
|
|
AutomaticFlaggedMessageViewStyle(horizontalSizeClass: nil)
|
|
)
|
|
}
|
|
|
|
extension EnvironmentValues {
|
|
public var flaggedMessageViewStyle: AnyFlaggedMessageViewStyle {
|
|
get { self[FlaggedMessageViewStyleKey.self] }
|
|
set { self[FlaggedMessageViewStyleKey.self] = newValue }
|
|
}
|
|
}
|
|
|
|
extension View {
|
|
public func flaggedMessageViewStyle(_ style: AnyFlaggedMessageViewStyle) -> some View {
|
|
environment(\.flaggedMessageViewStyle, style)
|
|
}
|
|
|
|
public func flaggedMessageViewStyle<S: FlaggedMessageViewStyle>(_ style: S) -> some View {
|
|
flaggedMessageViewStyle(AnyFlaggedMessageViewStyle(style))
|
|
}
|
|
}
|
|
|
|
extension Flagged.CheckResult.Status {
|
|
|
|
public var color: Color {
|
|
switch self {
|
|
case .good:
|
|
return .green
|
|
case .warning:
|
|
return .yellow
|
|
case .error:
|
|
return .red
|
|
}
|
|
}
|
|
}
|