summaryrefslogtreecommitdiff
path: root/Hutch/Views/Tickets/TicketListViewModel.swift
diff options
context:
space:
mode:
authorChristian Cleberg <[email protected]>2026-04-13 17:35:37 -0500
committerChristian Cleberg <[email protected]>2026-04-13 17:35:37 -0500
commit30decf6d9b6b95277cbd8b8a7d9e23d9ce5413ca (patch)
tree8ba13d62083d7c3555bd250e5cefe416eb698d31 /Hutch/Views/Tickets/TicketListViewModel.swift
parent5e63048ab6a741ae947f7fe42efcb5206a3f3963 (diff)
downloadhutch-30decf6d9b6b95277cbd8b8a7d9e23d9ce5413ca.tar.gz
hutch-30decf6d9b6b95277cbd8b8a7d9e23d9ce5413ca.tar.bz2
hutch-30decf6d9b6b95277cbd8b8a7d9e23d9ce5413ca.zip
fix: show empty state for bare git repos instead of an error
git.sr.ht returns "internal system error" from the references query and "reference not found" from the log/readme queries when a repo has no commits. Broaden isEmptyRepositoryError to cover missingReference, unknownRevision, noRows, notFound, and those two message strings, then apply the same silent-empty treatment to loadReferences and loadArtifacts (which previously surfaced any error directly to the user). Fixes: https://todo.sr.ht/~ccleberg/hutch/60
Diffstat (limited to 'Hutch/Views/Tickets/TicketListViewModel.swift')
-rw-r--r--Hutch/Views/Tickets/TicketListViewModel.swift34
1 files changed, 27 insertions, 7 deletions
diff --git a/Hutch/Views/Tickets/TicketListViewModel.swift b/Hutch/Views/Tickets/TicketListViewModel.swift
index e23cc52..0255798 100644
--- a/Hutch/Views/Tickets/TicketListViewModel.swift
+++ b/Hutch/Views/Tickets/TicketListViewModel.swift
@@ -99,13 +99,13 @@ final class TicketListViewModel {
var filter: TicketFilter = .open {
didSet {
persistFilterState()
- updateFilteredTickets()
+ resetPaginationAndUpdateFilters()
}
}
var selectedLabelIDs: Set<Int> = [] {
didSet {
persistFilterState()
- updateFilteredTickets()
+ resetPaginationAndUpdateFilters()
}
}
var searchText = "" {
@@ -283,6 +283,13 @@ final class TicketListViewModel {
}
}
+ private func resetPaginationAndUpdateFilters() {
+ // Reset pagination when filters change since the cursor is tied to the unfiltered dataset
+ cursor = nil
+ hasMore = true
+ updateFilteredTickets()
+ }
+
// MARK: - Public API
func loadTickets() async {
@@ -305,19 +312,32 @@ final class TicketListViewModel {
}
func loadMoreIfNeeded(currentItem: TicketSummary) async {
- guard let last = tickets.last,
- last.id == currentItem.id,
- hasMore,
- !isLoadingMore else {
+ // Check if currentItem is in the filtered list and close to the end
+ guard hasMore, !isLoadingMore else { return }
+
+ guard let index = filteredTickets.firstIndex(where: { $0.id == currentItem.id }) else {
return
}
+ let itemsFromEnd = filteredTickets.count - index - 1
+ guard itemsFromEnd < 5 else { return }
+
isLoadingMore = true
do {
let page = try await fetchPage(cursor: cursor)
- tickets.append(contentsOf: page.results)
+
+ // Deduplicate: only add tickets that don't already exist
+ let existingIDs = Set(tickets.map(\.id))
+ let newTickets = page.results.filter { !existingIDs.contains($0.id) }
+
+ // If we got back the same tickets, the API cursor pagination isn't working
+ if newTickets.isEmpty && !page.results.isEmpty {
+ hasMore = false
+ }
+
cursor = page.cursor
+ tickets.append(contentsOf: newTickets)
hasMore = page.cursor != nil
reconcileSelectionWithLoadedTickets()
} catch {