Files
Pulse/Sources/PulseUI/Features/Console/ConsoleEnvironment.swift
2023-04-29 15:02:22 -04:00

157 lines
4.6 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) 20202023 Alexander Grebenyuk (github.com/kean).
import CoreData
import Pulse
import Combine
import SwiftUI
/// Contains every dependency that the console views have.
///
/// - warning: It's marked with `ObservableObject` to make it possible to be used
/// with `@StateObject` and `@EnvironmentObject`, but it never changes.
final class ConsoleEnvironment: ObservableObject {
let title: String
let store: LoggerStore
let index: LoggerStoreIndex
let filters: ConsoleFiltersViewModel
let logCountObserver: ManagedObjectsCountObserver
let taskCountObserver: ManagedObjectsCountObserver
let router = ConsoleRouter()
let initialMode: ConsoleMode
@Published var mode: ConsoleMode
@Published var listOptions: ConsoleListOptions = .init()
var bindingForNetworkMode: Binding<Bool> {
Binding(get: {
self.mode == .network
}, set: {
self.mode = $0 ? .network : .all
})
}
private var cancellables: [AnyCancellable] = []
init(store: LoggerStore, mode: ConsoleMode = .all) {
self.store = store
switch mode {
case .all: self.title = "Console"
case .logs: self.title = "Logs"
case .network: self.title = "Network"
}
self.initialMode = mode
self.mode = mode
func makeDefaultOptions() -> ConsoleDataSource.PredicateOptions {
var options = ConsoleDataSource.PredicateOptions()
options.filters.shared.sessions.selection = [store.session.id]
return options
}
self.index = LoggerStoreIndex(store: store)
self.filters = ConsoleFiltersViewModel(options: makeDefaultOptions())
self.logCountObserver = ManagedObjectsCountObserver(
entity: LoggerMessageEntity.self,
context: store.viewContext,
sortDescriptior: NSSortDescriptor(keyPath: \LoggerMessageEntity.createdAt, ascending: false)
)
self.taskCountObserver = ManagedObjectsCountObserver(
entity: NetworkTaskEntity.self,
context: store.viewContext,
sortDescriptior: NSSortDescriptor(keyPath: \NetworkTaskEntity.createdAt, ascending: false)
)
bind()
}
private func bind() {
$mode.sink { [weak self] in
self?.filters.mode = $0
}.store(in: &cancellables)
filters.$options.sink { [weak self] in
self?.refreshCountObservers($0)
}.store(in: &cancellables)
}
private func refreshCountObservers(_ options: ConsoleDataSource.PredicateOptions) {
func makePredicate(for mode: ConsoleMode) -> NSPredicate? {
ConsoleDataSource.makePredicate(mode: mode, options: options)
}
logCountObserver.setPredicate(makePredicate(for: .logs))
taskCountObserver.setPredicate(makePredicate(for: .network))
}
func removeAllLogs() {
store.removeAll()
index.clear()
#if os(iOS)
runHapticFeedback(.success)
ToastView {
HStack {
Image(systemName: "trash")
Text("All messages removed")
}
}.show()
#endif
}
}
public enum ConsoleMode: String {
/// Displays both messages and network tasks with the ability
/// to switch between the two modes.
case all
/// Displays only regular messages.
case logs
/// Displays only network tasks.
case network
var hasLogs: Bool { self == .all || self == .logs }
var hasNetwork: Bool { self == .all || self == .network }
}
// MARK: Environment
private struct LoggerStoreKey: EnvironmentKey {
static let defaultValue: LoggerStore = .shared
}
private struct ConsoleRouterKey: EnvironmentKey {
static let defaultValue: ConsoleRouter = .init()
}
extension EnvironmentValues {
var store: LoggerStore {
get { self[LoggerStoreKey.self] }
set { self[LoggerStoreKey.self] = newValue }
}
var router: ConsoleRouter {
get { self[ConsoleRouterKey.self] }
set { self[ConsoleRouterKey.self] = newValue }
}
}
extension View {
func injecting(_ environment: ConsoleEnvironment) -> some View {
self.background(ConsoleRouterView()) // important: order
.environmentObject(environment)
.environmentObject(environment.router)
.environmentObject(environment.index)
.environmentObject(environment.filters)
.environmentObject(UserSettings.shared)
.environment(\.router, environment.router)
.environment(\.store, environment.store)
.environment(\.managedObjectContext, environment.store.viewContext)
}
}