From 9d7e2855d39dccf1d0d942850d13f9500108ceb5 Mon Sep 17 00:00:00 2001 From: Elian Doran Date: Sun, 14 Dec 2025 19:36:53 +0200 Subject: [PATCH] feat(layout): edited notes underneath title details --- .../src/translations/en/translation.json | 3 +- .../client/src/widgets/layout/InlineTitle.css | 18 ++++++++ .../client/src/widgets/layout/InlineTitle.tsx | 42 +++++++++++++++++- apps/client/src/widgets/react/Badge.tsx | 2 +- .../src/widgets/ribbon/EditedNotesTab.tsx | 44 +++++++++++-------- .../src/widgets/ribbon/RibbonDefinition.ts | 2 +- packages/commons/src/lib/attribute_names.ts | 1 + 7 files changed, 89 insertions(+), 23 deletions(-) diff --git a/apps/client/src/translations/en/translation.json b/apps/client/src/translations/en/translation.json index 7b1938f9d..478127bb9 100644 --- a/apps/client/src/translations/en/translation.json +++ b/apps/client/src/translations/en/translation.json @@ -1758,7 +1758,8 @@ "note_type_switcher_label": "Switch from {{type}} to:", "note_type_switcher_others": "More note types", "note_type_switcher_templates": "Templates", - "note_type_switcher_collection": "Collections" + "note_type_switcher_collection": "Collections", + "edited_notes": "Edited notes" }, "search_result": { "no_notes_found": "No notes have been found for given search parameters.", diff --git a/apps/client/src/widgets/layout/InlineTitle.css b/apps/client/src/widgets/layout/InlineTitle.css index 44a9dfc09..8b27f8e85 100644 --- a/apps/client/src/widgets/layout/InlineTitle.css +++ b/apps/client/src/widgets/layout/InlineTitle.css @@ -89,3 +89,21 @@ body.prefers-centered-content .inline-title { flex-shrink: 0; } } + +.edited-notes { + padding-top: 1em; + display: flex; + flex-wrap: wrap; + gap: 0.3em; + + .badge { + margin: 0; + a.tn-link { + color: inherit; + text-transform: none; + text-decoration: none; + display: inline-block; + } + } + +} diff --git a/apps/client/src/widgets/layout/InlineTitle.tsx b/apps/client/src/widgets/layout/InlineTitle.tsx index c3f86d10e..9ab237a1b 100644 --- a/apps/client/src/widgets/layout/InlineTitle.tsx +++ b/apps/client/src/widgets/layout/InlineTitle.tsx @@ -16,10 +16,12 @@ import server from "../../services/server"; import { formatDateTime } from "../../utils/formatters"; import NoteIcon from "../note_icon"; import NoteTitleWidget from "../note_title"; -import { Badge, BadgeWithDropdown } from "../react/Badge"; +import SimpleBadge, { Badge, BadgeWithDropdown } from "../react/Badge"; import { FormDropdownDivider, FormListItem } from "../react/FormList"; -import { useNoteBlob, useNoteContext, useNoteProperty, useStaticTooltip, useTriliumEvent } from "../react/hooks"; +import { useNoteBlob, useNoteContext, useNoteLabel, useNoteProperty, useStaticTooltip, useTriliumEvent } from "../react/hooks"; +import NoteLink from "../react/NoteLink"; import { joinElements } from "../react/react_utils"; +import { useEditedNotes } from "../ribbon/EditedNotesTab"; import { useNoteMetadata } from "../ribbon/NoteInfoTab"; import { onWheelHorizontalScroll } from "../widget_utils"; @@ -72,6 +74,7 @@ export default function InlineTitle() { + ); @@ -301,3 +304,38 @@ function useBuiltinTemplates() { return templates; } //#endregion + +//#region Edited Notes +function EditedNotes() { + const { note } = useNoteContext(); + const [ dateNote ] = useNoteLabel(note, "dateNote"); + + + return (note && dateNote && +
+ +
+ ); +} + +function EditedNotesContent({ note }: { note: FNote }) { + const editedNotes = useEditedNotes(note); + + return ( + <> + {t("note_title.edited_notes")}
+ {editedNotes?.map(editedNote => ( + + )} + /> + ))} + + ); +} +//#endregion diff --git a/apps/client/src/widgets/react/Badge.tsx b/apps/client/src/widgets/react/Badge.tsx index e7844368c..a03e6b5fb 100644 --- a/apps/client/src/widgets/react/Badge.tsx +++ b/apps/client/src/widgets/react/Badge.tsx @@ -10,7 +10,7 @@ import Icon from "./Icon"; interface SimpleBadgeProps { className?: string; - title: string; + title: ComponentChildren; } interface BadgeProps { diff --git a/apps/client/src/widgets/ribbon/EditedNotesTab.tsx b/apps/client/src/widgets/ribbon/EditedNotesTab.tsx index 4bdae4126..8e5d625b1 100644 --- a/apps/client/src/widgets/ribbon/EditedNotesTab.tsx +++ b/apps/client/src/widgets/ribbon/EditedNotesTab.tsx @@ -1,24 +1,16 @@ -import { useEffect, useState } from "preact/hooks"; -import { TabContext } from "./ribbon-interface"; import { EditedNotesResponse } from "@triliumnext/commons"; -import server from "../../services/server"; -import { t } from "../../services/i18n"; +import { useEffect, useState } from "preact/hooks"; + +import FNote from "../../entities/fnote"; import froca from "../../services/froca"; +import { t } from "../../services/i18n"; +import server from "../../services/server"; import NoteLink from "../react/NoteLink"; import { joinElements } from "../react/react_utils"; +import { TabContext } from "./ribbon-interface"; export default function EditedNotesTab({ note }: TabContext) { - const [ editedNotes, setEditedNotes ] = useState(); - - useEffect(() => { - if (!note) return; - server.get(`edited-notes/${note.getLabelValue("dateNote")}`).then(async editedNotes => { - editedNotes = editedNotes.filter((n) => n.noteId !== note.noteId); - const noteIds = editedNotes.flatMap((n) => n.noteId); - await froca.getNotes(noteIds, true); // preload all at once - setEditedNotes(editedNotes); - }); - }, [ note?.noteId ]); + const editedNotes = useEditedNotes(note); return (
{joinElements(editedNotes.map(editedNote => { return ( - + {editedNote.isDeleted ? ( {`${editedNote.title} ${t("edited_notes.deleted")}`} ) : ( @@ -40,12 +32,28 @@ export default function EditedNotesTab({ note }: TabContext) { )} - ) + ); }), " ")}
) : (
{t("edited_notes.no_edited_notes_found")}
)} - ) + ); +} + +export function useEditedNotes(note: FNote | null | undefined) { + const [ editedNotes, setEditedNotes ] = useState(); + + useEffect(() => { + if (!note) return; + server.get(`edited-notes/${note.getLabelValue("dateNote")}`).then(async editedNotes => { + editedNotes = editedNotes.filter((n) => n.noteId !== note.noteId); + const noteIds = editedNotes.flatMap((n) => n.noteId); + await froca.getNotes(noteIds, true); // preload all at once + setEditedNotes(editedNotes); + }); + }, [ note ]); + + return editedNotes; } diff --git a/apps/client/src/widgets/ribbon/RibbonDefinition.ts b/apps/client/src/widgets/ribbon/RibbonDefinition.ts index 0b8f568dd..178b022d3 100644 --- a/apps/client/src/widgets/ribbon/RibbonDefinition.ts +++ b/apps/client/src/widgets/ribbon/RibbonDefinition.ts @@ -53,7 +53,7 @@ export const RIBBON_TAB_DEFINITIONS: TabConfiguration[] = [ title: t("edited_notes.title"), icon: "bx bx-calendar-edit", content: EditedNotesTab, - show: ({ note }) => note?.hasOwnedLabel("dateNote"), + show: ({ note }) => !isNewLayout && note?.hasOwnedLabel("dateNote"), activate: () => options.is("editedNotesOpenInRibbon") }, { diff --git a/packages/commons/src/lib/attribute_names.ts b/packages/commons/src/lib/attribute_names.ts index ebbe22123..db1ffc9f2 100644 --- a/packages/commons/src/lib/attribute_names.ts +++ b/packages/commons/src/lib/attribute_names.ts @@ -18,6 +18,7 @@ type Labels = { language: string; originalFileName: string; pageUrl: string; + dateNote: string; // Search searchString: string;