mirror of
https://github.com/zadam/trilium.git
synced 2025-12-12 10:24:23 +01:00
feat(layout): created & modification date
This commit is contained in:
parent
28c1d0b3f5
commit
3060207d04
@ -51,6 +51,7 @@ import NoteActions from "../widgets/ribbon/NoteActions.jsx";
|
|||||||
import FormattingToolbar from "../widgets/ribbon/FormattingToolbar.jsx";
|
import FormattingToolbar from "../widgets/ribbon/FormattingToolbar.jsx";
|
||||||
import StandaloneRibbonAdapter from "../widgets/ribbon/components/StandaloneRibbonAdapter.jsx";
|
import StandaloneRibbonAdapter from "../widgets/ribbon/components/StandaloneRibbonAdapter.jsx";
|
||||||
import NoteBadges from "../widgets/BreadcrumbBadges.jsx";
|
import NoteBadges from "../widgets/BreadcrumbBadges.jsx";
|
||||||
|
import NoteTitleDetails from "../widgets/NoteTitleDetails.jsx";
|
||||||
|
|
||||||
export default class DesktopLayout {
|
export default class DesktopLayout {
|
||||||
|
|
||||||
@ -156,6 +157,7 @@ export default class DesktopLayout {
|
|||||||
new ScrollingContainer()
|
new ScrollingContainer()
|
||||||
.filling()
|
.filling()
|
||||||
.optChild(isNewLayout, titleRow)
|
.optChild(isNewLayout, titleRow)
|
||||||
|
.optChild(isNewLayout, <NoteTitleDetails />)
|
||||||
.optChild(!isNewLayout, new ContentHeader()
|
.optChild(!isNewLayout, new ContentHeader()
|
||||||
.child(<ReadOnlyNoteInfoBar />)
|
.child(<ReadOnlyNoteInfoBar />)
|
||||||
.child(<SharedInfo />)
|
.child(<SharedInfo />)
|
||||||
|
|||||||
22
apps/client/src/widgets/NoteTitleDetails.tsx
Normal file
22
apps/client/src/widgets/NoteTitleDetails.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -29,11 +29,30 @@ body.desktop .note-title-widget input.note-title {
|
|||||||
font-size: 180%;
|
font-size: 180%;
|
||||||
}
|
}
|
||||||
|
|
||||||
body.experimental-feature-new-layout .title-row {
|
body.experimental-feature-new-layout .title-row,
|
||||||
margin-top: 1em;
|
body.experimental-feature-new-layout .title-details {
|
||||||
max-width: var(--max-content-width);
|
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;
|
margin-inline: auto;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,30 +8,13 @@ import { formatDateTime } from "../../utils/formatters";
|
|||||||
import { formatSize } from "../../services/utils";
|
import { formatSize } from "../../services/utils";
|
||||||
import LoadingSpinner from "../react/LoadingSpinner";
|
import LoadingSpinner from "../react/LoadingSpinner";
|
||||||
import { useTriliumEvent } from "../react/hooks";
|
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) {
|
export default function NoteInfoTab({ note }: TabContext) {
|
||||||
const [ metadata, setMetadata ] = useState<MetadataResponse>();
|
const { isLoading, metadata, noteSizeResponse, subtreeSizeResponse, requestSizeInfo } = useNoteMetadata(note);
|
||||||
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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="note-info-widget">
|
<div className="note-info-widget">
|
||||||
@ -41,14 +24,14 @@ export default function NoteInfoTab({ note }: TabContext) {
|
|||||||
<span>{t("note_info_widget.note_id")}:</span>
|
<span>{t("note_info_widget.note_id")}:</span>
|
||||||
<span className="note-info-id selectable-text">{note.noteId}</span>
|
<span className="note-info-id selectable-text">{note.noteId}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="note-info-item">
|
{!isNewLayout && <div className="note-info-item">
|
||||||
<span>{t("note_info_widget.created")}:</span>
|
<span>{t("note_info_widget.created")}:</span>
|
||||||
<span className="selectable-text">{formatDateTime(metadata?.dateCreated)}</span>
|
<span className="selectable-text">{formatDateTime(metadata?.dateCreated)}</span>
|
||||||
</div>
|
</div>}
|
||||||
<div className="note-info-item">
|
{!isNewLayout && <div className="note-info-item">
|
||||||
<span>{t("note_info_widget.modified")}:</span>
|
<span>{t("note_info_widget.modified")}:</span>
|
||||||
<span className="selectable-text">{formatDateTime(metadata?.dateModified)}</span>
|
<span className="selectable-text">{formatDateTime(metadata?.dateModified)}</span>
|
||||||
</div>
|
</div>}
|
||||||
<div className="note-info-item">
|
<div className="note-info-item">
|
||||||
<span>{t("note_info_widget.type")}:</span>
|
<span>{t("note_info_widget.type")}:</span>
|
||||||
<span>
|
<span>
|
||||||
@ -64,16 +47,7 @@ export default function NoteInfoTab({ note }: TabContext) {
|
|||||||
className="calculate-button"
|
className="calculate-button"
|
||||||
icon="bx bx-calculator"
|
icon="bx bx-calculator"
|
||||||
text={t("note_info_widget.calculate")}
|
text={t("note_info_widget.calculate")}
|
||||||
onClick={() => {
|
onClick={requestSizeInfo}
|
||||||
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);
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@ -90,5 +64,45 @@ export default function NoteInfoTab({ note }: TabContext) {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</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 };
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user