chore(breadcrumb_badges/backlinks): display actual count of backlinks

This commit is contained in:
Elian Doran 2025-12-10 11:49:47 +02:00
parent b03e6c3b19
commit 42fc128f97
No known key found for this signature in database
2 changed files with 31 additions and 22 deletions

View File

@ -5,7 +5,7 @@ import { ComponentChildren, MouseEventHandler } from "preact";
import { useRef } from "preact/hooks"; import { useRef } from "preact/hooks";
import { t } from "../services/i18n"; import { t } from "../services/i18n";
import { BacklinksList } from "./FloatingButtonsDefinitions"; import { BacklinksList, useBacklinkCount } from "./FloatingButtonsDefinitions";
import Dropdown, { DropdownProps } from "./react/Dropdown"; import Dropdown, { DropdownProps } from "./react/Dropdown";
import { useIsNoteReadOnly, useNoteContext, useStaticTooltip } from "./react/hooks"; import { useIsNoteReadOnly, useNoteContext, useStaticTooltip } from "./react/hooks";
import Icon from "./react/Icon"; import Icon from "./react/Icon";
@ -65,8 +65,8 @@ function ShareBadge() {
} }
function BacklinksBadge() { function BacklinksBadge() {
const { note } = useNoteContext(); const { note, viewScope } = useNoteContext();
const count = 1; const count = useBacklinkCount(note, viewScope?.viewMode === "default");
return (note && count > 0 && return (note && count > 0 &&
<BadgeWithDropdown <BadgeWithDropdown
className="backlinks-badge backlinks-widget" className="backlinks-badge backlinks-widget"

View File

@ -5,7 +5,7 @@ import NoteContext from "../components/note_context";
import FNote from "../entities/fnote"; import FNote from "../entities/fnote";
import ActionButton, { ActionButtonProps } from "./react/ActionButton"; import ActionButton, { ActionButtonProps } from "./react/ActionButton";
import { useIsNoteReadOnly, useNoteLabelBoolean, useTriliumEvent, useTriliumOption, useWindowSize } from "./react/hooks"; import { useIsNoteReadOnly, useNoteLabelBoolean, useTriliumEvent, useTriliumOption, useWindowSize } from "./react/hooks";
import { useEffect, useLayoutEffect, useMemo, useRef, useState } from "preact/hooks"; import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "preact/hooks";
import { createImageSrcUrl, openInAppHelpFromUrl } from "../services/utils"; import { createImageSrcUrl, openInAppHelpFromUrl } from "../services/utils";
import server from "../services/server"; import server from "../services/server";
import { BacklinkCountResponse, BacklinksResponse, SaveSqlConsoleResponse } from "@triliumnext/commons"; import { BacklinkCountResponse, BacklinksResponse, SaveSqlConsoleResponse } from "@triliumnext/commons";
@ -20,6 +20,7 @@ import RawHtml from "./react/RawHtml";
import { ViewTypeOptions } from "./collections/interface"; import { ViewTypeOptions } from "./collections/interface";
import attributes from "../services/attributes"; import attributes from "../services/attributes";
import LoadResults from "../services/load_results"; import LoadResults from "../services/load_results";
import { isExperimentalFeatureEnabled } from "../services/experimental_features";
export interface FloatingButtonContext { export interface FloatingButtonContext {
parentComponent: Component; parentComponent: Component;
@ -76,6 +77,8 @@ export const POPUP_HIDDEN_FLOATING_BUTTONS: FloatingButtonsList = [
ToggleReadOnlyButton ToggleReadOnlyButton
]; ];
const isNewLayout = isExperimentalFeatureEnabled("new-layout");
function RefreshBackendLogButton({ note, parentComponent, noteContext, isDefaultViewMode }: FloatingButtonContext) { function RefreshBackendLogButton({ note, parentComponent, noteContext, isDefaultViewMode }: FloatingButtonContext) {
const isEnabled = (note.noteId === "_backendLog" || note.type === "render") && isDefaultViewMode; const isEnabled = (note.noteId === "_backendLog" || note.type === "render") && isDefaultViewMode;
return isEnabled && <FloatingButton return isEnabled && <FloatingButton
@ -308,22 +311,9 @@ function InAppHelpButton({ note }: FloatingButtonContext) {
} }
function Backlinks({ note, isDefaultViewMode }: FloatingButtonContext) { function Backlinks({ note, isDefaultViewMode }: FloatingButtonContext) {
let [ backlinkCount, setBacklinkCount ] = useState(0); const [ popupOpen, setPopupOpen ] = useState(false);
let [ popupOpen, setPopupOpen ] = useState(false);
const backlinksContainerRef = useRef<HTMLDivElement>(null); const backlinksContainerRef = useRef<HTMLDivElement>(null);
const backlinkCount = useBacklinkCount(note, isDefaultViewMode);
function refresh() {
if (!isDefaultViewMode) return;
server.get<BacklinkCountResponse>(`note-map/${note.noteId}/backlink-count`).then(resp => {
setBacklinkCount(resp.count);
});
}
useEffect(() => refresh(), [ note ]);
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
if (needsRefresh(note, loadResults)) refresh();
});
// Determine the max height of the container. // Determine the max height of the container.
const { windowHeight } = useWindowSize(); const { windowHeight } = useWindowSize();
@ -336,7 +326,7 @@ function Backlinks({ note, isDefaultViewMode }: FloatingButtonContext) {
} }
}, [ popupOpen, windowHeight ]); }, [ popupOpen, windowHeight ]);
const isEnabled = isDefaultViewMode && backlinkCount > 0; const isEnabled = !isNewLayout && isDefaultViewMode && backlinkCount > 0;
return (isEnabled && return (isEnabled &&
<div className="backlinks-widget has-overflow"> <div className="backlinks-widget has-overflow">
<div <div
@ -355,6 +345,25 @@ function Backlinks({ note, isDefaultViewMode }: FloatingButtonContext) {
); );
} }
export function useBacklinkCount(note: FNote | null | undefined, isDefaultViewMode: boolean) {
const [ backlinkCount, setBacklinkCount ] = useState(0);
const refresh = useCallback(() => {
if (!note || !isDefaultViewMode) return;
server.get<BacklinkCountResponse>(`note-map/${note.noteId}/backlink-count`).then(resp => {
setBacklinkCount(resp.count);
});
}, [ isDefaultViewMode, note ]);
useEffect(() => refresh(), [ note, isDefaultViewMode, refresh ]);
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
if (note && needsRefresh(note, loadResults)) refresh();
});
return backlinkCount;
}
export function BacklinksList({ note }: { note: FNote }) { export function BacklinksList({ note }: { note: FNote }) {
const [ backlinks, setBacklinks ] = useState<BacklinksResponse>([]); const [ backlinks, setBacklinks ] = useState<BacklinksResponse>([]);
@ -362,8 +371,8 @@ export function BacklinksList({ note }: { note: FNote }) {
server.get<BacklinksResponse>(`note-map/${note.noteId}/backlinks`).then(async (backlinks) => { server.get<BacklinksResponse>(`note-map/${note.noteId}/backlinks`).then(async (backlinks) => {
// prefetch all // prefetch all
const noteIds = backlinks const noteIds = backlinks
.filter(bl => "noteId" in bl) .filter(bl => "noteId" in bl)
.map((bl) => bl.noteId); .map((bl) => bl.noteId);
await froca.getNotes(noteIds); await froca.getNotes(noteIds);
setBacklinks(backlinks); setBacklinks(backlinks);
}); });