Conversation
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>
| // 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. |
There was a problem hiding this comment.
| // 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. |
| 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)) { |
There was a problem hiding this comment.
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
| .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 |
There was a problem hiding this comment.
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.
| // Composite so two sources' same-named status slug don't collide in | ||
| // sectionPaging's keys once a view's statuses can span multiple sources. |
There was a problem hiding this comment.
| // Composite so two sources' same-named status slug don't collide in | |
| // sectionPaging's keys once a view's statuses can span multiple sources. |
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: addedStatusRef = { slug: string; sourceId: string };UserDefinedView.statusSlugs: string[]->statusRefs: StatusRef[].src/db/client.ts: bumpedDB_VERSION13 -> 14; added a migration step that rewrites every existing view'sstatusSlugsintostatusRefsscoped toDEFAULT_SOURCE_ID.src/db/views.ts:viewSchemanow validatesstatusRefs: { slug, sourceId }[].src/db/statuses.ts:reassignTasksAndViewsandgetStatusUsagenow match a view'sstatusRefson{ slug, sourceId }, scoped toDEFAULT_SOURCE_ID(this store's own source) — a same-slug status belonging to a different source is left untouched.src/view-utils.ts:displayedTasksForViewandsectionTasksForStatusmatch on(slug, sourceId)instead of bare slug. AddedSectionRef(StatusRef | ArchiveView['id']) andsectionPagingKey(), a small composite-key helper (${sourceId}:${slug}) so two sources' same-named status slug can't collide in paging state.src/TasksProvider.tsx:requestTaskPagenow takes aSectionRefand derives its paging-dict key viasectionPagingKey. Actual fetching still always goes throughdefaultSource(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 fromcurrentView.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 tracksStatusRef[]and builds a ref from a chosenStatusas{ slug: status.slug, sourceId: status.sourceId }.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
sectionPagingKeycomposes${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/getStatusUsagescoping: sinceVIEWS_STOREis app-level (shared across all sources, not per-source — seesources/types.ts), these functions now only touch/count a view'sstatusRefsentries whosesourceIdmatchesDEFAULT_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— cleannpm run lint— clean (one pre-existing unrelated warning inMainPage.tsx, confirmed present onmaintoo)npx vitest run— 201/201 passing