mirror of
https://github.com/zadam/trilium.git
synced 2025-10-06 05:25:37 +08:00
feat(react/floating_buttons): port edit button
This commit is contained in:
parent
e290635ba5
commit
cdbb89482e
2 changed files with 63 additions and 92 deletions
|
@ -4,12 +4,16 @@ import Button from "./react/Button";
|
||||||
import ActionButton from "./react/ActionButton";
|
import ActionButton from "./react/ActionButton";
|
||||||
import FNote from "../entities/fnote";
|
import FNote from "../entities/fnote";
|
||||||
import NoteContext from "../components/note_context";
|
import NoteContext from "../components/note_context";
|
||||||
import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useTriliumEvent, useTriliumOption, useTriliumOptionBool } from "./react/hooks";
|
import { useNoteContext, useNoteLabel, useNoteLabelBoolean, useNoteProperty, useTriliumEvent, useTriliumEvents, useTriliumOption, useTriliumOptionBool } from "./react/hooks";
|
||||||
import { useContext, useEffect, useMemo, useState } from "preact/hooks";
|
import { useContext, useEffect, useMemo, useState } from "preact/hooks";
|
||||||
import { ParentComponent } from "./react/react_utils";
|
import { ParentComponent } from "./react/react_utils";
|
||||||
import Component from "../components/component";
|
import Component from "../components/component";
|
||||||
import { VNode } from "preact";
|
import { VNode } from "preact";
|
||||||
import attributes from "../services/attributes";
|
import attributes from "../services/attributes";
|
||||||
|
import appContext from "../components/app_context";
|
||||||
|
import protected_session_holder from "../services/protected_session_holder";
|
||||||
|
import options from "../services/options";
|
||||||
|
import { AttributeRow } from "../services/load_results";
|
||||||
|
|
||||||
interface FloatingButtonContext {
|
interface FloatingButtonContext {
|
||||||
parentComponent: Component;
|
parentComponent: Component;
|
||||||
|
@ -19,7 +23,7 @@ interface FloatingButtonContext {
|
||||||
|
|
||||||
interface FloatingButtonDefinition {
|
interface FloatingButtonDefinition {
|
||||||
component: (context: FloatingButtonContext) => VNode;
|
component: (context: FloatingButtonContext) => VNode;
|
||||||
isEnabled: (context: FloatingButtonContext) => boolean;
|
isEnabled: (context: FloatingButtonContext) => boolean | Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const FLOATING_BUTTON_DEFINITIONS: FloatingButtonDefinition[] = [
|
const FLOATING_BUTTON_DEFINITIONS: FloatingButtonDefinition[] = [
|
||||||
|
@ -37,9 +41,27 @@ const FLOATING_BUTTON_DEFINITIONS: FloatingButtonDefinition[] = [
|
||||||
(note.type === "mermaid" || note.getLabelValue("viewType") === "geoMap")
|
(note.type === "mermaid" || note.getLabelValue("viewType") === "geoMap")
|
||||||
&& note.isContentAvailable()
|
&& note.isContentAvailable()
|
||||||
&& noteContext.viewScope?.viewMode === "default"
|
&& noteContext.viewScope?.viewMode === "default"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
component: EditButton,
|
||||||
|
isEnabled: async ({ note, noteContext }) =>
|
||||||
|
noteContext.viewScope?.viewMode === "default"
|
||||||
|
&& (!note.isProtected || protected_session_holder.isProtectedSessionAvailable())
|
||||||
|
&& !options.is("databaseReadonly")
|
||||||
|
&& await noteContext?.isReadOnly()
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
async function getFloatingButtonDefinitions(context: FloatingButtonContext) {
|
||||||
|
const defs: FloatingButtonDefinition[] = [];
|
||||||
|
for (const def of FLOATING_BUTTON_DEFINITIONS) {
|
||||||
|
if (await def.isEnabled(context)) {
|
||||||
|
defs.push(def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return defs;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note:
|
* Note:
|
||||||
*
|
*
|
||||||
|
@ -64,15 +86,23 @@ export default function FloatingButtons() {
|
||||||
const [ refreshCounter, setRefreshCounter ] = useState(0);
|
const [ refreshCounter, setRefreshCounter ] = useState(0);
|
||||||
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
||||||
if (loadResults.getAttributeRows().find(attrRow => attributes.isAffecting(attrRow, note))) {
|
if (loadResults.getAttributeRows().find(attrRow => attributes.isAffecting(attrRow, note))) {
|
||||||
setRefreshCounter(refreshCounter+1);
|
setRefreshCounter(refreshCounter + 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
useTriliumEvent("readOnlyTemporarilyDisabled", ({ noteContext: eventNoteContext }) => {
|
||||||
|
if (noteContext?.ntxId === eventNoteContext.ntxId) {
|
||||||
|
setRefreshCounter(refreshCounter + 1);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const definitions = useMemo<FloatingButtonDefinition[]>(() => {
|
|
||||||
if (!context) return [];
|
|
||||||
return FLOATING_BUTTON_DEFINITIONS.filter(def => def.isEnabled(context));
|
|
||||||
}, [ context, refreshCounter ]);
|
|
||||||
|
|
||||||
|
// Manage the list of items
|
||||||
|
const noteMime = useNoteProperty(note, "mime");
|
||||||
|
const [ definitions, setDefinitions ] = useState<FloatingButtonDefinition[]>([]);
|
||||||
|
useEffect(() => {
|
||||||
|
if (!context) return;
|
||||||
|
getFloatingButtonDefinitions(context).then(setDefinitions);
|
||||||
|
}, [ context, refreshCounter, noteMime ]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="floating-buttons no-print">
|
<div className="floating-buttons no-print">
|
||||||
<div className="floating-buttons-children">
|
<div className="floating-buttons-children">
|
||||||
|
@ -115,6 +145,31 @@ function ToggleReadOnlyButton({ note }: FloatingButtonContext) {
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function EditButton({ noteContext }: FloatingButtonContext) {
|
||||||
|
const [ animationClass, setAnimationClass ] = useState("");
|
||||||
|
|
||||||
|
// make the edit button stand out on the first display, otherwise
|
||||||
|
// it's difficult to notice that the note is readonly
|
||||||
|
useEffect(() => {
|
||||||
|
setAnimationClass("bx-tada bx-lg");
|
||||||
|
setTimeout(() => {
|
||||||
|
setAnimationClass("");
|
||||||
|
}, 1700);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return <ActionButton
|
||||||
|
text={t("edit_button.edit_this_note")}
|
||||||
|
icon="bx bx-pencil"
|
||||||
|
className={animationClass}
|
||||||
|
onClick={() => {
|
||||||
|
if (noteContext.viewScope) {
|
||||||
|
noteContext.viewScope.readOnlyTemporarilyDisabled = true;
|
||||||
|
appContext.triggerEvent("readOnlyTemporarilyDisabled", { noteContext });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Show button that displays floating button after click on close button
|
* Show button that displays floating button after click on close button
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
import OnClickButtonWidget from "../buttons/onclick_button.js";
|
|
||||||
import appContext from "../../components/app_context.js";
|
|
||||||
import attributeService from "../../services/attributes.js";
|
|
||||||
import protectedSessionHolder from "../../services/protected_session_holder.js";
|
|
||||||
import { t } from "../../services/i18n.js";
|
|
||||||
import LoadResults from "../../services/load_results.js";
|
|
||||||
import type { AttributeRow } from "../../services/load_results.js";
|
|
||||||
import FNote from "../../entities/fnote.js";
|
|
||||||
import options from "../../services/options.js";
|
|
||||||
|
|
||||||
export default class EditButton extends OnClickButtonWidget {
|
|
||||||
isEnabled(): boolean {
|
|
||||||
return Boolean(super.isEnabled() && this.note && this.noteContext?.viewScope?.viewMode === "default");
|
|
||||||
}
|
|
||||||
|
|
||||||
constructor() {
|
|
||||||
super();
|
|
||||||
|
|
||||||
this.icon("bx-pencil")
|
|
||||||
.title(t("edit_button.edit_this_note"))
|
|
||||||
.titlePlacement("bottom")
|
|
||||||
.onClick((widget) => {
|
|
||||||
if (this.noteContext?.viewScope) {
|
|
||||||
this.noteContext.viewScope.readOnlyTemporarilyDisabled = true;
|
|
||||||
appContext.triggerEvent("readOnlyTemporarilyDisabled", { noteContext: this.noteContext });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async refreshWithNote(note: FNote): Promise<void> {
|
|
||||||
if (options.is("databaseReadonly")) {
|
|
||||||
this.toggleInt(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (note.isProtected && !protectedSessionHolder.isProtectedSessionAvailable()) {
|
|
||||||
this.toggleInt(false);
|
|
||||||
} else {
|
|
||||||
// prevent flickering by assuming hidden before async operation
|
|
||||||
this.toggleInt(false);
|
|
||||||
|
|
||||||
const wasVisible = this.isVisible();
|
|
||||||
|
|
||||||
// can't do this in isEnabled() since isReadOnly is async
|
|
||||||
const isReadOnly = await this.noteContext?.isReadOnly();
|
|
||||||
this.toggleInt(Boolean(isReadOnly));
|
|
||||||
|
|
||||||
// make the edit button stand out on the first display, otherwise
|
|
||||||
// it's difficult to notice that the note is readonly
|
|
||||||
if (this.isVisible() && !wasVisible && this.$widget) {
|
|
||||||
this.$widget.addClass("bx-tada bx-lg");
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.$widget?.removeClass("bx-tada bx-lg");
|
|
||||||
}, 1700);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await super.refreshWithNote(note);
|
|
||||||
}
|
|
||||||
|
|
||||||
entitiesReloadedEvent({ loadResults }: { loadResults: LoadResults }): void {
|
|
||||||
if (loadResults.getAttributeRows().find((attr: AttributeRow) =>
|
|
||||||
attr.type === "label" &&
|
|
||||||
attr.name?.toLowerCase().includes("readonly") &&
|
|
||||||
this.note &&
|
|
||||||
attributeService.isAffecting(attr, this.note)
|
|
||||||
)) {
|
|
||||||
if (this.noteContext?.viewScope) {
|
|
||||||
this.noteContext.viewScope.readOnlyTemporarilyDisabled = false;
|
|
||||||
}
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
readOnlyTemporarilyDisabledEvent() {
|
|
||||||
this.refresh();
|
|
||||||
}
|
|
||||||
|
|
||||||
async noteTypeMimeChangedEvent({ noteId }: { noteId: string }): Promise<void> {
|
|
||||||
if (this.isNote(noteId)) {
|
|
||||||
await this.refresh();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Add table
Reference in a new issue