import Foundation import SwiftUI extension Notification.Name { static let snackbarDidChange = Notification.Name("snackbarDidChange") } @MainActor final class SnackbarCenter: ObservableObject { static let shared = SnackbarCenter() @Published var current: SnackbarMessage? private var dismissTask: Task? func show( title: String, style: SnackbarStyle = .info, icon: String? = nil, duration: TimeInterval = 3.5, action: (() -> Void)? = nil ) { dismissTask?.cancel() current = SnackbarMessage( title: title, style: style, iconSystemName: icon, duration: duration, action: action ) dismissTask = Task { [weak self] in let nanos = UInt64(max(0.2, duration) * 1_000_000_000) try? await Task.sleep(nanoseconds: nanos) guard !Task.isCancelled else { return } self?.dismiss(animated: true) } } func handleTap() { let action = current?.action dismiss(animated: true) action?() } func dismiss(animated: Bool) { dismissTask?.cancel() dismissTask = nil if animated { withAnimation(.spring(response: 0.3, dampingFraction: 0.9)) { current = nil } } else { current = nil } } } enum SnackbarStyle: Sendable { case info case success case warning case error var backgroundColor: Color { switch self { case .info: return Color(hex: "#3B93F7") case .success: return Color(hex: "#2E7D32") case .warning: return Color(hex: "#C77700") case .error: return Color(hex: "#C62828") } } } struct SnackbarMessage: Identifiable { let id = UUID() let title: String let style: SnackbarStyle let iconSystemName: String? let duration: TimeInterval let action: (() -> Void)? }