import server from "./server.js"; import froca from "./froca.js"; import type FNote from "../entities/fnote.js"; import type { AttributeRow } from "./load_results.js"; async function addLabel(noteId: string, name: string, value: string = "") { await server.put(`notes/${noteId}/attribute`, { type: "label", name: name, value: value }); } async function setLabel(noteId: string, name: string, value: string = "") { await server.put(`notes/${noteId}/set-attribute`, { type: "label", name: name, value: value }); } async function removeAttributeById(noteId: string, attributeId: string) { await server.remove(`notes/${noteId}/attributes/${attributeId}`); } /** * Removes a label identified by its name from the given note, if it exists. Note that the label must be owned, i.e. * it will not remove inherited attributes. * * @param note the note from which to remove the label. * @param labelName the name of the label to remove. * @returns `true` if an attribute was identified and removed, `false` otherwise. */ function removeOwnedLabelByName(note: FNote, labelName: string) { const label = note.getOwnedLabel(labelName); if (label) { removeAttributeById(note.noteId, label.attributeId); return true; } return false; } /** * Sets the attribute of the given note to the provided value if its truthy, or removes the attribute if the value is falsy. * For an attribute with an empty value, pass an empty string instead. * * @param note the note to set the attribute to. * @param type the type of attribute (label or relation). * @param name the name of the attribute to set. * @param value the value of the attribute to set. */ async function setAttribute(note: FNote, type: "label" | "relation", name: string, value: string | null | undefined) { if (value) { // Create or update the attribute. await server.put(`notes/${note.noteId}/set-attribute`, { type, name, value }); } else { // Remove the attribute if it exists on the server but we don't define a value for it. const attributeId = note.getAttribute(type, name)?.attributeId; if (attributeId) { await server.remove(`notes/${note.noteId}/attributes/${attributeId}`); } } } /** * @returns - returns true if this attribute has the potential to influence the note in the argument. * That can happen in multiple ways: * 1. attribute is owned by the note * 2. attribute is owned by the template of the note * 3. attribute is owned by some note's ancestor and is inheritable */ function isAffecting(attrRow: AttributeRow, affectedNote: FNote | null | undefined) { if (!affectedNote || !attrRow) { return false; } const attrNote = attrRow.noteId && froca.notes[attrRow.noteId]; if (!attrNote) { // the note (owner of the attribute) is not even loaded into the cache, so it should not affect anything else return false; } const owningNotes = [affectedNote, ...affectedNote.getNotesToInheritAttributesFrom()]; for (const owningNote of owningNotes) { if (owningNote.noteId === attrNote.noteId) { return true; } } // TODO: This doesn't seem right. //@ts-ignore if (this.isInheritable) { for (const owningNote of owningNotes) { if (owningNote.hasAncestor(attrNote.noteId, true)) { return true; } } } return false; } export default { addLabel, setLabel, setAttribute, removeAttributeById, removeOwnedLabelByName, isAffecting };