diff options
| author | Christian Cleberg <[email protected]> | 2026-04-13 17:35:37 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-04-13 17:35:37 -0500 |
| commit | 30decf6d9b6b95277cbd8b8a7d9e23d9ce5413ca (patch) | |
| tree | 8ba13d62083d7c3555bd250e5cefe416eb698d31 /Hutch/Views/Tickets | |
| parent | 5e63048ab6a741ae947f7fe42efcb5206a3f3963 (diff) | |
| download | hutch-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')
| -rw-r--r-- | Hutch/Views/Tickets/TicketListViewModel.swift | 34 |
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 { |
