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(_ 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(_ 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 } } }