diff options
Diffstat (limited to 'Hutch/Views/Inbox')
| -rw-r--r-- | Hutch/Views/Inbox/InboxView.swift | 192 |
1 files changed, 3 insertions, 189 deletions
diff --git a/Hutch/Views/Inbox/InboxView.swift b/Hutch/Views/Inbox/InboxView.swift index c5299ec..a4870db 100644 --- a/Hutch/Views/Inbox/InboxView.swift +++ b/Hutch/Views/Inbox/InboxView.swift @@ -1,196 +1,10 @@ import SwiftUI +// Legacy wrapper kept temporarily so stale references continue to compile while +// the app transitions from Inbox to Work. struct InboxView: View { - @Environment(AppState.self) private var appState - @Environment(\.scenePhase) private var scenePhase - @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 { - if let viewModel { - listContent(viewModel) - } else { - SRHTLoadingStateView(message: "Loading inbox…") - } - } - .navigationTitle("Inbox") - .task { - let vm: InboxViewModel - if let viewModel { - vm = viewModel - } else { - let newViewModel = InboxViewModel( - client: appState.client, - defaults: appState.accountDefaults, - accountID: appState.activeAccountID - ) - viewModel = newViewModel - vm = newViewModel - } - - await vm.loadThreads() - } - .onChange(of: scenePhase) { _, newPhase in - guard newPhase == .active, let viewModel, !isShowingThreadDetail else { return } - Task { - await viewModel.loadThreads() - } - } - } - - @ViewBuilder - private func listContent(_ viewModel: InboxViewModel) -> some View { - @Bindable var vm = viewModel - - List { - Section { - Picker("Filter", selection: $vm.filter) { - ForEach(InboxThreadFilter.allCases, id: \.self) { filter in - Text(filter.rawValue).tag(filter) - } - } - .pickerStyle(.segmented) - .listRowBackground(Color.clear) - .listRowInsets(EdgeInsets()) - } - - ForEach(viewModel.filteredThreads) { thread in - Button { - selectThread(thread) - } label: { - InboxThreadRow(thread: thread) - } - .buttonStyle(.plain) - .swipeActions(edge: .leading, allowsFullSwipe: true) { - readStateAction(for: thread, in: viewModel) - } - .swipeActions(edge: .trailing, allowsFullSwipe: true) { - readStateAction(for: thread, in: viewModel) - } - } - } - .themedList() - .searchable( - text: $vm.searchText, - placement: .navigationBarDrawer(displayMode: .always), - prompt: "Search inbox" - ) - .listStyle(.plain) - .toolbar { - ToolbarItem(placement: .topBarTrailing) { - if viewModel.hasUnreadThreads { - Button("Mark All Read") { - withAnimation(.easeInOut(duration: 0.2)) { - viewModel.markAllThreadsRead() - } - } - } - } - } - .overlay { - if viewModel.isLoading, viewModel.threads.isEmpty { - SRHTLoadingStateView(message: "Loading inbox…") - } else if let error = viewModel.error, viewModel.threads.isEmpty { - SRHTErrorStateView( - title: "Failed to load inbox", - message: error, - retryAction: { await viewModel.loadThreads() } - ) - } else if !viewModel.threads.isEmpty, viewModel.filteredThreads.isEmpty { - ContentUnavailableView.search(text: viewModel.searchText) - } else if viewModel.threads.isEmpty, viewModel.error == nil { - ContentUnavailableView( - "Inbox Zero", - systemImage: "tray", - description: Text("You're up to date.") - ) - } - } - .connectivityOverlay(hasContent: !viewModel.threads.isEmpty) { - await viewModel.loadThreads() - } - .srhtErrorBanner(error: $vm.error) - .refreshable { - await viewModel.loadThreads() - } - .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.") - ) - } - } - } - - @ViewBuilder - private func readStateAction(for thread: InboxThreadSummary, in viewModel: InboxViewModel) -> some View { - Button { - withAnimation(.easeInOut(duration: 0.2)) { - viewModel.markThreadRead(thread) - } - } label: { - Label("Mark as Read", systemImage: "envelope.open") - } - .tint(.blue) - } - - private func selectThread(_ thread: InboxThreadSummary) { - cacheSelectedThread(thread) - isShowingThreadDetail = true - } - - 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 - 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() { - selectedThreadID = nil - selectedThreadSnapshot = nil - isShowingThreadDetail = false + WorkView() } } |
