summaryrefslogtreecommitdiff
path: root/Hutch/Views/Inbox
diff options
context:
space:
mode:
Diffstat (limited to 'Hutch/Views/Inbox')
-rw-r--r--Hutch/Views/Inbox/InboxView.swift11
-rw-r--r--Hutch/Views/Inbox/InboxViewModel.swift25
-rw-r--r--Hutch/Views/Inbox/ThreadDetailView.swift51
3 files changed, 85 insertions, 2 deletions
diff --git a/Hutch/Views/Inbox/InboxView.swift b/Hutch/Views/Inbox/InboxView.swift
index aa3765c..2447a6e 100644
--- a/Hutch/Views/Inbox/InboxView.swift
+++ b/Hutch/Views/Inbox/InboxView.swift
@@ -42,6 +42,17 @@ struct InboxView: 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)
diff --git a/Hutch/Views/Inbox/InboxViewModel.swift b/Hutch/Views/Inbox/InboxViewModel.swift
index edb7593..a01f836 100644
--- a/Hutch/Views/Inbox/InboxViewModel.swift
+++ b/Hutch/Views/Inbox/InboxViewModel.swift
@@ -56,12 +56,19 @@ private struct InboxEmailPreview: Decodable, Sendable {
let patch: InboxPatchPreview?
}
+enum InboxThreadFilter: String, CaseIterable, Sendable {
+ case all = "All"
+ case patches = "Patches"
+ case discussions = "Talk"
+}
+
@Observable
@MainActor
final class InboxViewModel {
private(set) var threads: [InboxThreadSummary] = []
private(set) var isLoading = false
var error: String?
+ var filter: InboxThreadFilter = .all
var searchText = ""
private let client: SRHTClient
@@ -181,9 +188,10 @@ final class InboxViewModel {
}
var filteredThreads: [InboxThreadSummary] {
+ let filteredByKind = Self.filterThreads(threads, filter: filter)
let q = searchText.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
- guard !q.isEmpty else { return threads }
- return threads.filter {
+ guard !q.isEmpty else { return filteredByKind }
+ return filteredByKind.filter {
$0.displaySubject.lowercased().contains(q) ||
$0.listName.lowercased().contains(q) ||
$0.latestSender.canonicalName.lowercased().contains(q)
@@ -389,4 +397,17 @@ final class InboxViewModel {
}
return nil
}
+
+ nonisolated static func filterThreads(_ threads: [InboxThreadSummary], filter: InboxThreadFilter) -> [InboxThreadSummary] {
+ threads.filter { thread in
+ switch filter {
+ case .all:
+ return true
+ case .patches:
+ return thread.containsPatch
+ case .discussions:
+ return !thread.containsPatch
+ }
+ }
+ }
}
diff --git a/Hutch/Views/Inbox/ThreadDetailView.swift b/Hutch/Views/Inbox/ThreadDetailView.swift
index c595497..03aa779 100644
--- a/Hutch/Views/Inbox/ThreadDetailView.swift
+++ b/Hutch/Views/Inbox/ThreadDetailView.swift
@@ -18,6 +18,7 @@ struct ThreadDetailView: View {
@State private var hasMarkedCurrentThreadViewed = false
@State private var suppressAutoMarkViewed = false
@State private var isUnread: Bool
+ @State private var isOpeningRepository = false
init(
thread: InboxThreadSummary,
@@ -119,6 +120,41 @@ struct ThreadDetailView: View {
.padding(.vertical, 4)
}
+ Section("Related") {
+ Button {
+ appState.navigateToMailingList(
+ InboxMailingListReference(
+ id: thread.listID,
+ rid: thread.listRID,
+ name: thread.listName,
+ owner: thread.listOwner
+ )
+ )
+ } label: {
+ Label(thread.listDisplayName, systemImage: "list.bullet")
+ }
+
+ if let repo = self.thread.repo {
+ Button {
+ openRepository(named: repo, ownerCanonicalName: thread.listOwner.canonicalName)
+ } label: {
+ HStack {
+ Label("\(thread.listOwner.canonicalName)/\(repo)", systemImage: "book.closed")
+ Spacer()
+ if isOpeningRepository {
+ ProgressView()
+ .controlSize(.small)
+ } else {
+ Image(systemName: "arrow.up.right")
+ .font(.caption)
+ .foregroundStyle(.tertiary)
+ }
+ }
+ }
+ .disabled(isOpeningRepository)
+ }
+ }
+
if let partialWarning = viewModel.partialWarning {
Section {
Text(partialWarning)
@@ -180,6 +216,21 @@ struct ThreadDetailView: View {
parts.append(thread.lastActivityAt.relativeDescription)
return parts.joined(separator: " • ")
}
+
+ private func openRepository(named repositoryName: String, ownerCanonicalName: String) {
+ guard !isOpeningRepository else { return }
+ isOpeningRepository = true
+ Task {
+ defer { isOpeningRepository = false }
+ do {
+ let ownerUsername = ownerCanonicalName.hasPrefix("~") ? String(ownerCanonicalName.dropFirst()) : ownerCanonicalName
+ let repository = try await appState.resolveRepository(owner: ownerUsername, name: repositoryName)
+ appState.navigateToRepository(repository)
+ } catch {
+ appState.presentRepositoryDeepLinkError()
+ }
+ }
+ }
}
private struct InboxMessageRow: View {