diff --git a/apps/client/src/menus/tree_context_menu.ts b/apps/client/src/menus/tree_context_menu.ts index 3f5aa9480..d7ff4b777 100644 --- a/apps/client/src/menus/tree_context_menu.ts +++ b/apps/client/src/menus/tree_context_menu.ts @@ -151,8 +151,17 @@ export default class TreeContextMenu implements SelectMenuItemEventListener { + const note = await froca.getNote(this.node.data.noteId); + if (!note) return; + attributes.toggleBooleanWithInheritance(note, "subtreeHidden"); + } + }, { title: t("tree-context-menu.sort-by"), command: "sortChildNotes", @@ -165,7 +174,7 @@ export default class TreeContextMenu implements SelectMenuItemEventListener[] }, { kind: "separator" }, diff --git a/apps/client/src/services/attributes.ts b/apps/client/src/services/attributes.ts index 4559c8f8b..359e7ab43 100644 --- a/apps/client/src/services/attributes.ts +++ b/apps/client/src/services/attributes.ts @@ -1,14 +1,15 @@ -import server from "./server.js"; -import froca from "./froca.js"; -import type FNote from "../entities/fnote.js"; -import type { AttributeRow } from "./load_results.js"; import { AttributeType } from "@triliumnext/commons"; +import type FNote from "../entities/fnote.js"; +import froca from "./froca.js"; +import type { AttributeRow } from "./load_results.js"; +import server from "./server.js"; + async function addLabel(noteId: string, name: string, value: string = "", isInheritable = false) { await server.put(`notes/${noteId}/attribute`, { type: "label", - name: name, - value: value, + name, + value, isInheritable }); } @@ -16,8 +17,8 @@ async function addLabel(noteId: string, name: string, value: string = "", isInhe export async function setLabel(noteId: string, name: string, value: string = "", isInheritable = false) { await server.put(`notes/${noteId}/set-attribute`, { type: "label", - name: name, - value: value, + name, + value, isInheritable }); } @@ -25,12 +26,33 @@ export async function setLabel(noteId: string, name: string, value: string = "", export async function setRelation(noteId: string, name: string, value: string = "", isInheritable = false) { await server.put(`notes/${noteId}/set-attribute`, { type: "relation", - name: name, - value: value, + name, + value, isInheritable }); } +/** + * Toggles a boolean label on the given note, taking inheritance into account. If the label is owned by the note, it + * will be removed. If the label is inherited from a parent note, it will be overridden to `false`. If the label does + * not exist, it will be added with an empty value. + * + * @param note the note on which to toggle the label. + * @param labelName the name of the label to toggle. + */ +export async function toggleBooleanWithInheritance(note: FNote, labelName: string) { + if (note.hasLabel(labelName)) { + // Can either be owned by us or inherited from parent. + if (note.hasOwnedLabel(labelName)) { + removeOwnedLabelByName(note, labelName); + } else { + setLabel(note.noteId, labelName, "false"); + } + } else { + addLabel(note.noteId, labelName); + } +} + async function removeAttributeById(noteId: string, attributeId: string) { await server.remove(`notes/${noteId}/attributes/${attributeId}`); } @@ -142,6 +164,7 @@ export default { setLabel, setRelation, setAttribute, + toggleBooleanWithInheritance, removeAttributeById, removeOwnedLabelByName, removeOwnedRelationByName, diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 54c10275b..25499ecbf 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1655,6 +1655,8 @@ "advanced": "Advanced", "expand-subtree": "Expand subtree", "collapse-subtree": "Collapse subtree", + "hide-subtree": "Hide subtree", + "show-subtree": "Show subtree", "sort-by": "Sort by...", "recent-changes-in-subtree": "Recent changes in subtree", "convert-to-attachment": "Convert to attachment",