Skip to content

Commit 5f4d032

Browse files
committed
Merge remote-tracking branch 'origin/main' into react/type_widgets
2 parents 763fa0b + 06eb30c commit 5f4d032

File tree

20 files changed

+628
-494
lines changed

20 files changed

+628
-494
lines changed

apps/client/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@triliumnext/client",
3-
"version": "0.99.3",
3+
"version": "0.99.4",
44
"description": "JQuery-based client for TriliumNext, used for both web and desktop (via Electron)",
55
"private": true,
66
"license": "AGPL-3.0-only",

apps/client/src/components/root_command_executor.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import protectedSessionService from "../services/protected_session.js";
77
import options from "../services/options.js";
88
import froca from "../services/froca.js";
99
import utils from "../services/utils.js";
10-
import LlmChatPanel from "../widgets/llm_chat_panel.js";
1110
import toastService from "../services/toast.js";
1211
import noteCreateService from "../services/note_create.js";
1312

apps/client/src/services/utils.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@ export function arrayEqual<T>(a: T[], b: T[]) {
841841
return true;
842842
}
843843

844-
type Indexed<T extends object> = T & { index: number };
844+
export type Indexed<T extends object> = T & { index: number };
845845

846846
/**
847847
* Given an object array, alters every object in the array to have an index field assigned to it.

apps/client/src/stylesheets/style.css

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2178,6 +2178,11 @@ body.zen .note-split .ribbon-container .classic-toolbar-widget {
21782178
background: var(--menu-background-color);
21792179
}
21802180

2181+
body.zen .note-split .ribbon-container .classic-toolbar-widget:not(:has(> .ck-toolbar)) {
2182+
/* Hide the toolbar wrapper if the toolbar is missing */
2183+
display: none;
2184+
}
2185+
21812186
body.zen .note-split:focus-within .ribbon-container .classic-toolbar-widget {
21822187
pointer-events: all;
21832188
}
@@ -2555,7 +2560,7 @@ iframe.print-iframe {
25552560
}
25562561

