diff options
| author | Christian Cleberg <[email protected]> | 2026-04-13 13:35:03 -0500 |
|---|---|---|
| committer | Christian Cleberg <[email protected]> | 2026-04-13 13:35:03 -0500 |
| commit | 0b84ecaf7ba0fb2cd3f3867da8dc732450b55b50 (patch) | |
| tree | c2d92448661d9e6ca76bd7c69e9d51131305214e /Hutch/Views/Pastes/PasteListViewModel.swift | |
| parent | 06b24715cd244475ded5482e926721a57278a3cf (diff) | |
| download | hutch-0b84ecaf7ba0fb2cd3f3867da8dc732450b55b50.tar.gz hutch-0b84ecaf7ba0fb2cd3f3867da8dc732450b55b50.tar.bz2 hutch-0b84ecaf7ba0fb2cd3f3867da8dc732450b55b50.zip | |
perf: cache filtered collections and add equatable row views
Convert filteredJobs, filteredPastes, and filteredTickets from computed
properties to stored properties updated via didSet on their inputs. Each
update function guards with an equality check before assigning, so
@Observable skips the notification when the filtered result hasn't
changed — preventing list re-renders on auto-refresh when no visible
data has actually changed.
Add Equatable conformance to BuildRowView, PasteRowView, TicketRowView,
and SelectableTicketRow, and apply .equatable() at each ForEach call
site. When a list does re-render, SwiftUI now skips body evaluation for
rows whose model value is identical to the previous pass.
Implements: https://todo.sr.ht/~ccleberg/hutch/57
Diffstat (limited to 'Hutch/Views/Pastes/PasteListViewModel.swift')
| -rw-r--r-- | Hutch/Views/Pastes/PasteListViewModel.swift | 28 |
1 files changed, 20 insertions, 8 deletions
diff --git a/Hutch/Views/Pastes/PasteListViewModel.swift b/Hutch/Views/Pastes/PasteListViewModel.swift index b521e18..75f2f4b 100644 --- a/Hutch/Views/Pastes/PasteListViewModel.swift +++ b/Hutch/Views/Pastes/PasteListViewModel.swift @@ -3,13 +3,18 @@ import Foundation @Observable @MainActor final class PasteListViewModel { - private(set) var pastes: [Paste] = [] + private(set) var pastes: [Paste] = [] { + didSet { updateFilteredPastes() } + } private(set) var isLoading = false private(set) var isLoadingMore = false private(set) var isRefreshing = false private(set) var isCreatingPaste = false var error: String? - var searchText = "" + var searchText = "" { + didSet { updateFilteredPastes() } + } + private(set) var filteredPastes: [Paste] = [] private var cursor: String? private var hasMore = true @@ -19,15 +24,22 @@ final class PasteListViewModel { self.service = service } - var filteredPastes: [Paste] { + private func updateFilteredPastes() { let q = searchText.trimmingCharacters(in: .whitespacesAndNewlines).lowercased() - guard !q.isEmpty else { return pastes } - return pastes.filter { - $0.files.contains { - ($0.filename?.lowercased().contains(q) == true) || - $0.hash.lowercased().hasPrefix(q) + let updated: [Paste] + if q.isEmpty { + updated = pastes + } else { + updated = pastes.filter { + $0.files.contains { + ($0.filename?.lowercased().contains(q) == true) || + $0.hash.lowercased().hasPrefix(q) + } } } + if updated != filteredPastes { + filteredPastes = updated + } } func loadPastes() async { |
