feat(layout): created & modification date

This commit is contained in:
Elian Doran 2025-12-09 22:22:28 +02:00
parent 28c1d0b3f5
commit 3060207d04
No known key found for this signature in database
4 changed files with 97 additions and 40 deletions

View File

@ -51,6 +51,7 @@ import NoteActions from "../widgets/ribbon/NoteActions.jsx";
import FormattingToolbar from "../widgets/ribbon/FormattingToolbar.jsx";
import StandaloneRibbonAdapter from "../widgets/ribbon/components/StandaloneRibbonAdapter.jsx";
import NoteBadges from "../widgets/BreadcrumbBadges.jsx";
import NoteTitleDetails from "../widgets/NoteTitleDetails.jsx";
export default class DesktopLayout {
@ -156,6 +157,7 @@ export default class DesktopLayout {
new ScrollingContainer()
.filling()
.optChild(isNewLayout, titleRow)
.optChild(isNewLayout, <NoteTitleDetails />)
.optChild(!isNewLayout, new ContentHeader()
.child(<ReadOnlyNoteInfoBar />)
.child(<SharedInfo />)

View File

@ -0,0 +1,22 @@
import { formatDateTime } from "../utils/formatters";
import { useNoteContext } from "./react/hooks";
import { joinElements } from "./react/react_utils";
import { useNoteMetadata } from "./ribbon/NoteInfoTab";
export default function NoteTitleDetails() {
const { note } = useNoteContext();
const { metadata } = useNoteMetadata(note);
return (
<div className="title-details">
{joinElements([
metadata?.dateCreated && <li>
Created on {formatDateTime(metadata.dateCreated, "medium", "none")}
</li>,
metadata?.dateModified && <li>
Modified on {formatDateTime(metadata.dateModified, "medium", "none")}
</li>
], " • ")}
</div>
);
}

View File

@ -29,11 +29,30 @@ body.desktop .note-title-widget input.note-title {
font-size: 180%;
}
body.experimental-feature-new-layout .title-row {
margin-top: 1em;
body.experimental-feature-new-layout .title-row,
body.experimental-feature-new-layout .title-details {
max-width: var(--max-content-width);
}
body.experimental-feature-new-layout.prefers-centered-content .title-row {
body.experimental-feature-new-layout .title-row {
margin-top: 2em;
margin-left: 12px;
}
body.experimental-feature-new-layout .title-details {
margin-top: 0;
contain: none;
padding: 0;
padding-inline-start: 24px;
opacity: 0.85;
display: flex;
gap: 0.25em;
margin: 0;
list-style-type: none;
margin-bottom: 2em;
}
body.experimental-feature-new-layout.prefers-centered-content .title-row,
body.experimental-feature-new-layout.prefers-centered-content .title-details {
margin-inline: auto;
}

View File

@ -8,30 +8,13 @@ import { formatDateTime } from "../../utils/formatters";
import { formatSize } from "../../services/utils";
import LoadingSpinner from "../react/LoadingSpinner";
import { useTriliumEvent } from "../react/hooks";
import { isExperimentalFeatureEnabled } from "../../services/experimental_features";
import FNote from "../../entities/fnote";
const isNewLayout = isExperimentalFeatureEnabled("new-layout");
export default function NoteInfoTab({ note }: TabContext) {
const [ metadata, setMetadata ] = useState<MetadataResponse>();
const [ isLoading, setIsLoading ] = useState(false);
const [ noteSizeResponse, setNoteSizeResponse ] = useState<NoteSizeResponse>();
const [ subtreeSizeResponse, setSubtreeSizeResponse ] = useState<SubtreeSizeResponse>();
function refresh() {
if (note) {
server.get<MetadataResponse>(`notes/${note?.noteId}/metadata`).then(setMetadata);
}
setNoteSizeResponse(undefined);
setSubtreeSizeResponse(undefined);
setIsLoading(false);
}
useEffect(refresh, [ note?.noteId ]);
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
const noteId = note?.noteId;
if (noteId && (loadResults.isNoteReloaded(noteId) || loadResults.isNoteContentReloaded(noteId))) {
refresh();
}
});
const { isLoading, metadata, noteSizeResponse, subtreeSizeResponse, requestSizeInfo } = useNoteMetadata(note);
return (
<div className="note-info-widget">
@ -41,14 +24,14 @@ export default function NoteInfoTab({ note }: TabContext) {
<span>{t("note_info_widget.note_id")}:</span>
<span className="note-info-id selectable-text">{note.noteId}</span>
</div>
<div className="note-info-item">
{!isNewLayout && <div className="note-info-item">
<span>{t("note_info_widget.created")}:</span>
<span className="selectable-text">{formatDateTime(metadata?.dateCreated)}</span>
</div>
<div className="note-info-item">
</div>}
{!isNewLayout && <div className="note-info-item">
<span>{t("note_info_widget.modified")}:</span>
<span className="selectable-text">{formatDateTime(metadata?.dateModified)}</span>
</div>
</div>}
<div className="note-info-item">
<span>{t("note_info_widget.type")}:</span>
<span>
@ -64,16 +47,7 @@ export default function NoteInfoTab({ note }: TabContext) {
className="calculate-button"
icon="bx bx-calculator"
text={t("note_info_widget.calculate")}
onClick={() => {
setIsLoading(true);
setTimeout(async () => {
await Promise.allSettled([
server.get<NoteSizeResponse>(`stats/note-size/${note.noteId}`).then(setNoteSizeResponse),
server.get<SubtreeSizeResponse>(`stats/subtree-size/${note.noteId}`).then(setSubtreeSizeResponse)
]);
setIsLoading(false);
}, 0);
}}
onClick={requestSizeInfo}
/>
)}
@ -90,5 +64,45 @@ export default function NoteInfoTab({ note }: TabContext) {
</>
)}
</div>
)
);
}
export function useNoteMetadata(note: FNote | null | undefined) {
const [ isLoading, setIsLoading ] = useState(false);
const [ noteSizeResponse, setNoteSizeResponse ] = useState<NoteSizeResponse>();
const [ subtreeSizeResponse, setSubtreeSizeResponse ] = useState<SubtreeSizeResponse>();
const [ metadata, setMetadata ] = useState<MetadataResponse>();
function refresh() {
if (note) {
server.get<MetadataResponse>(`notes/${note?.noteId}/metadata`).then(setMetadata);
}
setNoteSizeResponse(undefined);
setSubtreeSizeResponse(undefined);
setIsLoading(false);
}
function requestSizeInfo() {
if (!note) return;
setIsLoading(true);
setTimeout(async () => {
await Promise.allSettled([
server.get<NoteSizeResponse>(`stats/note-size/${note.noteId}`).then(setNoteSizeResponse),
server.get<SubtreeSizeResponse>(`stats/subtree-size/${note.noteId}`).then(setSubtreeSizeResponse)
]);
setIsLoading(false);
}, 0);
}
useEffect(refresh, [ note ]);
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
const noteId = note?.noteId;
if (noteId && (loadResults.isNoteReloaded(noteId) || loadResults.isNoteContentReloaded(noteId))) {
refresh();
}
});
return { isLoading, metadata, noteSizeResponse, subtreeSizeResponse, requestSizeInfo };
}