25572562
.scrolling-container > .note-detail.full-height,
2558-
.scrolling-container > .note-list-widget {
2563+
.scrolling-container > .note-list-widget.full-height {
25592564
position: relative;
25602565
flex-grow: 1;
25612566
width: 100%;

apps/client/src/widgets/react/hooks.tsx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { MutableRef, useCallback, useContext, useDebugValue, useEffect, useLayou
55
import { ParentComponent, refToJQuerySelector } from "./react_utils";
66
import { RefObject, VNode } from "preact";
77
import { Tooltip } from "bootstrap";
8-
import { ViewScope } from "../../services/link";
8+
import { ViewMode, ViewScope } from "../../services/link";
99
import appContext, { EventData, EventNames } from "../../components/app_context";
1010
import attributes from "../../services/attributes";
1111
import BasicWidget, { ReactWrappedWidget } from "../basic_widget";
@@ -261,6 +261,7 @@ export function useNoteContext() {
261261
const [ notePath, setNotePath ] = useState<string | null | undefined>();
262262
const [ note, setNote ] = useState<FNote | null | undefined>();
263263
const [ , setViewScope ] = useState<ViewScope>();
264+
const [ isReadOnlyTemporarilyDisabled, setIsReadOnlyTemporarilyDisabled ] = useState<boolean | null | undefined>(noteContext?.viewScope?.isReadOnly);
264265
const [ refreshCounter, setRefreshCounter ] = useState(0);
265266

266267
useEffect(() => {
@@ -280,6 +281,11 @@ export function useNoteContext() {
280281
setRefreshCounter(refreshCounter + 1);
281282
}
282283
});
284+
useTriliumEvent("readOnlyTemporarilyDisabled", ({ noteContext: eventNoteContext }) => {
285+
if (eventNoteContext.ntxId === noteContext?.ntxId) {
286+
setIsReadOnlyTemporarilyDisabled(eventNoteContext?.viewScope?.readOnlyTemporarilyDisabled);
287+
}
288+
});
283289

284290
const parentComponent = useContext(ParentComponent) as ReactWrappedWidget;
285291
useDebugValue(() => `notePath=${notePath}, ntxId=${noteContext?.ntxId}`);
@@ -293,7 +299,8 @@ export function useNoteContext() {
293299
viewScope: noteContext?.viewScope,
294300
componentId: parentComponent.componentId,
295301
noteContext,
296-
parentComponent
302+
parentComponent,
303+
isReadOnlyTemporarilyDisabled
297304
};
298305

299306
}

apps/client/src/widgets/ribbon/Ribbon.tsx

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "preact/hooks"
22
import { useNoteContext, useNoteProperty, useStaticTooltipWithKeyboardShortcut, useTriliumEvents } from "../react/hooks";
33
import "./style.css";
44

5-
import { numberObjectsInPlace } from "../../services/utils";
5+
import { Indexed, numberObjectsInPlace } from "../../services/utils";
66
import { EventNames } from "../../components/app_context";
77
import NoteActions from "./NoteActions";
88
import { KeyboardActionNames } from "@triliumnext/commons";
@@ -11,30 +11,47 @@ import { TabConfiguration, TitleContext } from "./ribbon-interface";
1111

1212
const TAB_CONFIGURATION = numberObjectsInPlace<TabConfiguration>(RIBBON_TAB_DEFINITIONS);
1313

14+
interface ComputedTab extends Indexed<TabConfiguration> {
15+
shouldShow: boolean;
16+
}
17+
1418
export default function Ribbon() {
15-
const { note, ntxId, hoistedNoteId, notePath, noteContext, componentId } = useNoteContext();
19+
const { note, ntxId, hoistedNoteId, notePath, noteContext, componentId, isReadOnlyTemporarilyDisabled } = useNoteContext();
1620
const noteType = useNoteProperty(note, "type");
17-
const titleContext: TitleContext = { note };
1821
const [ activeTabIndex, setActiveTabIndex ] = useState<number | undefined>();
19-
const computedTabs = useMemo(
20-
() => TAB_CONFIGURATION.map(tab => {
21-
const shouldShow = typeof tab.show === "boolean" ? tab.show : tab.show?.(titleContext);
22-
return {
22+
const [ computedTabs, setComputedTabs ] = useState<ComputedTab[]>();
23+
const titleContext: TitleContext = useMemo(() => ({
24+
note,
25+
noteContext
26+
}), [ note, noteContext ]);
27+
28+
async function refresh() {
29+
const computedTabs: ComputedTab[] = [];
30+
for (const tab of TAB_CONFIGURATION) {
31+
const shouldShow = await shouldShowTab(tab.show, titleContext);
32+
computedTabs.push({
2333
...tab,
24-
shouldShow
25-
}
26-
}),
27-
[ titleContext, note, noteType ]);
34+
shouldShow: !!shouldShow
35+
});
36+
}
37+
setComputedTabs(computedTabs);
38+
}
39+
40+
useEffect(() => {
41+
refresh();
42+
}, [ note, noteType, isReadOnlyTemporarilyDisabled ]);
2843

2944
// Automatically activate the first ribbon tab that needs to be activated whenever a note changes.
3045
useEffect(() => {
46+
if (!computedTabs) return;
3147
const tabToActivate = computedTabs.find(tab => tab.shouldShow && (typeof tab.activate === "boolean" ? tab.activate : tab.activate?.(titleContext)));
3248
setActiveTabIndex(tabToActivate?.index);
33-
}, [ note?.noteId ]);
49+
}, [ computedTabs, note?.noteId ]);
3450

3551
// Register keyboard shortcuts.
3652
const eventsToListenTo = useMemo(() => TAB_CONFIGURATION.filter(config => config.toggleCommand).map(config => config.toggleCommand) as EventNames[], []);
3753
useTriliumEvents(eventsToListenTo, useCallback((e, toggleCommand) => {
54+
if (!computedTabs) return;
3855
const correspondingTab = computedTabs.find(tab => tab.toggleCommand === toggleCommand);
3956
if (correspondingTab) {
4057
if (activeTabIndex !== correspondingTab.index) {
@@ -51,7 +68,7 @@ export default function Ribbon() {
5168
<>
5269
<div className="ribbon-top-row">
5370
<div className="ribbon-tab-container">
54-
{computedTabs.map(({ title, icon, index, toggleCommand, shouldShow }) => (
71+
{computedTabs && computedTabs.map(({ title, icon, index, toggleCommand, shouldShow }) => (
5572
shouldShow && <RibbonTab
5673
icon={icon}
5774
title={typeof title === "string" ? title : title(titleContext)}
@@ -74,7 +91,7 @@ export default function Ribbon() {
7491
</div>
7592

7693
<div className="ribbon-body-container">
77-
{computedTabs.map(tab => {
94+
{computedTabs && computedTabs.map(tab => {
7895
const isActive = tab.index === activeTabIndex;
7996
if (!isActive && !tab.stayInDom) {
8097
return;
@@ -129,3 +146,9 @@ function RibbonTab({ icon, title, active, onClick, toggleCommand }: { icon: stri
129146
)
130147
}
131148

149+
export async function shouldShowTab(showConfig: boolean | ((context: TitleContext) => Promise<boolean | null | undefined> | boolean | null | undefined), context: TitleContext) {
150+
if (showConfig === null || showConfig === undefined) return true;
151+
if (typeof showConfig === "boolean") return showConfig;
152+
if ("then" in showConfig) return await showConfig(context);
153+
return showConfig(context);
154+
}

apps/client/src/widgets/ribbon/RibbonDefinition.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ export const RIBBON_TAB_DEFINITIONS: TabConfiguration[] = [
2121
{
2222
title: t("classic_editor_toolbar.title"),
2323
icon: "bx bx-text",
24-
show: ({ note }) => note?.type === "text" && options.get("textNoteEditorType") === "ckeditor-classic",
24+
show: async ({ note, noteContext }) => note?.type === "text"
25+
&& options.get("textNoteEditorType") === "ckeditor-classic"
26+
&& !(await noteContext?.isReadOnly()),
2527
toggleCommand: "toggleRibbonTabClassicEditor",
2628
content: FormattingToolbar,
2729
activate: true,

apps/client/src/widgets/ribbon/components/StandaloneRibbonAdapter.tsx

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { ComponentChildren } from "preact";
22
import { useNoteContext } from "../../react/hooks";
3-
import { TabContext, TitleContext } from "../ribbon-interface";
3+
import { TabContext } from "../ribbon-interface";
44
import { useEffect, useMemo, useState } from "preact/hooks";
55
import { RIBBON_TAB_DEFINITIONS } from "../RibbonDefinition";
6+
import { shouldShowTab } from "../Ribbon";
67

78
interface StandaloneRibbonAdapterProps {
89
component: (props: TabContext) => ComponentChildren;
@@ -16,10 +17,11 @@ export default function StandaloneRibbonAdapter({ component }: StandaloneRibbonA
1617
const Component = component;
1718
const { note, ntxId, hoistedNoteId, notePath, noteContext, componentId } = useNoteContext();
1819
const definition = useMemo(() => RIBBON_TAB_DEFINITIONS.find(def => def.content === component), [ component ]);
19-
const [ shown, setShown ] = useState(unwrapShown(definition?.show, { note }));
20+
const [ shown, setShown ] = useState<boolean | null | undefined>(false);
2021

2122
useEffect(() => {
22-
setShown(unwrapShown(definition?.show, { note }));
23+
if (!definition) return;
24+
shouldShowTab(definition.show, { note, noteContext }).then(setShown);
2325
}, [ note ]);
2426

2527
return (
@@ -35,9 +37,3 @@ export default function StandaloneRibbonAdapter({ component }: StandaloneRibbonA
3537
/>
3638
);
3739
}
38-
39-
function unwrapShown(value: boolean | ((context: TitleContext) => boolean | null | undefined) | undefined, context: TitleContext) {
40-
if (!value) return true;
41-
if (typeof value === "boolean") return value;
42-
return !!value(context);
43-
}

apps/client/src/widgets/ribbon/ribbon-interface.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@ export interface TabContext {
1616

1717
export interface TitleContext {
1818
note: FNote | null | undefined;
19+
noteContext: NoteContext | undefined;
1920
}
2021

2122
export interface TabConfiguration {
2223
title: string | ((context: TitleContext) => string);
2324
icon: string;
2425
content: (context: TabContext) => VNode | false;
25-
show: boolean | ((context: TitleContext) => boolean | null | undefined);
26+
show: boolean | ((context: TitleContext) => Promise<boolean | null | undefined> | boolean | null | undefined);
2627
toggleCommand?: KeyboardActionNames;
2728
activate?: boolean | ((context: TitleContext) => boolean);
2829
/**

apps/desktop/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@triliumnext/desktop",
3-
"version": "0.99.3",
3+
"version": "0.99.4",
44
"description": "Build your personal knowledge base with Trilium Notes",
55
"private": true,
66
"main": "src/main.ts",

0 commit comments

Comments
 (0)