handoff and collab modes support per task for @user @bucket and #note
This commit is contained in:
+186
-126
@@ -373,6 +373,7 @@ export interface TaskShareRef {
|
||||
contextId: string;
|
||||
key: string;
|
||||
kind: 'user' | 'bucket' | 'note';
|
||||
policy?: 'collaborative' | 'handoff';
|
||||
}
|
||||
|
||||
export interface NoteRef {
|
||||
@@ -545,11 +546,31 @@ const getNoteByRef = (ref: NoteRef) =>
|
||||
store.notes.find(note => note.id === ref.noteId) ||
|
||||
store.notes.find(note => note.key === ref.key);
|
||||
|
||||
const DEFAULT_SHARE_POLICY_BY_KIND: Record<TaskShareRef["kind"], "collaborative" | "handoff"> = {
|
||||
user: "collaborative",
|
||||
bucket: "handoff",
|
||||
note: "handoff"
|
||||
};
|
||||
|
||||
const NOTE_HANDOFF_POLICY_TAG = "__note_policy_handoff__";
|
||||
|
||||
export const getNoteSharePolicy = (note: Pick<Note, "tags"> | null | undefined): "collaborative" | "handoff" =>
|
||||
note?.tags?.includes(NOTE_HANDOFF_POLICY_TAG) ? "handoff" : "collaborative";
|
||||
|
||||
export const getTaskSharePolicy = (ref: Pick<TaskShareRef, "kind" | "key" | "policy" | "contextId">): "collaborative" | "handoff" => {
|
||||
if (ref.policy) return ref.policy;
|
||||
|
||||
if (ref.kind === "note") {
|
||||
const note = store.notes.find(existing => existing.id === ref.contextId || existing.key === ref.key);
|
||||
return getNoteSharePolicy(note);
|
||||
}
|
||||
|
||||
const context = getContextByRef(ref as TaskShareRef);
|
||||
if (context?.policy) return context.policy;
|
||||
|
||||
return DEFAULT_SHARE_POLICY_BY_KIND[ref.kind];
|
||||
};
|
||||
|
||||
const buildNoteContext = (note: Note): ShareContext => ({
|
||||
id: note.id,
|
||||
key: note.key,
|
||||
@@ -604,6 +625,82 @@ const dedupeShareRefs = (refs: TaskShareRef[]) => {
|
||||
});
|
||||
};
|
||||
|
||||
const buildTaskShareRef = (
|
||||
parsedTag: Extract<ReturnType<typeof parseTags>[number], { kind: "share" | "note" }>,
|
||||
existingRefs: TaskShareRef[] = [],
|
||||
shareModeOverrides: Record<string, "collaborative" | "handoff"> = {}
|
||||
): TaskShareRef => {
|
||||
if (parsedTag.kind === "note") {
|
||||
const note = store.notes.find(existing => existing.key === parsedTag.key);
|
||||
const nextRefBase = {
|
||||
contextId: note?.id || parsedTag.key,
|
||||
key: parsedTag.key,
|
||||
kind: "note" as const
|
||||
};
|
||||
const existing = existingRefs.find(ref => ref.kind === "note" && (ref.contextId === nextRefBase.contextId || ref.key === nextRefBase.key));
|
||||
return {
|
||||
...nextRefBase,
|
||||
policy: shareModeOverrides[parsedTag.raw.toLowerCase()] || existing?.policy || getTaskSharePolicy(nextRefBase)
|
||||
};
|
||||
}
|
||||
|
||||
const context = findContextForShareTag(parsedTag);
|
||||
const nextRefBase = {
|
||||
contextId: context?.id || parsedTag.key,
|
||||
key: parsedTag.key,
|
||||
kind: (context?.kind || "bucket") as "user" | "bucket"
|
||||
};
|
||||
const existing = existingRefs.find(ref =>
|
||||
ref.kind === nextRefBase.kind &&
|
||||
(ref.contextId === nextRefBase.contextId || ref.key === nextRefBase.key)
|
||||
);
|
||||
return {
|
||||
...nextRefBase,
|
||||
policy: shareModeOverrides[parsedTag.raw.toLowerCase()] || existing?.policy || getTaskSharePolicy(nextRefBase)
|
||||
};
|
||||
};
|
||||
|
||||
export const getTaskShareModeForTag = (task: Pick<Task, "shareRefs">, tag: string): "collaborative" | "handoff" | undefined => {
|
||||
const parsed = parseTags([tag])[0];
|
||||
if (!parsed || (parsed.kind !== "share" && parsed.kind !== "note")) return undefined;
|
||||
|
||||
const ref = task.shareRefs.find(existing =>
|
||||
existing.key === parsed.key && (
|
||||
(parsed.kind === "note" && existing.kind === "note") ||
|
||||
(parsed.kind === "share" && existing.kind !== "note")
|
||||
)
|
||||
);
|
||||
|
||||
return ref ? getTaskSharePolicy(ref) : undefined;
|
||||
};
|
||||
|
||||
export const deriveTaskRelationsFromTags = (
|
||||
tags: string[],
|
||||
existingShareRefs: TaskShareRef[] = [],
|
||||
shareModeOverrides: Record<string, "collaborative" | "handoff"> = {}
|
||||
) => {
|
||||
const parsedTags = parseTags(tags);
|
||||
const labelTags = parsedTags.filter(tag => tag.kind === "label").map(tag => tag.display);
|
||||
const shareRefs = parsedTags
|
||||
.filter((tag): tag is Extract<typeof tag, { kind: "share" | "note" }> => tag.kind === "share" || tag.kind === "note")
|
||||
.map(tag => buildTaskShareRef(tag, existingShareRefs, shareModeOverrides));
|
||||
const noteRefs = parsedTags
|
||||
.filter(tag => tag.kind === "note")
|
||||
.map(tag => {
|
||||
const note = store.notes.find(existing => existing.key === tag.key);
|
||||
return {
|
||||
noteId: note?.id || tag.key,
|
||||
key: tag.key
|
||||
} satisfies NoteRef;
|
||||
});
|
||||
|
||||
return {
|
||||
labelTags,
|
||||
shareRefs: dedupeShareRefs(shareRefs),
|
||||
noteRefs: dedupeNoteRefs(noteRefs)
|
||||
};
|
||||
};
|
||||
|
||||
const dedupeNoteRefs = (refs: NoteRef[]) => {
|
||||
const seen = new Set<string>();
|
||||
return refs.filter(ref => {
|
||||
@@ -790,6 +887,43 @@ export const isCurrentUserContextTag = (tag: string) => {
|
||||
return hiddenTags.has(normalizedTag);
|
||||
};
|
||||
|
||||
export const isTaskLockedNoteTag = (task: Pick<Task, "tags" | "shareRefs" | "noteRefs">, tag: string) => {
|
||||
const normalizedTag = tag.trim();
|
||||
if (!normalizedTag.startsWith("#")) return false;
|
||||
|
||||
const noteKey = normalizeNoteKey(normalizedTag);
|
||||
const matchingNoteRefs = task.noteRefs.filter(ref => ref.key === noteKey || ref.noteId === noteKey);
|
||||
if (matchingNoteRefs.length === 0) return false;
|
||||
|
||||
const nonNoteContexts = task.shareRefs.filter(ref => ref.kind !== "note");
|
||||
const uniqueNoteRefs = dedupeNoteRefs(task.noteRefs);
|
||||
return uniqueNoteRefs.length === matchingNoteRefs.length && nonNoteContexts.length === 0;
|
||||
};
|
||||
|
||||
export const getVisibleTaskTags = (task: Pick<Task, "tags" | "shareRefs" | "noteRefs">, context: TaskContext = currentTaskContext()) => {
|
||||
const tags = task.tags || [];
|
||||
|
||||
return tags.filter(tag => {
|
||||
if (tag.endsWith("_favorite__") || isCurrentUserContextTag(tag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isTaskLockedNoteTag(task, tag)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof context === "object" && "bucketId" in context) {
|
||||
const bucketContext = store.contexts.find(existing => existing.id === context.bucketId);
|
||||
const bucketTagName = buildShareTag(bucketContext?.displayName || context.name).toLowerCase();
|
||||
if (tag.toLowerCase() === bucketTagName) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
const getSubscribedBucketContexts = () =>
|
||||
store.subscribedBuckets
|
||||
.map(id => store.contexts.find(context => context.id === id))
|
||||
@@ -803,16 +937,15 @@ export const matchesFilter = (task: Task) => {
|
||||
const activeShareContexts = task.shareRefs
|
||||
.map(ref => getContextByRef(ref))
|
||||
.filter((context): context is ShareContext => !!context && !context.deletedAt);
|
||||
const collaborativeUserShare = activeShareContexts.some(context =>
|
||||
context.kind === "user" &&
|
||||
context.policy === "collaborative" &&
|
||||
context.targetUserId === currentUserId
|
||||
);
|
||||
const bucketRefs = activeShareContexts.filter(context => context.kind === "bucket");
|
||||
const hasHandoffBucket = rawBucketRefs.some(ref => {
|
||||
const collaborativeUserShare = task.shareRefs.some(ref => {
|
||||
if (ref.kind !== "user" || getTaskSharePolicy(ref) !== "collaborative") return false;
|
||||
const context = getContextByRef(ref);
|
||||
return !context || context.policy === "handoff";
|
||||
return context?.targetUserId === currentUserId;
|
||||
});
|
||||
const bucketRefs = activeShareContexts.filter(context => context.kind === "bucket");
|
||||
const hasHandoffContext = task.shareRefs.some(ref =>
|
||||
ref.kind !== "user" && getTaskSharePolicy(ref) === "handoff"
|
||||
);
|
||||
const inPinnedBucket = rawBucketRefs.some(ref =>
|
||||
store.subscribedBuckets.includes(ref.contextId) ||
|
||||
store.subscribedBuckets.some(bucketId => store.contexts.find(context => context.id === bucketId)?.key === ref.key)
|
||||
@@ -835,7 +968,7 @@ export const matchesFilter = (task: Task) => {
|
||||
|
||||
// 0. Context Filter
|
||||
if (isPersonalView) {
|
||||
if (hasHandoffBucket) return false;
|
||||
if (hasHandoffContext) return false;
|
||||
const isOwner = task.ownerId === currentUserId;
|
||||
if (!isOwner && !collaborativeUserShare && !inPersonalContext) return false;
|
||||
} else if (typeof ctx === 'object' && 'bucketId' in ctx) {
|
||||
@@ -940,7 +1073,7 @@ export const isTaskCollaborativelyShared = (task: Task) =>
|
||||
task.shareRefs.some(ref => {
|
||||
const currentUserId = pb.authStore.model?.id;
|
||||
const context = getContextByRef(ref);
|
||||
const policy = context?.policy || (ref.kind === "user" ? "collaborative" : "handoff");
|
||||
const policy = getTaskSharePolicy(ref);
|
||||
const targetUserId = context?.targetUserId || null;
|
||||
return ref.kind === "user" && policy === "collaborative" && !!targetUserId && targetUserId !== currentUserId;
|
||||
});
|
||||
@@ -1091,25 +1224,15 @@ const mapRecordToTask = (r: any): Task => {
|
||||
const legacyTags: string[] = r.tags || [];
|
||||
const parsedTags = parseTags(legacyTags).filter(tag => tag.raw !== "__template__");
|
||||
const shareRefs: TaskShareRef[] = Array.isArray(r.shareRefs)
|
||||
? r.shareRefs
|
||||
? r.shareRefs.map((ref: any) => ({
|
||||
contextId: ref.contextId,
|
||||
key: ref.key,
|
||||
kind: ref.kind,
|
||||
policy: ref.policy
|
||||
}))
|
||||
: parsedTags
|
||||
.filter(tag => tag.kind === "share" || tag.kind === "note")
|
||||
.map(tag => {
|
||||
if (tag.kind === "note") {
|
||||
const note = store.notes.find(existing => existing.key === tag.key);
|
||||
return {
|
||||
contextId: note?.id || tag.key,
|
||||
key: tag.key,
|
||||
kind: "note"
|
||||
};
|
||||
}
|
||||
const context = findContextForShareTag(tag);
|
||||
return {
|
||||
contextId: context?.id || tag.key,
|
||||
key: tag.key,
|
||||
kind: context?.kind || "bucket"
|
||||
};
|
||||
});
|
||||
.filter((tag): tag is Extract<(typeof parsedTags)[number], { kind: "share" | "note" }> => tag.kind === "share" || tag.kind === "note")
|
||||
.map(tag => buildTaskShareRef(tag));
|
||||
const noteRefs: NoteRef[] = Array.isArray(r.noteRefs)
|
||||
? r.noteRefs
|
||||
: parsedTags
|
||||
@@ -1124,7 +1247,8 @@ const mapRecordToTask = (r: any): Task => {
|
||||
const noteShareRefs: TaskShareRef[] = noteRefs.map(ref => ({
|
||||
contextId: ref.noteId || ref.key,
|
||||
key: ref.key,
|
||||
kind: "note"
|
||||
kind: "note",
|
||||
policy: shareRefs.find(existing => existing.kind === "note" && (existing.contextId === ref.noteId || existing.key === ref.key))?.policy
|
||||
}));
|
||||
const mergedShareRefs = dedupeShareRefs([...shareRefs, ...noteShareRefs]);
|
||||
const labelTags: string[] = Array.isArray(r.labelTags)
|
||||
@@ -1182,10 +1306,7 @@ const buildTaskMigrationPayload = (
|
||||
const ownerContext = task.ownerId ? ownerContextByUserId.get(task.ownerId) : undefined;
|
||||
let nextShareRefs = dedupeShareRefs(task.shareRefs);
|
||||
let nextNoteRefs = dedupeNoteRefs([...task.noteRefs, ...linkedNoteRefs]);
|
||||
const hasHandoffBucket = nextShareRefs.some(ref => {
|
||||
const context = getContextByRef(ref);
|
||||
return context?.kind === "bucket" && context.policy === "handoff";
|
||||
});
|
||||
const hasHandoffContext = nextShareRefs.some(ref => ref.kind !== "user" && getTaskSharePolicy(ref) === "handoff");
|
||||
|
||||
if (ownerContext) {
|
||||
const matchesOwnerContext = (ref: TaskShareRef) =>
|
||||
@@ -1195,14 +1316,15 @@ const buildTaskMigrationPayload = (
|
||||
normalizeContextKey(ownerContext.displayName) === ref.key
|
||||
);
|
||||
|
||||
if (hasHandoffBucket) {
|
||||
if (hasHandoffContext) {
|
||||
nextShareRefs = nextShareRefs.filter(ref => !matchesOwnerContext(ref));
|
||||
} else if (!nextShareRefs.some(matchesOwnerContext)) {
|
||||
nextShareRefs = [
|
||||
{
|
||||
contextId: ownerContext.id,
|
||||
key: ownerContext.key,
|
||||
kind: "user" as const
|
||||
kind: "user" as const,
|
||||
policy: getTaskSharePolicy({ contextId: ownerContext.id, key: ownerContext.key, kind: "user" })
|
||||
},
|
||||
...nextShareRefs
|
||||
];
|
||||
@@ -1495,15 +1617,11 @@ const shouldTaskBeVisible = (task: any, currentUserId: string): boolean => {
|
||||
|
||||
if (mappedTask.shareRefs.some(ref => ref.kind === "bucket")) return true;
|
||||
|
||||
const contexts = mappedTask.shareRefs
|
||||
.map(ref => getContextByRef(ref))
|
||||
.filter((context): context is ShareContext => !!context && !context.deletedAt);
|
||||
|
||||
return contexts.some(context =>
|
||||
context.kind === "user" &&
|
||||
context.policy === "collaborative" &&
|
||||
context.targetUserId === currentUserId
|
||||
);
|
||||
return mappedTask.shareRefs.some(ref => {
|
||||
if (ref.kind !== "user" || getTaskSharePolicy(ref) !== "collaborative") return false;
|
||||
const context = getContextByRef(ref);
|
||||
return !!context && !context.deletedAt && context.targetUserId === currentUserId;
|
||||
});
|
||||
};
|
||||
|
||||
export const subscribeToRealtime = async () => {
|
||||
@@ -2092,7 +2210,6 @@ export const initStore = async () => {
|
||||
.filter(context =>
|
||||
!context.deletedAt &&
|
||||
context.kind === "user" &&
|
||||
context.policy === "collaborative" &&
|
||||
context.targetUserId === currentUserId
|
||||
)
|
||||
.map(context => buildShareTag(context.displayName));
|
||||
@@ -2464,7 +2581,7 @@ export const savePersonalContextSupervisors = async (supervisorUserIds: string[]
|
||||
}
|
||||
};
|
||||
|
||||
export const addTask = async (title: string, options?: { dueDate?: Date | string, priority?: number, tags?: string[], content?: string, size?: number }) => {
|
||||
export const addTask = async (title: string, options?: { dueDate?: Date | string, priority?: number, tags?: string[], content?: string, size?: number, shareModeByTag?: Record<string, "collaborative" | "handoff"> }) => {
|
||||
if (!pb.authStore.isValid) return;
|
||||
|
||||
// Default to ~24h from now if no date provided
|
||||
@@ -2478,35 +2595,7 @@ export const addTask = async (title: string, options?: { dueDate?: Date | string
|
||||
const tags = withActiveContextTags(options?.tags ?? ["work"]);
|
||||
const content = options?.content ?? "";
|
||||
const size = options?.size ?? 3;
|
||||
const parsedTags = parseTags(tags);
|
||||
const labelTags = parsedTags.filter(tag => tag.kind === "label").map(tag => tag.display);
|
||||
const shareRefs = parsedTags
|
||||
.filter(tag => tag.kind === "share" || tag.kind === "note")
|
||||
.map(tag => {
|
||||
if (tag.kind === "note") {
|
||||
const note = store.notes.find(existing => existing.key === tag.key);
|
||||
return {
|
||||
contextId: note?.id || tag.key,
|
||||
key: tag.key,
|
||||
kind: "note"
|
||||
} satisfies TaskShareRef;
|
||||
}
|
||||
const context = findContextForShareTag(tag);
|
||||
return {
|
||||
contextId: context?.id || tag.key,
|
||||
key: tag.key,
|
||||
kind: context?.kind || "bucket"
|
||||
} satisfies TaskShareRef;
|
||||
});
|
||||
const noteRefs = parsedTags
|
||||
.filter(tag => tag.kind === "note")
|
||||
.map(tag => {
|
||||
const note = store.notes.find(existing => existing.key === tag.key);
|
||||
return {
|
||||
noteId: note?.id || tag.key,
|
||||
key: tag.key
|
||||
} satisfies NoteRef;
|
||||
});
|
||||
const { labelTags, shareRefs, noteRefs } = deriveTaskRelationsFromTags(tags, [], options?.shareModeByTag || {});
|
||||
|
||||
const tempId = "temp-" + Date.now();
|
||||
const initialTask: Task = {
|
||||
@@ -2596,13 +2685,24 @@ export const copyTask = async (id: string) => {
|
||||
const original = store.tasks.find(t => t.id === id);
|
||||
if (!original) return;
|
||||
|
||||
const shareModeByTag = Object.fromEntries(
|
||||
original.shareRefs.map(ref => {
|
||||
const context = getContextByRef(ref);
|
||||
const tag = ref.kind === "note"
|
||||
? buildNoteTag(context?.key || ref.key)
|
||||
: buildShareTag(context?.displayName || ref.key);
|
||||
return [tag.toLowerCase(), getTaskSharePolicy(ref)];
|
||||
})
|
||||
) as Record<string, "collaborative" | "handoff">;
|
||||
|
||||
// Create a new task based on the original
|
||||
await addTask(`${original.title} (Copy)`, {
|
||||
priority: original.priority,
|
||||
dueDate: original.dueDate,
|
||||
tags: [...(original.tags || [])],
|
||||
content: original.content,
|
||||
size: original.size
|
||||
size: original.size,
|
||||
shareModeByTag
|
||||
});
|
||||
|
||||
toast.success("Task duplicated");
|
||||
@@ -2624,7 +2724,7 @@ const checkForHandoffs = (task: Task, updates: Partial<Task>): Partial<Task> =>
|
||||
|
||||
for (const ref of refs) {
|
||||
const context = getContextByRef(ref);
|
||||
if (!context || context.policy !== "handoff") continue;
|
||||
if (!context || getTaskSharePolicy(ref) !== "handoff") continue;
|
||||
const nextShareRefs = stripSenderPersonalContext(refs);
|
||||
const labelTags = updates.labelTags || task.labelTags;
|
||||
const noteRefs = updates.noteRefs || task.noteRefs;
|
||||
@@ -2639,7 +2739,7 @@ const checkForHandoffs = (task: Task, updates: Partial<Task>): Partial<Task> =>
|
||||
};
|
||||
}
|
||||
|
||||
if (context.kind === "bucket") {
|
||||
if (context.kind === "bucket" || context.kind === "note") {
|
||||
return {
|
||||
...updates,
|
||||
shareRefs: nextShareRefs,
|
||||
@@ -2680,35 +2780,12 @@ export const updateTask = async (id: string, updates: Partial<Task>) => {
|
||||
let favoriteTagOverride: string | null | undefined;
|
||||
|
||||
if (finalUpdates.tags) {
|
||||
const parsedTags = parseTags(finalUpdates.tags);
|
||||
finalUpdates.labelTags = parsedTags.filter(tag => tag.kind === "label").map(tag => tag.display);
|
||||
finalUpdates.shareRefs = parsedTags
|
||||
.filter(tag => tag.kind === "share" || tag.kind === "note")
|
||||
.map(tag => {
|
||||
if (tag.kind === "note") {
|
||||
const note = store.notes.find(existing => existing.key === tag.key);
|
||||
return {
|
||||
contextId: note?.id || tag.key,
|
||||
key: tag.key,
|
||||
kind: "note"
|
||||
} satisfies TaskShareRef;
|
||||
}
|
||||
const context = findContextForShareTag(tag);
|
||||
return {
|
||||
contextId: context?.id || tag.key,
|
||||
key: tag.key,
|
||||
kind: context?.kind || "bucket"
|
||||
} satisfies TaskShareRef;
|
||||
});
|
||||
finalUpdates.noteRefs = parsedTags
|
||||
.filter(tag => tag.kind === "note")
|
||||
.map(tag => {
|
||||
const note = store.notes.find(existing => existing.key === tag.key);
|
||||
return {
|
||||
noteId: note?.id || tag.key,
|
||||
key: tag.key
|
||||
} satisfies NoteRef;
|
||||
});
|
||||
if (!finalUpdates.shareRefs || !finalUpdates.noteRefs || !finalUpdates.labelTags) {
|
||||
const derived = deriveTaskRelationsFromTags(finalUpdates.tags, currentTask.shareRefs);
|
||||
finalUpdates.labelTags = finalUpdates.labelTags || derived.labelTags;
|
||||
finalUpdates.shareRefs = finalUpdates.shareRefs || derived.shareRefs;
|
||||
finalUpdates.noteRefs = finalUpdates.noteRefs || derived.noteRefs;
|
||||
}
|
||||
favoriteTagOverride = finalUpdates.tags.find(tag => tag.endsWith("_favorite__")) || null;
|
||||
}
|
||||
|
||||
@@ -3068,7 +3145,6 @@ export const loadAllHistory = async () => {
|
||||
.filter(context =>
|
||||
!context.deletedAt &&
|
||||
context.kind === "user" &&
|
||||
context.policy === "collaborative" &&
|
||||
context.targetUserId === userId
|
||||
)
|
||||
.map(context => buildShareTag(context.displayName));
|
||||
@@ -3149,33 +3225,17 @@ export const loadTasksLinkedToNote = async (note: Pick<Note, "id" | "key" | "tit
|
||||
|
||||
const noteTag = buildNoteTag(note.key || note.title);
|
||||
const escapedNoteTag = noteTag.replace(/"/g, '\\"');
|
||||
const linkedTaskIds = [...new Set((note.tasks || []).filter(Boolean))];
|
||||
const idFilter = linkedTaskIds.length > 0
|
||||
? ` || (${linkedTaskIds.map(taskId => `id = "${taskId}"`).join(" || ")})`
|
||||
: "";
|
||||
|
||||
try {
|
||||
const idFetchResults = await Promise.allSettled(
|
||||
linkedTaskIds.map(taskId =>
|
||||
pb.collection(TASGRID_COLLECTION).getOne(taskId, {
|
||||
fields: getTaskListFields(),
|
||||
requestKey: null
|
||||
})
|
||||
)
|
||||
);
|
||||
const idRecords = idFetchResults
|
||||
.filter((result): result is PromiseFulfilledResult<any> => result.status === "fulfilled")
|
||||
.map(result => result.value);
|
||||
|
||||
const listRecords = await pb.collection(TASGRID_COLLECTION).getFullList({
|
||||
filter: `((tags ~ "${escapedNoteTag}")${idFilter}) && tags !~ "__template__"`,
|
||||
filter: `(tags ~ "${escapedNoteTag}") && tags !~ "__template__"`,
|
||||
sort: "-updated",
|
||||
fields: getTaskListFields(),
|
||||
requestKey: `note-linked-${note.id}`
|
||||
}).catch(() => []);
|
||||
|
||||
const recordMap = new Map<string, any>();
|
||||
[...idRecords, ...listRecords].forEach(record => {
|
||||
listRecords.forEach(record => {
|
||||
if (!record?.id) return;
|
||||
if (record.tags?.includes("__template__")) return;
|
||||
recordMap.set(record.id, record);
|
||||
|
||||
Reference in New Issue
Block a user