Skip to content

feat: offline search — SwiftUI feature layer - #229

Open
danieltmbr wants to merge 3 commits into
mainfrom
feat/search-native-ui
Open

feat: offline search — SwiftUI feature layer#229
danieltmbr wants to merge 3 commits into
mainfrom
feat/search-native-ui

Conversation

@danieltmbr

Copy link
Copy Markdown
Owner

Summary

Stacks on #228 (persistence & networking layer).

  • Per-tab .searchable bars on Blog, Catalogue, and Quotes — each filters only its own content, respecting active language/category/source filters
  • Dedicated Search tab with sectioned results (Posts / Catalogue / Quotes) and a scope bar; catalogue section surfaces note-body snippets
  • CatalogueSearchEngine: @MainActor @Observable engine that drives catalogue results via CatalogueStore.search; needed because @Query can't follow the PreviewRecordNoteRecord UUID link
  • BlogSearchAction / CatalogueSearchAction / QuotesSearchAction: environment-injected seams — local predicate query runs first for instant cached results, then the network search upserts new data and triggers a second local pass
  • Detail section fix: all six typed sections (Song/Album/Book/Movie/Podcast/Playlist) now accept item: PreviewRecord and coalesce typed + preview fields so a half-materialised item (preview exists, typed record not yet cached) renders a header immediately; .task fires a single-item fetch on appear when the typed record is absent

Test plan

  • Xcode build passes for app-core and Reader target
  • Blog tab: search term filters posts; results update after network search upserts previously-unsynced posts
  • Catalogue tab: title match, subtitle match, and note-body match all return results; category + language filters narrow while searching
  • Quotes tab: body search works; source + language filters apply during search
  • Search tab: sectioned results render; scope bar narrows correctly; catalogue section shows note snippets
  • Airplane mode: repeat a previously-run search — cached results (including note snippets) render without network
  • Half-materialised catalogue item: search a term matching only a note body of an unsynced item, tap the result — preview header renders immediately, enriches once the typed-record fetch completes

Note: If the app crashes on launch with a SwiftData migration error due to the secondaryInfo optionality change (from #228), delete the app to reset the cache — Reader's DB is a disposable network cache.

🤖 Generated with Claude Code

@danieltmbr
danieltmbr force-pushed the feat/search-native-ui branch from f562be8 to 807b86b Compare July 6, 2026 15:23
@danieltmbr
danieltmbr changed the base branch from feat/search-native-api to main July 6, 2026 15:23
@danieltmbr
danieltmbr force-pushed the feat/search-native-ui branch from 807b86b to a1bdb45 Compare July 7, 2026 07:09
@danieltmbr
danieltmbr changed the base branch from main to feat/search-backend July 7, 2026 09:50
@danieltmbr
danieltmbr force-pushed the feat/search-native-ui branch 2 times, most recently from 6b5bd0a to 3d5e278 Compare July 7, 2026 10:32
danieltmbr and others added 3 commits July 7, 2026 11:36
- Per-tab .searchable bars on Blog, Catalogue, and Quotes — each filters only
  its own content, respecting active language/category/source filters
- Dedicated Search tab with sectioned results (Posts / Catalogue / Quotes)
  and a scope bar; catalogue section shows note-body snippets
- CatalogueSearchEngine: @observable engine that drives catalogue results via
  CatalogueStore.search; needed because @query can't cross the
  PreviewRecord→NoteRecord UUID link
- BlogSearchAction / CatalogueSearchAction / QuotesSearchAction: environment-
  injected seams; local predicate query runs first for instant results, then
  the network search upserts and triggers a second local pass
- Detail section fix: typed sections (Song/Album/Book/…) now accept
  item: PreviewRecord and coalesce typed+preview fields so a half-materialised
  item shows a header immediately; .task fires a single-item fetch on appear
  when the typed record is absent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…list views

Wire up the paginated backend endpoints from the previous commit into the native app:
- Blog/Quotes/Catalogue: run local search immediately on keystroke (flash fix); network
  fetch debounced 300 ms; re-run local after upsert to surface new results
- CatalogueModel/QuotesModel/BlogModel: add loadMore() + isLoadingMore + hasMore state;
  loadMoreRevision counter lets CatalogueSearchEngine re-run after network upserts
- CataloguePageLoadAction / QuotesPageLoadAction: environment-injected load-more seams
- ReaderCatalogue / ReaderQuotes: cursor-tracking coordinators that thread nextCursor
  across search() and loadMore() calls against the paginated API endpoints
- Unified list views per tab: single @Query-based browse list + search-engine-driven
  results list, each with a load-more cell and status line
- ReaderApp: wire all three tabs to the paginated loaders

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…match PostsLoader pattern

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant