Skip to content

[#258] Make view status lists source-aware (statusSlugs -> statusRefs) - #268

Open
Plyb wants to merge 3 commits into
mainfrom
issue-258-source-aware-views
Open

Plyb wants to merge 3 commits into
mainfrom
issue-258-source-aware-views

Conversation

@Plyb

@Plyb Plyb commented Jul 21, 2026

Copy link
Copy Markdown
Owner

Closes #258

Summary

Follow-up to #12 / PR1 (#257): views now reference statuses as { slug, sourceId } pairs (StatusRef) instead of bare slugs, so a single view's status list can span multiple sources without a same-named slug in a different source being ambiguous.

Changes

  • src/types.ts: added StatusRef = { slug: string; sourceId: string }; UserDefinedView.statusSlugs: string[] -> statusRefs: StatusRef[].
  • src/db/client.ts: bumped DB_VERSION 13 -> 14; added a migration step that rewrites every existing view's statusSlugs into statusRefs scoped to DEFAULT_SOURCE_ID.
  • src/db/views.ts: viewSchema now validates statusRefs: { slug, sourceId }[].
  • src/db/statuses.ts: reassignTasksAndViews and getStatusUsage now match a view's statusRefs on { slug, sourceId }, scoped to DEFAULT_SOURCE_ID (this store's own source) — a same-slug status belonging to a different source is left untouched.
  • src/view-utils.ts: displayedTasksForView and sectionTasksForStatus match on (slug, sourceId) instead of bare slug. Added SectionRef (StatusRef | ArchiveView['id']) and sectionPagingKey(), a small composite-key helper (${sourceId}:${slug}) so two sources' same-named status slug can't collide in paging state.
  • src/TasksProvider.tsx: requestTaskPage now takes a SectionRef and derives its paging-dict key via sectionPagingKey. Actual fetching still always goes through defaultSource (unchanged) — real multi-source paging/merging is out of scope here per the issue, landing with Git integration #10/GitHub issues integration #11.
  • src/tasks-context.ts: requestTaskPage's type updated to (section: SectionRef) => void.
  • src/MainPage.tsx: sections are now built from currentView.statusRefs; status lookup, sectionTasksForStatus, and paging keys all resolve per-ref.
  • src/SettingsPage.tsx, src/ViewEditorModal.tsx, src/modal-derivations.ts (partitionStatuses): the view editor now tracks StatusRef[] and builds a ref from a chosen Status as { slug: status.slug, sourceId: status.sourceId }.
  • Tests updated/added across db.test.ts (including a new v13->v14 migration test), view-utils.test.ts, synthetic-view-utils.test.ts, modal-derivations.test.ts, TasksProvider.test.tsx.

Design notes

  • Section-key scheme: sectionPagingKey composes ${sourceId}:${slug} for real statuses, and passes the archive sentinel (__archived__) through unchanged as an opaque string — matching the existing pattern of overloading the paging-dict key with that sentinel.
  • reassignTasksAndViews / getStatusUsage scoping: since VIEWS_STORE is app-level (shared across all sources, not per-source — see sources/types.ts), these functions now only touch/count a view's statusRefs entries whose sourceId matches DEFAULT_SOURCE_ID (this store's own source), so a status that happens to share a slug with a status in a different source is never renamed or counted as "in use" by mistake.

Test plan

  • npx tsc -b — clean
  • npm run lint — clean (one pre-existing unrelated warning in MainPage.tsx, confirmed present on main too)
  • npx vitest run — 201/201 passing

Views now reference statuses as {slug, sourceId} pairs instead of bare
slugs, so a single view can span statuses from multiple sources without
a same-named slug in a different source colliding. Adds a v13->v14
migration rewriting each existing view's statusSlugs into statusRefs
scoped to the default (indexeddb) source, scopes
reassignTasksAndViews/getStatusUsage's view matching to the calling
store's own source, and threads StatusRef through the view editor,
displayedTasksForView/sectionTasksForStatus, and TasksProvider's
section-paging keys (composite sourceId:slug, to avoid the same
collision in paging state).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings July 21, 2026 13:41

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

Comment thread src/db/statuses.ts Outdated
Comment on lines +46 to +48
// A view's statusRefs can reference statuses from other sources too (see
// StatusRef in types.ts); only rewrite refs pointing at this store's own
// source, so a same-slug status belonging to a different source is untouched.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// A view's statusRefs can reference statuses from other sources too (see
// StatusRef in types.ts); only rewrite refs pointing at this store's own
// source, so a same-slug status belonging to a different source is untouched.

Comment thread src/db/statuses.ts Outdated
for (const view of views) {
if (view.statusSlugs.includes(oldSlug)) {
viewStore.put({ ...view, statusSlugs: view.statusSlugs.map((s) => (s === oldSlug ? newSlug : s)) })
if (view.statusRefs.some((ref) => ref.sourceId === DEFAULT_SOURCE_ID && ref.slug === oldSlug)) {

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other sources are going to also need this machinery for reassigning the refs in views. Extract out this reassignTasksAndViews. Have the new version take in a source, and use that source to do the task reassignment, while the view reassignment can stay in the function itself. This means adding a new method to TaskSource for reassigning just tasks. It probably also means we'd be able to remove the reassignTasksAndViews from the TaskSource interface

Comment thread src/db/statuses.ts Outdated
.filter((t) => t.statusSlug === slug)
.map((t) => t.id)
const viewIds = views.filter((v) => v.statusSlugs.includes(slug)).map((v) => v.id)
const viewIds = views

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A similar comment applies to this function. Extract out the function, then use a source to do the source-specific part while keeping the view-related stuff (which is source agnostic) in the function itself.

Comment thread src/view-utils.ts Outdated
Comment on lines +33 to +34
// Composite so two sources' same-named status slug don't collide in
// sectionPaging's keys once a view's statuses can span multiple sources.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Composite so two sources' same-named status slug don't collide in
// sectionPaging's keys once a view's statuses can span multiple sources.

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.

Sources Core (PR2): make views source-aware

2 participants