import SwiftUI /// The label style for the transient in-app notification banners. /// /// The style renders the label's icon next to its title, over a padded, capsule Liquid Glass background. The icon is tinted with a color /// matching the download event given at initialization — green for a started download, yellow for a cancelled one, and red for a /// failed one — and the title wears a medium-weight callout font. struct NotificationLabelStyle: LabelStyle { // MARK: Properties /// The download event whose matching color tints the label's icon. private let event: AppNotification.Event // MARK: Initializers /// Creates a notification label style for a given download event. /// /// - Parameter event: The download event whose matching color tints the label's icon. fileprivate init( event: AppNotification.Event ) { self.event = event } // MARK: Methods /// Creates the styled body of the label. /// /// - Parameter configuration: The properties of the label being styled. public func makeBody( configuration: Configuration ) -> some View { HStack( spacing: Constant.Spacing.stack ) { configuration.icon .font(.title3) .foregroundStyle(event.imageStyle) configuration.title .font(.callout) .fontWeight(.medium) .foregroundStyle(.primary) } .padding(.vertical, Constant.Padding.vertical) .padding(.horizontal, Constant.Padding.horizontal) .glassEffect( .regular, in: .capsule ) } } // MARK: - Styles extension LabelStyle where Self == NotificationLabelStyle { /// The label style for the transient in-app notification banners. /// /// - Parameter event: The download event whose matching color tints the label's icon. /// - Returns: A notification label style for the given event. static func notification( event: AppNotification.Event ) -> NotificationLabelStyle { .init(event: event) } } // MARK: - AppNotification.Event+Properties private extension AppNotification.Event { /// The color that tints the icon of a notification label: green for a started download, yellow for a cancelled one, and red /// for a failed one. var imageStyle: Color { switch self { case .cancelled: .yellow case .failed: .red case .started: .green } } } // MARK: - Constants /// The constant values used across the label style. private enum Constant { /// The padding constants. enum Padding { /// The horizontal padding between a label's content and its background. static let horizontal: CGFloat = 16 /// The vertical padding between a label's content and its background. static let vertical: CGFloat = 8 } /// The spacing constants. enum Spacing { /// The spacing between the elements of a stack. static let stack: CGFloat = 16 } } // MARK: - Previews #Preview( "Notification label style" ) { VStack( alignment: .leading, spacing: 16 ) { Label { Text(verbatim: "Downloading the speech model for Dutch (Netherlands)…") } icon: { Image(systemName: "arrow.down.circle") } .labelStyle(.notification( event: .started )) Label { Text(verbatim: "The speech model download for Dutch (Netherlands) was cancelled.") } icon: { Image(systemName: "xmark.circle") } .labelStyle(.notification( event: .cancelled )) Label { Text(verbatim: "The speech model for German (Germany) could not be downloaded.") } icon: { Image(systemName: "exclamationmark.triangle") } .labelStyle(.notification( event: .failed )) } }