mirror of
https://github.com/zadam/trilium.git
synced 2025-11-10 00:19:04 +01:00
client: add a read-only note info bar
This commit is contained in:
parent
d5cb6a86c8
commit
8e90826aef
@ -1,47 +1,48 @@
|
|||||||
import FlexContainer from "../widgets/containers/flex_container.js";
|
|
||||||
import TabRowWidget from "../widgets/tab_row.js";
|
|
||||||
import LeftPaneContainer from "../widgets/containers/left_pane_container.js";
|
|
||||||
import NoteTreeWidget from "../widgets/note_tree.js";
|
|
||||||
import NoteTitleWidget from "../widgets/note_title.jsx";
|
|
||||||
import NoteDetailWidget from "../widgets/note_detail.js";
|
|
||||||
import PromotedAttributesWidget from "../widgets/promoted_attributes.js";
|
|
||||||
import NoteIconWidget from "../widgets/note_icon.jsx";
|
|
||||||
import ScrollingContainer from "../widgets/containers/scrolling_container.js";
|
|
||||||
import RootContainer from "../widgets/containers/root_container.js";
|
|
||||||
import WatchedFileUpdateStatusWidget from "../widgets/watched_file_update_status.js";
|
|
||||||
import SpacerWidget from "../widgets/spacer.js";
|
|
||||||
import QuickSearchWidget from "../widgets/quick_search.js";
|
|
||||||
import SplitNoteContainer from "../widgets/containers/split_note_container.js";
|
|
||||||
import CreatePaneButton from "../widgets/buttons/create_pane_button.js";
|
|
||||||
import ClosePaneButton from "../widgets/buttons/close_pane_button.js";
|
|
||||||
import RightPaneContainer from "../widgets/containers/right_pane_container.js";
|
|
||||||
import NoteWrapperWidget from "../widgets/note_wrapper.js";
|
|
||||||
import FindWidget from "../widgets/find.js";
|
|
||||||
import TocWidget from "../widgets/toc.js";
|
|
||||||
import HighlightsListWidget from "../widgets/highlights_list.js";
|
|
||||||
import PasswordNoteSetDialog from "../widgets/dialogs/password_not_set.js";
|
|
||||||
import LauncherContainer from "../widgets/containers/launcher_container.js";
|
|
||||||
import MovePaneButton from "../widgets/buttons/move_pane_button.js";
|
|
||||||
import UploadAttachmentsDialog from "../widgets/dialogs/upload_attachments.js";
|
|
||||||
import ScrollPadding from "../widgets/scroll_padding.js";
|
|
||||||
import options from "../services/options.js";
|
|
||||||
import utils from "../services/utils.js";
|
|
||||||
import type { AppContext } from "../components/app_context.js";
|
|
||||||
import type { WidgetsByParent } from "../services/bundle.js";
|
|
||||||
import { applyModals } from "./layout_commons.js";
|
import { applyModals } from "./layout_commons.js";
|
||||||
import Ribbon from "../widgets/ribbon/Ribbon.jsx";
|
|
||||||
import FloatingButtons from "../widgets/FloatingButtons.jsx";
|
|
||||||
import { DESKTOP_FLOATING_BUTTONS } from "../widgets/FloatingButtonsDefinitions.jsx";
|
import { DESKTOP_FLOATING_BUTTONS } from "../widgets/FloatingButtonsDefinitions.jsx";
|
||||||
import SearchResult from "../widgets/search_result.jsx";
|
import ApiLog from "../widgets/api_log.jsx";
|
||||||
|
import ClosePaneButton from "../widgets/buttons/close_pane_button.js";
|
||||||
|
import CloseZenModeButton from "../widgets/close_zen_button.jsx";
|
||||||
|
import CreatePaneButton from "../widgets/buttons/create_pane_button.js";
|
||||||
|
import FindWidget from "../widgets/find.js";
|
||||||
|
import FlexContainer from "../widgets/containers/flex_container.js";
|
||||||
|
import FloatingButtons from "../widgets/FloatingButtons.jsx";
|
||||||
import GlobalMenu from "../widgets/buttons/global_menu.jsx";
|
import GlobalMenu from "../widgets/buttons/global_menu.jsx";
|
||||||
|
import HighlightsListWidget from "../widgets/highlights_list.js";
|
||||||
|
import LauncherContainer from "../widgets/containers/launcher_container.js";
|
||||||
|
import LeftPaneContainer from "../widgets/containers/left_pane_container.js";
|
||||||
|
import LeftPaneToggle from "../widgets/buttons/left_pane_toggle.js";
|
||||||
|
import MovePaneButton from "../widgets/buttons/move_pane_button.js";
|
||||||
|
import NoteDetailWidget from "../widgets/note_detail.js";
|
||||||
|
import NoteIconWidget from "../widgets/note_icon.jsx";
|
||||||
|
import NoteList from "../widgets/collections/NoteList.jsx";
|
||||||
|
import NoteTitleWidget from "../widgets/note_title.jsx";
|
||||||
|
import NoteTreeWidget from "../widgets/note_tree.js";
|
||||||
|
import NoteWrapperWidget from "../widgets/note_wrapper.js";
|
||||||
|
import options from "../services/options.js";
|
||||||
|
import PasswordNoteSetDialog from "../widgets/dialogs/password_not_set.js";
|
||||||
|
import PromotedAttributesWidget from "../widgets/promoted_attributes.js";
|
||||||
|
import QuickSearchWidget from "../widgets/quick_search.js";
|
||||||
|
import ReadOnlyNoteInfoBar from "../widgets/read_only_note_info-bar.jsx";
|
||||||
|
import Ribbon from "../widgets/ribbon/Ribbon.jsx";
|
||||||
|
import RightPaneContainer from "../widgets/containers/right_pane_container.js";
|
||||||
|
import RootContainer from "../widgets/containers/root_container.js";
|
||||||
|
import ScrollingContainer from "../widgets/containers/scrolling_container.js";
|
||||||
|
import ScrollPadding from "../widgets/scroll_padding.js";
|
||||||
|
import SearchResult from "../widgets/search_result.jsx";
|
||||||
|
import SharedInfo from "../widgets/shared_info.jsx";
|
||||||
|
import SpacerWidget from "../widgets/spacer.js";
|
||||||
|
import SplitNoteContainer from "../widgets/containers/split_note_container.js";
|
||||||
import SqlResults from "../widgets/sql_result.js";
|
import SqlResults from "../widgets/sql_result.js";
|
||||||
import SqlTableSchemas from "../widgets/sql_table_schemas.js";
|
import SqlTableSchemas from "../widgets/sql_table_schemas.js";
|
||||||
|
import TabRowWidget from "../widgets/tab_row.js";
|
||||||
import TitleBarButtons from "../widgets/title_bar_buttons.jsx";
|
import TitleBarButtons from "../widgets/title_bar_buttons.jsx";
|
||||||
import LeftPaneToggle from "../widgets/buttons/left_pane_toggle.js";
|
import TocWidget from "../widgets/toc.js";
|
||||||
import ApiLog from "../widgets/api_log.jsx";
|
import type { AppContext } from "../components/app_context.js";
|
||||||
import CloseZenModeButton from "../widgets/close_zen_button.jsx";
|
import type { WidgetsByParent } from "../services/bundle.js";
|
||||||
import SharedInfo from "../widgets/shared_info.jsx";
|
import UploadAttachmentsDialog from "../widgets/dialogs/upload_attachments.js";
|
||||||
import NoteList from "../widgets/collections/NoteList.jsx";
|
import utils from "../services/utils.js";
|
||||||
|
import WatchedFileUpdateStatusWidget from "../widgets/watched_file_update_status.js";
|
||||||
|
|
||||||
export default class DesktopLayout {
|
export default class DesktopLayout {
|
||||||
|
|
||||||
@ -130,6 +131,7 @@ export default class DesktopLayout {
|
|||||||
)
|
)
|
||||||
.child(<Ribbon />)
|
.child(<Ribbon />)
|
||||||
.child(<SharedInfo />)
|
.child(<SharedInfo />)
|
||||||
|
.child(<ReadOnlyNoteInfoBar />)
|
||||||
.child(new WatchedFileUpdateStatusWidget())
|
.child(new WatchedFileUpdateStatusWidget())
|
||||||
.child(<FloatingButtons items={DESKTOP_FLOATING_BUTTONS} />)
|
.child(<FloatingButtons items={DESKTOP_FLOATING_BUTTONS} />)
|
||||||
.child(
|
.child(
|
||||||
|
|||||||
@ -1,32 +1,33 @@
|
|||||||
|
import { applyModals } from "./layout_commons.js";
|
||||||
|
import { MOBILE_FLOATING_BUTTONS } from "../widgets/FloatingButtonsDefinitions.jsx";
|
||||||
|
import { useNoteContext } from "../widgets/react/hooks.jsx";
|
||||||
|
import CloseZenModeButton from "../widgets/close_zen_button.js";
|
||||||
|
import FilePropertiesTab from "../widgets/ribbon/FilePropertiesTab.jsx";
|
||||||
import FlexContainer from "../widgets/containers/flex_container.js";
|
import FlexContainer from "../widgets/containers/flex_container.js";
|
||||||
import NoteTitleWidget from "../widgets/note_title.js";
|
import FloatingButtons from "../widgets/FloatingButtons.jsx";
|
||||||
import NoteDetailWidget from "../widgets/note_detail.js";
|
|
||||||
import QuickSearchWidget from "../widgets/quick_search.js";
|
|
||||||
import NoteTreeWidget from "../widgets/note_tree.js";
|
|
||||||
import ScreenContainer from "../widgets/mobile_widgets/screen_container.js";
|
|
||||||
import ScrollingContainer from "../widgets/containers/scrolling_container.js";
|
|
||||||
import GlobalMenuWidget from "../widgets/buttons/global_menu.js";
|
import GlobalMenuWidget from "../widgets/buttons/global_menu.js";
|
||||||
import LauncherContainer from "../widgets/containers/launcher_container.js";
|
import LauncherContainer from "../widgets/containers/launcher_container.js";
|
||||||
import RootContainer from "../widgets/containers/root_container.js";
|
|
||||||
import SharedInfoWidget from "../widgets/shared_info.js";
|
|
||||||
import PromotedAttributesWidget from "../widgets/promoted_attributes.js";
|
|
||||||
import SidebarContainer from "../widgets/mobile_widgets/sidebar_container.js";
|
|
||||||
import type AppContext from "../components/app_context.js";
|
|
||||||
import TabRowWidget from "../widgets/tab_row.js";
|
|
||||||
import MobileEditorToolbar from "../widgets/type_widgets/ckeditor/mobile_editor_toolbar.js";
|
|
||||||
import { applyModals } from "./layout_commons.js";
|
|
||||||
import FilePropertiesTab from "../widgets/ribbon/FilePropertiesTab.jsx";
|
|
||||||
import { useNoteContext } from "../widgets/react/hooks.jsx";
|
|
||||||
import FloatingButtons from "../widgets/FloatingButtons.jsx";
|
|
||||||
import { MOBILE_FLOATING_BUTTONS } from "../widgets/FloatingButtonsDefinitions.jsx";
|
|
||||||
import ToggleSidebarButton from "../widgets/mobile_widgets/toggle_sidebar_button.jsx";
|
|
||||||
import CloseZenModeButton from "../widgets/close_zen_button.js";
|
|
||||||
import NoteWrapperWidget from "../widgets/note_wrapper.js";
|
|
||||||
import MobileDetailMenu from "../widgets/mobile_widgets/mobile_detail_menu.js";
|
import MobileDetailMenu from "../widgets/mobile_widgets/mobile_detail_menu.js";
|
||||||
|
import MobileEditorToolbar from "../widgets/type_widgets/ckeditor/mobile_editor_toolbar.js";
|
||||||
|
import NoteDetailWidget from "../widgets/note_detail.js";
|
||||||
import NoteList from "../widgets/collections/NoteList.jsx";
|
import NoteList from "../widgets/collections/NoteList.jsx";
|
||||||
import StandaloneRibbonAdapter from "../widgets/ribbon/components/StandaloneRibbonAdapter.jsx";
|
import NoteTitleWidget from "../widgets/note_title.js";
|
||||||
|
import NoteTreeWidget from "../widgets/note_tree.js";
|
||||||
|
import NoteWrapperWidget from "../widgets/note_wrapper.js";
|
||||||
|
import PromotedAttributesWidget from "../widgets/promoted_attributes.js";
|
||||||
|
import QuickSearchWidget from "../widgets/quick_search.js";
|
||||||
|
import ReadOnlyNoteInfoBar from "../widgets/read_only_note_info-bar.jsx";
|
||||||
|
import RootContainer from "../widgets/containers/root_container.js";
|
||||||
|
import ScreenContainer from "../widgets/mobile_widgets/screen_container.js";
|
||||||
|
import ScrollingContainer from "../widgets/containers/scrolling_container.js";
|
||||||
import SearchDefinitionTab from "../widgets/ribbon/SearchDefinitionTab.jsx";
|
import SearchDefinitionTab from "../widgets/ribbon/SearchDefinitionTab.jsx";
|
||||||
import SearchResult from "../widgets/search_result.jsx";
|
import SearchResult from "../widgets/search_result.jsx";
|
||||||
|
import SharedInfoWidget from "../widgets/shared_info.js";
|
||||||
|
import SidebarContainer from "../widgets/mobile_widgets/sidebar_container.js";
|
||||||
|
import StandaloneRibbonAdapter from "../widgets/ribbon/components/StandaloneRibbonAdapter.jsx";
|
||||||
|
import TabRowWidget from "../widgets/tab_row.js";
|
||||||
|
import ToggleSidebarButton from "../widgets/mobile_widgets/toggle_sidebar_button.jsx";
|
||||||
|
import type AppContext from "../components/app_context.js";
|
||||||
|
|
||||||
const MOBILE_CSS = `
|
const MOBILE_CSS = `
|
||||||
<style>
|
<style>
|
||||||
@ -150,6 +151,7 @@ export default class MobileLayout {
|
|||||||
.child(<MobileDetailMenu />)
|
.child(<MobileDetailMenu />)
|
||||||
)
|
)
|
||||||
.child(<SharedInfoWidget />)
|
.child(<SharedInfoWidget />)
|
||||||
|
.child(<ReadOnlyNoteInfoBar />)
|
||||||
.child(<FloatingButtons items={MOBILE_FLOATING_BUTTONS} />)
|
.child(<FloatingButtons items={MOBILE_FLOATING_BUTTONS} />)
|
||||||
.child(new PromotedAttributesWidget())
|
.child(new PromotedAttributesWidget())
|
||||||
.child(
|
.child(
|
||||||
|
|||||||
@ -1636,6 +1636,12 @@
|
|||||||
"shared_locally": "This note is shared locally on {{- link}}.",
|
"shared_locally": "This note is shared locally on {{- link}}.",
|
||||||
"help_link": "For help visit <a href=\"https://triliumnext.github.io/Docs/Wiki/sharing.html\">wiki</a>."
|
"help_link": "For help visit <a href=\"https://triliumnext.github.io/Docs/Wiki/sharing.html\">wiki</a>."
|
||||||
},
|
},
|
||||||
|
"read-only-info": {
|
||||||
|
"read-only-note": "This is a read-only note.",
|
||||||
|
"auto-read-only-note": "This note was automatically set to read-only to improve navigation speed.",
|
||||||
|
"auto-read-only-learn-more": "Learn more about Automatic Read-Only Mode"
|
||||||
|
|
||||||
|
},
|
||||||
"note_types": {
|
"note_types": {
|
||||||
"text": "Text",
|
"text": "Text",
|
||||||
"code": "Code",
|
"code": "Code",
|
||||||
|
|||||||
22
apps/client/src/widgets/read-only-note-info-bar.css
Normal file
22
apps/client/src/widgets/read-only-note-info-bar.css
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
div.read-only-note-info-bar-widget {
|
||||||
|
contain: none;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
background: var(--read-only-note-info-bar-background, var(--accented-background-color));
|
||||||
|
padding: 6px 20px;
|
||||||
|
color: var(--read-only-note-info-bar-color);
|
||||||
|
opacity: 0;
|
||||||
|
max-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.read-only-note-info-bar-widget.visible {
|
||||||
|
max-height: unset;
|
||||||
|
opacity: 1;
|
||||||
|
transition: opacity 300ms ease-out;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.read-only-note-info-bar-widget button {
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 2px 8px;
|
||||||
|
}
|
||||||
80
apps/client/src/widgets/read_only_note_info-bar.tsx
Normal file
80
apps/client/src/widgets/read_only_note_info-bar.tsx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import "./read-only-note-info-bar.css";
|
||||||
|
import { t } from "../services/i18n";
|
||||||
|
import { useCallback, useEffect, useState } from "preact/hooks";
|
||||||
|
import { useNoteContext, useTriliumEvent } from "./react/hooks"
|
||||||
|
import appContext from "../components/app_context";
|
||||||
|
import Button from "./react/Button";
|
||||||
|
import FNote from "../entities/fnote";
|
||||||
|
import NoteContext from "../components/note_context";
|
||||||
|
import options from "../services/options";
|
||||||
|
import protected_session_holder from "../services/protected_session_holder";
|
||||||
|
|
||||||
|
export default function ReadOnlyNoteInfoBar() {
|
||||||
|
const {isReadOnly, enableEditing} = useIsReadOnly();
|
||||||
|
const {note} = useNoteContext();
|
||||||
|
|
||||||
|
return <div class={`read-only-note-info-bar-widget ${(isReadOnly) ? " visible" : ""}`}>
|
||||||
|
{isReadOnly && <>
|
||||||
|
{note?.isLabelTruthy("readOnly") ? (
|
||||||
|
<div>{t("read-only-info.read-only-note")}</div>
|
||||||
|
) : (
|
||||||
|
<div>
|
||||||
|
{t("read-only-info.auto-read-only-note")}
|
||||||
|
|
||||||
|
<a class="tn-link"
|
||||||
|
href="https://docs.triliumnotes.org/user-guide/concepts/notes/read-only-notes#automatic-read-only-mode">
|
||||||
|
|
||||||
|
{t("read-only-info.auto-read-only-learn-more")}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<Button text="Edit note" icon="bx-pencil" onClick={() => enableEditing()} />
|
||||||
|
</>}
|
||||||
|
</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function useIsReadOnly() {
|
||||||
|
const {note, noteContext} = useNoteContext();
|
||||||
|
const [isReadOnly, setIsReadOnly] = useState(false);
|
||||||
|
|
||||||
|
const enableEditing = useCallback(() => {
|
||||||
|
if (noteContext?.viewScope) {
|
||||||
|
noteContext.viewScope.readOnlyTemporarilyDisabled = true;
|
||||||
|
appContext.triggerEvent("readOnlyTemporarilyDisabled", {noteContext});
|
||||||
|
}
|
||||||
|
}, [noteContext]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (note && noteContext) {
|
||||||
|
isNoteReadOnly(note, noteContext).then((readOnly) => {
|
||||||
|
setIsReadOnly(readOnly);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [note, noteContext]);
|
||||||
|
|
||||||
|
useTriliumEvent("readOnlyTemporarilyDisabled", ({noteContext: eventNoteContext}) => {
|
||||||
|
if (noteContext?.ntxId === eventNoteContext.ntxId) {
|
||||||
|
setIsReadOnly(false);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return {isReadOnly, enableEditing};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function isNoteReadOnly(note: FNote, noteContext: NoteContext) {
|
||||||
|
|
||||||
|
if (note.isProtected && !protected_session_holder.isProtectedSessionAvailable()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (options.is("databaseReadonly")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (noteContext.viewScope?.viewMode !== "default" || !await noteContext.isReadOnly()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user