diff options
| author | Christian Cleberg <[email protected]> | 2026-03-19 16:35:41 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-03-19 16:35:41 -0500 |
| commit | 9065ef6e92245a44390e336cc1ae515ae706cdd7 (patch) | |
| tree | 413a9d930bd8159205afe567fe1e65fbfd8733c2 /Hutch/Views/Inbox/InboxView.swift | |
| parent | 165d97fd2867cf5923aeb1c5fc00f6e60bcaaa6e (diff) | |
| download | hutch-9065ef6e92245a44390e336cc1ae515ae706cdd7.tar.gz hutch-9065ef6e92245a44390e336cc1ae515ae706cdd7.tar.bz2 hutch-9065ef6e92245a44390e336cc1ae515ae706cdd7.zip | |
fix: inbox thread handling bug
Diffstat (limited to 'Hutch/Views/Inbox/InboxView.swift')
| -rw-r--r-- | Hutch/Views/Inbox/InboxView.swift | 87 |
1 files changed, 83 insertions, 4 deletions
diff --git a/Hutch/Views/Inbox/InboxView.swift b/Hutch/Views/Inbox/InboxView.swift index 304e62d..2b92738 100644 --- a/Hutch/Views/Inbox/InboxView.swift +++ b/Hutch/Views/Inbox/InboxView.swift @@ -1,8 +1,14 @@ import SwiftUI +import os + +private let inboxNavigationLogger = Logger(subsystem: "net.cleberg.Hutch", category: "InboxNavigation") struct InboxView: View { @Environment(AppState.self) private var appState @State private var viewModel: InboxViewModel? + @State private var selectedThreadID: InboxThreadSummary.ID? + @State private var selectedThreadSnapshot: InboxThreadSummary? + @State private var isShowingThreadDetail = false var body: some View { Group { @@ -28,9 +34,12 @@ struct InboxView: View { List { ForEach(viewModel.threads) { thread in - NavigationLink(value: thread) { + Button { + selectThread(thread) + } label: { InboxThreadRow(thread: thread) } + .buttonStyle(.plain) .swipeActions(edge: .leading, allowsFullSwipe: true) { readStateAction(for: thread, in: viewModel) } @@ -64,9 +73,37 @@ struct InboxView: View { .refreshable { await viewModel.loadThreads() } - .navigationDestination(for: InboxThreadSummary.self) { thread in - ThreadDetailView(thread: thread) { - viewModel.markThreadRead(thread) + .onChange(of: viewModel.threads) { _, threads in + syncSelectedThreadSnapshot(with: threads) + } + .navigationDestination(isPresented: Binding( + get: { isShowingThreadDetail && selectedThread(for: viewModel) != nil }, + set: { isPresented in + if !isPresented { + clearSelection() + } + isShowingThreadDetail = isPresented + } + )) { + if let thread = selectedThread(for: viewModel) { + ThreadDetailView(thread: thread) { + viewModel.markThreadRead(thread) + } + .onAppear { + cacheSelectedThread(thread) + } + .onDisappear { + handleThreadDetailDisappear(for: thread.id) + } + } else { + ContentUnavailableView( + "Thread Unavailable", + systemImage: "tray", + description: Text("This thread could not be restored.") + ) + .onAppear { + inboxNavigationLogger.error("Inbox navigation destination missing thread snapshot") + } } } } @@ -82,6 +119,48 @@ struct InboxView: View { } .tint(.blue) } + + 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) { + selectedThreadID = thread.id + selectedThreadSnapshot = thread + } + + private func selectedThread(for viewModel: InboxViewModel) -> InboxThreadSummary? { + guard let selectedThreadID else { return selectedThreadSnapshot } + return viewModel.thread(withID: selectedThreadID) ?? (selectedThreadSnapshot?.id == selectedThreadID ? selectedThreadSnapshot : nil) + } + + 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() + } + + private func syncSelectedThreadSnapshot(with threads: [InboxThreadSummary]) { + guard let selectedThreadID else { return } + guard let updatedThread = threads.first(where: { $0.id == selectedThreadID }) else { return } + selectedThreadSnapshot = updatedThread + } + + private func clearSelection() { + if let selectedThreadID { + inboxNavigationLogger.debug("Inbox selection cleared: threadID=\(selectedThreadID, privacy: .public)") + } + selectedThreadID = nil + selectedThreadSnapshot = nil + isShowingThreadDetail = false + } } struct InboxThreadRow: View { |
