mirror of
https://github.com/zadam/trilium.git
synced 2025-10-20 15:19:01 +02:00
refactor(react/dialogs): integrate self-triggering modal in more dialogs
This commit is contained in:
parent
cd5467bf5c
commit
fa97ec6c72
@ -1,4 +1,3 @@
|
||||
import { openDialog } from "../../services/dialog.js";
|
||||
import ReactBasicWidget from "../react/ReactBasicWidget.js";
|
||||
import Modal from "../react/Modal.js";
|
||||
import { t } from "../../services/i18n.js";
|
||||
@ -9,19 +8,26 @@ import openService from "../../services/open.js";
|
||||
import { useState } from "preact/hooks";
|
||||
import type { CSSProperties } from "preact/compat";
|
||||
import type { AppInfo } from "@triliumnext/commons";
|
||||
import useTriliumEvent from "../react/hooks.jsx";
|
||||
|
||||
function AboutDialogComponent() {
|
||||
let [appInfo, setAppInfo] = useState<AppInfo | null>(null);
|
||||
|
||||
async function onShown() {
|
||||
const appInfo = await server.get<AppInfo>("app-info");
|
||||
setAppInfo(appInfo);
|
||||
}
|
||||
|
||||
let [shown, setShown] = useState(false);
|
||||
const forceWordBreak: CSSProperties = { wordBreak: "break-all" };
|
||||
|
||||
useTriliumEvent("openAboutDialog", () => setShown(true));
|
||||
|
||||
return (
|
||||
<Modal className="about-dialog" size="lg" title={t("about.title")} onShown={onShown}>
|
||||
<Modal className="about-dialog"
|
||||
size="lg"
|
||||
title={t("about.title")}
|
||||
show={shown}
|
||||
onShown={async () => {
|
||||
const appInfo = await server.get<AppInfo>("app-info");
|
||||
setAppInfo(appInfo);
|
||||
}}
|
||||
onHidden={() => setShown(false)}
|
||||
>
|
||||
<table className="table table-borderless">
|
||||
<tbody>
|
||||
<tr>
|
||||
@ -83,7 +89,4 @@ export default class AboutDialog extends ReactBasicWidget {
|
||||
return <AboutDialogComponent />;
|
||||
}
|
||||
|
||||
async openAboutDialogEvent() {
|
||||
openDialog(this.$widget);
|
||||
}
|
||||
}
|
@ -1,5 +1,4 @@
|
||||
import { EventData } from "../../components/app_context";
|
||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
||||
import { closeActiveDialog } from "../../services/dialog";
|
||||
import { t } from "../../services/i18n";
|
||||
import Modal from "../react/Modal";
|
||||
import ReactBasicWidget from "../react/ReactBasicWidget";
|
||||
@ -10,24 +9,28 @@ import { useRef, useState } from "preact/hooks";
|
||||
import tree from "../../services/tree";
|
||||
import { useEffect } from "react";
|
||||
import note_autocomplete, { Suggestion } from "../../services/note_autocomplete";
|
||||
import type { default as TextTypeWidget } from "../type_widgets/editable_text.js";
|
||||
import { default as TextTypeWidget } from "../type_widgets/editable_text.js";
|
||||
import { logError } from "../../services/ws";
|
||||
import FormGroup from "../react/FormGroup.js";
|
||||
import { refToJQuerySelector } from "../react/react_utils";
|
||||
import useTriliumEvent from "../react/hooks";
|
||||
|
||||
type LinkType = "reference-link" | "external-link" | "hyper-link";
|
||||
|
||||
interface AddLinkDialogProps {
|
||||
text?: string;
|
||||
textTypeWidget?: TextTypeWidget;
|
||||
}
|
||||
|
||||
function AddLinkDialogComponent({ text: _text, textTypeWidget }: AddLinkDialogProps) {
|
||||
const [ text, setText ] = useState(_text ?? "");
|
||||
function AddLinkDialogComponent() {
|
||||
const [ textTypeWidget, setTextTypeWidget ] = useState<TextTypeWidget>();
|
||||
const [ text, setText ] = useState<string>();
|
||||
const [ linkTitle, setLinkTitle ] = useState("");
|
||||
const hasSelection = textTypeWidget?.hasSelection();
|
||||
const [ linkType, setLinkType ] = useState<LinkType>(hasSelection ? "hyper-link" : "reference-link");
|
||||
const [ suggestion, setSuggestion ] = useState<Suggestion | null>(null);
|
||||
const [ shown, setShown ] = useState(false);
|
||||
|
||||
useTriliumEvent("showAddLinkDialog", ( { textTypeWidget, text }) => {
|
||||
setTextTypeWidget(textTypeWidget);
|
||||
setText(text);
|
||||
setShown(true);
|
||||
});
|
||||
|
||||
async function setDefaultLinkTitle(noteId: string) {
|
||||
const noteTitle = await tree.getNoteTitle(noteId);
|
||||
@ -100,7 +103,11 @@ function AddLinkDialogComponent({ text: _text, textTypeWidget }: AddLinkDialogPr
|
||||
footer={<Button text={t("add_link.button_add_link")} keyboardShortcut="Enter" />}
|
||||
onSubmit={onSubmit}
|
||||
onShown={onShown}
|
||||
onHidden={() => setSuggestion(null)}
|
||||
onHidden={() => {
|
||||
setSuggestion(null);
|
||||
setShown(false);
|
||||
}}
|
||||
show={shown}
|
||||
>
|
||||
<FormGroup label={t("add_link.note")}>
|
||||
<NoteAutocomplete
|
||||
@ -150,18 +157,9 @@ function AddLinkDialogComponent({ text: _text, textTypeWidget }: AddLinkDialogPr
|
||||
}
|
||||
|
||||
export default class AddLinkDialog extends ReactBasicWidget {
|
||||
|
||||
private props: AddLinkDialogProps = {};
|
||||
|
||||
get component() {
|
||||
return <AddLinkDialogComponent {...this.props} />;
|
||||
}
|
||||
|
||||
async showAddLinkDialogEvent({ textTypeWidget, text = "" }: EventData<"showAddLinkDialog">) {
|
||||
this.props.text = text;
|
||||
this.props.textTypeWidget = textTypeWidget;
|
||||
this.doRender();
|
||||
await openDialog(this.$widget);
|
||||
return <AddLinkDialogComponent />;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import { EventData } from "../../components/app_context";
|
||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
||||
import { closeActiveDialog } from "../../services/dialog";
|
||||
import { t } from "../../services/i18n";
|
||||
import Modal from "../react/Modal";
|
||||
import ReactBasicWidget from "../react/ReactBasicWidget";
|
||||
@ -12,50 +11,52 @@ import Button from "../react/Button";
|
||||
import bulk_action from "../../services/bulk_action";
|
||||
import toast from "../../services/toast";
|
||||
import RenameNoteBulkAction from "../bulk_actions/note/rename_note";
|
||||
import { RawHtmlBlock } from "../react/RawHtml";
|
||||
import FNote from "../../entities/fnote";
|
||||
import froca from "../../services/froca";
|
||||
import useTriliumEvent from "../react/hooks";
|
||||
|
||||
interface BulkActionProps {
|
||||
bulkActionNote?: FNote | null;
|
||||
selectedOrActiveNoteIds?: string[];
|
||||
}
|
||||
|
||||
function BulkActionComponent({ selectedOrActiveNoteIds, bulkActionNote }: BulkActionProps) {
|
||||
function BulkActionComponent() {
|
||||
const [ selectedOrActiveNoteIds, setSelectedOrActiveNoteIds ] = useState<string[]>();
|
||||
const [ bulkActionNote, setBulkActionNote ] = useState<FNote | null>();
|
||||
const [ includeDescendants, setIncludeDescendants ] = useState(false);
|
||||
const [ affectedNoteCount, setAffectedNoteCount ] = useState(0);
|
||||
const [ existingActions, setExistingActions ] = useState<RenameNoteBulkAction[]>([]);
|
||||
const [ shown, setShown ] = useState(false);
|
||||
|
||||
if (!selectedOrActiveNoteIds || !bulkActionNote) {
|
||||
return;
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
server.post<BulkActionAffectedNotes>("bulk-action/affected-notes", {
|
||||
noteIds: selectedOrActiveNoteIds,
|
||||
includeDescendants
|
||||
}).then(({ affectedNoteCount }) => setAffectedNoteCount(affectedNoteCount));
|
||||
}, [ selectedOrActiveNoteIds, includeDescendants ]);
|
||||
|
||||
function refreshExistingActions() {
|
||||
setExistingActions(bulk_action.parseActions(bulkActionNote!));
|
||||
}
|
||||
|
||||
useEffect(() => refreshExistingActions(), []);
|
||||
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
||||
if (loadResults.getAttributeRows().find((row) =>
|
||||
row.type === "label" && row.name === "action" && row.noteId === "_bulkAction")) {
|
||||
refreshExistingActions();
|
||||
}
|
||||
useTriliumEvent("openBulkActionsDialog", async ({ selectedOrActiveNoteIds }) => {
|
||||
setSelectedOrActiveNoteIds(selectedOrActiveNoteIds);
|
||||
setBulkActionNote(await froca.getNote("_bulkAction"));
|
||||
setShown(true);
|
||||
});
|
||||
|
||||
return ( selectedOrActiveNoteIds &&
|
||||
if (selectedOrActiveNoteIds && bulkActionNote) {
|
||||
useEffect(() => {
|
||||
server.post<BulkActionAffectedNotes>("bulk-action/affected-notes", {
|
||||
noteIds: selectedOrActiveNoteIds,
|
||||
includeDescendants
|
||||
}).then(({ affectedNoteCount }) => setAffectedNoteCount(affectedNoteCount));
|
||||
}, [ selectedOrActiveNoteIds, includeDescendants ]);
|
||||
|
||||
function refreshExistingActions() {
|
||||
setExistingActions(bulk_action.parseActions(bulkActionNote!));
|
||||
}
|
||||
|
||||
useEffect(() => refreshExistingActions(), []);
|
||||
useTriliumEvent("entitiesReloaded", ({ loadResults }) => {
|
||||
if (loadResults.getAttributeRows().find((row) =>
|
||||
row.type === "label" && row.name === "action" && row.noteId === "_bulkAction")) {
|
||||
refreshExistingActions();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className="bulk-actions-dialog"
|
||||
size="xl"
|
||||
title={t("bulk_actions.bulk_actions")}
|
||||
footer={<Button text={t("bulk_actions.execute_bulk_actions")} primary />}
|
||||
show={shown}
|
||||
onSubmit={async () => {
|
||||
await server.post("bulk-action/execute", {
|
||||
noteIds: selectedOrActiveNoteIds,
|
||||
@ -65,6 +66,7 @@ function BulkActionComponent({ selectedOrActiveNoteIds, bulkActionNote }: BulkAc
|
||||
toast.showMessage(t("bulk_actions.bulk_actions_executed"), 3000);
|
||||
closeActiveDialog();
|
||||
}}
|
||||
onHidden={() => setShown(false)}
|
||||
>
|
||||
<h4>{t("bulk_actions.affected_notes")}: <span>{affectedNoteCount}</span></h4>
|
||||
<FormCheckbox
|
||||
@ -114,19 +116,8 @@ function ExistingActionsList({ existingActions }: { existingActions?: RenameNote
|
||||
|
||||
export default class BulkActionsDialog extends ReactBasicWidget {
|
||||
|
||||
private props: BulkActionProps = {};
|
||||
|
||||
get component() {
|
||||
return <BulkActionComponent {...this.props} />
|
||||
}
|
||||
|
||||
async openBulkActionsDialogEvent({ selectedOrActiveNoteIds }: EventData<"openBulkActionsDialog">) {
|
||||
this.props = {
|
||||
selectedOrActiveNoteIds,
|
||||
bulkActionNote: await froca.getNote("_bulkAction")
|
||||
};
|
||||
this.doRender();
|
||||
openDialog(this.$widget);
|
||||
return <BulkActionComponent />
|
||||
}
|
||||
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
import { useRef, useState } from "preact/compat";
|
||||
import appContext, { EventData } from "../../components/app_context";
|
||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
||||
import appContext from "../../components/app_context";
|
||||
import { closeActiveDialog } from "../../services/dialog";
|
||||
import { t } from "../../services/i18n";
|
||||
import Modal from "../react/Modal";
|
||||
import ReactBasicWidget from "../react/ReactBasicWidget";
|
||||
@ -15,16 +15,32 @@ import tree from "../../services/tree";
|
||||
import branches from "../../services/branches";
|
||||
import toast from "../../services/toast";
|
||||
import NoteList from "../react/NoteList";
|
||||
import useTriliumEvent from "../react/hooks";
|
||||
|
||||
interface CloneToDialogProps {
|
||||
clonedNoteIds?: string[];
|
||||
}
|
||||
|
||||
function CloneToDialogComponent({ clonedNoteIds }: CloneToDialogProps) {
|
||||
function CloneToDialogComponent() {
|
||||
const [ clonedNoteIds, setClonedNoteIds ] = useState<string[]>();
|
||||
const [ prefix, setPrefix ] = useState("");
|
||||
const [ suggestion, setSuggestion ] = useState<Suggestion | null>(null);
|
||||
const [ shown, setShown ] = useState(false);
|
||||
const autoCompleteRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useTriliumEvent("cloneNoteIdsTo", ({ noteIds }) => {
|
||||
if (!noteIds || noteIds.length === 0) {
|
||||
noteIds = [appContext.tabManager.getActiveContextNoteId() ?? ""];
|
||||
}
|
||||
|
||||
const clonedNoteIds: string[] = [];
|
||||
|
||||
for (const noteId of noteIds) {
|
||||
if (!clonedNoteIds.includes(noteId)) {
|
||||
clonedNoteIds.push(noteId);
|
||||
}
|
||||
}
|
||||
|
||||
setClonedNoteIds(clonedNoteIds);
|
||||
setShown(true);
|
||||
});
|
||||
|
||||
function onSubmit() {
|
||||
if (!clonedNoteIds) {
|
||||
return;
|
||||
@ -49,6 +65,8 @@ function CloneToDialogComponent({ clonedNoteIds }: CloneToDialogProps) {
|
||||
footer={<Button text={t("clone_to.clone_to_selected_note")} keyboardShortcut="Enter" />}
|
||||
onSubmit={onSubmit}
|
||||
onShown={() => triggerRecentNotes(autoCompleteRef.current)}
|
||||
onHidden={() => setShown(false)}
|
||||
show={shown}
|
||||
>
|
||||
<h5>{t("clone_to.notes_to_clone")}</h5>
|
||||
<NoteList style={{ maxHeight: "200px", overflow: "auto" }} noteIds={clonedNoteIds} />
|
||||
@ -67,29 +85,9 @@ function CloneToDialogComponent({ clonedNoteIds }: CloneToDialogProps) {
|
||||
}
|
||||
|
||||
export default class CloneToDialog extends ReactBasicWidget {
|
||||
|
||||
private props: CloneToDialogProps = {};
|
||||
|
||||
get component() {
|
||||
return <CloneToDialogComponent {...this.props} />;
|
||||
}
|
||||
|
||||
async cloneNoteIdsToEvent({ noteIds }: EventData<"cloneNoteIdsTo">) {
|
||||
if (!noteIds || noteIds.length === 0) {
|
||||
noteIds = [appContext.tabManager.getActiveContextNoteId() ?? ""];
|
||||
}
|
||||
|
||||
const clonedNoteIds: string[] = [];
|
||||
|
||||
for (const noteId of noteIds) {
|
||||
if (!clonedNoteIds.includes(noteId)) {
|
||||
clonedNoteIds.push(noteId);
|
||||
}
|
||||
}
|
||||
|
||||
this.props = { clonedNoteIds };
|
||||
this.doRender();
|
||||
openDialog(this.$widget);
|
||||
return <CloneToDialogComponent />;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { openDialog } from "../../services/dialog.js";
|
||||
import ReactBasicWidget from "../react/ReactBasicWidget.js";
|
||||
import Modal from "../react/Modal.jsx";
|
||||
import { t } from "../../services/i18n.js";
|
||||
@ -7,10 +6,18 @@ import { CommandNames } from "../../components/app_context.js";
|
||||
import RawHtml from "../react/RawHtml.jsx";
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import keyboard_actions from "../../services/keyboard_actions.js";
|
||||
import useTriliumEvent from "../react/hooks.jsx";
|
||||
|
||||
function HelpDialogComponent() {
|
||||
const [ shown, setShown ] = useState(false);
|
||||
useTriliumEvent("showCheatsheet", () => setShown(true));
|
||||
|
||||
return (
|
||||
<Modal title={t("help.title")} className="help-dialog use-tn-links" minWidth="90%" size="lg" scrollable>
|
||||
<Modal
|
||||
title={t("help.title")} className="help-dialog use-tn-links" minWidth="90%" size="lg" scrollable
|
||||
onHidden={() => setShown(false)}
|
||||
show={shown}
|
||||
>
|
||||
<div className="help-cards row row-cols-md-3 g-3">
|
||||
<Card title={t("help.noteNavigation")}>
|
||||
<ul>
|
||||
@ -161,7 +168,4 @@ export default class HelpDialog extends ReactBasicWidget {
|
||||
return <HelpDialogComponent />;
|
||||
}
|
||||
|
||||
showCheatsheetEvent() {
|
||||
openDialog(this.$widget);
|
||||
}
|
||||
}
|
||||
|
@ -1,13 +1,17 @@
|
||||
import { useRef } from "react";
|
||||
import { closeActiveDialog, openDialog } from "../../services/dialog.js";
|
||||
import { closeActiveDialog } from "../../services/dialog.js";
|
||||
import { t } from "../../services/i18n.js";
|
||||
import utils from "../../services/utils.js";
|
||||
import Button from "../react/Button.js";
|
||||
import Modal from "../react/Modal.js";
|
||||
import ReactBasicWidget from "../react/ReactBasicWidget.js";
|
||||
import { useState } from "preact/hooks";
|
||||
import useTriliumEvent from "../react/hooks.jsx";
|
||||
|
||||
function IncorrectCpuArchDialogComponent() {
|
||||
const [ shown, setShown ] = useState(false);
|
||||
const downloadButtonRef = useRef<HTMLButtonElement>(null);
|
||||
useTriliumEvent("showCpuArchWarning", () => setShown(true));
|
||||
|
||||
return (
|
||||
<Modal
|
||||
@ -33,6 +37,8 @@ function IncorrectCpuArchDialogComponent() {
|
||||
<Button text={t("cpu_arch_warning.continue_anyway")}
|
||||
onClick={() => closeActiveDialog()} />
|
||||
</>}
|
||||
onHidden={() => setShown(false)}
|
||||
show={shown}
|
||||
>
|
||||
<p>{utils.isMac() ? t("cpu_arch_warning.message_macos") : t("cpu_arch_warning.message_windows")}</p>
|
||||
<p>{t("cpu_arch_warning.recommendation")}</p>
|
||||
@ -46,8 +52,4 @@ export default class IncorrectCpuArchDialog extends ReactBasicWidget {
|
||||
return <IncorrectCpuArchDialogComponent />
|
||||
}
|
||||
|
||||
showCpuArchWarningEvent() {
|
||||
openDialog(this.$widget);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useRef, useState } from "preact/compat";
|
||||
import { useCallback, useRef, useState } from "preact/compat";
|
||||
import appContext from "../../components/app_context";
|
||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
||||
import { closeActiveDialog } from "../../services/dialog";
|
||||
import { t } from "../../services/i18n";
|
||||
import server from "../../services/server";
|
||||
import toast from "../../services/toast";
|
||||
@ -8,6 +8,7 @@ import utils from "../../services/utils";
|
||||
import Modal from "../react/Modal";
|
||||
import ReactBasicWidget from "../react/ReactBasicWidget";
|
||||
import Button from "../react/Button";
|
||||
import useTriliumEvent from "../react/hooks";
|
||||
|
||||
interface RenderMarkdownResponse {
|
||||
htmlContent: string;
|
||||
@ -15,7 +16,26 @@ interface RenderMarkdownResponse {
|
||||
|
||||
function MarkdownImportDialogComponent() {
|
||||
const markdownImportTextArea = useRef<HTMLTextAreaElement>(null);
|
||||
let [ text, setText ] = useState("");
|
||||
let [ text, setText ] = useState("");
|
||||
let [ shown, setShown ] = useState(false);
|
||||
|
||||
const triggerImport = useCallback(() => {
|
||||
if (appContext.tabManager.getActiveContextNoteType() !== "text") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (utils.isElectron()) {
|
||||
const { clipboard } = utils.dynamicRequire("electron");
|
||||
const text = clipboard.readText();
|
||||
|
||||
convertMarkdownToHtml(text);
|
||||
} else {
|
||||
setShown(true);
|
||||
}
|
||||
}, []);
|
||||
|
||||
useTriliumEvent("importMarkdownInline", triggerImport);
|
||||
useTriliumEvent("pasteMarkdownIntoText", triggerImport);
|
||||
|
||||
async function sendForm() {
|
||||
await convertMarkdownToHtml(text);
|
||||
@ -28,6 +48,8 @@ function MarkdownImportDialogComponent() {
|
||||
className="markdown-import-dialog" title={t("markdown_import.dialog_title")} size="lg"
|
||||
footer={<Button className="markdown-import-button" text={t("markdown_import.import_button")} onClick={sendForm} keyboardShortcut="Ctrl+Space" />}
|
||||
onShown={() => markdownImportTextArea.current?.focus()}
|
||||
onHidden={() => setShown(false) }
|
||||
show={shown}
|
||||
>
|
||||
<p>{t("markdown_import.modal_body_text")}</p>
|
||||
<textarea ref={markdownImportTextArea} value={text}
|
||||
@ -49,26 +71,6 @@ export default class MarkdownImportDialog extends ReactBasicWidget {
|
||||
return <MarkdownImportDialogComponent />;
|
||||
}
|
||||
|
||||
async importMarkdownInlineEvent() {
|
||||
if (appContext.tabManager.getActiveContextNoteType() !== "text") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (utils.isElectron()) {
|
||||
const { clipboard } = utils.dynamicRequire("electron");
|
||||
const text = clipboard.readText();
|
||||
|
||||
convertMarkdownToHtml(text);
|
||||
} else {
|
||||
openDialog(this.$widget);
|
||||
}
|
||||
}
|
||||
|
||||
async pasteMarkdownIntoTextEvent() {
|
||||
// BC with keyboard shortcuts command
|
||||
await this.importMarkdownInlineEvent();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
async function convertMarkdownToHtml(markdownContent: string) {
|
||||
|
@ -1,8 +1,7 @@
|
||||
import ReactBasicWidget from "../react/ReactBasicWidget";
|
||||
import Modal from "../react/Modal";
|
||||
import { t } from "../../services/i18n";
|
||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
||||
import { EventData } from "../../components/app_context";
|
||||
import { closeActiveDialog } from "../../services/dialog";
|
||||
import NoteList from "../react/NoteList";
|
||||
import FormGroup from "../react/FormGroup";
|
||||
import NoteAutocomplete from "../react/NoteAutocomplete";
|
||||
@ -13,15 +12,19 @@ import tree from "../../services/tree";
|
||||
import froca from "../../services/froca";
|
||||
import branches from "../../services/branches";
|
||||
import toast from "../../services/toast";
|
||||
import useTriliumEvent from "../react/hooks";
|
||||
|
||||
interface MoveToDialogProps {
|
||||
movedBranchIds?: string[];
|
||||
}
|
||||
|
||||
function MoveToDialogComponent({ movedBranchIds }: MoveToDialogProps) {
|
||||
function MoveToDialogComponent() {
|
||||
const [ movedBranchIds, setMovedBranchIds ] = useState<string[]>();
|
||||
const [ suggestion, setSuggestion ] = useState<Suggestion | null>(null);
|
||||
const [ shown, setShown ] = useState(false);
|
||||
const autoCompleteRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useTriliumEvent("moveBranchIdsTo", ({ branchIds }) => {
|
||||
setMovedBranchIds(branchIds);
|
||||
setShown(true);
|
||||
});
|
||||
|
||||
async function onSubmit() {
|
||||
const notePath = suggestion?.notePath;
|
||||
if (!notePath) {
|
||||
@ -49,6 +52,8 @@ function MoveToDialogComponent({ movedBranchIds }: MoveToDialogProps) {
|
||||
footer={<Button text={t("move_to.move_button")} keyboardShortcut="Enter" />}
|
||||
onSubmit={onSubmit}
|
||||
onShown={() => triggerRecentNotes(autoCompleteRef.current)}
|
||||
onHidden={() => setShown(false)}
|
||||
show={shown}
|
||||
>
|
||||
<h5>{t("move_to.notes_to_move")}</h5>
|
||||
<NoteList branchIds={movedBranchIds} />
|
||||
@ -65,17 +70,8 @@ function MoveToDialogComponent({ movedBranchIds }: MoveToDialogProps) {
|
||||
|
||||
export default class MoveToDialog extends ReactBasicWidget {
|
||||
|
||||
private props: MoveToDialogProps = {};
|
||||
|
||||
get component() {
|
||||
return <MoveToDialogComponent {...this.props} />;
|
||||
}
|
||||
|
||||
async moveBranchIdsToEvent({ branchIds }: EventData<"moveBranchIdsTo">) {
|
||||
const movedBranchIds = branchIds;
|
||||
this.props = { movedBranchIds };
|
||||
this.doRender();
|
||||
openDialog(this.$widget);
|
||||
return <MoveToDialogComponent />;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,11 +1,16 @@
|
||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
||||
import { closeActiveDialog } from "../../services/dialog";
|
||||
import ReactBasicWidget from "../react/ReactBasicWidget";
|
||||
import Modal from "../react/Modal";
|
||||
import { t } from "../../services/i18n";
|
||||
import Button from "../react/Button";
|
||||
import appContext from "../../components/app_context";
|
||||
import { useState } from "preact/hooks";
|
||||
import useTriliumEvent from "../react/hooks";
|
||||
|
||||
function PasswordNotSetDialogComponent() {
|
||||
const [ shown, setShown ] = useState(false);
|
||||
useTriliumEvent("showPasswordNotSet", () => setShown(true));
|
||||
|
||||
return (
|
||||
<Modal
|
||||
size="md" className="password-not-set-dialog"
|
||||
@ -14,6 +19,8 @@ function PasswordNotSetDialogComponent() {
|
||||
closeActiveDialog();
|
||||
appContext.triggerCommand("showOptions", { section: "_optionsPassword" });
|
||||
}} />}
|
||||
onHidden={() => setShown(false)}
|
||||
show={shown}
|
||||
>
|
||||
<p>{t("password_not_set.body1")}</p>
|
||||
<p>{t("password_not_set.body2")}</p>
|
||||
@ -27,8 +34,4 @@ export default class PasswordNotSetDialog extends ReactBasicWidget {
|
||||
return <PasswordNotSetDialogComponent />;
|
||||
}
|
||||
|
||||
showPasswordNotSetEvent() {
|
||||
openDialog(this.$widget);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,16 +1,20 @@
|
||||
import { useRef, useState } from "preact/hooks";
|
||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
||||
import { closeActiveDialog } from "../../services/dialog";
|
||||
import { t } from "../../services/i18n";
|
||||
import Button from "../react/Button";
|
||||
import FormTextBox from "../react/FormTextBox";
|
||||
import Modal from "../react/Modal";
|
||||
import ReactBasicWidget from "../react/ReactBasicWidget";
|
||||
import protected_session from "../../services/protected_session";
|
||||
import useTriliumEvent from "../react/hooks";
|
||||
|
||||
function ProtectedSessionPasswordDialogComponent() {
|
||||
const [ shown, setShown ] = useState(false);
|
||||
const [ password, setPassword ] = useState("");
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
useTriliumEvent("showProtectedSessionPasswordDialog", () => setShown(true));
|
||||
|
||||
return (
|
||||
<Modal
|
||||
className="protected-session-password-dialog"
|
||||
@ -20,6 +24,8 @@ function ProtectedSessionPasswordDialogComponent() {
|
||||
footer={<Button text={t("protected_session_password.start_button")} />}
|
||||
onSubmit={() => protected_session.setupProtectedSession(password)}
|
||||
onShown={() => inputRef.current?.focus()}
|
||||
onHidden={() => setShown(false)}
|
||||
show={shown}
|
||||
>
|
||||
<label htmlFor="protected-session-password" className="col-form-label">{t("protected_session_password.form_label")}</label>
|
||||
<FormTextBox
|
||||
@ -39,10 +45,6 @@ export default class ProtectedSessionPasswordDialog extends ReactBasicWidget {
|
||||
return <ProtectedSessionPasswordDialogComponent />;
|
||||
}
|
||||
|
||||
showProtectedSessionPasswordDialogEvent() {
|
||||
openDialog(this.$widget);
|
||||
}
|
||||
|
||||
closeProtectedSessionPasswordDialogEvent() {
|
||||
closeActiveDialog();
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
import appContext, { EventData } from "../../components/app_context";
|
||||
import dialog, { closeActiveDialog, openDialog } from "../../services/dialog";
|
||||
import appContext from "../../components/app_context";
|
||||
import dialog, { closeActiveDialog } from "../../services/dialog";
|
||||
import { t } from "../../services/i18n";
|
||||
import server from "../../services/server";
|
||||
import toast from "../../services/toast";
|
||||
@ -14,14 +14,19 @@ import { formatDateTime } from "../../utils/formatters";
|
||||
import link from "../../services/link";
|
||||
import RawHtml from "../react/RawHtml";
|
||||
import ws from "../../services/ws";
|
||||
import useTriliumEvent from "../react/hooks";
|
||||
|
||||
interface RecentChangesDialogProps {
|
||||
ancestorNoteId?: string;
|
||||
}
|
||||
|
||||
function RecentChangesDialogComponent({ ancestorNoteId }: RecentChangesDialogProps) {
|
||||
function RecentChangesDialogComponent() {
|
||||
const [ ancestorNoteId, setAncestorNoteId ] = useState<string>();
|
||||
const [ groupedByDate, setGroupedByDate ] = useState<Map<String, RecentChangesRow[]>>();
|
||||
const [ needsRefresh, setNeedsRefresh ] = useState<boolean>(false);
|
||||
const [ needsRefresh, setNeedsRefresh ] = useState(false);
|
||||
const [ shown, setShown ] = useState(false);
|
||||
|
||||
useTriliumEvent("showRecentChanges", ({ ancestorNoteId }) => {
|
||||
setNeedsRefresh(true);
|
||||
setAncestorNoteId(ancestorNoteId ?? hoisted_note.getHoistedNoteId());
|
||||
setShown(true);
|
||||
});
|
||||
|
||||
if (!groupedByDate || needsRefresh) {
|
||||
useEffect(() => {
|
||||
@ -43,7 +48,7 @@ function RecentChangesDialogComponent({ ancestorNoteId }: RecentChangesDialogPro
|
||||
})
|
||||
}
|
||||
|
||||
return (ancestorNoteId &&
|
||||
return (
|
||||
<Modal
|
||||
title={t("recent_changes.title")}
|
||||
className="recent-changes-dialog"
|
||||
@ -61,6 +66,8 @@ function RecentChangesDialogComponent({ ancestorNoteId }: RecentChangesDialogPro
|
||||
}}
|
||||
/>
|
||||
}
|
||||
onHidden={() => setShown(false)}
|
||||
show={shown}
|
||||
>
|
||||
<div className="recent-changes-content">
|
||||
{groupedByDate?.size
|
||||
@ -152,18 +159,8 @@ function DeletedNoteLink({ change }: { change: RecentChangesRow }) {
|
||||
|
||||
export default class RecentChangesDialog extends ReactBasicWidget {
|
||||
|
||||
private props: RecentChangesDialogProps = {};
|
||||
|
||||
get component() {
|
||||
return <RecentChangesDialogComponent {...this.props} />
|
||||
}
|
||||
|
||||
async showRecentChangesEvent({ ancestorNoteId }: EventData<"showRecentChanges">) {
|
||||
this.props = {
|
||||
ancestorNoteId: ancestorNoteId ?? hoisted_note.getHoistedNoteId()
|
||||
};
|
||||
this.doRender();
|
||||
openDialog(this.$widget);
|
||||
return <RecentChangesDialogComponent />
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import type { RevisionPojo, RevisionItem } from "@triliumnext/commons";
|
||||
import appContext, { EventData } from "../../components/app_context";
|
||||
import appContext from "../../components/app_context";
|
||||
import FNote from "../../entities/fnote";
|
||||
import dialog, { closeActiveDialog, openDialog } from "../../services/dialog";
|
||||
import dialog, { closeActiveDialog } from "../../services/dialog";
|
||||
import froca from "../../services/froca";
|
||||
import { t } from "../../services/i18n";
|
||||
import server from "../../services/server";
|
||||
@ -18,26 +18,35 @@ import { CSSProperties } from "preact/compat";
|
||||
import open from "../../services/open";
|
||||
import ActionButton from "../react/ActionButton";
|
||||
import options from "../../services/options";
|
||||
import useTriliumEvent from "../react/hooks";
|
||||
|
||||
interface RevisionsDialogProps {
|
||||
note?: FNote;
|
||||
}
|
||||
|
||||
function RevisionsDialogComponent({ note }: RevisionsDialogProps) {
|
||||
const [ revisions, setRevisions ] = useState<RevisionItem[]>([]);
|
||||
function RevisionsDialogComponent() {
|
||||
const [ note, setNote ] = useState<FNote>();
|
||||
const [ revisions, setRevisions ] = useState<RevisionItem[]>();
|
||||
const [ currentRevision, setCurrentRevision ] = useState<RevisionItem>();
|
||||
const [ shown, setShown ] = useState(false);
|
||||
|
||||
if (note) {
|
||||
useEffect(() => {
|
||||
useTriliumEvent("showRevisions", async ({ noteId }) => {
|
||||
const note = await getNote(noteId);
|
||||
if (note) {
|
||||
setNote(note);
|
||||
setShown(true);
|
||||
}
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (note?.noteId) {
|
||||
server.get<RevisionItem[]>(`notes/${note.noteId}/revisions`).then(setRevisions);
|
||||
}, [ note.noteId ]);
|
||||
}
|
||||
} else {
|
||||
setRevisions(undefined);
|
||||
}
|
||||
}, [ note?.noteId ]);
|
||||
|
||||
if (revisions?.length && !currentRevision) {
|
||||
setCurrentRevision(revisions[0]);
|
||||
}
|
||||
|
||||
return (note &&
|
||||
return (
|
||||
<Modal
|
||||
className="revisions-dialog"
|
||||
size="xl"
|
||||
@ -49,7 +58,7 @@ function RevisionsDialogComponent({ note }: RevisionsDialogProps) {
|
||||
onClick={async () => {
|
||||
const text = t("revisions.confirm_delete_all");
|
||||
|
||||
if (await dialog.confirm(text)) {
|
||||
if (note && await dialog.confirm(text)) {
|
||||
await server.remove(`notes/${note.noteId}/revisions`);
|
||||
|
||||
closeActiveDialog();
|
||||
@ -59,11 +68,16 @@ function RevisionsDialogComponent({ note }: RevisionsDialogProps) {
|
||||
}
|
||||
footer={<RevisionFooter note={note} />}
|
||||
footerStyle={{ paddingTop: 0, paddingBottom: 0 }}
|
||||
onHidden={() => {
|
||||
setShown(false);
|
||||
setNote(undefined);
|
||||
}}
|
||||
show={shown}
|
||||
>
|
||||
<RevisionsList
|
||||
revisions={revisions}
|
||||
revisions={revisions ?? []}
|
||||
onSelect={(revisionId) => {
|
||||
const correspondingRevision = revisions.find((r) => r.revisionId === revisionId);
|
||||
const correspondingRevision = (revisions ?? []).find((r) => r.revisionId === revisionId);
|
||||
if (correspondingRevision) {
|
||||
setCurrentRevision(correspondingRevision);
|
||||
}
|
||||
@ -239,7 +253,7 @@ function RevisionContent({ revisionItem, fullRevision }: { revisionItem?: Revisi
|
||||
}
|
||||
}
|
||||
|
||||
function RevisionFooter({ note }: { note: FNote }) {
|
||||
function RevisionFooter({ note }: { note?: FNote }) {
|
||||
if (!note) {
|
||||
return <></>;
|
||||
}
|
||||
@ -268,18 +282,8 @@ function RevisionFooter({ note }: { note: FNote }) {
|
||||
|
||||
export default class RevisionsDialog extends ReactBasicWidget {
|
||||
|
||||
private props: RevisionsDialogProps = {};
|
||||
|
||||
get component() {
|
||||
return <RevisionsDialogComponent {...this.props} />
|
||||
}
|
||||
|
||||
async showRevisionsEvent({ noteId }: EventData<"showRevisions">) {
|
||||
this.props = {
|
||||
note: await getNote(noteId) ?? undefined
|
||||
};
|
||||
this.doRender();
|
||||
openDialog(this.$widget);
|
||||
return <RevisionsDialogComponent />
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { useState } from "preact/hooks";
|
||||
import { EventData } from "../../components/app_context";
|
||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
||||
import { closeActiveDialog } from "../../services/dialog";
|
||||
import { t } from "../../services/i18n";
|
||||
import Button from "../react/Button";
|
||||
import FormCheckbox from "../react/FormCheckbox";
|
||||
@ -10,13 +9,21 @@ import Modal from "../react/Modal";
|
||||
import ReactBasicWidget from "../react/ReactBasicWidget";
|
||||
import server from "../../services/server";
|
||||
import FormGroup from "../react/FormGroup";
|
||||
import useTriliumEvent from "../react/hooks";
|
||||
|
||||
function SortChildNotesDialogComponent({ parentNoteId }: { parentNoteId?: string }) {
|
||||
function SortChildNotesDialogComponent() {
|
||||
const [ parentNoteId, setParentNoteId ] = useState<string>();
|
||||
const [ sortBy, setSortBy ] = useState("title");
|
||||
const [ sortDirection, setSortDirection ] = useState("asc");
|
||||
const [ foldersFirst, setFoldersFirst ] = useState(false);
|
||||
const [ sortNatural, setSortNatural ] = useState(false);
|
||||
const [ sortLocale, setSortLocale ] = useState("");
|
||||
const [ shown, setShown ] = useState(false);
|
||||
|
||||
useTriliumEvent("sortChildNotes", ({ node }) => {
|
||||
setParentNoteId(node.data.noteId);
|
||||
setShown(true);
|
||||
});
|
||||
|
||||
async function onSubmit() {
|
||||
await server.put(`notes/${parentNoteId}/sort-children`, {
|
||||
@ -31,12 +38,14 @@ function SortChildNotesDialogComponent({ parentNoteId }: { parentNoteId?: string
|
||||
closeActiveDialog();
|
||||
}
|
||||
|
||||
return (parentNoteId &&
|
||||
return (
|
||||
<Modal
|
||||
className="sort-child-notes-dialog"
|
||||
title={t("sort_child_notes.sort_children_by")}
|
||||
size="lg" maxWidth={500}
|
||||
onSubmit={onSubmit}
|
||||
onHidden={() => setShown(false)}
|
||||
show={shown}
|
||||
footer={<Button text={t("sort_child_notes.sort")} keyboardShortcut="Enter" />}
|
||||
>
|
||||
<h5>{t("sort_child_notes.sorting_criteria")}</h5>
|
||||
@ -88,17 +97,8 @@ function SortChildNotesDialogComponent({ parentNoteId }: { parentNoteId?: string
|
||||
|
||||
export default class SortChildNotesDialog extends ReactBasicWidget {
|
||||
|
||||
private parentNoteId?: string;
|
||||
|
||||
get component() {
|
||||
return <SortChildNotesDialogComponent parentNoteId={this.parentNoteId} />;
|
||||
return <SortChildNotesDialogComponent />;
|
||||
}
|
||||
|
||||
async sortChildNotesEvent({ node }: EventData<"sortChildNotes">) {
|
||||
this.parentNoteId = node.data.noteId;
|
||||
this.doRender();
|
||||
openDialog(this.$widget);
|
||||
}
|
||||
|
||||
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "preact/compat";
|
||||
import { closeActiveDialog, openDialog } from "../../services/dialog";
|
||||
import { closeActiveDialog } from "../../services/dialog";
|
||||
import { t } from "../../services/i18n";
|
||||
import Button from "../react/Button";
|
||||
import FormCheckbox from "../react/FormCheckbox";
|
||||
@ -9,18 +9,21 @@ import Modal from "../react/Modal";
|
||||
import ReactBasicWidget from "../react/ReactBasicWidget";
|
||||
import options from "../../services/options";
|
||||
import importService from "../../services/import.js";
|
||||
import { EventData } from "../../components/app_context";
|
||||
import tree from "../../services/tree";
|
||||
import useTriliumEvent from "../react/hooks";
|
||||
|
||||
interface UploadAttachmentsDialogProps {
|
||||
parentNoteId?: string;
|
||||
}
|
||||
|
||||
function UploadAttachmentsDialogComponent({ parentNoteId }: UploadAttachmentsDialogProps) {
|
||||
function UploadAttachmentsDialogComponent() {
|
||||
const [ parentNoteId, setParentNoteId ] = useState<string>();
|
||||
const [ files, setFiles ] = useState<FileList | null>(null);
|
||||
const [ shrinkImages, setShrinkImages ] = useState(options.is("compressImages"));
|
||||
const [ isUploading, setIsUploading ] = useState(false);
|
||||
const [ description, setDescription ] = useState<string | undefined>(undefined);
|
||||
const [ shown, setShown ] = useState(false);
|
||||
|
||||
useTriliumEvent("showUploadAttachmentsDialog", ({ noteId }) => {
|
||||
setParentNoteId(noteId);
|
||||
setShown(true);
|
||||
});
|
||||
|
||||
if (parentNoteId) {
|
||||
useEffect(() => {
|
||||
@ -29,23 +32,25 @@ function UploadAttachmentsDialogComponent({ parentNoteId }: UploadAttachmentsDia
|
||||
}, [parentNoteId]);
|
||||
}
|
||||
|
||||
return (parentNoteId &&
|
||||
return (
|
||||
<Modal
|
||||
className="upload-attachments-dialog"
|
||||
size="lg"
|
||||
title={t("upload_attachments.upload_attachments_to_note")}
|
||||
footer={<Button text={t("upload_attachments.upload")} primary disabled={!files || isUploading} />}
|
||||
onSubmit={async () => {
|
||||
if (!files) {
|
||||
if (!files || !parentNoteId) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
setIsUploading(true);
|
||||
const filesCopy = Array.from(files);
|
||||
await importService.uploadFiles("attachments", parentNoteId, filesCopy, { shrinkImages });
|
||||
setIsUploading(false);
|
||||
closeActiveDialog();
|
||||
}}
|
||||
onHidden={() => setShown(false)}
|
||||
show={shown}
|
||||
>
|
||||
<FormGroup label={t("upload_attachments.choose_files")} description={description}>
|
||||
<FormFileUpload onChange={setFiles} multiple />
|
||||
@ -64,16 +69,8 @@ function UploadAttachmentsDialogComponent({ parentNoteId }: UploadAttachmentsDia
|
||||
|
||||
export default class UploadAttachmentsDialog extends ReactBasicWidget {
|
||||
|
||||
private props: UploadAttachmentsDialogProps = {};
|
||||
|
||||
get component() {
|
||||
return <UploadAttachmentsDialogComponent {...this.props} />;
|
||||
}
|
||||
|
||||
showUploadAttachmentsDialogEvent({ noteId }: EventData<"showUploadAttachmentsDialog">) {
|
||||
this.props = { parentNoteId: noteId };
|
||||
this.doRender();
|
||||
openDialog(this.$widget);
|
||||
return <UploadAttachmentsDialogComponent />;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user