feat: full offline search for Blog, Catalogue, and Quotes - #227
Closed
danieltmbr wants to merge 5 commits into
Closed
feat: full offline search for Blog, Catalogue, and Quotes#227danieltmbr wants to merge 5 commits into
danieltmbr wants to merge 5 commits into
Conversation
Adds per-tab .searchable bars (Blog, Catalogue, Quotes) and wires up the dedicated Search tab. Every search runs a local SwiftData predicate query (instant cached results) and concurrently fires a network search that upserts results, keeping offline and online parity. Catalogue search required two-step DB queries: PreviewRecord.search (title/subtitle + category/language via #Predicate composition) and NoteRecord.search (body text + language). Makes PreviewRecord.secondaryInfo non-optional (String = "") to enable SQL CONTAINS[cd] predicates — SwiftData cannot generate SQL for TERNARY/optional-chain on the CONTAINS LHS. CatalogueSearchResult and CatalogueSearchEngine moved to app-persistence so predicates run against internal record fields directly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Typed detail sections (Song/Album/Book/Movie/Podcast/Playlist) now pass `item: PreviewRecord` instead of `previewID: UUID`, always render a `CatalogueItemHeader` coalescing from the typed record when available or the preview as fallback, publish `catalogueItemRefresh` unconditionally from `item.sourceID`, and fire a single-item fetch on appear only when the typed record is absent — so search-upserted previews materialise immediately on navigation rather than silently showing nothing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…point URL PostSearchRequest now targets api/posts (the list endpoint) with PageResult<PostResponse> as the response type, matching the backend change that folded search into the list via the ?term= query param. PostSearchQuery moves to tmbr-core alongside CatalogueSearchQuery and QuoteSearchQuery. ReaderApp uses results.items to unwrap the page. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…e/quotes?term= Backend folded quote search into the list endpoint; mirror that on the native side by re-adding QuoteSearchQuery (same shape as PostSearchQuery) and updating the path from the now-removed /api/catalogue/quotes/search. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
QuoteSearchQuery, QuoteSearchRequest, and QuoteSearchLoader were defined but never injected into QuotesModel in any app target. Removing to keep the search request surface honest. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
danieltmbr
force-pushed
the
feat/search-native
branch
from
July 6, 2026 12:07
65b033c to
ac359cd
Compare
Owner
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
.searchablebars on Blog, Catalogue, and Quotes tabs — each searches only its own content, respecting active language/category/source filtersCatalogueSearchEngine(@MainActor @Observable) drives catalogue results since@Querycan't cross thePreviewRecord→NoteRecordUUID link; delegates all DB work toCatalogueStore.searchPersistence layer changes:
PreviewRecord.secondaryInfo:String?→String = ""— SwiftData cannot generateCONTAINS[cd]SQL withTERNARY/optional-chain on the LHS; non-optional unblocks pure DB text search. DTO (PreviewResponse.secondaryInfo: String?) unchanged; coalesced at sync boundaryPreviewRecord.search(term:categories:languages:): composed predicate —textMatch(2 expressions) +filterMatchviaevaluateto avoid compiler type-check timeoutNoteRecord.search(term:languages:),PostRecord.search(term:languages:),QuoteRecord.search(term:sources:languages:): standard single-predicate text + filterCatalogueStore.search(...): two predicate-driven fetches (previews + notes) + targeted ID fetch for note→preview resolution; no full-table scansCatalogueStore.upsertSearch(_:): safe partial upsert — upserts items and note-body matches without quote reconciliation (preserves existing quotes)Test plan
app-persistence,app-core, and Reader target🤖 Generated with Claude Code