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); // Adjust toIndex after removing the element // When moving forward (right), the removal shifts indices left let adjustedToIndex = toIndex; if (fromIndex < toIndex) { adjustedToIndex = toIndex - 1; } newColumns.splice(adjustedToIndex, 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); } } } }