mirror of
https://github.com/zadam/trilium.git
synced 2026-05-06 09:36:37 +02:00
feat(mobile/nav): avoid flicker caused by preview rendering
This commit is contained in:
@@ -207,19 +207,22 @@ function NoteAttributes({ note }: { note: FNote }) {
|
||||
return <span className="note-list-attributes" ref={ref} />;
|
||||
}
|
||||
|
||||
export function NoteContent({ note, trim, noChildrenList, highlightedTokens, includeArchivedNotes, showTextRepresentation }: {
|
||||
export function NoteContent({ note, trim, noChildrenList, highlightedTokens, includeArchivedNotes, showTextRepresentation, onReady }: {
|
||||
note: FNote;
|
||||
trim?: boolean;
|
||||
noChildrenList?: boolean;
|
||||
highlightedTokens: string[] | null | undefined;
|
||||
includeArchivedNotes: boolean;
|
||||
showTextRepresentation?: boolean;
|
||||
onReady?: () => void;
|
||||
}) {
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
const highlightSearch = useImperativeSearchHighlighlighting(highlightedTokens);
|
||||
|
||||
const [ready, setReady] = useState(false);
|
||||
const [noteType, setNoteType] = useState<string>("none");
|
||||
const onReadyRef = useRef(onReady);
|
||||
onReadyRef.current = onReady;
|
||||
|
||||
useEffect(() => {
|
||||
const contentElement = contentRef.current;
|
||||
@@ -233,6 +236,7 @@ export function NoteContent({ note, trim, noChildrenList, highlightedTokens, inc
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
setReady(false);
|
||||
content_renderer.getRenderedContent(note, {
|
||||
trim,
|
||||
noChildrenList,
|
||||
@@ -250,12 +254,14 @@ export function NoteContent({ note, trim, noChildrenList, highlightedTokens, inc
|
||||
highlightSearch(contentRef.current);
|
||||
setNoteType(type);
|
||||
setReady(true);
|
||||
onReadyRef.current?.();
|
||||
})
|
||||
.catch(e => {
|
||||
console.warn(`Caught error while rendering note '${note.noteId}' of type '${note.type}'`);
|
||||
console.error(e);
|
||||
contentRef.current?.replaceChildren(t("collections.rendering_error"));
|
||||
setReady(true);
|
||||
onReadyRef.current?.();
|
||||
});
|
||||
}, [ note, highlightedTokens ]);
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
}
|
||||
|
||||
.mobile-navigator-scroll {
|
||||
position: relative;
|
||||
flex: 1 1 auto;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
@@ -34,6 +35,22 @@
|
||||
padding-bottom: env(safe-area-inset-bottom, 0);
|
||||
}
|
||||
|
||||
.mobile-navigator-scroll.is-pending > .mobile-navigator-current-tile,
|
||||
.mobile-navigator-scroll.is-pending > .mobile-navigator-list {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.mobile-navigator-placeholder {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.8em;
|
||||
color: var(--muted-text-color, var(--main-text-color));
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.mobile-navigator-current-tile {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -79,6 +79,16 @@ export default function MobileNoteNavigator() {
|
||||
const children = useNavigatorChildren(parentNote, hideArchived, isLoaded);
|
||||
const canGoBack = stack.length > 1;
|
||||
|
||||
// Gate the visible body on the content preview being ready to avoid a layout shift
|
||||
// when the preview finishes rendering. Tied to the parent's noteId so a stale "ready"
|
||||
// flag from the previous column can't leak into the new one.
|
||||
const [readyForNoteId, setReadyForNoteId] = useState<string | null>(null);
|
||||
const previewReady = !!parentNote && readyForNoteId === parentNote.noteId;
|
||||
const bodyVisible = previewReady && isLoaded;
|
||||
const onPreviewReady = useCallback(() => {
|
||||
if (parentNote) setReadyForNoteId(parentNote.noteId);
|
||||
}, [parentNote]);
|
||||
|
||||
const goBack = useCallback(() => {
|
||||
manualStackRef.current = true;
|
||||
setStack((prev) => (prev.length > 1 ? prev.slice(0, -1) : prev));
|
||||
@@ -117,40 +127,38 @@ export default function MobileNoteNavigator() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mobile-navigator-scroll">
|
||||
<div
|
||||
className={clsx("mobile-navigator-current-tile", {
|
||||
"is-active": isCurrentActive,
|
||||
"is-archived": parentNote?.isArchived
|
||||
})}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={openCurrent}
|
||||
>
|
||||
<div className="mobile-navigator-current-header">
|
||||
<Icon icon={parentIcon ?? "bx bx-folder"} className="mobile-navigator-current-icon" />
|
||||
<span className="mobile-navigator-current-title">
|
||||
{parentNote?.title ?? t("mobile_note_navigator.loading")}
|
||||
</span>
|
||||
<Icon icon="bx bx-link-external" className="mobile-navigator-current-open" />
|
||||
</div>
|
||||
{parentNote && (
|
||||
<div className={clsx("mobile-navigator-scroll", !bodyVisible && "is-pending")}>
|
||||
{parentNote && (
|
||||
<div
|
||||
className={clsx("mobile-navigator-current-tile", {
|
||||
"is-active": isCurrentActive,
|
||||
"is-archived": parentNote.isArchived
|
||||
})}
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={openCurrent}
|
||||
>
|
||||
<div className="mobile-navigator-current-header">
|
||||
<Icon icon={parentIcon ?? "bx bx-folder"} className="mobile-navigator-current-icon" />
|
||||
<span className="mobile-navigator-current-title">{parentNote.title}</span>
|
||||
<Icon icon="bx bx-link-external" className="mobile-navigator-current-open" />
|
||||
</div>
|
||||
<div className="mobile-navigator-current-preview">
|
||||
<NoteContent
|
||||
key={parentNote.noteId}
|
||||
note={parentNote}
|
||||
trim
|
||||
noChildrenList
|
||||
highlightedTokens={null}
|
||||
includeArchivedNotes={!hideArchived}
|
||||
onReady={onPreviewReady}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="mobile-navigator-list">
|
||||
{!isLoaded ? (
|
||||
<NoItems icon="bx bx-loader" text={t("mobile_note_navigator.loading")} />
|
||||
) : children.length === 0 ? (
|
||||
{!isLoaded ? null : children.length === 0 ? (
|
||||
<NoItems icon="bx bx-folder-open" text={t("mobile_note_navigator.empty")} />
|
||||
) : (
|
||||
children.map((child) => (
|
||||
@@ -166,6 +174,12 @@ export default function MobileNoteNavigator() {
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!bodyVisible && (
|
||||
<div className="mobile-navigator-placeholder">
|
||||
<span className="bx bx-loader bx-spin" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user