note-linked tasks are now efficient
This commit is contained in:
+84
-7
@@ -359,7 +359,7 @@ export interface ShareContext {
|
||||
id: string;
|
||||
key: string;
|
||||
displayName: string;
|
||||
kind: 'user' | 'bucket';
|
||||
kind: 'user' | 'bucket' | 'note';
|
||||
policy: 'collaborative' | 'handoff';
|
||||
targetUserId?: string | null;
|
||||
supervisorUserIds?: string[];
|
||||
@@ -372,7 +372,7 @@ export interface ShareContext {
|
||||
export interface TaskShareRef {
|
||||
contextId: string;
|
||||
key: string;
|
||||
kind: 'user' | 'bucket';
|
||||
kind: 'user' | 'bucket' | 'note';
|
||||
}
|
||||
|
||||
export interface NoteRef {
|
||||
@@ -529,6 +529,12 @@ const findContextForShareTag = (tag: { key: string; raw: string }) =>
|
||||
null;
|
||||
|
||||
const getContextByRef = (ref: TaskShareRef) =>
|
||||
(ref.kind === "note"
|
||||
? (() => {
|
||||
const note = store.notes.find(existing => existing.id === ref.contextId || existing.key === ref.key);
|
||||
return note ? buildNoteContext(note) : null;
|
||||
})()
|
||||
: null) ||
|
||||
store.contexts.find(context => !context.deletedAt && context.id === ref.contextId) ||
|
||||
store.contexts.find(context => !context.deletedAt && context.key === ref.key && context.kind === ref.kind) ||
|
||||
store.contexts.find(context => !context.deletedAt && context.kind === ref.kind && normalizeContextKey(context.displayName) === ref.key) ||
|
||||
@@ -539,6 +545,21 @@ const getNoteByRef = (ref: NoteRef) =>
|
||||
store.notes.find(note => note.id === ref.noteId) ||
|
||||
store.notes.find(note => note.key === ref.key);
|
||||
|
||||
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";
|
||||
|
||||
const buildNoteContext = (note: Note): ShareContext => ({
|
||||
id: note.id,
|
||||
key: note.key,
|
||||
displayName: note.key || note.title,
|
||||
kind: "note",
|
||||
policy: getNoteSharePolicy(note),
|
||||
targetUserId: note.user,
|
||||
deletedAt: note.deletedAt ?? null
|
||||
});
|
||||
|
||||
const buildTaskTags = (
|
||||
labelTags: string[],
|
||||
shareRefs: TaskShareRef[],
|
||||
@@ -549,6 +570,9 @@ const buildTaskTags = (
|
||||
...labelTags,
|
||||
...shareRefs.map(ref => {
|
||||
const context = getContextByRef(ref);
|
||||
if (ref.kind === "note") {
|
||||
return buildNoteTag(context?.key || ref.key);
|
||||
}
|
||||
if (context?.displayName) {
|
||||
return buildShareTag(context.displayName);
|
||||
}
|
||||
@@ -1069,8 +1093,16 @@ const mapRecordToTask = (r: any): Task => {
|
||||
const shareRefs: TaskShareRef[] = Array.isArray(r.shareRefs)
|
||||
? r.shareRefs
|
||||
: parsedTags
|
||||
.filter(tag => tag.kind === "share")
|
||||
.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,
|
||||
@@ -1089,11 +1121,17 @@ const mapRecordToTask = (r: any): Task => {
|
||||
key: tag.key
|
||||
};
|
||||
});
|
||||
const noteShareRefs: TaskShareRef[] = noteRefs.map(ref => ({
|
||||
contextId: ref.noteId || ref.key,
|
||||
key: ref.key,
|
||||
kind: "note"
|
||||
}));
|
||||
const mergedShareRefs = dedupeShareRefs([...shareRefs, ...noteShareRefs]);
|
||||
const labelTags: string[] = Array.isArray(r.labelTags)
|
||||
? r.labelTags
|
||||
: parsedTags.filter(tag => tag.kind === "label").map(tag => tag.display);
|
||||
const favoriteTag = legacyTags.find((tag: string) => tag.endsWith("_favorite__"));
|
||||
const tags = buildTaskTags(labelTags, shareRefs, noteRefs, favoriteTag);
|
||||
const tags = buildTaskTags(labelTags, mergedShareRefs, noteRefs, favoriteTag);
|
||||
return {
|
||||
id: r.id,
|
||||
title: r.title,
|
||||
@@ -1104,7 +1142,7 @@ const mapRecordToTask = (r: any): Task => {
|
||||
completed: r.status === 10 || r.completed,
|
||||
tags: tags,
|
||||
labelTags,
|
||||
shareRefs,
|
||||
shareRefs: mergedShareRefs,
|
||||
noteRefs,
|
||||
ownerId: unwrapRelationId(r.user),
|
||||
createdBy: unwrapRelationId(r.createdBy) || unwrapRelationId(r.user),
|
||||
@@ -2368,6 +2406,29 @@ export const setContextPolicy = async (contextId: string, policy: "collaborative
|
||||
}
|
||||
};
|
||||
|
||||
export const setNoteContextPolicy = async (noteId: string, policy: "collaborative" | "handoff") => {
|
||||
if (!pb.authStore.isValid || !noteId) return false;
|
||||
|
||||
const note = store.notes.find(existing => existing.id === noteId);
|
||||
if (!note) return false;
|
||||
|
||||
const baseTags = (note.tags || []).filter(tag => tag !== NOTE_HANDOFF_POLICY_TAG);
|
||||
const nextTags = policy === "handoff" ? [...baseTags, NOTE_HANDOFF_POLICY_TAG] : baseTags;
|
||||
const previousTags = note.tags || [];
|
||||
|
||||
setStore("notes", n => n.id === noteId, "tags", nextTags);
|
||||
|
||||
try {
|
||||
await pb.collection(NOTES_COLLECTION).update(noteId, { tags: nextTags }, { requestKey: null });
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error("Failed to update note context policy:", err);
|
||||
setStore("notes", n => n.id === noteId, "tags", previousTags);
|
||||
toast.error("Failed to update note sharing mode");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
export const savePersonalContextSupervisors = async (supervisorUserIds: string[]) => {
|
||||
const userId = pb.authStore.model?.id;
|
||||
if (!userId) return;
|
||||
@@ -2420,8 +2481,16 @@ export const addTask = async (title: string, options?: { dueDate?: Date | string
|
||||
const parsedTags = parseTags(tags);
|
||||
const labelTags = parsedTags.filter(tag => tag.kind === "label").map(tag => tag.display);
|
||||
const shareRefs = parsedTags
|
||||
.filter(tag => tag.kind === "share")
|
||||
.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,
|
||||
@@ -2614,8 +2683,16 @@ export const updateTask = async (id: string, updates: Partial<Task>) => {
|
||||
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")
|
||||
.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,
|
||||
|
||||
Reference in New Issue
Block a user