feat(tasks): linked tasks sync title and subtasks with their note in both directions
MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 2m32s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Failing after 2m32s
Subtasks of a task linked to a note checkbox are tied 1:1 to checkboxes of that note (subtasks[].blockId), wherever they are moved. ReconcileDocTaskLinks now carries tick, title and subtasks from the note to the task and adopts new checkboxes directly under the task; SyncTaskToDoc carries tick, title and subtasks to the note and adds checkboxes for new subtasks. Deleting is not mirrored. Deepening a task ties its subtasks right away; saves without links keep the server's links. The sync response reports taskLinks/subtaskLinks; the editor shows an "Unteraufgabe" badge. v0.36.0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f91926c208
commit
5a8922011d
20 files changed
+511
-53
No files matched your search
@@ -121,7 +121,12 @@ export function diffFlat(d: Doc, blocks: DocumentBlock[], newOpId: () => string)
|
||||
}
|
||||
|
||||
/** Converts the tree into the flat list used by the editor components. */
|
||||
export function toDocumentBlocks(d: Doc, docId: string, taskLinks?: Map<string, string>): DocumentBlock[] {
|
||||
export function toDocumentBlocks(
|
||||
d: Doc,
|
||||
docId: string,
|
||||
taskLinks?: Map<string, string>,
|
||||
subtaskLinks?: Map<string, string>
|
||||
): DocumentBlock[] {
|
||||
return flatten(d).map((b, i) => ({
|
||||
id: b.id,
|
||||
documentId: docId,
|
||||
@@ -133,6 +138,7 @@ export function toDocumentBlocks(d: Doc, docId: string, taskLinks?: Map<string,
|
||||
level: b.level,
|
||||
position: (i + 1) * 1000,
|
||||
taskId: taskLinks?.get(b.id),
|
||||
subtaskOf: subtaskLinks?.get(b.id),
|
||||
conflictOf: b.props.conflictOf,
|
||||
conflictAuthor: b.props.conflictAuthor,
|
||||
}));
|
||||
|
||||
@@ -133,6 +133,7 @@ export class DocSyncEngine {
|
||||
private lockedByOthers: LeaseInfo[] = [];
|
||||
private conflicts: Result[] = [];
|
||||
private taskLinks = new Map<string, string>();
|
||||
private subtaskLinks = new Map<string, string>();
|
||||
|
||||
private focused: string | null = null;
|
||||
private activity = new Map<string, number>(); // blockId -> last edit/focus time
|
||||
@@ -234,7 +235,7 @@ export class DocSyncEngine {
|
||||
errorMessage: this.errorMessage,
|
||||
role: this.role,
|
||||
title: this.local.title,
|
||||
blocks: toDocumentBlocks(this.local, this.docId, this.taskLinks),
|
||||
blocks: toDocumentBlocks(this.local, this.docId, this.taskLinks, this.subtaskLinks),
|
||||
pendingCount: this.pending.length + (this.inflight?.length || 0),
|
||||
shared: this.shared,
|
||||
sessions: this.sessions,
|
||||
@@ -252,8 +253,9 @@ export class DocSyncEngine {
|
||||
}
|
||||
|
||||
/** Task IDs of blocks linked to tasks (kept outside the synced tree). */
|
||||
setTaskLinks(links: Map<string, string>): void {
|
||||
setTaskLinks(links: Map<string, string>, subtaskLinks?: Map<string, string>): void {
|
||||
this.taskLinks = new Map(links);
|
||||
if (subtaskLinks) this.subtaskLinks = new Map(subtaskLinks);
|
||||
this.emit();
|
||||
}
|
||||
|
||||
@@ -562,6 +564,9 @@ export class DocSyncEngine {
|
||||
this.role = res.role;
|
||||
this.shared = res.shared;
|
||||
this.sessions = res.sessions;
|
||||
// The server reports the links after every sync, so badges follow subtasks it adopted or added
|
||||
if (res.taskLinks) this.taskLinks = new Map(Object.entries(res.taskLinks));
|
||||
if (res.subtaskLinks) this.subtaskLinks = new Map(Object.entries(res.subtaskLinks));
|
||||
this.presence = res.presence || [];
|
||||
this.lockedByOthers = res.leases?.others || [];
|
||||
|
||||
|
||||
@@ -51,6 +51,9 @@ export interface SyncResponse {
|
||||
sessions: number;
|
||||
presence: Presence[];
|
||||
leases: { granted: string[]; denied: string[]; others: LeaseInfo[] };
|
||||
// Checkboxes linked to a task / to a subtask (block → task), for the badges
|
||||
taskLinks?: Record<string, string>;
|
||||
subtaskLinks?: Record<string, string>;
|
||||
}
|
||||
|
||||
/** status 0 = network error or timeout; otherwise the HTTP status. */
|
||||
|
||||
@@ -138,6 +138,8 @@ export interface SubTask {
|
||||
title: string;
|
||||
isCompleted: boolean;
|
||||
createdAt?: string;
|
||||
// Note checkbox this subtask is tied to (its task is linked to a note); kept by the server
|
||||
blockId?: string;
|
||||
}
|
||||
|
||||
// Repeat rule of a task: completing it creates the next one (due date one interval later)
|
||||
@@ -315,6 +317,8 @@ export interface DocumentBlock {
|
||||
docId?: string;
|
||||
documentId?: string;
|
||||
taskId?: string;
|
||||
// Checkbox tied to a subtask of this task (the task itself is linked to another checkbox)
|
||||
subtaskOf?: string;
|
||||
type: BlockType;
|
||||
content: string;
|
||||
checked?: boolean;
|
||||
|
||||
Reference in new issue
Block a user