Files

241 lines
7.6 KiB
Swift

// The MIT License (MIT)
//
// Copyright (c) 2020-2026 Alexander Grebenyuk (github.com/kean).
import SwiftUI
import CoreData
import Pulse
import Combine
package final class ConsoleRouter: ObservableObject {
#if os(macOS)
@Published package var _selection: (any Hashable)?
#endif
@Published package var shareItems: ShareItems?
@Published package var isShowingFilters = false
@Published package var isShowingSettings = false
@Published package var isShowingSessions = false
@Published package var isShowingShareStore = false
package init() {}
}
#if os(macOS)
@available(macOS 15, *)
package enum ConsoleSelectedItem: Hashable {
case entity(NSManagedObjectID)
case occurrence(NSManagedObjectID, ConsoleSearchOccurrence)
}
@available(macOS 15, *)
extension ConsoleRouter {
// Selection for macOS split-view navigation
package var selection: ConsoleSelectedItem? {
get { _selection as? ConsoleSelectedItem }
set { _selection = newValue }
}
}
#endif
struct ConsoleRouterView: View {
@EnvironmentObject var environment: ConsoleEnvironment
@ObservedObject var router: ConsoleRouter
var body: some View {
if #available(iOS 18, tvOS 18, macOS 15, watchOS 11, visionOS 1, *) {
contents
}
}
}
#if os(iOS) || os(visionOS)
@available(iOS 18, tvOS 18, macOS 15, watchOS 11, visionOS 1, *)
extension ConsoleRouterView {
var contents: some View {
Text("").invisible()
.sheet(isPresented: $router.isShowingFilters) { destinationFilters }
.sheet(isPresented: $router.isShowingSettings) { destinationSettings }
.sheet(isPresented: $router.isShowingSessions) { destinationSessions }
.sheet(isPresented: $router.isShowingShareStore) { destinationShareStore }
.sheet(item: $router.shareItems, content: ShareView.init)
}
private var destinationFilters: some View {
NavigationView {
ConsoleFiltersView()
.inlineNavigationTitle("Filters")
.toolbar {
ToolbarItem(placement: .confirmationAction) {
makeButton(role: .confirm) {
router.isShowingFilters = false
}
}
}
.dynamicTypeSize(...DynamicTypeSize.xxxLarge)
}
.presentationDetents([.medium, .large])
}
@ViewBuilder
private var destinationSessions: some View {
NavigationView {
SessionsView()
.navigationTitle("Sessions")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItemGroup(placement: .navigationBarLeading) {
makeButton(role: .close) {
router.isShowingSessions = false
}
}
}
}
}
@ViewBuilder
private var destinationSettings: some View {
if let store = environment.store as? LoggerStore {
NavigationView {
SettingsView(store: store)
.navigationTitle("Settings")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(trailing: Button(action: { router.isShowingSettings = false }) {
Text("Done")
})
}
}
}
private var destinationShareStore: some View {
NavigationView {
ShareStoreView(onDismiss: { router.isShowingShareStore = false })
}.presentationDetents([.medium, .large])
}
}
#elseif os(watchOS)
@available(iOS 18, tvOS 18, macOS 15, watchOS 11, visionOS 1, *)
extension ConsoleRouterView {
var contents: some View {
Text("").invisible()
.sheet(isPresented: $router.isShowingSettings) { destinationSettings }
.sheet(isPresented: $router.isShowingFilters) { destinationFilters }
.sheet(isPresented: $router.isShowingSessions) { destinationSessions }
}
@ViewBuilder
private var destinationSettings: some View {
if let store = environment.store as? LoggerStore {
NavigationView {
SettingsView(store: store)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button(action: {
router.isShowingSettings = false
}, label: {
Image(systemName: "xmark")
})
}
}
}
}
}
private var destinationFilters: some View {
NavigationView {
ConsoleFiltersView()
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button(action: {
router.isShowingFilters = false
}, label: {
Image(systemName: "xmark")
})
}
}
}
}
private var destinationSessions: some View {
NavigationView {
SessionsView()
.navigationTitle("Sessions")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button(action: {
router.isShowingSessions = false
}, label: {
Image(systemName: "xmark")
})
}
}
}
}
}
#elseif os(tvOS)
@available(iOS 18, tvOS 18, macOS 15, watchOS 11, visionOS 1, *)
extension ConsoleRouterView {
var contents: some View {
Text("").invisible()
.sheet(isPresented: $router.isShowingSessions) { destinationSessions }
}
private var destinationSessions: some View {
NavigationView {
SessionsView()
.navigationTitle("Sessions")
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button(action: { router.isShowingSessions = false }) {
Image(systemName: "xmark")
}
}
}
}
}
}
#else
@available(macOS 15, *)
extension ConsoleRouterView {
var contents: some View {
Text("").invisible()
.sheet(isPresented: $router.isShowingFilters) { destinationFilters }
.sheet(isPresented: $router.isShowingSettings) { destinationSettings }
.sheet(isPresented: $router.isShowingShareStore) { destinationShareStore }
}
private var destinationFilters: some View {
ConsoleFiltersView()
.frame(width: 320, height: 500)
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button("Done") { router.isShowingFilters = false }
}
}
}
private var destinationSettings: some View {
SettingsView()
.frame(width: 320, height: UserSettings.shared.isRemoteLoggingHidden ? 175 : 420)
.navigationTitle("Settings")
.toolbar {
ToolbarItem(placement: .cancellationAction) {
Button("Close") { router.isShowingSettings = false }
}
}
}
private var destinationShareStore: some View {
ShareStoreView(onDismiss: { router.isShowingShareStore = false })
.frame(width: 340, height: 400)
}
}
#endif