From b548a69414fcfcae1f8440860e8123456c59a869 Mon Sep 17 00:00:00 2001 From: Antoine van der Lee <4329185+AvdLee@users.noreply.github.com> Date: Wed, 26 Jun 2024 11:30:28 +0200 Subject: [PATCH 01/14] Change methods to public to allow service initialization --- Sources/Pulse/LoggerStore/LoggerStore.swift | 2 +- .../RemoteLogger-Connection.swift | 27 ++++++++++--------- .../RemoteLogger/RemoteLogger-Protocol.swift | 14 ++++++---- Sources/Pulse/RemoteLogger/RemoteLogger.swift | 10 +++---- 4 files changed, 29 insertions(+), 24 deletions(-) diff --git a/Sources/Pulse/LoggerStore/LoggerStore.swift b/Sources/Pulse/LoggerStore/LoggerStore.swift index 14fcc76a9..7b7205b9a 100644 --- a/Sources/Pulse/LoggerStore/LoggerStore.swift +++ b/Sources/Pulse/LoggerStore/LoggerStore.swift @@ -347,7 +347,7 @@ extension LoggerStore { } /// Handles event created by the current store and dispatches it to observers. - func handle(_ event: Event) { + public func handle(_ event: Event) { guard let event = configuration.willHandleEvent(event) else { return } diff --git a/Sources/Pulse/RemoteLogger/RemoteLogger-Connection.swift b/Sources/Pulse/RemoteLogger/RemoteLogger-Connection.swift index 6118a1d25..5147eb4dc 100644 --- a/Sources/Pulse/RemoteLogger/RemoteLogger-Connection.swift +++ b/Sources/Pulse/RemoteLogger/RemoteLogger-Connection.swift @@ -11,13 +11,13 @@ import OSLog import Pulse #endif -protocol RemoteLoggerConnectionDelegate: AnyObject { +public protocol RemoteLoggerConnectionDelegate: AnyObject { func connection(_ connection: RemoteLogger.Connection, didChangeState newState: NWConnection.State) func connection(_ connection: RemoteLogger.Connection, didReceiveEvent event: RemoteLogger.Connection.Event) } -extension RemoteLogger { - final class Connection { +public extension RemoteLogger { + public final class Connection { var endpoint: NWEndpoint { connection.endpoint } private let connection: NWConnection private var buffer = Data() @@ -31,14 +31,15 @@ extension RemoteLogger { self.init(NWConnection(to: endpoint, using: parameters)) } - init(_ connection: NWConnection) { + public init(_ connection: NWConnection, delegate: RemoteLoggerConnectionDelegate? = nil) { self.connection = connection + self.delegate = delegate let isLogEnabled = UserDefaults.standard.bool(forKey: "com.github.kean.pulse.debug") self.log = isLogEnabled ? OSLog(subsystem: "com.github.kean.pulse", category: "RemoteLogger") : .disabled } - - func start(on queue: DispatchQueue) { + + public func start(on queue: DispatchQueue) { connection.stateUpdateHandler = { [weak self] state in guard let self = self else { return } DispatchQueue.main.async { @@ -49,15 +50,15 @@ extension RemoteLogger { connection.start(queue: queue) } - enum Event { + public enum Event { case packet(Packet) case error(Error) case completed } - struct Packet { - let code: UInt8 - let body: Data + public struct Packet { + public let code: UInt8 + public let body: Data } private func receive() { @@ -130,7 +131,7 @@ extension RemoteLogger { } } - func send(code: UInt8, data: Data) { + public func send(code: UInt8, data: Data) { do { let data = try encode(code: code, body: data) let log = self.log @@ -144,7 +145,7 @@ extension RemoteLogger { } } - func send(code: UInt8, entity: T) { + public func send(code: UInt8, entity: T) { do { let data = try JSONEncoder().encode(entity) send(code: code, data: data) @@ -206,7 +207,7 @@ extension RemoteLogger { } } - func cancel() { + public func cancel() { connection.cancel() } } diff --git a/Sources/Pulse/RemoteLogger/RemoteLogger-Protocol.swift b/Sources/Pulse/RemoteLogger/RemoteLogger-Protocol.swift index 65ed641f0..df031d3da 100644 --- a/Sources/Pulse/RemoteLogger/RemoteLogger-Protocol.swift +++ b/Sources/Pulse/RemoteLogger/RemoteLogger-Protocol.swift @@ -8,8 +8,8 @@ import Network import Pulse #endif -extension RemoteLogger { - enum PacketCode: UInt8, Equatable { +public extension RemoteLogger { + public enum PacketCode: UInt8, Equatable { // Handshake case clientHello = 0 // PacketClientHello case serverHello = 1 // ServerHelloResponse @@ -44,7 +44,7 @@ extension RemoteLogger { init() {} } - struct PacketNetworkMessage { + public struct PacketNetworkMessage { private struct Manifest: Codable { let messageSize: UInt32 let requestBodySize: UInt32 @@ -83,7 +83,7 @@ extension RemoteLogger { return data } - static func decode(_ data: Data) throws -> LoggerStore.Event.NetworkTaskCompleted { + public static func decode(_ data: Data) throws -> LoggerStore.Event.NetworkTaskCompleted { guard data.count >= Manifest.size else { throw PacketParsingError.notEnoughData // Should never happen } @@ -195,8 +195,12 @@ extension RemoteLogger { case openTaskDetails } - struct ServerHelloResponse: Codable { + public struct ServerHelloResponse: Codable { let version: String + + public init(version: String) { + self.version = version + } } } diff --git a/Sources/Pulse/RemoteLogger/RemoteLogger.swift b/Sources/Pulse/RemoteLogger/RemoteLogger.swift index 22417a07c..de1f04337 100644 --- a/Sources/Pulse/RemoteLogger/RemoteLogger.swift +++ b/Sources/Pulse/RemoteLogger/RemoteLogger.swift @@ -322,7 +322,7 @@ public final class RemoteLogger: ObservableObject, RemoteLoggerConnectionDelegat openConnection(to: server, passcode: passcode) } - + private func connectionDidTimeout(isProtected: Bool) { os_log("Connection did timeout", log: log) connectionCompletion?(.failure(self.connectionError ?? .unknown(isProtected: isProtected))) @@ -341,7 +341,7 @@ public final class RemoteLogger: ObservableObject, RemoteLoggerConnectionDelegat } private func saveServer(named name: String) { - os_log("Save server %{private}@", log: log, name) + os_log("Save server %{private}@", log: log, name) knownServers.removeAll(where: { $0 == name }) knownServers.append(name) saveKnownServers() @@ -371,7 +371,7 @@ public final class RemoteLogger: ObservableObject, RemoteLoggerConnectionDelegat // MARK: RemoteLoggerConnectionDelegate - func connection(_ connection: Connection, didChangeState newState: NWConnection.State) { + public func connection(_ connection: Connection, didChangeState newState: NWConnection.State) { os_log("Connection did change state to %{public}@", log: log, "\(newState)") switch newState { @@ -390,7 +390,7 @@ public final class RemoteLogger: ObservableObject, RemoteLoggerConnectionDelegat } } - func connection(_ connection: Connection, didReceiveEvent event: Connection.Event) { + public func connection(_ connection: Connection, didReceiveEvent event: Connection.Event) { switch event { case .packet(let packet): do { @@ -730,5 +730,5 @@ extension RemoteLogger.ConnectionState { } extension RemoteLogger { - public static let serviceType = "_pulse._tcp" + public static var serviceType = "_pulse._tcp" } From 22eaa12009714fb1c6402f95356267cdffc75e96 Mon Sep 17 00:00:00 2001 From: Antoine van der Lee <4329185+AvdLee@users.noreply.github.com> Date: Fri, 28 Jun 2024 10:53:52 +0200 Subject: [PATCH 02/14] Fix dark mode background --- Sources/PulseUI/Features/Console/List/ConsoleListView.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sources/PulseUI/Features/Console/List/ConsoleListView.swift b/Sources/PulseUI/Features/Console/List/ConsoleListView.swift index 4fa8b20b6..197e135a0 100644 --- a/Sources/PulseUI/Features/Console/List/ConsoleListView.swift +++ b/Sources/PulseUI/Features/Console/List/ConsoleListView.swift @@ -174,7 +174,8 @@ private struct _ConsoleListView: View { } else { ConsoleListContentView(proxy: proxy) } - } + }.scrollContentBackground(.hidden) + .background(Color.black.opacity(0.32)) } .environment(\.defaultMinListRowHeight, 1) } From 5e15e40b2c9cd9f4071ac93ccc837a2a5a94ed45 Mon Sep 17 00:00:00 2001 From: Antoine van der Lee <4329185+AvdLee@users.noreply.github.com> Date: Mon, 1 Jul 2024 08:51:37 +0200 Subject: [PATCH 03/14] Reset background --- Sources/PulseUI/Features/Console/List/ConsoleListView.swift | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Sources/PulseUI/Features/Console/List/ConsoleListView.swift b/Sources/PulseUI/Features/Console/List/ConsoleListView.swift index 197e135a0..4fa8b20b6 100644 --- a/Sources/PulseUI/Features/Console/List/ConsoleListView.swift +++ b/Sources/PulseUI/Features/Console/List/ConsoleListView.swift @@ -174,8 +174,7 @@ private struct _ConsoleListView: View { } else { ConsoleListContentView(proxy: proxy) } - }.scrollContentBackground(.hidden) - .background(Color.black.opacity(0.32)) + } } .environment(\.defaultMinListRowHeight, 1) } From 2626dcd594fc5638d83a7e40601fd520721e27a9 Mon Sep 17 00:00:00 2001 From: Antoine van der Lee <4329185+AvdLee@users.noreply.github.com> Date: Mon, 1 Jul 2024 08:59:02 +0200 Subject: [PATCH 04/14] Hide list background to allow window background to shine through --- Sources/PulseUI/Features/Console/List/ConsoleListView.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/PulseUI/Features/Console/List/ConsoleListView.swift b/Sources/PulseUI/Features/Console/List/ConsoleListView.swift index 4fa8b20b6..f1571a5bd 100644 --- a/Sources/PulseUI/Features/Console/List/ConsoleListView.swift +++ b/Sources/PulseUI/Features/Console/List/ConsoleListView.swift @@ -174,7 +174,7 @@ private struct _ConsoleListView: View { } else { ConsoleListContentView(proxy: proxy) } - } + }.scrollContentBackground(.hidden) } .environment(\.defaultMinListRowHeight, 1) } From 9cc6bf827658c58a41bc18fefbb933247cb8f1b1 Mon Sep 17 00:00:00 2001 From: Antoine van der Lee <4329185+AvdLee@users.noreply.github.com> Date: Mon, 1 Jul 2024 09:01:37 +0200 Subject: [PATCH 05/14] Update documentation regarding mode --- Sources/PulseUI/Features/Console/ConsoleView.swift | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sources/PulseUI/Features/Console/ConsoleView.swift b/Sources/PulseUI/Features/Console/ConsoleView.swift index 7dbb0de7e..f08866616 100644 --- a/Sources/PulseUI/Features/Console/ConsoleView.swift +++ b/Sources/PulseUI/Features/Console/ConsoleView.swift @@ -12,9 +12,9 @@ extension ConsoleView { /// /// - parameters: /// - store: The store to display. By default, `LoggerStore/shared`. - /// - mode: The console mode. By default, ``ConsoleMode/all``. If you change - /// the mode to ``ConsoleMode/network``, the console will only display the - /// network messages. + /// - mode: The initial console mode. By default, ``ConsoleMode/all``. If you change + /// the mode to ``ConsoleMode/network``, the console will display the + /// network messages up on appearance. /// - delegate: The delegate that allows you to customize multiple aspects /// of the console view. public init( From 45524056b57afc442ff4db6cd47dee144acaed49 Mon Sep 17 00:00:00 2001 From: Antoine van der Lee <4329185+AvdLee@users.noreply.github.com> Date: Mon, 1 Jul 2024 09:10:20 +0200 Subject: [PATCH 06/14] Add helpers for toolbar buttons --- Sources/PulseUI/Features/Console/ConsoleView-macos.swift | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Sources/PulseUI/Features/Console/ConsoleView-macos.swift b/Sources/PulseUI/Features/Console/ConsoleView-macos.swift index 082f0c13f..174c1b4ac 100644 --- a/Sources/PulseUI/Features/Console/ConsoleView-macos.swift +++ b/Sources/PulseUI/Features/Console/ConsoleView-macos.swift @@ -60,18 +60,21 @@ private struct ConsoleMainView: View { Button(action: { isShowingFilters = true }) { Label("Show Filters", systemImage: "line.3.horizontal.decrease.circle") } + .help("Show Filters") .popover(isPresented: $isShowingFilters) { ConsoleFiltersView().frame(width: 300).fixedSize() } Button(action: { isShowingSessions = true }) { Label("Show Sessions", systemImage: "list.clipboard") } + .help("Show Sessions") .popover(isPresented: $isShowingSessions) { SessionsView().frame(width: 300, height: 420) } Button(action: { isShowingSettings = true }) { Label("Show Settings", systemImage: "gearshape") } + .help("Show Settings") .popover(isPresented: $isShowingSettings) { SettingsView().frame(width: 300, height: 420) } @@ -88,10 +91,11 @@ private struct ConsoleMainView: View { if !(environment.store.options.contains(.readonly)) { Toggle(isOn: $isNowEnabled) { Image(systemName: "clock") - } + }.help("Now Mode: Automatically scrolls to the top of the view to display newly incoming network requests.") Button(action: { isSharingStore = true }) { Image(systemName: "square.and.arrow.up") } + .help("Share a session") .popover(isPresented: $isSharingStore, arrowEdge: .bottom) { ShareStoreView(onDismiss: {}) .frame(width: 240).fixedSize() @@ -99,6 +103,7 @@ private struct ConsoleMainView: View { Button(action: { environment.store.removeAll() }) { Image(systemName: "trash") } + .help("Clear current session") } } } From d59122c6d407aede4957631ad4ca6de8e7eabc8b Mon Sep 17 00:00:00 2001 From: Antoine van der Lee <4329185+AvdLee@users.noreply.github.com> Date: Mon, 1 Jul 2024 09:35:52 +0200 Subject: [PATCH 07/14] Update console minWidth for macOS --- Sources/PulseUI/Features/Console/ConsoleView-macos.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/PulseUI/Features/Console/ConsoleView-macos.swift b/Sources/PulseUI/Features/Console/ConsoleView-macos.swift index 174c1b4ac..44613315f 100644 --- a/Sources/PulseUI/Features/Console/ConsoleView-macos.swift +++ b/Sources/PulseUI/Features/Console/ConsoleView-macos.swift @@ -51,7 +51,7 @@ private struct ConsoleMainView: View { private var contentView: some View { ConsoleListView() - .frame(minWidth: 200, idealWidth: 400, minHeight: 120, idealHeight: 480) + .frame(minWidth: 400, idealWidth: 500, minHeight: 120, idealHeight: 480) .toolbar { ToolbarItemGroup(placement: .navigation) { contentToolbarNavigationItems From d74f5fc1b33e14223d1bb58693b31f65020c2370 Mon Sep 17 00:00:00 2001 From: Antoine van der Lee <4329185+AvdLee@users.noreply.github.com> Date: Mon, 1 Jul 2024 09:44:42 +0200 Subject: [PATCH 08/14] Remove redundant public modifiers since extension is public --- Sources/Pulse/RemoteLogger/RemoteLogger-Connection.swift | 2 +- Sources/Pulse/RemoteLogger/RemoteLogger-Protocol.swift | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Sources/Pulse/RemoteLogger/RemoteLogger-Connection.swift b/Sources/Pulse/RemoteLogger/RemoteLogger-Connection.swift index 5147eb4dc..4ac99ca03 100644 --- a/Sources/Pulse/RemoteLogger/RemoteLogger-Connection.swift +++ b/Sources/Pulse/RemoteLogger/RemoteLogger-Connection.swift @@ -17,7 +17,7 @@ public protocol RemoteLoggerConnectionDelegate: AnyObject { } public extension RemoteLogger { - public final class Connection { + final class Connection { var endpoint: NWEndpoint { connection.endpoint } private let connection: NWConnection private var buffer = Data() diff --git a/Sources/Pulse/RemoteLogger/RemoteLogger-Protocol.swift b/Sources/Pulse/RemoteLogger/RemoteLogger-Protocol.swift index df031d3da..036a80cf0 100644 --- a/Sources/Pulse/RemoteLogger/RemoteLogger-Protocol.swift +++ b/Sources/Pulse/RemoteLogger/RemoteLogger-Protocol.swift @@ -9,7 +9,7 @@ import Pulse #endif public extension RemoteLogger { - public enum PacketCode: UInt8, Equatable { + enum PacketCode: UInt8, Equatable { // Handshake case clientHello = 0 // PacketClientHello case serverHello = 1 // ServerHelloResponse @@ -44,7 +44,7 @@ public extension RemoteLogger { init() {} } - public struct PacketNetworkMessage { + struct PacketNetworkMessage { private struct Manifest: Codable { let messageSize: UInt32 let requestBodySize: UInt32 @@ -195,7 +195,7 @@ public extension RemoteLogger { case openTaskDetails } - public struct ServerHelloResponse: Codable { + struct ServerHelloResponse: Codable { let version: String public init(version: String) { From 1d661f53378aaa083e9934c55ae893b3ecfa9712 Mon Sep 17 00:00:00 2001 From: Antoine van der Lee <4329185+AvdLee@users.noreply.github.com> Date: Mon, 1 Jul 2024 09:48:46 +0200 Subject: [PATCH 09/14] Remove window style for macOS demo --- Demo/macOS/Pulse_Demo_macOSApp.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/Demo/macOS/Pulse_Demo_macOSApp.swift b/Demo/macOS/Pulse_Demo_macOSApp.swift index 4e5aa62ad..8de578c90 100644 --- a/Demo/macOS/Pulse_Demo_macOSApp.swift +++ b/Demo/macOS/Pulse_Demo_macOSApp.swift @@ -11,7 +11,6 @@ struct Pulse_Demo_macOSApp: App { WindowGroup { ConsoleView(store: .demo) } - .windowStyle(.hiddenTitleBar) .windowToolbarStyle(.unified(showsTitle: false)) } } From 904aa79e302281e3b24c486fa25b78579db5f605 Mon Sep 17 00:00:00 2001 From: Antoine van der Lee <4329185+AvdLee@users.noreply.github.com> Date: Mon, 1 Jul 2024 10:14:04 +0200 Subject: [PATCH 10/14] Improve styling of macOS session view --- .../Features/Sessions/SessionListView.swift | 40 ++++++++++++------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/Sources/PulseUI/Features/Sessions/SessionListView.swift b/Sources/PulseUI/Features/Sessions/SessionListView.swift index af1b29da0..8f465cc11 100644 --- a/Sources/PulseUI/Features/Sessions/SessionListView.swift +++ b/Sources/PulseUI/Features/Sessions/SessionListView.swift @@ -27,34 +27,40 @@ struct SessionListView: View { @Environment(\.store) private var store var body: some View { - if sessions.isEmpty { - Text("No Recorded Sessions") - .frame(maxWidth: .infinity, maxHeight: .infinity) - .foregroundColor(.secondary) - } else { - content - .onAppear { refreshGroups() } - .onChange(of: sessions.count) { _ in refreshGroups() } + VStack(spacing: 0) { + Text("Recorded sessions") + .padding(.vertical, 10) + Divider() + if sessions.isEmpty { + Text("No Recorded Sessions") + .frame(maxWidth: .infinity, maxHeight: .infinity) + .foregroundColor(.secondary) + } else { + content + .onAppear { refreshGroups() } + .onChange(of: sessions.count) { _ in refreshGroups() } + } } } @ViewBuilder private var content: some View { #if os(macOS) - VStack { + VStack(spacing: 0) { list - HStack { + #if PULSE_STANDALONE_APP + HStack { NavigatorFilterBar(text: $filterTerm) .frame(maxWidth: 200) .help("Show sessions with matching name") + } #else - SearchBar(title: "Filter", imageName: "line.3.horizontal.decrease.circle", text: $filterTerm) - .frame(maxWidth: 200) - .help("Show sessions with matching name") + Divider() + SearchBar(title: "Filter", imageName: "line.3.horizontal.decrease.circle", text: $filterTerm) + .help("Show sessions with matching name") + .padding(8) #endif - Spacer() - }.padding(8) } #else list @@ -92,10 +98,14 @@ struct SessionListView: View { private func makeHeader(for startDate: Date, sessions: [LoggerSessionEntity]) -> some View { HStack { + #if os(macOS) + PlainListSectionHeaderSeparator(title: sectionTitleFormatter.string(from: startDate) + " (\(sessions.count))") + #else (Text(sectionTitleFormatter.string(from: startDate)) + Text(" (\(sessions.count))").foregroundColor(.secondary.opacity(0.5))) .font(.headline) .padding(.vertical, 6) + #endif #if os(iOS) || os(visionOS) if editMode?.wrappedValue.isEditing ?? false { From b636cf5fecf1d361c9a9b5411fc4d2b029ca3f18 Mon Sep 17 00:00:00 2001 From: Antoine van der Lee <4329185+AvdLee@users.noreply.github.com> Date: Mon, 1 Jul 2024 10:18:05 +0200 Subject: [PATCH 11/14] Hide section title on iOS since we have the navigation bar title --- Sources/PulseUI/Features/Sessions/SessionListView.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Sources/PulseUI/Features/Sessions/SessionListView.swift b/Sources/PulseUI/Features/Sessions/SessionListView.swift index 8f465cc11..90e77f761 100644 --- a/Sources/PulseUI/Features/Sessions/SessionListView.swift +++ b/Sources/PulseUI/Features/Sessions/SessionListView.swift @@ -28,9 +28,11 @@ struct SessionListView: View { var body: some View { VStack(spacing: 0) { + #if os(macOS) Text("Recorded sessions") .padding(.vertical, 10) Divider() + #endif if sessions.isEmpty { Text("No Recorded Sessions") .frame(maxWidth: .infinity, maxHeight: .infinity) From 0b18f13e1bf8cdd54453aa6e2ff1f307ff5851f0 Mon Sep 17 00:00:00 2001 From: Antoine van der Lee <4329185+AvdLee@users.noreply.github.com> Date: Mon, 1 Jul 2024 11:11:17 +0200 Subject: [PATCH 12/14] Allow configuring Store share outputs --- Pulse.xcodeproj/project.pbxproj | 4 +++ .../Console/ConsoleConfiguration.swift | 20 ++++++++++++++ .../Features/Console/ConsoleEnvironment.swift | 4 ++- .../Features/Console/ConsoleView.swift | 6 +++-- Sources/PulseUI/Helpers/ShareItems.swift | 12 ++++++++- Sources/PulseUI/Views/ShareStoreView.swift | 14 +++++----- .../PulseUI/Views/ShareStoreViewModel.swift | 26 +++++++++++++++++++ 7 files changed, 76 insertions(+), 10 deletions(-) create mode 100644 Sources/PulseUI/Features/Console/ConsoleConfiguration.swift diff --git a/Pulse.xcodeproj/project.pbxproj b/Pulse.xcodeproj/project.pbxproj index 65070e4c4..4f7a20820 100644 --- a/Pulse.xcodeproj/project.pbxproj +++ b/Pulse.xcodeproj/project.pbxproj @@ -275,6 +275,7 @@ 0CFF79E229EC1FA200BE767B /* ImageProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0CFF79E029EC1F7800BE767B /* ImageProcessor.swift */; }; 25E740012BE0FA58008C6DC3 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 25E740002BE0FA58008C6DC3 /* PrivacyInfo.xcprivacy */; }; 25E740072BE10B4C008C6DC3 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 25E740062BE10B4C008C6DC3 /* PrivacyInfo.xcprivacy */; }; + 849DD7D92C32A9CE002D6787 /* ConsoleConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 849DD7D82C32A9CE002D6787 /* ConsoleConfiguration.swift */; }; E95D6C562B7314E6004D28E4 /* HARDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = E95D6C552B7314E6004D28E4 /* HARDocument.swift */; }; /* End PBXBuildFile section */ @@ -737,6 +738,7 @@ 25E740062BE10B4C008C6DC3 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; 49E82A8526D107A00070244F /* AlamofireIntegration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlamofireIntegration.swift; sourceTree = ""; }; 49E82A8826D1083D0070244F /* MoyaIntegration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoyaIntegration.swift; sourceTree = ""; }; + 849DD7D82C32A9CE002D6787 /* ConsoleConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConsoleConfiguration.swift; sourceTree = ""; }; E95D6C552B7314E6004D28E4 /* HARDocument.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HARDocument.swift; sourceTree = ""; }; /* End PBXFileReference section */ @@ -1398,6 +1400,7 @@ 0CA2632D2BFA3855003C8E97 /* ConsoleDelegate.swift */, 0CF0D60A296F189600EED9D4 /* ConsoleEnvironment.swift */, 0CECF63C2996BE12000F9CCD /* ConsoleDataSource.swift */, + 849DD7D82C32A9CE002D6787 /* ConsoleConfiguration.swift */, ); path = Console; sourceTree = ""; @@ -2136,6 +2139,7 @@ 0CDC3BED2971B936003BD1FF /* ConsoleSearchLogLevelsCell.swift in Sources */, 0CF0D620296F189600EED9D4 /* Foundation+Extensions.swift in Sources */, 0CF0D66C296F189600EED9D4 /* NetworkRequestInfoCell.swift in Sources */, + 849DD7D92C32A9CE002D6787 /* ConsoleConfiguration.swift in Sources */, 0CB63A15297438A600525165 /* ConsoleSearchViewModel.swift in Sources */, 0CF0D632296F189600EED9D4 /* ShareStoreView.swift in Sources */, 0C9751582A32CCC400DC46FF /* RemoteLoggerEnterPasswordView.swift in Sources */, diff --git a/Sources/PulseUI/Features/Console/ConsoleConfiguration.swift b/Sources/PulseUI/Features/Console/ConsoleConfiguration.swift new file mode 100644 index 000000000..c91a7326a --- /dev/null +++ b/Sources/PulseUI/Features/Console/ConsoleConfiguration.swift @@ -0,0 +1,20 @@ +// The MIT License (MIT) +// +// Created by A.J. van der Lee on 01/07/2024. +// Copyright © 2024 kean. All rights reserved. +// + +import Foundation + +/// Defines UI configurations to enable/disable elements or change behavior. +public struct ConsoleConfiguration { + public static let `default` = ConsoleConfiguration() + + let shareStoreOutputs: [ShareStoreOutput] + + /// Creates a new `ConsoleConfiguration` + /// - Parameter shareStoreOutputs: The available store share outputs. Defaults to `allCases`. + public init(shareStoreOutputs: [ShareStoreOutput] = ShareStoreOutput.allCases) { + self.shareStoreOutputs = shareStoreOutputs + } +} diff --git a/Sources/PulseUI/Features/Console/ConsoleEnvironment.swift b/Sources/PulseUI/Features/Console/ConsoleEnvironment.swift index 54b90330e..d2b181063 100644 --- a/Sources/PulseUI/Features/Console/ConsoleEnvironment.swift +++ b/Sources/PulseUI/Features/Console/ConsoleEnvironment.swift @@ -24,6 +24,7 @@ final class ConsoleEnvironment: ObservableObject { let initialMode: ConsoleMode let delegate: ConsoleViewDelegate + let configuration: ConsoleConfiguration @Published var mode: ConsoleMode @Published var listOptions: ConsoleListOptions = .init() @@ -38,7 +39,7 @@ final class ConsoleEnvironment: ObservableObject { private var cancellables: [AnyCancellable] = [] - init(store: LoggerStore, mode: ConsoleMode = .all, delegate: ConsoleViewDelegate = DefaultConsoleViewDelegate()) { + init(store: LoggerStore, mode: ConsoleMode = .all, configuration: ConsoleConfiguration = .default, delegate: ConsoleViewDelegate = DefaultConsoleViewDelegate()) { self.store = store switch mode { case .all: self.title = "Console" @@ -54,6 +55,7 @@ final class ConsoleEnvironment: ObservableObject { } self.delegate = delegate + self.configuration = configuration func makeDefaultOptions() -> ConsoleDataSource.PredicateOptions { var options = ConsoleDataSource.PredicateOptions() diff --git a/Sources/PulseUI/Features/Console/ConsoleView.swift b/Sources/PulseUI/Features/Console/ConsoleView.swift index f08866616..f3dbdd721 100644 --- a/Sources/PulseUI/Features/Console/ConsoleView.swift +++ b/Sources/PulseUI/Features/Console/ConsoleView.swift @@ -9,19 +9,21 @@ import Combine extension ConsoleView { /// Initializes the console view. - /// + /// /// - parameters: /// - store: The store to display. By default, `LoggerStore/shared`. /// - mode: The initial console mode. By default, ``ConsoleMode/all``. If you change /// the mode to ``ConsoleMode/network``, the console will display the /// network messages up on appearance. + /// - configuration: Configuration options to alter the UI behavior and settings. /// - delegate: The delegate that allows you to customize multiple aspects /// of the console view. public init( store: LoggerStore = .shared, mode: ConsoleMode = .all, + configuration: ConsoleConfiguration = .default, delegate: ConsoleViewDelegate? = nil ) { - self.init(environment: .init(store: store, mode: mode, delegate: delegate ?? DefaultConsoleViewDelegate())) + self.init(environment: .init(store: store, mode: mode, configuration: configuration, delegate: delegate ?? DefaultConsoleViewDelegate())) } } diff --git a/Sources/PulseUI/Helpers/ShareItems.swift b/Sources/PulseUI/Helpers/ShareItems.swift index 6f76eed7a..82db05d98 100644 --- a/Sources/PulseUI/Helpers/ShareItems.swift +++ b/Sources/PulseUI/Helpers/ShareItems.swift @@ -6,7 +6,7 @@ import Foundation import Pulse import CoreData -public enum ShareStoreOutput: String, RawRepresentable { +public enum ShareStoreOutput: String, RawRepresentable, CaseIterable { case store, package, text, html, har var fileExtension: String { @@ -17,6 +17,16 @@ public enum ShareStoreOutput: String, RawRepresentable { case .har: return "har" } } + + var interfaceTitle: String { + switch self { + case .store: "Pulse" + case .package: "Pulse (Package)" + case .text: "Plain Text" + case .html: "HTML" + case .har: "HAR" + } + } } struct ShareItems: Identifiable { diff --git a/Sources/PulseUI/Views/ShareStoreView.swift b/Sources/PulseUI/Views/ShareStoreView.swift index aff08b38d..31a5b4090 100644 --- a/Sources/PulseUI/Views/ShareStoreView.swift +++ b/Sources/PulseUI/Views/ShareStoreView.swift @@ -20,6 +20,7 @@ struct ShareStoreView: View { @StateObject private var viewModel = ShareStoreViewModel() @Environment(\.store) private var store: LoggerStore + @EnvironmentObject private var environment: ConsoleEnvironment var body: some View { content @@ -30,6 +31,7 @@ struct ShareStoreView: View { viewModel.sessions = [store.session.id] } viewModel.store = store + viewModel.environment = environment } } @@ -95,12 +97,12 @@ struct ShareStoreView: View { } Section { Picker("Output", selection: $viewModel.output) { - Text("Pulse").tag(ShareStoreOutput.store) - Text("Plain Text").tag(ShareStoreOutput.text) - Text("HTML").tag(ShareStoreOutput.html) - Text("HAR").tag(ShareStoreOutput.har) - Divider() - Text("Pulse (Package)").tag(ShareStoreOutput.package) + ForEach(viewModel.shareStoreOutputs, id: \.rawValue) { shareOutput in + if shareOutput == .package { + Divider() + } + Text(shareOutput.interfaceTitle).tag(shareOutput) + } } #if os(macOS) .labelsHidden() diff --git a/Sources/PulseUI/Views/ShareStoreViewModel.swift b/Sources/PulseUI/Views/ShareStoreViewModel.swift index 0c37fe275..40a9ec60d 100644 --- a/Sources/PulseUI/Views/ShareStoreViewModel.swift +++ b/Sources/PulseUI/Views/ShareStoreViewModel.swift @@ -13,6 +13,7 @@ import Combine // Sharing options @Published var sessions: Set = [] @Published var logLevels = Set(LoggerStore.Level.allCases) + @Published var shareStoreOutputs: [ShareStoreOutput] = ShareStoreOutput.allCases @Published var output: ShareStoreOutput @Published private(set) var isPreparingForSharing = false @@ -20,6 +21,11 @@ import Combine @Published var shareItems: ShareItems? var store: LoggerStore? + var environment: ConsoleEnvironment? { + didSet { + updateForCurrentEnvironment() + } + } init() { output = UserSettings.shared.sharingOutput @@ -31,6 +37,26 @@ import Combine saveSharingOptions() prepareForSharing() } + + private func updateForCurrentEnvironment() { + guard let environment else { return } + shareStoreOutputs = environment.configuration.shareStoreOutputs.sorted(by: { lhs, rhs in + /// Make sure the .package is always last since we show a Divider() in front of it. + switch (lhs, rhs) { + case (.package, _): + return false + case (_, .package): + return true + default: + return lhs.rawValue < rhs.rawValue + } + }) + + if !shareStoreOutputs.contains(output), let shareStoreOutput = shareStoreOutputs.first { + /// Update the selected output to one of the available options. + output = shareStoreOutput + } + } private func saveSharingOptions() { UserSettings.shared.sharingOutput = output From c99e4c7707a79166969488d1c61539ca1a2795f5 Mon Sep 17 00:00:00 2001 From: Antoine van der Lee <4329185+AvdLee@users.noreply.github.com> Date: Mon, 1 Jul 2024 11:35:33 +0200 Subject: [PATCH 13/14] Allow configuring whether remote logging setting is available --- .../Features/Console/ConsoleConfiguration.swift | 8 +++++++- .../Features/Settings/SettingsView-macos.swift | 13 ++++++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Sources/PulseUI/Features/Console/ConsoleConfiguration.swift b/Sources/PulseUI/Features/Console/ConsoleConfiguration.swift index c91a7326a..84d685d0d 100644 --- a/Sources/PulseUI/Features/Console/ConsoleConfiguration.swift +++ b/Sources/PulseUI/Features/Console/ConsoleConfiguration.swift @@ -11,10 +11,16 @@ public struct ConsoleConfiguration { public static let `default` = ConsoleConfiguration() let shareStoreOutputs: [ShareStoreOutput] + let allowRemoteLogging: Bool /// Creates a new `ConsoleConfiguration` /// - Parameter shareStoreOutputs: The available store share outputs. Defaults to `allCases`. - public init(shareStoreOutputs: [ShareStoreOutput] = ShareStoreOutput.allCases) { + /// - Parameter allowRemoteLogging: Enable/disable the remote logging option. + public init( + shareStoreOutputs: [ShareStoreOutput] = ShareStoreOutput.allCases, + allowRemoteLogging: Bool = true + ) { self.shareStoreOutputs = shareStoreOutputs + self.allowRemoteLogging = allowRemoteLogging } } diff --git a/Sources/PulseUI/Features/Settings/SettingsView-macos.swift b/Sources/PulseUI/Features/Settings/SettingsView-macos.swift index 612f839b7..7409e3533 100644 --- a/Sources/PulseUI/Features/Settings/SettingsView-macos.swift +++ b/Sources/PulseUI/Features/Settings/SettingsView-macos.swift @@ -12,14 +12,17 @@ struct SettingsView: View { @State private var shareItems: ShareItems? @Environment(\.store) private var store + @EnvironmentObject private var environment: ConsoleEnvironment var body: some View { List { - if store === RemoteLogger.shared.store { - RemoteLoggerSettingsView(viewModel: .shared) - } else { - Text("Not available") - .foregroundColor(.secondary) + if environment.configuration.allowRemoteLogging { + if store === RemoteLogger.shared.store { + RemoteLoggerSettingsView(viewModel: .shared) + } else { + Text("Not available") + .foregroundColor(.secondary) + } } Section(header: Text("Store")) { if #available(macOS 13, *), let info = try? store.info() { From 742955bca723601589130c64d189c2205d69d840 Mon Sep 17 00:00:00 2001 From: Antoine van der Lee <4329185+AvdLee@users.noreply.github.com> Date: Mon, 1 Jul 2024 13:30:42 +0200 Subject: [PATCH 14/14] Update styling of Settings View popover on Mac --- Sources/PulseUI/Features/Console/ConsoleView-macos.swift | 4 ++-- .../PulseUI/Features/Settings/SettingsView-macos.swift | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Sources/PulseUI/Features/Console/ConsoleView-macos.swift b/Sources/PulseUI/Features/Console/ConsoleView-macos.swift index 44613315f..7a210ec52 100644 --- a/Sources/PulseUI/Features/Console/ConsoleView-macos.swift +++ b/Sources/PulseUI/Features/Console/ConsoleView-macos.swift @@ -75,8 +75,8 @@ private struct ConsoleMainView: View { Label("Show Settings", systemImage: "gearshape") } .help("Show Settings") - .popover(isPresented: $isShowingSettings) { - SettingsView().frame(width: 300, height: 420) + .popover(isPresented: $isShowingSettings, arrowEdge: .bottom) { + SettingsView().frame(width: 300, height: environment.configuration.allowRemoteLogging ? 420 : 175) } } } diff --git a/Sources/PulseUI/Features/Settings/SettingsView-macos.swift b/Sources/PulseUI/Features/Settings/SettingsView-macos.swift index 7409e3533..b9aa03be4 100644 --- a/Sources/PulseUI/Features/Settings/SettingsView-macos.swift +++ b/Sources/PulseUI/Features/Settings/SettingsView-macos.swift @@ -7,6 +7,7 @@ import SwiftUI import Pulse +@available(macOS 13, *) struct SettingsView: View { @State private var isPresentingShareStoreView = false @State private var shareItems: ShareItems? @@ -24,11 +25,14 @@ struct SettingsView: View { .foregroundColor(.secondary) } } - Section(header: Text("Store")) { + Section { if #available(macOS 13, *), let info = try? store.info() { LoggerStoreSizeChart(info: info, sizeLimit: store.configuration.sizeLimit) } + } header: { + PlainListSectionHeaderSeparator(title: "Store") } + Section { HStack { Button("Show in Finder") { @@ -41,13 +45,14 @@ struct SettingsView: View { } } } - } + }.listStyle(.sidebar).scrollContentBackground(.hidden) } } // MARK: - Preview #if DEBUG +@available(macOS 13, *) struct UserSettingsView_Previews: PreviewProvider { static var previews: some View { SettingsView()