Skip to content

Commit

Permalink
Merge pull request #145 from kean/new-mode-toggle
Browse files Browse the repository at this point in the history
New mode toggle
  • Loading branch information
kean authored Jan 22, 2023
2 parents 27f3d5f + e8c667e commit 338fe18
Show file tree
Hide file tree
Showing 21 changed files with 310 additions and 77 deletions.
4 changes: 4 additions & 0 deletions Pulse.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
0C7A0DFB297C33F300B4B69D /* ConsoleRouterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7A0DFA297C33F300B4B69D /* ConsoleRouterView.swift */; };
0C7A0DFD297C382300B4B69D /* ConsoleShareButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7A0DFC297C382300B4B69D /* ConsoleShareButton.swift */; };
0C7A0E00297C51CE00B4B69D /* ConsoleListOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7A0DFF297C51CE00B4B69D /* ConsoleListOptions.swift */; };
0C7A0E02297CE71400B4B69D /* FormattersTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C7A0E01297CE71400B4B69D /* FormattersTests.swift */; };
0C7A834026D1CA290082634F /* repos.json in Resources */ = {isa = PBXBuildFile; fileRef = 0CFF9BD825D6199A0069DB6A /* repos.json */; };
0C9F04F92884F34A0035239F /* Pulse_Demo_macOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0C9F04F82884F34A0035239F /* Pulse_Demo_macOSApp.swift */; };
0C9F04FD2884F34A0035239F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0C9F04FC2884F34A0035239F /* Assets.xcassets */; };
Expand Down Expand Up @@ -545,6 +546,7 @@
0C7A0DFA297C33F300B4B69D /* ConsoleRouterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleRouterView.swift; sourceTree = "<group>"; };
0C7A0DFC297C382300B4B69D /* ConsoleShareButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleShareButton.swift; sourceTree = "<group>"; };
0C7A0DFF297C51CE00B4B69D /* ConsoleListOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleListOptions.swift; sourceTree = "<group>"; };
0C7A0E01297CE71400B4B69D /* FormattersTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FormattersTests.swift; sourceTree = "<group>"; };
0C9F04F62884F34A0035239F /* Pulse Demo macOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Pulse Demo macOS.app"; sourceTree = BUILT_PRODUCTS_DIR; };
0C9F04F82884F34A0035239F /* Pulse_Demo_macOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pulse_Demo_macOSApp.swift; sourceTree = "<group>"; };
0C9F04FC2884F34A0035239F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1483,6 +1485,7 @@
0CAF1D78297B5FF4002E2722 /* ConsoleSearchServiceTests.swift */,
0CAF1D76297B0810002E2722 /* ConsoleSearchSuggestionTests.swift */,
0C63A389297A44B300F6A6A5 /* StringSearchOptionsTests.swift */,
0C7A0E01297CE71400B4B69D /* FormattersTests.swift */,
0CF0D6C9296F18FD00EED9D4 /* MockTests.swift */,
);
path = PulseUITests;
Expand Down Expand Up @@ -2228,6 +2231,7 @@
files = (
0C63A38A297A44B300F6A6A5 /* StringSearchOptionsTests.swift in Sources */,
0CAF1D77297B0810002E2722 /* ConsoleSearchSuggestionTests.swift in Sources */,
0C7A0E02297CE71400B4B69D /* FormattersTests.swift in Sources */,
0CF0D6CB296F18FD00EED9D4 /* ConsoleViewModelTests.swift in Sources */,
0CB63A292975A94D00525165 /* ConsoleSearchTokenTests.swift in Sources */,
0CAF1D79297B5FF4002E2722 /* ConsoleSearchServiceTests.swift in Sources */,
Expand Down
10 changes: 7 additions & 3 deletions Sources/PulseUI/Features/Console/ConsoleView-ios.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ private struct _ConsoleRegularContentView: View {
footerView
}

#warning("implement on other platforms and move to the list?")
@ViewBuilder
private var footerView: some View {
if #available(iOS 15, *), viewModel.searchCriteriaViewModel.criteria.shared.dates == .session, viewModel.list.options.order == .descending {
Expand All @@ -143,8 +142,13 @@ private struct _ConsoleRegularContentView: View {
#if DEBUG
struct ConsoleView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
ConsoleView(viewModel: .init(store: .mock))
Group {
NavigationView {
ConsoleView(viewModel: .init(store: .mock))
}.previewDisplayName("Console")
NavigationView {
ConsoleView.network(store: .mock)
}.previewDisplayName("Network")
}
}
}
Expand Down
15 changes: 9 additions & 6 deletions Sources/PulseUI/Features/Console/ConsoleView-macos.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ private struct ConsoleToolbarItems: View {
var body: some View {
ConsoleSettingsButton(store: viewModel.store)
Spacer()
ConsoleToolbarModePickerButton(viewModel: viewModel.searchCriteriaViewModel)
ConsoleToolbarModePickerButton(viewModel: viewModel)
.keyboardShortcut("n", modifiers: [.command, .shift])
ConsoleToolbarToggleOnlyErrorsButton(viewModel: viewModel.searchCriteriaViewModel)
.keyboardShortcut("e", modifiers: [.command, .shift])
Expand Down Expand Up @@ -146,13 +146,16 @@ private struct FilterPopoverToolbarButton: View {
}

private struct ConsoleToolbarModePickerButton: View {
@ObservedObject var viewModel: ConsoleSearchCriteriaViewModel
let viewModel: ConsoleViewModel
@State private var mode: ConsoleMode = .all

var body: some View {
Button(action: { viewModel.isOnlyNetwork.toggle() }) {
Image(systemName: viewModel.isOnlyNetwork ? "arrow.down.circle.fill" : "arrow.down.circle")
.foregroundColor(viewModel.isOnlyNetwork ? Color.accentColor : Color.secondary)
}.help("Automatically Scroll to Recent Messages (⇧⌘N)")
Button(action: { mode = (mode == .tasks ? .all : .tasks) }) {
Image(systemName: mode == .tasks ? "arrow.down.circle.fill" : "arrow.down.circle")
.foregroundColor(mode == .tasks ? Color.accentColor : Color.secondary)
}
.help("Automatically Scroll to Recent Messages (⇧⌘N)")
.onChange(of: mode) { viewModel.mode = $0 }
}
}

Expand Down
6 changes: 4 additions & 2 deletions Sources/PulseUI/Features/Console/ConsoleView-tvos.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,12 @@ public struct ConsoleView: View {

private struct ConsoleMenuView: View {
let store: LoggerStore
let consoleViewModel: ConsoleViewModel
@ObservedObject var viewModel: ConsoleSearchCriteriaViewModel
@ObservedObject var router: ConsoleRouter

init(viewModel: ConsoleViewModel) {
self.consoleViewModel = viewModel
self.store = viewModel.store
self.viewModel = viewModel.searchCriteriaViewModel
self.router = viewModel.router
Expand All @@ -58,11 +60,11 @@ private struct ConsoleMenuView: View {
Toggle(isOn: $viewModel.isOnlyErrors) {
Label("Errors Only", systemImage: "exclamationmark.octagon")
}
Toggle(isOn: $viewModel.isOnlyNetwork) {
Toggle(isOn: consoleViewModel.bindingForNetworkMode) {
Label("Network Only", systemImage: "arrow.down.circle")
}
NavigationLink(destination: destinationFilters) {
Label(viewModel.isOnlyNetwork ? "Network Filters" : "Message Filters", systemImage: "line.3.horizontal.decrease.circle")
Label(consoleViewModel.bindingForNetworkMode.wrappedValue ? "Network Filters" : "Message Filters", systemImage: "line.3.horizontal.decrease.circle")
}
} header: { Text("Quick Filters") }
if !store.isArchive {
Expand Down
6 changes: 4 additions & 2 deletions Sources/PulseUI/Features/Console/ConsoleView-watchos.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,22 @@ public struct ConsoleView: View {
}

private struct ConsoleToolbarView: View {
var consoleViewModel: ConsoleViewModel
@ObservedObject var viewModel: ConsoleSearchCriteriaViewModel
@ObservedObject var router: ConsoleRouter

init(viewModel: ConsoleViewModel) {
self.consoleViewModel = viewModel
self.viewModel = viewModel.searchCriteriaViewModel
self.router = viewModel.router
}

var body: some View {
HStack {
Button(action: { viewModel.isOnlyNetwork.toggle() } ) {
Button(action: { consoleViewModel.bindingForNetworkMode.wrappedValue.toggle() } ) {
Image(systemName: "arrow.down.circle")
}
.background(viewModel.isOnlyNetwork ? Rectangle().foregroundColor(.blue).cornerRadius(8) : nil)
.background(consoleViewModel.bindingForNetworkMode.wrappedValue ? Rectangle().foregroundColor(.blue).cornerRadius(8) : nil)

Button(action: { viewModel.isOnlyErrors.toggle() }) {
Image(systemName: "exclamationmark.octagon")
Expand Down
19 changes: 17 additions & 2 deletions Sources/PulseUI/Features/Console/ConsoleViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import SwiftUI

final class ConsoleViewModel: ObservableObject {
let title: String
let isNetworkModeEnabled: Bool
let isNetwork: Bool
let store: LoggerStore

let list: ConsoleListViewModel
Expand All @@ -36,14 +36,29 @@ final class ConsoleViewModel: ObservableObject {
didSet { refreshListsVisibility() }
}

var mode: ConsoleMode = .all {
didSet {
list.update(mode: mode)
searchCriteriaViewModel.mode = mode
}
}

var bindingForNetworkMode: Binding<Bool> {
Binding(get: {
self.mode == .tasks
}, set: {
self.mode = $0 ? .tasks : .all
})
}

var onDismiss: (() -> Void)?

private var cancellables: [AnyCancellable] = []

init(store: LoggerStore, isOnlyNetwork: Bool = false) {
self.title = isOnlyNetwork ? "Network" : "Console"
self.store = store
self.isNetworkModeEnabled = isOnlyNetwork
self.isNetwork = isOnlyNetwork

self.searchCriteriaViewModel = ConsoleSearchCriteriaViewModel(store: store)
self.searchBarViewModel = ConsoleSearchBarViewModel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ struct ConsoleListContentView: View {
}

private func makeName(for section: NSFetchedResultsSectionInfo) -> String {
if !viewModel.isOnlyNetwork {
if viewModel.mode != .tasks {
if viewModel.options.messageGroupBy == .level {
let rawValue = Int16(Int(section.name) ?? 0)
return (LoggerStore.Level(rawValue: rawValue) ?? .debug).name.capitalized
Expand Down
78 changes: 60 additions & 18 deletions Sources/PulseUI/Features/Console/List/ConsoleListViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,29 @@ final class ConsoleListViewModel: NSObject, NSFetchedResultsControllerDelegate,
}
}

@Published private(set) var mode: ConsoleMode = .all

/// This exist strickly to workaround List performance issues
private var scrollPosition: ScrollPosition = .nearTop
private var visibleEntityCountLimit = fetchBatchSize
private var visibleObjectIDs: Set<NSManagedObjectID> = []

private(set) var isOnlyNetwork = false
var grouping: ConsoleListGroupBy { isOnlyNetwork ? options.taskGroupBy : options.messageGroupBy }
var grouping: ConsoleListGroupBy { mode == .tasks ? options.taskGroupBy : options.messageGroupBy }
private let store: LoggerStore
private let searchCriteriaViewModel: ConsoleSearchCriteriaViewModel
private var controller: NSFetchedResultsController<NSManagedObject>?
private var cancellables: [AnyCancellable] = []

let logCountObserver: ManagedObjectsCountObserver
let taskCountObserver: ManagedObjectsCountObserver

init(store: LoggerStore, criteria: ConsoleSearchCriteriaViewModel) {
self.store = store
self.searchCriteriaViewModel = criteria

self.logCountObserver = ManagedObjectsCountObserver(entity: LoggerMessageEntity.self, context: store.viewContext, sortDescriptior: NSSortDescriptor(key: "createdAt", ascending: false))
self.taskCountObserver = ManagedObjectsCountObserver(entity: NetworkTaskEntity.self, context: store.viewContext, sortDescriptior: NSSortDescriptor(key: "createdAt", ascending: false))

super.init()

$entities.sink { [entitiesSubject] in
Expand All @@ -65,21 +72,24 @@ final class ConsoleListViewModel: NSObject, NSFetchedResultsControllerDelegate,
.sink { [weak self] _ in self?.refresh() }
.store(in: &cancellables)

// important: no drop first and refreshes immediately
searchCriteriaViewModel.$isOnlyNetwork.sink { [weak self] in
self?.isOnlyNetwork = $0
self?.refreshController()
}.store(in: &cancellables)

$options.dropFirst().receive(on: DispatchQueue.main).sink { [weak self] _ in
self?.refreshController()
}.store(in: &cancellables)

refreshController()
}

func update(mode: ConsoleMode) {
self.mode = mode
self.refreshController()
}

// MARK: - NSFetchedResultsController

func refreshController() {
let request: NSFetchRequest<NSManagedObject>
let sortKey = isOnlyNetwork ? options.taskSortBy.key : options.messageSortBy.key
if isOnlyNetwork {
let sortKey = mode == .tasks ? options.taskSortBy.key : options.messageSortBy.key
if mode == .tasks {
request = .init(entityName: "\(NetworkTaskEntity.self)")
request.sortDescriptors = [
grouping.key.map { NSSortDescriptor(key: $0, ascending: grouping.isAscending) },
Expand All @@ -94,29 +104,55 @@ final class ConsoleListViewModel: NSObject, NSFetchedResultsControllerDelegate,
].compactMap { $0 }
}
request.fetchBatchSize = fetchBatchSize
controller = NSFetchedResultsController(fetchRequest: request, managedObjectContext: store.viewContext, sectionNameKeyPath: grouping.key, cacheName: nil)
controller = NSFetchedResultsController(
fetchRequest: request,
managedObjectContext: store.viewContext,
sectionNameKeyPath: grouping.key,
cacheName: nil
)
controller?.delegate = self

refresh()
}

func refresh() {
// Search messages
guard let controller = controller else {
return assertionFailure()
}
let criteria = searchCriteriaViewModel
if isOnlyNetwork {
controller.fetchRequest.predicate = ConsoleSearchCriteria.makeNetworkPredicates(criteria: criteria.criteria, isOnlyErrors: criteria.isOnlyErrors, filterTerm: criteria.filterTerm)
} else {
controller.fetchRequest.predicate = ConsoleSearchCriteria.makeMessagePredicates(criteria: criteria.criteria, isOnlyErrors: criteria.isOnlyErrors, filterTerm: criteria.filterTerm)
}
controller.fetchRequest.predicate = makePredicate(for: mode)
try? controller.performFetch()

logCountObserver.setPredicate(makePredicate(for: .logs))
taskCountObserver.setPredicate(makePredicate(for: .tasks))

reloadMessages()
didRefresh.send(())
}

private func makePredicate(for mode: ConsoleMode) -> NSPredicate? {
let criteria = searchCriteriaViewModel

func makeMessagesPredicate(isMessageOnly: Bool) -> NSPredicate? {
var predicates: [NSPredicate] = []
if isMessageOnly {
predicates.append(NSPredicate(format: "task == NULL"))
}
if let predicate = ConsoleSearchCriteria.makeMessagePredicates(criteria: criteria.criteria, isOnlyErrors: criteria.isOnlyErrors, filterTerm: criteria.filterTerm) {
predicates.append(predicate)
}
return predicates.isEmpty ? nil : NSCompoundPredicate(andPredicateWithSubpredicates: predicates)
}

switch mode {
case .all:
return makeMessagesPredicate(isMessageOnly: false)
case .logs:
return makeMessagesPredicate(isMessageOnly: true)
case .tasks:
return ConsoleSearchCriteria.makeNetworkPredicates(criteria: criteria.criteria, isOnlyErrors: criteria.isOnlyErrors, filterTerm: criteria.filterTerm)
}
}

// MARK: - NSFetchedResultsControllerDelegate

func controller(_ controller: NSFetchedResultsController<NSFetchRequestResult>, didChangeContentWith diff: CollectionDifference<NSManagedObjectID>) {
Expand Down Expand Up @@ -186,3 +222,9 @@ final class ConsoleListViewModel: NSObject, NSFetchedResultsControllerDelegate,
}

private let fetchBatchSize = 100

enum ConsoleMode: String {
case all
case logs
case tasks
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ struct ConsoleContextMenu: View {
@ViewBuilder
private var sortByMenu: some View {
Menu(content: {
if viewModel.searchCriteriaViewModel.isOnlyNetwork {
if viewModel.mode == .tasks {
Picker("Sort By", selection: $listViewModel.options.taskSortBy) {
ForEach(ConsoleListOptions.TaskSortBy.allCases, id: \.self) {
Text($0.rawValue).tag($0)
Expand All @@ -105,7 +105,7 @@ struct ConsoleContextMenu: View {
@ViewBuilder
private var groupByMenu: some View {
Menu(content: {
if viewModel.searchCriteriaViewModel.isOnlyNetwork {
if viewModel.mode == .tasks {
Picker("Group By", selection: $listViewModel.options.taskGroupBy) {
ForEach(ConsoleListOptions.TaskGroupBy.allCases, id: \.self) {
Text($0.rawValue).tag($0)
Expand Down
14 changes: 13 additions & 1 deletion Sources/PulseUI/Features/Console/Views/ConsoleEntityCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,15 @@ private struct _ConsoleMessageCell: View {
@State private var shareItems: ShareItems?

var body: some View {
#if os(iOS)
let cell = ConsoleMessageCell(viewModel: .init(message: message), isDisclosureNeeded: true)
.background(NavigationLink("", destination: LazyConsoleDetailsView(message: message).id(message.objectID)).opacity(0))
#else
let cell = NavigationLink(destination: LazyConsoleDetailsView(message: message).id(message.objectID)) {
ConsoleMessageCell(viewModel: .init(message: message))
ConsoleMessageCell(viewModel: .init(message: message), isDisclosureNeeded: true)
}
#endif

#if os(iOS)
if #available(iOS 15, *) {
cell.swipeActions(edge: .leading, allowsFullSwipe: true) {
Expand Down Expand Up @@ -74,9 +80,15 @@ private struct _ConsoleTaskCell: View {
@State private var shareItems: ShareItems?

var body: some View {
#if os(iOS)
let cell = ConsoleTaskCell(viewModel: .init(task: task), isDisclosureNeeded: true)
.background(NavigationLink("", destination: LazyNetworkInspectorView(task: task).id(task.objectID)).opacity(0))
#else
let cell = NavigationLink(destination: LazyNetworkInspectorView(task: task).id(task.objectID)) {
ConsoleTaskCell(viewModel: .init(task: task))
}
#endif

#if os(iOS)
if #available(iOS 15, *) {
cell.swipeActions(edge: .leading, allowsFullSwipe: true) {
Expand Down
Loading

0 comments on commit 338fe18

Please sign in to comment.