Files
Pulse/Sources/PulseUI/Features/Console/ConsoleMessageViewModel.swift
2022-08-12 19:19:39 -04:00

195 lines
5.8 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// The MIT License (MIT)
//
// Copyright (c) 20202022 Alexander Grebenyuk (github.com/kean).
import SwiftUI
import Pulse
import CoreData
import Combine
final class ConsoleMessageViewModel: Pinnable {
let title: String
let text: String
let textColor: Color
let badge: BadgeViewModel?
private let message: LoggerMessageEntity
private let searchCriteriaViewModel: ConsoleSearchCriteriaViewModel?
private(set) lazy var time = ConsoleMessageViewModel.timeFormatter.string(from: message.createdAt)
#if os(iOS)
lazy var textColor2 = UIColor.textColor(for: LoggerStore.Level(rawValue: message.level) ?? .debug)
lazy var attributedTitle: NSAttributedString = {
let string = NSMutableAttributedString()
let level = LoggerStore.Level(rawValue: message.level) ?? .debug
if let badge = badge {
string.append(badge.title, [.foregroundColor: UIColor.badgeColor(for: level)])
}
if message.label.name != "default" {
let prefix = badge == nil ? "" : " · "
string.append("\(prefix)\(message.label.name.capitalized)", [.foregroundColor: UIColor.secondaryLabel])
}
return string
}()
#endif
static let timeFormatter: DateFormatter = {
let formatter = DateFormatter()
formatter.locale = Locale(identifier: "en_US")
formatter.dateFormat = "HH:mm:ss.SSS"
return formatter
}()
private(set) lazy var pinViewModel = PinButtonViewModel(message: message)
init(message: LoggerMessageEntity, searchCriteriaViewModel: ConsoleSearchCriteriaViewModel? = nil) {
let time = ConsoleMessageViewModel.timeFormatter.string(from: message.createdAt)
if message.label.name == "default" {
self.title = time
} else {
self.title = "\(time) · \(message.label.name.capitalized)"
}
self.text = message.text
self.textColor = ConsoleMessageStyle.textColor(level: LoggerStore.Level(rawValue: message.level) ?? .debug)
self.badge = BadgeViewModel(message: message)
self.message = message
self.searchCriteriaViewModel = searchCriteriaViewModel
}
// MARK: Context Menu
#if os(iOS) || os(macOS)
func share() -> ShareItems {
ShareItems([ConsoleShareService.share(message)])
}
func copy() -> String {
message.text
}
var focusLabel: String {
message.label.name.capitalized
}
func focus() {
searchCriteriaViewModel?.criteria.labels.isEnabled = true
searchCriteriaViewModel?.criteria.labels.focused = message.label.name
}
func hide() {
searchCriteriaViewModel?.criteria.labels.isEnabled = true
searchCriteriaViewModel?.criteria.labels.hidden.insert(message.label.name)
}
#endif
}
private extension BadgeViewModel {
init?(message: LoggerMessageEntity) {
guard let model = LoggerStore.Level(rawValue: message.level).flatMap(BadgeViewModel.init) else {
return nil
}
self = model
}
init?(level: LoggerStore.Level) {
switch level {
case .critical, .error, .warning, .info, .notice:
self.init(title: level.name.uppercased(), color: .badgeColor(for: level))
case .debug, .trace:
return nil // Don't show
}
}
}
extension Color {
static func badgeColor(for level: LoggerStore.Level) -> Color {
switch level {
#if os(macOS)
case .error: return Color(Palette.red)
case .critical: return Color(Palette.red)
#else
case .critical: return .red
case .error: return .red
#endif
case .warning: return .orange
case .info: return .blue
case .notice: return .indigo
case .debug: return .primary
case .trace: return .primary
}
}
static func textColor(for level: LoggerStore.Level) -> Color {
switch level {
#if os(macOS)
case .error: return Color(Palette.red)
case .critical: return Color(Palette.red)
#else
case .critical: return .red
case .error: return .red
#endif
case .warning: return .orange
case .info: return .blue
case .notice: return .blue
case .debug: return .primary
case .trace: return .primary
}
}
}
#if os(iOS)
extension UIColor {
static func badgeColor(for level: LoggerStore.Level) -> UIColor {
switch level {
case .critical: return .systemRed
case .error: return .systemRed
case .warning: return .systemOrange
case .info: return .systemBlue
case .notice: return .systemBlue
case .debug: return .label
case .trace: return .label
}
}
static func textColor(for level: LoggerStore.Level) -> UIColor {
switch level {
case .trace: return .secondaryLabel
case .debug, .info: return .label
case .notice, .warning: return .systemOrange
case .error, .critical: return .systemRed
}
}
}
#endif
#if os(macOS)
enum ConsoleMessageStyle {
static func textColor(level: LoggerStore.Level) -> Color {
switch level {
case .trace: return .secondary
case .debug: return .primary
case .info: return .primary
case .notice: return .orange
case .warning: return .orange
case .error: return Color(Palette.red)
case .critical: return Color(Palette.red)
}
}
}
#else
enum ConsoleMessageStyle {
static func textColor(level: LoggerStore.Level) -> Color {
switch level {
case .trace: return .primary
case .debug: return .primary
case .info: return .primary
case .notice: return .orange
case .warning: return .orange
case .error: return .red
case .critical: return .red
}
}
}
#endif