Add in-app diagnostic logging + resilient inbox; bump to 1.2
Build iOS App / Build .ipa and publish source (push) Failing after 5m44s
Build iOS App / Build .ipa and publish source (push) Failing after 5m44s
- LogStore: bounded in-memory + persisted log of API calls and errors, viewable and shareable from Settings → View logs. - GiteaClient: log every request/response status (esp. 404s). - Inbox: a single failing repo no longer wipes the whole inbox — per-repo errors are caught, logged, and the rest still load. - Bump to 1.2 so SideStore offers the update.
This commit is contained in:
1 parent
43f4d76356
commit
b51b2ee5c8
6 files changed
+202
-11
No files matched your search
@@ -0,0 +1,77 @@
|
||||
import SwiftUI
|
||||
import UIKit
|
||||
|
||||
/// Diagnostic log viewer. Shows the in-memory/persisted log entries with a
|
||||
/// Share button (so the user can send logs to Hermes) and a Clear button.
|
||||
struct LogsView: View {
|
||||
@ObservedObject private var store = LogStore.shared
|
||||
@State private var showShare = false
|
||||
|
||||
var body: some View {
|
||||
Group {
|
||||
if store.entries.isEmpty {
|
||||
ContentUnavailableView(
|
||||
"No logs yet",
|
||||
systemImage: "doc.text",
|
||||
description: Text("Run the app and reproduce the issue, then come back here."))
|
||||
} else {
|
||||
List {
|
||||
ForEach(store.entries.reversed()) { entry in
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
HStack {
|
||||
Text(entry.level.rawValue)
|
||||
.font(.caption2.weight(.bold))
|
||||
.foregroundColor(color(for: entry.level))
|
||||
Text(entry.timestamp, style: .time)
|
||||
.font(.caption2)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
Text(entry.message)
|
||||
.font(.caption.monospaced())
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
.padding(.vertical, 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Logs")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .primaryAction) {
|
||||
Button {
|
||||
showShare = true
|
||||
} label: {
|
||||
Image(systemName: "square.and.arrow.up")
|
||||
}
|
||||
.disabled(store.entries.isEmpty)
|
||||
}
|
||||
ToolbarItem(placement: .cancellationAction) {
|
||||
Button("Clear") { store.clear() }
|
||||
.disabled(store.entries.isEmpty)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showShare) {
|
||||
ShareSheet(text: store.text)
|
||||
}
|
||||
}
|
||||
|
||||
private func color(for level: LogStore.Entry.Level) -> Color {
|
||||
switch level {
|
||||
case .info: return .secondary
|
||||
case .warn: return .orange
|
||||
case .error: return .red
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Minimal share sheet wrapper (ShareLink is iOS 16+; this is explicit).
|
||||
struct ShareSheet: UIViewControllerRepresentable {
|
||||
let text: String
|
||||
|
||||
func makeUIViewController(context: Context) -> UIActivityViewController {
|
||||
UIActivityViewController(activityItems: [text], applicationActivities: nil)
|
||||
}
|
||||
func updateUIViewController(_ uiViewController: UIActivityViewController,
|
||||
context: Context) {}
|
||||
}
|
||||
Reference in new issue
Block a user