Files
Endoleap/src/lib/board-directory.ts
T

51 lines
2.5 KiB
TypeScript

import { matchesRef, type Board } from "./workspace.ts";
type Request = <T>(path: string, options?: RequestInit) => Promise<T>;
/** Include children and finish pagination before building ownership paths. */
export async function loadBoardDirectory(request: Request, workspace: string, signal?: AbortSignal) {
const boards = new Map<string, Board>();
let offset = 0;
const seen = new Set<number>();
while (true) {
if (seen.has(offset)) throw new Error("invalid_pagination");
seen.add(offset);
const page = await request<{ items: Board[]; next_offset?: number | null }>(`workspaces/${encodeURIComponent(workspace)}/materials?kind=board&sort=modified&include_subgraphs=true&limit=100&offset=${offset}`, { signal });
for (const board of page.items) boards.set(board.id, board);
if (page.next_offset == null) return [...boards.values()];
if (!Number.isSafeInteger(page.next_offset) || page.next_offset <= offset) throw new Error("invalid_pagination");
offset = page.next_offset;
}
}
export type BoardBranch = { board: Board; children: BoardBranch[] };
export function boardDirectory(boards: Board[]): BoardBranch[] {
const entries = new Map(boards.map((board) => [board.id, { board, children: [] as BoardBranch[] }]));
const roots: BoardBranch[] = [];
for (const entry of entries.values()) {
const seen = new Set([entry.board.id]);
let parent = entry.board.parent_board_id;
let cyclic = false;
while (parent && entries.has(parent)) {
if (seen.has(parent)) { cyclic = true; break; }
seen.add(parent); parent = entries.get(parent)!.board.parent_board_id;
}
const owner = entry.board.parent_board_id && entries.get(entry.board.parent_board_id);
if (owner && !cyclic) owner.children.push(entry); else roots.push(entry);
}
return roots;
}
export function activeBoardAncestors(boards: Board[], ref?: string) {
const result = new Set<string>();
let item = boards.find((board) => matchesRef(board, ref));
while (item && !result.has(item.id)) { result.add(item.id); item = boards.find((board) => board.id === item!.parent_board_id); }
return result;
}
export function filterBoardDirectory(branches: BoardBranch[], query: string): BoardBranch[] {
const term = query.trim().toLocaleLowerCase();
if (!term) return branches;
return branches.flatMap((branch) => {
if (branch.board.title.toLocaleLowerCase().includes(term)) return [branch];
const children = filterBoardDirectory(branch.children, term);
return children.length ? [{ ...branch, children }] : [];
});
}