Files
Trilium/apps/client/src/widgets/collections/board/api.ts

203 lines
6.9 KiB
TypeScript
Raw Normal View History

import { BoardViewData } from ".";
import appContext from "../../../components/app_context";
import FNote from "../../../entities/fnote";
import attributes from "../../../services/attributes";
import branches from "../../../services/branches";
import { executeBulkActions } from "../../../services/bulk_action";
import froca from "../../../services/froca";
import { t } from "../../../services/i18n";
import note_create from "../../../services/note_create";
import server from "../../../services/server";
import { ColumnMap } from "./data";
export default class BoardApi {
constructor(
private byColumn: ColumnMap | undefined,
public columns: string[],
private parentNote: FNote,
private statusAttribute: string,
private viewConfig: BoardViewData,
private saveConfig: (newConfig: BoardViewData) => void,
private setBranchIdToEdit: (branchId: string | undefined) => void
) {};
async createNewItem(column: string, title: string) {
try {
// Get the parent note path
const parentNotePath = this.parentNote.noteId;
// Create a new note as a child of the parent note
const { note: newNote, branch: newBranch } = await note_create.createNote(parentNotePath, {
activate: false,
title
});
if (newNote && newBranch) {
await this.changeColumn(newNote.noteId, column);
}
} catch (error) {
console.error("Failed to create new item:", error);
}
}
async changeColumn(noteId: string, newColumn: string) {
await attributes.setLabel(noteId, this.statusAttribute, newColumn);
}
async addNewColumn(columnName: string) {
if (!columnName.trim()) {
return;
}
if (!this.viewConfig) {
this.viewConfig = {};
}
if (!this.viewConfig.columns) {
this.viewConfig.columns = [];
}
// Add the new column to persisted data if it doesn't exist
const existingColumn = this.viewConfig.columns.find(col => col.value === columnName);
if (!existingColumn) {
this.viewConfig.columns.push({ value: columnName });
this.saveConfig(this.viewConfig);
}
}
async removeColumn(column: string) {
// Remove the value from the notes.
const noteIds = this.byColumn?.get(column)?.map(item => item.note.noteId) || [];
await executeBulkActions(noteIds, [
{
name: "deleteLabel",
labelName: this.statusAttribute
}
]);
this.viewConfig.columns = (this.viewConfig.columns ?? []).filter(col => col.value !== column);
this.saveConfig(this.viewConfig);
}
async renameColumn(oldValue: string, newValue: string) {
const noteIds = this.byColumn?.get(oldValue)?.map(item => item.note.noteId) || [];
// Change the value in the notes.
await executeBulkActions(noteIds, [
{
name: "updateLabelValue",
labelName: this.statusAttribute,
labelValue: newValue
}
]);
// Rename the column in the persisted data.
for (const column of this.viewConfig.columns || []) {
if (column.value === oldValue) {
column.value = newValue;
}
}
this.saveConfig(this.viewConfig);
}
reorderColumn(fromIndex: number, toIndex: number) {
if (!this.columns || fromIndex === toIndex) return;
const newColumns = [...this.columns];
const [movedColumn] = newColumns.splice(fromIndex, 1);
newColumns.splice(toIndex, 0, movedColumn);
// Update view config with new column order
const newViewConfig = {
...this.viewConfig,
columns: newColumns.map(col => ({ value: col }))
};
this.saveConfig(newViewConfig);
return newColumns;
}
async insertRowAtPosition(
column: string,
relativeToBranchId: string,
direction: "before" | "after") {
const { note, branch } = await note_create.createNote(this.parentNote.noteId, {
activate: false,
targetBranchId: relativeToBranchId,
target: direction,
title: t("board_view.new-item")
});
if (!note || !branch) {
throw new Error("Failed to create note");
}
const { noteId } = note;
await this.changeColumn(noteId, column);
this.startEditing(branch.branchId);
return note;
}
openNote(noteId: string) {
appContext.triggerCommand("openInPopup", { noteIdOrPath: noteId });
}
startEditing(branchId: string) {
this.setBranchIdToEdit(branchId);
}
dismissEditingTitle() {
this.setBranchIdToEdit(undefined);
}
renameCard(noteId: string, newTitle: string) {
return server.put(`notes/${noteId}/title`, { title: newTitle.trim() });
}
removeFromBoard(noteId: string) {
const note = froca.getNoteFromCache(noteId);
if (!note) return;
return attributes.removeOwnedLabelByName(note, this.statusAttribute);
}
async moveWithinBoard(noteId: string, sourceBranchId: string, sourceIndex: number, targetIndex: number, sourceColumn: string, targetColumn: string) {
const targetItems = this.byColumn?.get(targetColumn) ?? [];
const note = froca.getNoteFromCache(noteId);
if (!note) return;
if (sourceColumn !== targetColumn) {
// Moving to a different column
await this.changeColumn(noteId, targetColumn);
// If there are items in the target column, reorder
if (targetItems.length > 0 && targetIndex < targetItems.length) {
const targetBranch = targetItems[targetIndex].branch;
await branches.moveBeforeBranch([ sourceBranchId ], targetBranch.branchId);
}
} else if (sourceIndex !== targetIndex) {
// Reordering within the same column
let targetBranchId: string | null = null;
if (targetIndex < targetItems.length) {
// Moving before an existing item
const adjustedIndex = sourceIndex < targetIndex ? targetIndex : targetIndex;
if (adjustedIndex < targetItems.length) {
targetBranchId = targetItems[adjustedIndex].branch.branchId;
if (targetBranchId) {
await branches.moveBeforeBranch([ sourceBranchId ], targetBranchId);
}
}
} else if (targetIndex > 0) {
// Moving to the end - place after the last item
const lastItem = targetItems[targetItems.length - 1];
await branches.moveAfterBranch([ sourceBranchId ], lastItem.branch.branchId);
}
}
}
}