fix(core): drill-down lists descendants under untracked intermediates
listEntries({ parentId: X }) now applies the top-level rule relative to X: this user's
live entries on descendants of X (up to three levels) with no live-entry work strictly
between them and X, so it agrees with childSummary. Restoring an engagement's re-parenting
onto a soft-deleted entry throws ValidationError("entryId", "target entry is deleted").
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
00fc0180b0
commit
c6865b2522
5 files changed
+49
-5
No files matched your search
@@ -730,6 +730,21 @@ const HAS_ENTRY_ANCESTOR = sql<boolean>`exists (select 1 from works a1
|
||||
and (pe.work_id = a1.id or pe.work_id = a2.id or pe.work_id = a3.id)
|
||||
where a1.id = w.parent_id)`;
|
||||
|
||||
/**
|
||||
* True when the entry's work is a descendant of `parentId` (up to three levels) with no work strictly
|
||||
* between them carrying a live entry of this user: the top-level rule, relative to `parentId`.
|
||||
*/
|
||||
function underParent(parentId: string) {
|
||||
const liveEntryOn = (ids: ReturnType<typeof sql>) =>
|
||||
sql`exists (select 1 from entries pe where pe.user_id = e.user_id and pe.deleted_at is null and pe.work_id in (${ids}))`;
|
||||
return sql<boolean>`(w.parent_id = ${parentId}
|
||||
or exists (select 1 from works p1 where p1.id = w.parent_id and p1.parent_id = ${parentId}
|
||||
and not ${liveEntryOn(sql`p1.id`)})
|
||||
or exists (select 1 from works p1 inner join works p2 on p2.id = p1.parent_id
|
||||
where p1.id = w.parent_id and p2.parent_id = ${parentId}
|
||||
and not ${liveEntryOn(sql`p1.id, p2.id`)}))`;
|
||||
}
|
||||
|
||||
function viewQuery(k: Executor, userId: string) {
|
||||
return k
|
||||
.selectFrom("entries as e")
|
||||
@@ -801,7 +816,8 @@ export interface EntryFilter {
|
||||
q?: string;
|
||||
/**
|
||||
* Default `null`: the top of the user's library, i.e. entries with no ancestor work (up to three
|
||||
* levels) carrying a live entry of this user. A work id lists that work's direct children.
|
||||
* levels) carrying a live entry of this user. A work id applies the same rule relative to that work:
|
||||
* its descendants (up to three levels) with no live-entry work strictly between them and it.
|
||||
*/
|
||||
parentId?: string | null;
|
||||
sort?: "updated" | "title" | "rating";
|
||||
@@ -829,7 +845,7 @@ export async function listEntries(
|
||||
|
||||
let q = viewQuery(db.k, userId);
|
||||
const parentId = filter.parentId === undefined ? null : filter.parentId;
|
||||
q = parentId === null ? q.where(sql<boolean>`not ${HAS_ENTRY_ANCESTOR}`) : q.where("w.parent_id", "=", parentId);
|
||||
q = q.where(parentId === null ? sql<boolean>`not ${HAS_ENTRY_ANCESTOR}` : underParent(parentId));
|
||||
if (filter.mediaType) q = q.where(sql<string>`coalesce(e.media_type_override, w.media_type)`, "=", filter.mediaType);
|
||||
if (filter.status) q = q.where("e.status", "=", filter.status);
|
||||
if (filter.tag) {
|
||||
|
||||
Reference in new issue
Block a user