summaryrefslogtreecommitdiff
path: root/Hutch/Views/Inbox
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-03-19 16:59:16 -0500
committerChristian Cleberg <[email protected]>2026-03-19 16:59:16 -0500
commitb82bbeeea48ad27832a355c2a41408559c411104 (patch)
tree0af45101300c75970471573b09f878a1b3763b38 /Hutch/Views/Inbox
parent9065ef6e92245a44390e336cc1ae515ae706cdd7 (diff)
downloadhutch-b82bbeeea48ad27832a355c2a41408559c411104.tar.gz
hutch-b82bbeeea48ad27832a355c2a41408559c411104.tar.bz2
hutch-b82bbeeea48ad27832a355c2a41408559c411104.zip
v2.1: bundled polish and fixes
Diffstat (limited to 'Hutch/Views/Inbox')
-rw-r--r--Hutch/Views/Inbox/InboxView.swift15
-rw-r--r--Hutch/Views/Inbox/InboxViewModel.swift17
-rw-r--r--Hutch/Views/Inbox/ThreadDetailView.swift14
-rw-r--r--Hutch/Views/Inbox/ThreadViewModel.swift47
4 files changed, 11 insertions, 82 deletions
diff --git a/Hutch/Views/Inbox/InboxView.swift b/Hutch/Views/Inbox/InboxView.swift
index 2b92738..74f78d5 100644
--- a/Hutch/Views/Inbox/InboxView.swift
+++ b/Hutch/Views/Inbox/InboxView.swift
@@ -1,7 +1,4 @@
import SwiftUI
-import os
-
-private let inboxNavigationLogger = Logger(subsystem: "net.cleberg.Hutch", category: "InboxNavigation")
struct InboxView: View {
@Environment(AppState.self) private var appState
@@ -101,9 +98,6 @@ struct InboxView: View {
systemImage: "tray",
description: Text("This thread could not be restored.")
)
- .onAppear {
- inboxNavigationLogger.error("Inbox navigation destination missing thread snapshot")
- }
}
}
}
@@ -123,9 +117,6 @@ struct InboxView: View {
private func selectThread(_ thread: InboxThreadSummary) {
cacheSelectedThread(thread)
isShowingThreadDetail = true
- inboxNavigationLogger.debug(
- "Inbox navigation triggered: threadID=\(thread.id, privacy: .public) subject=\(thread.subject, privacy: .public)"
- )
}
private func cacheSelectedThread(_ thread: InboxThreadSummary) {
@@ -140,9 +131,6 @@ struct InboxView: View {
private func handleThreadDetailDisappear(for threadID: String) {
let isActiveSelection = selectedThreadID == threadID
- inboxNavigationLogger.debug(
- "Inbox thread detail disappeared: threadID=\(threadID, privacy: .public) activeSelection=\(isActiveSelection, privacy: .public)"
- )
guard isActiveSelection else { return }
clearSelection()
}
@@ -154,9 +142,6 @@ struct InboxView: View {
}
private func clearSelection() {
- if let selectedThreadID {
- inboxNavigationLogger.debug("Inbox selection cleared: threadID=\(selectedThreadID, privacy: .public)")
- }
selectedThreadID = nil
selectedThreadSnapshot = nil
isShowingThreadDetail = false
diff --git a/Hutch/Views/Inbox/InboxViewModel.swift b/Hutch/Views/Inbox/InboxViewModel.swift
index e4a0664..00d47b5 100644
--- a/Hutch/Views/Inbox/InboxViewModel.swift
+++ b/Hutch/Views/Inbox/InboxViewModel.swift
@@ -136,7 +136,7 @@ final class InboxViewModel {
return lhs.lastActivityAt > rhs.lastActivityAt
}
} catch {
- inboxListLogger.error("Inbox request failed: type=inbox error=\(error.localizedDescription, privacy: .public)")
+ inboxListLogger.error("Inbox request failed")
self.error = "Failed to load inbox"
}
}
@@ -144,17 +144,11 @@ final class InboxViewModel {
func markThreadRead(_ thread: InboxThreadSummary) {
let viewedAt = max(Date(), thread.lastActivityAt)
InboxReadStateStore.markViewed(viewedAt, for: thread.id)
- inboxListLogger.debug(
- "Inbox mark read: key=\(thread.id, privacy: .public) latestActivityAt=\(thread.lastActivityAt.ISO8601Format(), privacy: .public) storedLastViewedAt=\(viewedAt.ISO8601Format(), privacy: .public)"
- )
threads.removeAll { $0.id == thread.id }
}
func markThreadUnread(_ thread: InboxThreadSummary) {
InboxReadStateStore.markUnread(for: thread.id)
- inboxListLogger.debug(
- "Inbox mark unread: key=\(thread.id, privacy: .public) latestActivityAt=\(thread.lastActivityAt.ISO8601Format(), privacy: .public) storedLastViewedAt=nil"
- )
updateThread(thread, isUnread: true)
}
@@ -237,7 +231,7 @@ final class InboxViewModel {
summaries.append(contentsOf: batchResult.0)
failureMessages.append(contentsOf: batchResult.1)
for failure in batchResult.1 {
- inboxListLogger.error("Inbox request failed: type=listThreads \(failure, privacy: .public)")
+ inboxListLogger.error("Inbox thread list request failed: \(failure, privacy: .private)")
}
startIndex = endIndex
}
@@ -259,14 +253,7 @@ final class InboxViewModel {
return response.list.threads.results.prefix(listThreadFetchLimit).map { thread in
let groupingKey = "\(mailingList.rid)#\(thread.subject.replacingOccurrences(of: #"\s+"#, with: " ", options: .regularExpression).trimmingCharacters(in: .whitespacesAndNewlines).replacingOccurrences(of: #"^(?:(?:re|fwd?)\s*:\s*)+"#, with: "", options: [.regularExpression, .caseInsensitive]).lowercased())"
- let lastViewedAt = InboxReadStateStore.lastViewedAt(for: groupingKey)
let isUnread = InboxReadStateStore.isUnread(threadID: groupingKey, lastActivityAt: thread.updated)
- inboxListLogger.debug(
- "Inbox thread grouping candidate: listRID=\(mailingList.rid, privacy: .public) rootMessageID=\(thread.root.messageID, privacy: .public) rootEmailID=\(thread.root.id, privacy: .public) groupingKey=\(groupingKey, privacy: .public)"
- )
- inboxListLogger.debug(
- "Inbox unread state: key=\(groupingKey, privacy: .public) latestActivityAt=\(thread.updated.ISO8601Format(), privacy: .public) lastViewedAt=\(lastViewedAt?.ISO8601Format() ?? "nil", privacy: .public) isUnread=\(isUnread, privacy: .public)"
- )
return InboxThreadSummary(
rootEmailID: thread.root.id,
rootMessageID: thread.root.messageID,
diff --git a/Hutch/Views/Inbox/ThreadDetailView.swift b/Hutch/Views/Inbox/ThreadDetailView.swift
index c5d26a5..e4c34e7 100644
--- a/Hutch/Views/Inbox/ThreadDetailView.swift
+++ b/Hutch/Views/Inbox/ThreadDetailView.swift
@@ -4,7 +4,6 @@ import SwiftUI
import UIKit
private let inboxReplyLogger = Logger(subsystem: "net.cleberg.Hutch", category: "InboxReply")
-private let inboxThreadNavigationLogger = Logger(subsystem: "net.cleberg.Hutch", category: "InboxThreadNavigation")
struct ThreadDetailView: View {
let thread: InboxThreadSummary
@@ -53,18 +52,12 @@ struct ThreadDetailView: View {
isUnread = thread.isUnread
await vm.loadThread()
}
- .onAppear {
- inboxThreadNavigationLogger.debug("Inbox thread detail appeared: threadID=\(thread.id, privacy: .public)")
- }
.onChange(of: viewModel?.thread?.id) { _, threadID in
guard threadID != nil, !hasMarkedCurrentThreadViewed, !suppressAutoMarkViewed else { return }
hasMarkedCurrentThreadViewed = true
isUnread = false
onViewed()
}
- .onDisappear {
- inboxThreadNavigationLogger.debug("Inbox thread detail view disappeared: threadID=\(thread.id, privacy: .public)")
- }
.sheet(item: Binding(
get: { viewModel?.composeDraft },
set: { _ in viewModel?.dismissReply() }
@@ -72,14 +65,13 @@ struct ThreadDetailView: View {
MailComposeView(draft: draft) { result in
switch result {
case .failed(let message):
- inboxReplyLogger.error("Inbox reply failed for thread \(thread.debugIdentifierSummary, privacy: .public): \(message, privacy: .public)")
+ inboxReplyLogger.error("Inbox reply failed")
viewModel?.error = message
case .cancelled:
- inboxReplyLogger.debug("Inbox reply cancelled for thread \(thread.debugIdentifierSummary, privacy: .public)")
+ break
case .saved:
- inboxReplyLogger.debug("Inbox reply draft saved for thread \(thread.debugIdentifierSummary, privacy: .public)")
+ break
case .sent:
- inboxReplyLogger.debug("Inbox reply handed off to Mail for thread \(thread.debugIdentifierSummary, privacy: .public)")
replySuccessMessage = "Reply handed off to Mail."
Task {
await viewModel?.loadThread()
diff --git a/Hutch/Views/Inbox/ThreadViewModel.swift b/Hutch/Views/Inbox/ThreadViewModel.swift
index c042fba..6b70a41 100644
--- a/Hutch/Views/Inbox/ThreadViewModel.swift
+++ b/Hutch/Views/Inbox/ThreadViewModel.swift
@@ -172,8 +172,6 @@ final class ThreadViewModel {
partialWarning = nil
defer { isLoading = false }
- inboxLogger.debug("Opening inbox thread: \(self.summary.debugIdentifierSummary, privacy: .public)")
-
do {
let threadPayloads = try await fetchThreadPayloads()
@@ -207,9 +205,7 @@ final class ThreadViewModel {
}
} catch {
hadPartialReplyFailure = true
- inboxLogger.error(
- "Inbox thread descendants failed for \(self.summary.debugIdentifierSummary, privacy: .public): \(error.localizedDescription, privacy: .public)"
- )
+ inboxLogger.error("Inbox thread descendants failed")
}
}
@@ -241,9 +237,9 @@ final class ThreadViewModel {
if thread == nil {
self.error = "Failed to load thread"
} else {
- self.error = error.localizedDescription
+ self.error = error.userFacingMessage
}
- inboxLogger.error("Inbox thread detail failed for \(self.summary.debugIdentifierSummary, privacy: .public): \(error.localizedDescription, privacy: .public)")
+ inboxLogger.error("Inbox thread detail failed")
}
}
@@ -275,17 +271,9 @@ final class ThreadViewModel {
private func fetchThreadByMessageID(rootMessageID: String) async throws -> InboxThreadPayloadDetail? {
let candidateMessageIDs = Self.messageIDCandidates(from: rootMessageID)
- inboxLogger.debug(
- "Inbox thread lookup IDs: subject=\(self.summary.subject, privacy: .public) rootEmailID=\(self.summary.rootEmailID, privacy: .public) rootMessageID=\(rootMessageID, privacy: .public) candidates=\(candidateMessageIDs.joined(separator: ", "), privacy: .public)"
- )
-
var lastLookupError: Error?
for messageID in candidateMessageIDs {
- inboxLogger.debug(
- "Inbox thread detail lookup request: rid=\(self.summary.listRID, privacy: .public) messageID=\(messageID, privacy: .public)"
- )
-
do {
let response: InboxThreadLookupResponse = try await Self.executeGraphQLRequest(
client: client,
@@ -303,10 +291,6 @@ final class ThreadViewModel {
} catch let error as SRHTError {
switch error {
case .graphQLErrors(let errors):
- let combinedMessage = errors.map(\.message).joined(separator: " | ")
- inboxLogger.error(
- "Inbox thread message lookup failed: rid=\(self.summary.listRID, privacy: .public) messageID=\(messageID, privacy: .public) errors=\(combinedMessage, privacy: .public)"
- )
if errors.allSatisfy({ $0.message.localizedCaseInsensitiveContains("no rows in result set") }) {
lastLookupError = error
continue
@@ -318,11 +302,7 @@ final class ThreadViewModel {
}
}
- if let lastLookupError {
- inboxLogger.debug(
- "Inbox thread message lookup exhausted candidates for \(self.summary.debugIdentifierSummary, privacy: .public): \(lastLookupError.localizedDescription, privacy: .public)"
- )
- }
+ _ = lastLookupError
return nil
}
@@ -348,7 +328,7 @@ final class ThreadViewModel {
)
} catch {
if Self.isRecoverableNoRows(error) {
- inboxLogger.error("Inbox thread page scan recoverable miss for \(self.summary.debugIdentifierSummary, privacy: .public): \(error.localizedDescription, privacy: .public)")
+ inboxLogger.error("Inbox thread page scan missed a recoverable result")
return nil
}
throw error
@@ -358,11 +338,6 @@ final class ThreadViewModel {
return nil
}
- let candidates = threadPage.results.map { payload in
- "subject=\(payload.subject ?? "<nil>") rootEmailID=\(payload.root?.id.map(String.init) ?? "<nil>") rootMessageID=\(payload.root?.messageID ?? "<nil>")"
- }.joined(separator: " | ")
- inboxLogger.debug("Inbox thread detail page candidates: \(candidates, privacy: .public)")
-
if let matchedThread = threadPage.results.first(where: {
$0.root?.messageID == targetRootMessageID ||
$0.root?.id == summary.rootEmailID ||
@@ -428,9 +403,7 @@ final class ThreadViewModel {
)
} catch {
if Self.isRecoverableNoRows(error) {
- inboxLogger.error(
- "Inbox descendant page recoverable miss: thread=\(self.summary.debugIdentifierSummary, privacy: .public) messageID=\(messageID, privacy: .public) error=\(error.localizedDescription, privacy: .public)"
- )
+ inboxLogger.error("Inbox descendant page missed a recoverable result")
continue
}
throw error
@@ -449,9 +422,6 @@ final class ThreadViewModel {
error = "This thread is not ready to reply to yet."
return
}
- inboxLogger.debug(
- "Preparing inbox reply: subject=\(thread.subject, privacy: .public) listRID=\(thread.listRID, privacy: .public) rootMessageID=\(thread.rootMessageID, privacy: .public) recipient=\(thread.replyRecipient, privacy: .public) senderIdentity=system-mail-account"
- )
composeDraft = MailComposeDraft(
recipients: [thread.replyRecipient],
ccRecipients: [],
@@ -732,11 +702,6 @@ final class ThreadViewModel {
)
let (data, _) = try await URLSession.shared.data(for: request)
- #if DEBUG
- let responseBody = String(data: data, encoding: .utf8) ?? "<non-utf8 response>"
- inboxLogger.debug("Inbox thread raw GraphQL response: \(responseBody, privacy: .public)")
- #endif
-
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .srhtFlexible
let envelope = try decoder.decode(GraphQLResponse<T>.self, from: data)