@bscotch/blork-shared
Advanced tools
Comparing version 0.13.0 to 0.14.0
@@ -94,3 +94,5 @@ import { BaseError } from './errors.js'; | ||
force?: boolean; | ||
permanently?: boolean; | ||
}): Promise<void>; | ||
recoverTasks(taskId: string, recursive?: boolean): Promise<void>; | ||
addTaskComment(taskId: string, comment: Partial<CommentData>): Promise<TaskData>; | ||
@@ -97,0 +99,0 @@ updateTaskComment(taskId: string, commentId: string, patch: CommentPatch): Promise<TaskData>; |
@@ -5,3 +5,3 @@ import { z } from 'zod'; | ||
import { stringSchema } from './types.lib.js'; | ||
import { taskPatchBackloggedSchema, taskPatchBlockSchema, taskPatchDescriptionSchema, taskPatchDoneSchema, taskPatchHoursSchema, taskPatchLinkSchema, taskPatchLockedSchema, taskPatchMoveSchema, taskPatchNonownerSchema, taskPatchOwnerSchema, taskPatchSchema, taskPatchSnoozeSchema, taskPatchStarredSchema, taskPatchTaggedSchema, taskPatchTitleSchema, taskPatchUnblockSchema, taskPatchUnlinkSchema, taskPostSchema, taskSchema, } from './types.tasks.js'; | ||
import { taskPatchBackloggedSchema, taskPatchBlockSchema, taskPatchDescriptionSchema, taskPatchDoneSchema, taskPatchHoursSchema, taskPatchLinkSchema, taskPatchLockedSchema, taskPatchMoveSchema, taskPatchNonownerSchema, taskPatchOwnerSchema, taskPatchRecoverSchema, taskPatchSchema, taskPatchSnoozeSchema, taskPatchStarredSchema, taskPatchTaggedSchema, taskPatchTitleSchema, taskPatchUnblockSchema, taskPatchUnlinkSchema, taskPostSchema, taskSchema, } from './types.tasks.js'; | ||
import { taskTemplatePatchSchema, taskTemplatePostSchema, taskTemplateSchema, } from './types.templates.js'; | ||
@@ -261,5 +261,15 @@ import { safeUserOrGroupDataSchema, userAccessNewTokenSchema, userDataSchema, userGroupDataSchema, userGroupPatchMembersSchema, userLoginPostSchema, userOrGroupDataSchema, userPatchPinsSchema, userPostSchema, } from './types.users.js'; | ||
assertStatus: 204, | ||
query: options?.force ? { force: true } : undefined, | ||
query: { force: options?.force, permanently: options?.permanently }, | ||
}); | ||
} | ||
async recoverTasks(taskId, recursive) { | ||
await this.request('PATCH', `/api/tasks/${encodeURIComponent(taskId)}`, { | ||
body: taskPatchRecoverSchema.parse({ | ||
kind: 'recover', | ||
taskId, | ||
recursive, | ||
}), | ||
assertStatus: 200, | ||
}); | ||
} | ||
//#endregion TASKS | ||
@@ -266,0 +276,0 @@ //#region COMMENTS |
@@ -12,3 +12,8 @@ import { BlorkQuery } from './search.types.js'; | ||
} | ||
export declare function searchTasks(query: string, taskCache: TaskCache, users: UserOrGroupData[], currentUserId: string): SearchTasksResults; | ||
export interface SearchContext { | ||
users: UserOrGroupData[]; | ||
currentUserId: string; | ||
allowDeleted?: boolean; | ||
} | ||
export declare function searchTasks(query: string, taskCache: TaskCache, context: SearchContext): SearchTasksResults; | ||
//# sourceMappingURL=search.d.ts.map |
@@ -13,3 +13,3 @@ import uFuzzy from '@leeoniya/ufuzzy'; | ||
}); | ||
export function searchTasks(query, taskCache, users, currentUserId) { | ||
export function searchTasks(query, taskCache, context) { | ||
const parsedQuery = parseQuery(query); | ||
@@ -25,2 +25,4 @@ const results = new Set(); | ||
const matches = (task, filters) => { | ||
if (task.deletedAt && !context.allowDeleted) | ||
return false; | ||
if (!_matchCache.has(task)) { | ||
@@ -31,3 +33,3 @@ _matchCache.set(task, new Map()); | ||
if (!filterMatches.has(filters)) { | ||
filterMatches.set(filters, matchesFilters(taskCache, task, filters, users, currentUserId)); | ||
filterMatches.set(filters, matchesFilters(taskCache, task, filters, context)); | ||
} | ||
@@ -69,7 +71,7 @@ return filterMatches.get(filters); | ||
} | ||
function matchesFilters(taskCache, task, filters, users, currentUserId) { | ||
function matchesFilters(taskCache, task, filters, context) { | ||
// For each kind of filter (if provided) all terms must match. | ||
// Start with the simple checks and go from there! | ||
if (!matchesIdFilters(task, filters) || | ||
!matchesOwnerFilters(task, filters, users, currentUserId) || | ||
!matchesOwnerFilters(task, filters, context.users, context.currentUserId) || | ||
!matchesHasFilters(taskCache, task, filters) || | ||
@@ -246,2 +248,5 @@ !matchesHoursFilters(taskCache, task, filters) || | ||
} | ||
else if (term === 'deleted') { | ||
return !!task.deletedAt; | ||
} | ||
return false; | ||
@@ -248,0 +253,0 @@ } |
@@ -38,2 +38,4 @@ import { CommentData, TaskData, UserData, UserDataSafe } from './index.js'; | ||
created: boolean; | ||
/** Whether the task has just been flagged for deletion */ | ||
deleted: boolean; | ||
/** Whether the updated task got a changed title */ | ||
@@ -66,2 +68,7 @@ retitled: boolean; | ||
* the operation were allowed (so it can be used for force-deletions). | ||
* | ||
* Tasks are "deleted" by *flagging them* as deleted, with the | ||
* exception of mirrored tasks that are mirrored outside of the | ||
* deleted subtree. Those mirrors are removed from the deleted | ||
* subtree, but the original task is not flagged as deleted. | ||
*/ | ||
@@ -156,2 +163,7 @@ export declare function planTaskDeletion(cache: TaskCache, taskIdToDelete: string): TaskDeletionPlan; | ||
export declare function taskAsMarkdown(taskId: string, tasks: TaskCache, users: UserCache, indent?: number): string; | ||
/** | ||
* Given a task, return `true` if it is "empty", meaning that | ||
* it has no title, description, comments, attachments, children, etc. | ||
*/ | ||
export declare function isEmptyTask(task: TaskData): boolean; | ||
//# sourceMappingURL=tasks.d.ts.map |
@@ -73,2 +73,3 @@ import { assert } from './errors.js'; | ||
created: !oldTask, | ||
deleted: !!(!oldTask?.deletedAt && updatedTask.deletedAt), | ||
retitled: oldTask ? oldTask.title !== updatedTask.title : false, | ||
@@ -148,2 +149,7 @@ done: !!(oldTask ? !oldTask.done && updatedTask.done : false), | ||
* the operation were allowed (so it can be used for force-deletions). | ||
* | ||
* Tasks are "deleted" by *flagging them* as deleted, with the | ||
* exception of mirrored tasks that are mirrored outside of the | ||
* deleted subtree. Those mirrors are removed from the deleted | ||
* subtree, but the original task is not flagged as deleted. | ||
*/ | ||
@@ -160,3 +166,2 @@ export function planTaskDeletion(cache, taskIdToDelete) { | ||
const updateParents = new Map(); | ||
const updateChildren = new Map(); | ||
const tasksToDelete = new Set(); | ||
@@ -171,27 +176,23 @@ tasksToDelete.add(taskIdToDelete); | ||
const task = cache.get(taskId); | ||
assert(task, `Task ${taskId} not found`); | ||
const parent = cache.get(fromParentId); | ||
assert(parent, `Parent task ${fromParentId} not found`); | ||
// Update the parent's children to exclude this one. Use the | ||
// copy so that successive rounds update it (and so we don't mutate the cache!) | ||
const parentChildren = (updateChildren.get(fromParentId) || [...parent.children]).filter((id) => id !== taskId); | ||
updateChildren.set(fromParentId, parentChildren); | ||
// Same deal with the parents of the target task | ||
const taskParents = (updateParents.get(taskId) || [...task.parents]).filter((id) => id !== fromParentId); | ||
const taskParents = (updateParents.get(taskId) || [...(task?.parents || [])]).filter((id) => id !== fromParentId); | ||
if (taskParents.length) { | ||
updateParents.set(taskId, taskParents); | ||
// Since this task still has other parents, we do NOT | ||
// Since this task still has other parents (it has a mirror), we do NOT | ||
// want to continue recursing. | ||
} | ||
else { | ||
updateParents.delete(taskId); | ||
// Then we want to fully delete this task, which means | ||
// also continuing to evaluate its children. | ||
tasksToDelete.add(taskId); | ||
if (taskId !== taskIdToDelete && !task.done) { | ||
if (taskId !== taskIdToDelete && !task?.done) { | ||
incompleteBlockers.add(taskId); | ||
} | ||
else if (task.locked) { | ||
else if (task?.locked) { | ||
lockedBlockers.add(taskId); | ||
} | ||
for (const childId of task.children) { | ||
for (const childId of task?.children || []) { | ||
evaluateForDeletion(childId, taskId); | ||
@@ -206,6 +207,16 @@ } | ||
} | ||
for (const toDelete of tasksToDelete) { | ||
// No need to update parents or children of tasks that are being deleted | ||
updateParents.delete(toDelete); | ||
updateChildren.delete(toDelete); | ||
// For the parents we're updating (for mirrored tasks), | ||
// for downstream convenience we want to go ahead and compute | ||
// the implied child updates. | ||
const updateChildren = new Map(); | ||
for (const [taskId] of updateParents) { | ||
// This task must be deleted from the children of all deleted tasks | ||
for (const toDelete of tasksToDelete) { | ||
const children = updateChildren.get(toDelete) || [ | ||
...(cache.get(toDelete)?.children || []), | ||
]; | ||
if (children.includes(taskId)) { | ||
updateChildren.set(toDelete, children.filter((id) => id !== taskId)); | ||
} | ||
} | ||
} | ||
@@ -497,3 +508,6 @@ return { | ||
if (isMine && task.done) { | ||
userTasks.doneTaskCount++; | ||
if (!task.deletedAt) { | ||
// We only want to display counts for tasks that are not deleted | ||
userTasks.doneTaskCount++; | ||
} | ||
// If at least one parent is incomplete OR | ||
@@ -603,2 +617,15 @@ // none of the parents are "mine", then this is a | ||
} | ||
/** | ||
* Given a task, return `true` if it is "empty", meaning that | ||
* it has no title, description, comments, attachments, children, etc. | ||
*/ | ||
export function isEmptyTask(task) { | ||
if (task.title || | ||
task.description || | ||
task.comments?.length || | ||
task.files?.length) { | ||
return false; | ||
} | ||
return true; | ||
} | ||
//# sourceMappingURL=tasks.js.map |
@@ -20,2 +20,3 @@ import { z } from 'zod'; | ||
updatedAt: z.ZodDefault<z.ZodEffects<z.ZodDate, Date, unknown>>; | ||
deletedAt: z.ZodNullable<z.ZodOptional<z.ZodEffects<z.ZodDate, Date, unknown>>>; | ||
creator: z.ZodString; | ||
@@ -97,2 +98,3 @@ locked: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>; | ||
description?: string | null | undefined; | ||
deletedAt?: Date | null | undefined; | ||
locked?: boolean | null | undefined; | ||
@@ -134,2 +136,3 @@ backlogged?: boolean | null | undefined; | ||
description?: string | null | undefined; | ||
deletedAt?: unknown; | ||
locked?: boolean | null | undefined; | ||
@@ -173,2 +176,3 @@ backlogged?: boolean | null | undefined; | ||
description?: string | null | undefined; | ||
deletedAt?: Date | null | undefined; | ||
locked?: boolean | null | undefined; | ||
@@ -300,2 +304,16 @@ backlogged?: boolean | null | undefined; | ||
}>; | ||
export type TaskPatchRecover = z.infer<typeof taskPatchRecoverSchema>; | ||
export declare const taskPatchRecoverSchema: z.ZodObject<{ | ||
kind: z.ZodLiteral<"recover">; | ||
taskId: z.ZodString; | ||
recursive: z.ZodOptional<z.ZodBoolean>; | ||
}, "strip", z.ZodTypeAny, { | ||
taskId: string; | ||
kind: "recover"; | ||
recursive?: boolean | undefined; | ||
}, { | ||
taskId: string; | ||
kind: "recover"; | ||
recursive?: boolean | undefined; | ||
}>; | ||
export type TaskPatchDone = z.infer<typeof taskPatchDoneSchema>; | ||
@@ -468,2 +486,14 @@ export declare const taskPatchDoneSchema: z.ZodObject<{ | ||
}>, z.ZodObject<{ | ||
kind: z.ZodLiteral<"recover">; | ||
taskId: z.ZodString; | ||
recursive: z.ZodOptional<z.ZodBoolean>; | ||
}, "strip", z.ZodTypeAny, { | ||
taskId: string; | ||
kind: "recover"; | ||
recursive?: boolean | undefined; | ||
}, { | ||
taskId: string; | ||
kind: "recover"; | ||
recursive?: boolean | undefined; | ||
}>, z.ZodObject<{ | ||
kind: z.ZodLiteral<"set-done">; | ||
@@ -617,2 +647,3 @@ done: z.ZodBoolean; | ||
updatedAt: z.ZodDefault<z.ZodEffects<z.ZodDate, Date, unknown>>; | ||
deletedAt: z.ZodNullable<z.ZodOptional<z.ZodEffects<z.ZodDate, Date, unknown>>>; | ||
creator: z.ZodString; | ||
@@ -694,2 +725,3 @@ locked: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>; | ||
description?: string | null | undefined; | ||
deletedAt?: Date | null | undefined; | ||
locked?: boolean | null | undefined; | ||
@@ -731,2 +763,3 @@ backlogged?: boolean | null | undefined; | ||
description?: string | null | undefined; | ||
deletedAt?: unknown; | ||
locked?: boolean | null | undefined; | ||
@@ -770,2 +803,3 @@ backlogged?: boolean | null | undefined; | ||
description?: string | null | undefined; | ||
deletedAt?: Date | null | undefined; | ||
locked?: boolean | null | undefined; | ||
@@ -813,2 +847,3 @@ backlogged?: boolean | null | undefined; | ||
description?: string | null | undefined; | ||
deletedAt?: Date | null | undefined; | ||
locked?: boolean | null | undefined; | ||
@@ -858,2 +893,3 @@ backlogged?: boolean | null | undefined; | ||
updatedAt: z.ZodDefault<z.ZodEffects<z.ZodDate, Date, unknown>>; | ||
deletedAt: z.ZodNullable<z.ZodOptional<z.ZodEffects<z.ZodDate, Date, unknown>>>; | ||
creator: z.ZodString; | ||
@@ -935,2 +971,3 @@ locked: z.ZodNullable<z.ZodOptional<z.ZodBoolean>>; | ||
description?: string | null | undefined; | ||
deletedAt?: Date | null | undefined; | ||
locked?: boolean | null | undefined; | ||
@@ -972,2 +1009,3 @@ backlogged?: boolean | null | undefined; | ||
description?: string | null | undefined; | ||
deletedAt?: unknown; | ||
locked?: boolean | null | undefined; | ||
@@ -1011,2 +1049,3 @@ backlogged?: boolean | null | undefined; | ||
description?: string | null | undefined; | ||
deletedAt?: Date | null | undefined; | ||
locked?: boolean | null | undefined; | ||
@@ -1054,2 +1093,3 @@ backlogged?: boolean | null | undefined; | ||
description?: string | null | undefined; | ||
deletedAt?: Date | null | undefined; | ||
locked?: boolean | null | undefined; | ||
@@ -1056,0 +1096,0 @@ backlogged?: boolean | null | undefined; |
@@ -38,2 +38,3 @@ import { z } from 'zod'; | ||
updatedAt: dateSchema.default(() => new Date()), | ||
deletedAt: dateSchema.optional().nullable(), | ||
creator: stringSchema, | ||
@@ -120,2 +121,7 @@ locked: z.boolean().optional().nullable(), | ||
}); | ||
export const taskPatchRecoverSchema = z.object({ | ||
kind: z.literal('recover'), | ||
taskId: stringSchema, | ||
recursive: z.boolean().optional(), | ||
}); | ||
export const taskPatchDoneSchema = z.object({ | ||
@@ -178,2 +184,3 @@ kind: z.literal('set-done'), | ||
taskPatchMoveSchema, | ||
taskPatchRecoverSchema, | ||
taskPatchDoneSchema, | ||
@@ -180,0 +187,0 @@ taskPatchLockedSchema, |
@@ -9,2 +9,3 @@ import { z } from 'zod'; | ||
skipSnoozeConfirmation: z.ZodOptional<z.ZodBoolean>; | ||
recoveryMode: z.ZodOptional<z.ZodBoolean>; | ||
}, "strip", z.ZodTypeAny, { | ||
@@ -14,2 +15,3 @@ useCtrlToOpenCardLinks?: boolean | undefined; | ||
skipSnoozeConfirmation?: boolean | undefined; | ||
recoveryMode?: boolean | undefined; | ||
}, { | ||
@@ -19,2 +21,3 @@ useCtrlToOpenCardLinks?: boolean | undefined; | ||
skipSnoozeConfirmation?: boolean | undefined; | ||
recoveryMode?: boolean | undefined; | ||
}>; | ||
@@ -21,0 +24,0 @@ export type UserGroupData = z.infer<typeof userGroupDataSchema>; |
@@ -9,2 +9,3 @@ import { z } from 'zod'; | ||
skipSnoozeConfirmation: z.boolean().optional(), | ||
recoveryMode: z.boolean().optional(), | ||
}); | ||
@@ -11,0 +12,0 @@ const userDataBaseSchema = z.object({ |
{ | ||
"name": "@bscotch/blork-shared", | ||
"version": "0.13.0", | ||
"version": "0.14.0", | ||
"description": "Client and shared utilities for Blork projects.", | ||
@@ -5,0 +5,0 @@ "keywords": [], |
@@ -87,5 +87,7 @@ # Blork! Shared Utilities and Types | ||
taskCache, | ||
users, | ||
'my-user-id', | ||
{ | ||
users, | ||
currentUserId: 'my-user-id', | ||
}, | ||
); | ||
``` |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
457367
7381
93