diff --git a/CHANGELOG.md b/CHANGELOG.md
index 05a837f15..ccebc16ad 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Pulse 1.x
+## Pulse 1.1.0
+
+*May 14, 2022*
+
+- [iOS, watchOS] Update message details design, display custom metadata – [#81](https://github.com/kean/Pulse/pull/81)
+- [iOS] Fix an issue with search toolbar not showing up during searching
+
## Pulse 1.0.3
*May 3, 2022*
diff --git a/Pulse.xcodeproj/xcshareddata/xcschemes/Pulse Demo watchOS (Complication).xcscheme b/Pulse.xcodeproj/xcshareddata/xcschemes/Pulse Demo watchOS (Complication).xcscheme
index 6b098f190..b69bd06c8 100644
--- a/Pulse.xcodeproj/xcshareddata/xcschemes/Pulse Demo watchOS (Complication).xcscheme
+++ b/Pulse.xcodeproj/xcshareddata/xcschemes/Pulse Demo watchOS (Complication).xcscheme
@@ -55,10 +55,8 @@
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "32">
-
+
-
+
-
+
-
-
-
-
-
+
diff --git a/Pulse.xcodeproj/xcshareddata/xcschemes/Pulse Demo watchOS.xcscheme b/Pulse.xcodeproj/xcshareddata/xcschemes/Pulse Demo watchOS.xcscheme
index 21c04c8e8..9a3f7d31d 100644
--- a/Pulse.xcodeproj/xcshareddata/xcschemes/Pulse Demo watchOS.xcscheme
+++ b/Pulse.xcodeproj/xcshareddata/xcschemes/Pulse Demo watchOS.xcscheme
@@ -54,10 +54,8 @@
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
-
+
-
+
-
+
-
-
-
-
-
+
diff --git a/Sources/PulseUI/Features/MessageDetails/ConsoleMessageDetailsView.swift b/Sources/PulseUI/Features/MessageDetails/ConsoleMessageDetailsView.swift
index d21fcc8c3..af337dc82 100644
--- a/Sources/PulseUI/Features/MessageDetails/ConsoleMessageDetailsView.swift
+++ b/Sources/PulseUI/Features/MessageDetails/ConsoleMessageDetailsView.swift
@@ -16,10 +16,13 @@ struct ConsoleMessageDetailsView: View {
var body: some View {
contents
.navigationBarTitle("", displayMode: .inline)
- .navigationBarItems(trailing: HStack(spacing: 18) {
+ .navigationBarItems(trailing: HStack(spacing: 14) {
if let badge = viewModel.badge {
BadgeView(viewModel: BadgeViewModel(title: badge.title, color: badge.color.opacity(colorScheme == .light ? 0.25 : 0.5)))
}
+ NavigationLink(destination: ConsoleMessageMetadataView(message: viewModel.message)) {
+ Image(systemName: "info.circle")
+ }
PinButton(viewModel: viewModel.pin, isTextNeeded: false)
ShareButton {
self.isShowingShareSheet = true
@@ -32,10 +35,21 @@ struct ConsoleMessageDetailsView: View {
#elseif os(watchOS)
var body: some View {
ScrollView {
- contents
- }.toolbar(content: {
- PinButton(viewModel: viewModel.pin, isTextNeeded: false)
- })
+ VStack {
+ HStack {
+ PinButton3(viewModel: viewModel.pin)
+ NavigationLink(destination: ConsoleMessageMetadataView(message: viewModel.message)) {
+ VStack(spacing: 4) {
+ Image(systemName: "info.circle")
+ .foregroundColor(.blue)
+ Text("Details")
+ .font(.caption2)
+ }.frame(height: 42)
+ }
+ }
+ contents
+ }
+ }
}
#elseif os(tvOS)
var body: some View {
@@ -45,7 +59,6 @@ struct ConsoleMessageDetailsView: View {
private var contents: some View {
VStack {
- tags
textView
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
}
@@ -76,25 +89,40 @@ struct ConsoleMessageDetailsView: View {
.background(Color.gray.opacity(0.15))
.cornerRadius(8)
}
- #else
- private var tags: some View {
- VStack(alignment: .leading) {
- ForEach(viewModel.tags, id: \.title) { tag in
- HStack {
- Text(tag.title)
- .font(.caption)
- .foregroundColor(.secondary)
- Text(tag.value)
- .font(.caption)
- .bold()
- .foregroundColor(.secondary)
- }
+ #endif
+}
+
+#if DEBUG
+@available(iOS 13.0, tvOS 14.0, watchOS 7.0, *)
+struct ConsoleMessageDetailsView_Previews: PreviewProvider {
+ static var previews: some View {
+ Group {
+ NavigationView {
+ ConsoleMessageDetailsView(viewModel: .init(store: LoggerStore.mock, message: makeMockMessage()))
}
}
- .padding(16)
- .frame(maxWidth: .infinity, alignment: .leading)
- .background(Color.gray.opacity(0.15))
}
- #endif
}
+
+func makeMockMessage() -> LoggerMessageEntity {
+ let entity = LoggerMessageEntity(context: LoggerStore.mock.container.viewContext)
+ entity.text = "test"
+ entity.createdAt = Date()
+ entity.label = "auth"
+ entity.level = "critical"
+ entity.session = UUID().uuidString
+ entity.file = "~/Develop/Pulse/LoggerStore.swift"
+ entity.filename = "LoggerStore.swift"
+ entity.function = "createMockMessage()"
+ entity.line = 12
+
+ let meta = LoggerMetadataEntity(context: LoggerStore.mock.container.viewContext)
+ meta.key = "customKey"
+ meta.value = "customValue"
+
+ entity.metadata = Set([meta])
+ return entity
+}
+#endif
+
#endif
diff --git a/Sources/PulseUI/Features/MessageDetails/ConsoleMessageDetailsViewModel.swift b/Sources/PulseUI/Features/MessageDetails/ConsoleMessageDetailsViewModel.swift
index a8f5cc1f9..690faf001 100644
--- a/Sources/PulseUI/Features/MessageDetails/ConsoleMessageDetailsViewModel.swift
+++ b/Sources/PulseUI/Features/MessageDetails/ConsoleMessageDetailsViewModel.swift
@@ -13,7 +13,7 @@ final class ConsoleMessageDetailsViewModel {
let text: String
let badge: BadgeViewModel?
- private let message: LoggerMessageEntity
+ let message: LoggerMessageEntity
private let store: LoggerStore
static let dateFormatter: DateFormatter = {
diff --git a/Sources/PulseUI/Features/MessageDetails/ConsoleMessageMetadataView.swift b/Sources/PulseUI/Features/MessageDetails/ConsoleMessageMetadataView.swift
new file mode 100644
index 000000000..7d60de13b
--- /dev/null
+++ b/Sources/PulseUI/Features/MessageDetails/ConsoleMessageMetadataView.swift
@@ -0,0 +1,104 @@
+// The MIT License (MIT)
+//
+// Copyright (c) 2020–2022 Alexander Grebenyuk (github.com/kean).
+
+import SwiftUI
+import PulseCore
+
+#if os(iOS) || os(tvOS) || os(watchOS)
+@available(iOS 13.0, tvOS 14.0, watchOS 7.0, *)
+struct ConsoleMessageMetadataView: View {
+ let message: LoggerMessageEntity
+
+ @State private var isMetadataRawLinkActive = false
+
+ var body: some View {
+ contents
+ .background(linksView)
+#if os(iOS)
+ .navigationBarTitle("Details", displayMode: .inline)
+#endif
+ }
+
+ @ViewBuilder
+ private var contents: some View {
+ ScrollView {
+ #if os(iOS) || os(tvOS)
+ VStack {
+ stackContents
+ }.padding()
+ #elseif os(watchOS)
+ VStack(spacing: 16) {
+ stackContents
+ }
+ #endif
+ }
+ }
+
+ @ViewBuilder
+ private var stackContents: some View {
+ KeyValueSectionView(viewModel: .init(title: "Summary", color: message.tintColor, items: [
+ ("Date", dateFormatter.string(from: message.createdAt)),
+ ("Level", message.level),
+ ("Label", message.label.nonEmpty)
+ ]))
+ KeyValueSectionView(viewModel: .init(title: "Details", color: .secondary, items: [
+ ("Session", message.session.nonEmpty),
+ ("File", message.file.nonEmpty),
+ ("Filename", message.filename.nonEmpty),
+ ("Function", message.function.nonEmpty),
+ ("Line", message.line == 0 ? nil : "\(message.line)"),
+ ]))
+ KeyValueSectionView(viewModel: metadataViewModel)
+ }
+
+ private var metadataViewModel: KeyValueSectionViewModel {
+ KeyValueSectionViewModel(title: "Metadata", color: .indigo, action: .init(action: {
+ isMetadataRawLinkActive = true
+ }, title: "View"), items: metadataItems)
+ }
+
+ private var metadataItems: [(String, String?)] {
+ message.metadata.sorted(by: { $0.key < $1.key }).map { ($0.key, $0.value )}
+ }
+
+ private var linksView: some View {
+ NavigationLink(destination: NetworkHeadersDetailsView(viewModel: metadataViewModel), isActive: $isMetadataRawLinkActive) {
+ EmptyView()
+ }.opacity(0)
+ }
+}
+
+@available(iOS 13.0, tvOS 14.0, watchOS 7.0, *)
+private extension LoggerMessageEntity {
+ var tintColor: Color {
+ Color.badgeColor(for: .init(rawValue: level) ?? .debug)
+ }
+}
+
+private extension String {
+ var nonEmpty: String? {
+ isEmpty ? nil : self
+ }
+}
+
+private let dateFormatter: DateFormatter = {
+ let formatter = DateFormatter()
+ formatter.dateFormat = "HH:mm:ss.SSS, yyyy-MM-dd"
+ return formatter
+}()
+
+#if DEBUG
+@available(iOS 13.0, tvOS 14.0, watchOS 7.0, *)
+struct ConsoleMessageMetadataView_Previews: PreviewProvider {
+ static var previews: some View {
+ Group {
+ NavigationView {
+ ConsoleMessageMetadataView(message: makeMockMessage())
+ }
+ }
+ }
+}
+#endif
+
+#endif
diff --git a/Sources/PulseUI/Views/PinButton.swift b/Sources/PulseUI/Views/PinButton.swift
index d691c9566..a9bcd658d 100644
--- a/Sources/PulseUI/Views/PinButton.swift
+++ b/Sources/PulseUI/Views/PinButton.swift
@@ -34,6 +34,24 @@ struct PinButton2: View {
}
}
+#if os(watchOS)
+@available(iOS 14.0, tvOS 14.0, watchOS 7.0, *)
+struct PinButton3: View {
+ @ObservedObject var viewModel: PinButtonViewModel
+
+ var body: some View {
+ Button(action: viewModel.togglePin) {
+ VStack(spacing: 4) {
+ Image(systemName: viewModel.isPinned ? "pin.slash" : "pin")
+ .foregroundColor(.blue)
+ Text(viewModel.isPinned ? "Remove Pin" : "Pin")
+ .font(.caption2)
+ }.frame(height: 42)
+ }
+ }
+}
+#endif
+
#if os(iOS)
@available(iOS 13.0, *)
extension UIAction {
diff --git a/Sources/PulseUI/Views/SearchBar.swift b/Sources/PulseUI/Views/SearchBar.swift
index c41933db2..2afee19f9 100644
--- a/Sources/PulseUI/Views/SearchBar.swift
+++ b/Sources/PulseUI/Views/SearchBar.swift
@@ -29,11 +29,11 @@ struct SearchBar: UIViewRepresentable {
}
func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {
-
+ self.onEditingChanged?(true)
}
func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
-
+ self.onEditingChanged?(false)
}
}