Files

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