fix: issues #60 #61 #62 #63 — inbox, refresh, subtask depth, chevron/detail #64

Merged
maximus merged 5 commits from issue-60-fix-duplicate-inbox into master 2026-04-09 01:33:40 +00:00
2 changed files with 38 additions and 28 deletions
Showing only changes of commit 6c36ebcce5 - Show all commits

View file

@ -202,32 +202,44 @@ async function processOperation(op: SyncOperation, userId: string) {
// If the incoming list is an inbox, check for an existing inbox and merge
if (incomingIsInbox) {
const [existingInbox] = await db
.select()
.from(slLists)
.where(and(eq(slLists.userId, userId), eq(slLists.isInbox, true), isNull(slLists.deletedAt)));
await db.transaction(async (tx) => {
const [existingInbox] = await tx
.select()
.from(slLists)
.where(and(eq(slLists.userId, userId), eq(slLists.isInbox, true), isNull(slLists.deletedAt)));
if (existingInbox && existingInbox.id !== entityId) {
// Reassign all tasks from the old inbox to the new one
await db.update(slTasks)
.set({ listId: entityId, updatedAt: now })
.where(and(eq(slTasks.listId, existingInbox.id), eq(slTasks.userId, userId)));
// Soft-delete the old inbox
await db.update(slLists)
.set({ deletedAt: now, updatedAt: now })
.where(eq(slLists.id, existingInbox.id));
}
if (existingInbox && existingInbox.id !== entityId) {
// Reassign all tasks (including subtasks) from the old inbox to the new one
await tx.update(slTasks)
.set({ listId: entityId, updatedAt: now })
.where(and(eq(slTasks.listId, existingInbox.id), eq(slTasks.userId, userId)));
// Soft-delete the old inbox
await tx.update(slLists)
.set({ deletedAt: now, updatedAt: now })
.where(eq(slLists.id, existingInbox.id));
}
await tx.insert(slLists).values({
id: entityId,
userId,
name: d.name as string || 'Untitled',
color: d.color as string | undefined,
icon: d.icon as string | undefined,
position: d.position as number | undefined,
isInbox: incomingIsInbox,
}).onConflictDoNothing();
});
} else {
await db.insert(slLists).values({
id: entityId,
userId,
name: d.name as string || 'Untitled',
color: d.color as string | undefined,
icon: d.icon as string | undefined,
position: d.position as number | undefined,
isInbox: incomingIsInbox,
}).onConflictDoNothing();
}
await db.insert(slLists).values({
id: entityId,
userId,
name: d.name as string || 'Untitled',
color: d.color as string | undefined,
icon: d.icon as string | undefined,
position: d.position as number | undefined,
isInbox: incomingIsInbox,
}).onConflictDoNothing();
} else if (action === 'update') {
await verifyOwnership(slLists, entityId, userId);
await db.update(slLists)

View file

@ -18,12 +18,10 @@ async function seed() {
.from(slLists)
.where(and(eq(slLists.userId, userId), eq(slLists.isInbox, true)));
// Use the same fixed inbox ID as the mobile app to avoid duplicates during sync
const INBOX_ID = '00000000-0000-0000-0000-000000000001';
if (existing.length === 0) {
// Let the DB generate a random UUID — the sync endpoint handles
// inbox deduplication when mobile pushes its fixed-ID inbox.
await db.insert(slLists).values({
id: INBOX_ID,
userId,
name: 'Inbox',
isInbox: true,