You've already forked joplin
mirror of
https://github.com/laurent22/joplin.git
synced 2025-07-13 00:10:37 +02:00
Chore: Desktop: Fix eslint issues and strengthen types in NoteEditor.tsx (#10449)
This commit is contained in:
@ -322,6 +322,8 @@ packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.js
|
|||||||
packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.js
|
packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.js
|
||||||
packages/app-desktop/gui/NoteEditor/utils/useNoteSearchBar.js
|
packages/app-desktop/gui/NoteEditor/utils/useNoteSearchBar.js
|
||||||
packages/app-desktop/gui/NoteEditor/utils/usePluginServiceRegistration.js
|
packages/app-desktop/gui/NoteEditor/utils/usePluginServiceRegistration.js
|
||||||
|
packages/app-desktop/gui/NoteEditor/utils/useScheduleSaveCallbacks.js
|
||||||
|
packages/app-desktop/gui/NoteEditor/utils/useScrollWhenReadyOptions.js
|
||||||
packages/app-desktop/gui/NoteEditor/utils/useSearchMarkers.js
|
packages/app-desktop/gui/NoteEditor/utils/useSearchMarkers.js
|
||||||
packages/app-desktop/gui/NoteEditor/utils/useWindowCommandHandler.js
|
packages/app-desktop/gui/NoteEditor/utils/useWindowCommandHandler.js
|
||||||
packages/app-desktop/gui/NoteList/NoteList2.js
|
packages/app-desktop/gui/NoteList/NoteList2.js
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@ -301,6 +301,8 @@ packages/app-desktop/gui/NoteEditor/utils/useMarkupToHtml.js
|
|||||||
packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.js
|
packages/app-desktop/gui/NoteEditor/utils/useMessageHandler.js
|
||||||
packages/app-desktop/gui/NoteEditor/utils/useNoteSearchBar.js
|
packages/app-desktop/gui/NoteEditor/utils/useNoteSearchBar.js
|
||||||
packages/app-desktop/gui/NoteEditor/utils/usePluginServiceRegistration.js
|
packages/app-desktop/gui/NoteEditor/utils/usePluginServiceRegistration.js
|
||||||
|
packages/app-desktop/gui/NoteEditor/utils/useScheduleSaveCallbacks.js
|
||||||
|
packages/app-desktop/gui/NoteEditor/utils/useScrollWhenReadyOptions.js
|
||||||
packages/app-desktop/gui/NoteEditor/utils/useSearchMarkers.js
|
packages/app-desktop/gui/NoteEditor/utils/useSearchMarkers.js
|
||||||
packages/app-desktop/gui/NoteEditor/utils/useWindowCommandHandler.js
|
packages/app-desktop/gui/NoteEditor/utils/useWindowCommandHandler.js
|
||||||
packages/app-desktop/gui/NoteList/NoteList2.js
|
packages/app-desktop/gui/NoteList/NoteList2.js
|
||||||
|
@ -26,6 +26,10 @@ export interface AppStateDialog {
|
|||||||
props: Record<string, any>;
|
props: Record<string, any>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface EditorScrollPercents {
|
||||||
|
[noteId: string]: number;
|
||||||
|
}
|
||||||
|
|
||||||
export interface AppState extends State {
|
export interface AppState extends State {
|
||||||
route: AppStateRoute;
|
route: AppStateRoute;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
@ -34,8 +38,7 @@ export interface AppState extends State {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
windowContentSize: any;
|
windowContentSize: any;
|
||||||
watchedNoteFiles: string[];
|
watchedNoteFiles: string[];
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
lastEditorScrollPercents: EditorScrollPercents;
|
||||||
lastEditorScrollPercents: any;
|
|
||||||
devToolsVisible: boolean;
|
devToolsVisible: boolean;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
visibleDialogs: any; // empty object if no dialog is visible. Otherwise contains the list of visible dialogs.
|
visibleDialogs: any; // empty object if no dialog is visible. Otherwise contains the list of visible dialogs.
|
||||||
|
@ -3,19 +3,18 @@ import { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
|||||||
import TinyMCE from './NoteBody/TinyMCE/TinyMCE';
|
import TinyMCE from './NoteBody/TinyMCE/TinyMCE';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import MultiNoteActions from '../MultiNoteActions';
|
import MultiNoteActions from '../MultiNoteActions';
|
||||||
import { htmlToMarkdown, formNoteToNote } from './utils';
|
import { htmlToMarkdown } from './utils';
|
||||||
import useSearchMarkers from './utils/useSearchMarkers';
|
import useSearchMarkers from './utils/useSearchMarkers';
|
||||||
import useNoteSearchBar from './utils/useNoteSearchBar';
|
import useNoteSearchBar from './utils/useNoteSearchBar';
|
||||||
import useMessageHandler from './utils/useMessageHandler';
|
import useMessageHandler from './utils/useMessageHandler';
|
||||||
import useWindowCommandHandler from './utils/useWindowCommandHandler';
|
import useWindowCommandHandler from './utils/useWindowCommandHandler';
|
||||||
import useDropHandler from './utils/useDropHandler';
|
import useDropHandler from './utils/useDropHandler';
|
||||||
import useMarkupToHtml from './utils/useMarkupToHtml';
|
import useMarkupToHtml from './utils/useMarkupToHtml';
|
||||||
import useFormNote, { OnLoadEvent } from './utils/useFormNote';
|
import useFormNote, { OnLoadEvent, SetFormNote } from './utils/useFormNote';
|
||||||
import useEffectiveNoteId from './utils/useEffectiveNoteId';
|
import useEffectiveNoteId from './utils/useEffectiveNoteId';
|
||||||
import useFolder from './utils/useFolder';
|
import useFolder from './utils/useFolder';
|
||||||
import styles_ from './styles';
|
import styles_ from './styles';
|
||||||
import { NoteEditorProps, FormNote, ScrollOptions, ScrollOptionTypes, OnChangeEvent, NoteBodyEditorProps, AllAssetsOptions, NoteBodyEditorRef } from './utils/types';
|
import { NoteEditorProps, FormNote, OnChangeEvent, NoteBodyEditorProps, AllAssetsOptions, NoteBodyEditorRef } from './utils/types';
|
||||||
import ResourceEditWatcher from '@joplin/lib/services/ResourceEditWatcher/index';
|
|
||||||
import CommandService from '@joplin/lib/services/CommandService';
|
import CommandService from '@joplin/lib/services/CommandService';
|
||||||
import ToolbarButton from '../ToolbarButton/ToolbarButton';
|
import ToolbarButton from '../ToolbarButton/ToolbarButton';
|
||||||
import Button, { ButtonLevel } from '../Button/Button';
|
import Button, { ButtonLevel } from '../Button/Button';
|
||||||
@ -26,7 +25,6 @@ import { _, _n } from '@joplin/lib/locale';
|
|||||||
import TagList from '../TagList';
|
import TagList from '../TagList';
|
||||||
import NoteTitleBar from './NoteTitle/NoteTitleBar';
|
import NoteTitleBar from './NoteTitle/NoteTitleBar';
|
||||||
import markupLanguageUtils from '../../utils/markupLanguageUtils';
|
import markupLanguageUtils from '../../utils/markupLanguageUtils';
|
||||||
import usePrevious from '../hooks/usePrevious';
|
|
||||||
import Setting from '@joplin/lib/models/Setting';
|
import Setting from '@joplin/lib/models/Setting';
|
||||||
import stateToWhenClauseContext from '../../services/commands/stateToWhenClauseContext';
|
import stateToWhenClauseContext from '../../services/commands/stateToWhenClauseContext';
|
||||||
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
|
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
|
||||||
@ -37,7 +35,7 @@ import NoteSearchBar from '../NoteSearchBar';
|
|||||||
import { reg } from '@joplin/lib/registry';
|
import { reg } from '@joplin/lib/registry';
|
||||||
import Note from '@joplin/lib/models/Note';
|
import Note from '@joplin/lib/models/Note';
|
||||||
import Folder from '@joplin/lib/models/Folder';
|
import Folder from '@joplin/lib/models/Folder';
|
||||||
const bridge = require('@electron/remote').require('./bridge').default;
|
import bridge from '../../services/bridge';
|
||||||
import NoteRevisionViewer from '../NoteRevisionViewer';
|
import NoteRevisionViewer from '../NoteRevisionViewer';
|
||||||
import { parseShareCache } from '@joplin/lib/services/share/reducer';
|
import { parseShareCache } from '@joplin/lib/services/share/reducer';
|
||||||
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
|
import useAsyncEffect from '@joplin/lib/hooks/useAsyncEffect';
|
||||||
@ -51,6 +49,8 @@ import CodeMirror5 from './NoteBody/CodeMirror/v5/CodeMirror';
|
|||||||
import { openItemById } from './utils/contextMenu';
|
import { openItemById } from './utils/contextMenu';
|
||||||
import getPluginSettingValue from '@joplin/lib/services/plugins/utils/getPluginSettingValue';
|
import getPluginSettingValue from '@joplin/lib/services/plugins/utils/getPluginSettingValue';
|
||||||
import { MarkupLanguage } from '@joplin/renderer';
|
import { MarkupLanguage } from '@joplin/renderer';
|
||||||
|
import useScrollWhenReadyOptions from './utils/useScrollWhenReadyOptions';
|
||||||
|
import useScheduleSaveCallbacks from './utils/useScheduleSaveCallbacks';
|
||||||
|
|
||||||
const commands = [
|
const commands = [
|
||||||
require('./commands/showRevisions'),
|
require('./commands/showRevisions'),
|
||||||
@ -61,20 +61,21 @@ const toolbarButtonUtils = new ToolbarButtonUtils(CommandService.instance());
|
|||||||
function NoteEditor(props: NoteEditorProps) {
|
function NoteEditor(props: NoteEditorProps) {
|
||||||
const [showRevisions, setShowRevisions] = useState(false);
|
const [showRevisions, setShowRevisions] = useState(false);
|
||||||
const [titleHasBeenManuallyChanged, setTitleHasBeenManuallyChanged] = useState(false);
|
const [titleHasBeenManuallyChanged, setTitleHasBeenManuallyChanged] = useState(false);
|
||||||
const [scrollWhenReady, setScrollWhenReady] = useState<ScrollOptions>(null);
|
|
||||||
const [isReadOnly, setIsReadOnly] = useState<boolean>(false);
|
const [isReadOnly, setIsReadOnly] = useState<boolean>(false);
|
||||||
|
|
||||||
const editorRef = useRef<NoteBodyEditorRef>();
|
const editorRef = useRef<NoteBodyEditorRef>();
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
const titleInputRef = useRef<HTMLInputElement>();
|
||||||
const titleInputRef = useRef<any>();
|
|
||||||
const isMountedRef = useRef(true);
|
const isMountedRef = useRef(true);
|
||||||
const noteSearchBarRef = useRef(null);
|
const noteSearchBarRef = useRef(null);
|
||||||
|
|
||||||
|
const setFormNoteRef = useRef<SetFormNote>();
|
||||||
|
const { saveNoteIfWillChange, scheduleSaveNote } = useScheduleSaveCallbacks({
|
||||||
|
setFormNote: setFormNoteRef, dispatch: props.dispatch, editorRef,
|
||||||
|
});
|
||||||
const formNote_beforeLoad = useCallback(async (event: OnLoadEvent) => {
|
const formNote_beforeLoad = useCallback(async (event: OnLoadEvent) => {
|
||||||
await saveNoteIfWillChange(event.formNote);
|
await saveNoteIfWillChange(event.formNote);
|
||||||
setShowRevisions(false);
|
setShowRevisions(false);
|
||||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
}, [saveNoteIfWillChange]);
|
||||||
}, []);
|
|
||||||
|
|
||||||
const formNote_afterLoad = useCallback(async () => {
|
const formNote_afterLoad = useCallback(async () => {
|
||||||
setTitleHasBeenManuallyChanged(false);
|
setTitleHasBeenManuallyChanged(false);
|
||||||
@ -92,6 +93,7 @@ function NoteEditor(props: NoteEditorProps) {
|
|||||||
onBeforeLoad: formNote_beforeLoad,
|
onBeforeLoad: formNote_beforeLoad,
|
||||||
onAfterLoad: formNote_afterLoad,
|
onAfterLoad: formNote_afterLoad,
|
||||||
});
|
});
|
||||||
|
setFormNoteRef.current = setFormNote;
|
||||||
|
|
||||||
const formNoteRef = useRef<FormNote>();
|
const formNoteRef = useRef<FormNote>();
|
||||||
formNoteRef.current = { ...formNote };
|
formNoteRef.current = { ...formNote };
|
||||||
@ -116,53 +118,6 @@ function NoteEditor(props: NoteEditorProps) {
|
|||||||
|
|
||||||
const styles = styles_(props);
|
const styles = styles_(props);
|
||||||
|
|
||||||
function scheduleSaveNote(formNote: FormNote) {
|
|
||||||
if (!formNote.saveActionQueue) throw new Error('saveActionQueue is not set!!'); // Sanity check
|
|
||||||
|
|
||||||
// reg.logger().debug('Scheduling...', formNote);
|
|
||||||
|
|
||||||
const makeAction = (formNote: FormNote) => {
|
|
||||||
return async function() {
|
|
||||||
const note = await formNoteToNote(formNote);
|
|
||||||
reg.logger().debug('Saving note...', note);
|
|
||||||
const savedNote = await Note.save(note);
|
|
||||||
|
|
||||||
setFormNote((prev: FormNote) => {
|
|
||||||
return { ...prev, user_updated_time: savedNote.user_updated_time, hasChanged: false };
|
|
||||||
});
|
|
||||||
|
|
||||||
void ExternalEditWatcher.instance().updateNoteFile(savedNote);
|
|
||||||
|
|
||||||
props.dispatch({
|
|
||||||
type: 'EDITOR_NOTE_STATUS_REMOVE',
|
|
||||||
id: formNote.id,
|
|
||||||
});
|
|
||||||
|
|
||||||
eventManager.emit(EventName.NoteContentChange, { note: savedNote });
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
formNote.saveActionQueue.push(makeAction(formNote));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveNoteIfWillChange(formNote: FormNote) {
|
|
||||||
if (!formNote.id || !formNote.bodyWillChangeId) return;
|
|
||||||
|
|
||||||
const body = await editorRef.current.content();
|
|
||||||
|
|
||||||
scheduleSaveNote({
|
|
||||||
...formNote,
|
|
||||||
body: body,
|
|
||||||
bodyWillChangeId: 0,
|
|
||||||
bodyChangeId: 0,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveNoteAndWait(formNote: FormNote) {
|
|
||||||
await saveNoteIfWillChange(formNote);
|
|
||||||
return formNote.saveActionQueue.waitForAllDone();
|
|
||||||
}
|
|
||||||
|
|
||||||
const whiteBackgroundNoteRendering = formNote.markup_language === MarkupLanguage.Html;
|
const whiteBackgroundNoteRendering = formNote.markup_language === MarkupLanguage.Html;
|
||||||
|
|
||||||
const markupToHtml = useMarkupToHtml({
|
const markupToHtml = useMarkupToHtml({
|
||||||
@ -201,26 +156,8 @@ function NoteEditor(props: NoteEditorProps) {
|
|||||||
id: formNote.id,
|
id: formNote.id,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
}, [props.isProvisional, formNote.id, props.dispatch]);
|
||||||
}, [props.isProvisional, formNote.id]);
|
|
||||||
|
|
||||||
const previousNoteId = usePrevious(formNote.id);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (formNote.id === previousNoteId) return;
|
|
||||||
|
|
||||||
if (editorRef.current) {
|
|
||||||
editorRef.current.resetScroll();
|
|
||||||
}
|
|
||||||
|
|
||||||
setScrollWhenReady({
|
|
||||||
type: props.selectedNoteHash ? ScrollOptionTypes.Hash : ScrollOptionTypes.Percent,
|
|
||||||
value: props.selectedNoteHash ? props.selectedNoteHash : props.lastEditorScrollPercents[formNote.id] || 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
void ResourceEditWatcher.instance().stopWatchingAll();
|
|
||||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
|
||||||
}, [formNote.id, previousNoteId]);
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
const onFieldChange = useCallback((field: string, value: any, changeId = 0) => {
|
const onFieldChange = useCallback((field: string, value: any, changeId = 0) => {
|
||||||
@ -263,19 +200,16 @@ function NoteEditor(props: NoteEditorProps) {
|
|||||||
// The previously loaded note, that was modified, will be saved via saveNoteIfWillChange()
|
// The previously loaded note, that was modified, will be saved via saveNoteIfWillChange()
|
||||||
} else {
|
} else {
|
||||||
setFormNote(newNote);
|
setFormNote(newNote);
|
||||||
scheduleSaveNote(newNote);
|
void scheduleSaveNote(newNote);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
}, [handleProvisionalFlag, formNote, setFormNote, isNewNote, titleHasBeenManuallyChanged, scheduleSaveNote]);
|
||||||
}, [handleProvisionalFlag, formNote, isNewNote, titleHasBeenManuallyChanged]);
|
|
||||||
|
|
||||||
useWindowCommandHandler({
|
useWindowCommandHandler({
|
||||||
dispatch: props.dispatch,
|
dispatch: props.dispatch,
|
||||||
formNote,
|
|
||||||
setShowLocalSearch,
|
setShowLocalSearch,
|
||||||
noteSearchBarRef,
|
noteSearchBarRef,
|
||||||
editorRef,
|
editorRef,
|
||||||
titleInputRef,
|
titleInputRef,
|
||||||
saveNoteAndWait,
|
|
||||||
setFormNote,
|
setFormNote,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -339,10 +273,15 @@ function NoteEditor(props: NoteEditorProps) {
|
|||||||
id: formNote.id,
|
id: formNote.id,
|
||||||
status: 'saving',
|
status: 'saving',
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
}, [formNote, setFormNote, handleProvisionalFlag, props.dispatch]);
|
||||||
}, [formNote, handleProvisionalFlag]);
|
|
||||||
|
|
||||||
const onMessage = useMessageHandler(scrollWhenReady, setScrollWhenReady, editorRef, setLocalSearchResultCount, props.dispatch, formNote, htmlToMarkdown, markupToHtml);
|
const { scrollWhenReady, clearScrollWhenReady } = useScrollWhenReadyOptions({
|
||||||
|
noteId: formNote.id,
|
||||||
|
selectedNoteHash: props.selectedNoteHash,
|
||||||
|
lastEditorScrollPercents: props.lastEditorScrollPercents,
|
||||||
|
editorRef,
|
||||||
|
});
|
||||||
|
const onMessage = useMessageHandler(scrollWhenReady, clearScrollWhenReady, editorRef, setLocalSearchResultCount, props.dispatch, formNote, htmlToMarkdown, markupToHtml);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
const externalEditWatcher_noteChange = useCallback((event: any) => {
|
const externalEditWatcher_noteChange = useCallback((event: any) => {
|
||||||
@ -355,8 +294,7 @@ function NoteEditor(props: NoteEditorProps) {
|
|||||||
|
|
||||||
setFormNote(newFormNote);
|
setFormNote(newFormNote);
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
}, [formNote, setFormNote]);
|
||||||
}, [formNote]);
|
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
const onNotePropertyChange = useCallback((event: any) => {
|
const onNotePropertyChange = useCallback((event: any) => {
|
||||||
@ -373,8 +311,7 @@ function NoteEditor(props: NoteEditorProps) {
|
|||||||
|
|
||||||
return newFormNote;
|
return newFormNote;
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
}, [setFormNote]);
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
eventManager.on(EventName.AlarmChange, onNotePropertyChange);
|
eventManager.on(EventName.AlarmChange, onNotePropertyChange);
|
||||||
@ -409,8 +346,7 @@ function NoteEditor(props: NoteEditorProps) {
|
|||||||
});
|
});
|
||||||
}, [props.dispatch]);
|
}, [props.dispatch]);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
function renderNoNotes(rootStyle: React.CSSProperties) {
|
||||||
function renderNoNotes(rootStyle: any) {
|
|
||||||
const emptyDivStyle = {
|
const emptyDivStyle = {
|
||||||
backgroundColor: 'black',
|
backgroundColor: 'black',
|
||||||
opacity: 0.1,
|
opacity: 0.1,
|
||||||
@ -493,7 +429,7 @@ function NoteEditor(props: NoteEditorProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onRichTextReadMoreLinkClick = useCallback(() => {
|
const onRichTextReadMoreLinkClick = useCallback(() => {
|
||||||
bridge().openExternal('https://joplinapp.org/help/apps/rich_text_editor');
|
void bridge().openExternal('https://joplinapp.org/help/apps/rich_text_editor');
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const onRichTextDismissLinkClick = useCallback(() => {
|
const onRichTextDismissLinkClick = useCallback(() => {
|
||||||
@ -521,8 +457,7 @@ function NoteEditor(props: NoteEditorProps) {
|
|||||||
if (showRevisions) {
|
if (showRevisions) {
|
||||||
const theme = themeStyle(props.themeId);
|
const theme = themeStyle(props.themeId);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
const revStyle: React.CSSProperties = {
|
||||||
const revStyle: any = {
|
|
||||||
// ...props.style,
|
// ...props.style,
|
||||||
display: 'inline-flex',
|
display: 'inline-flex',
|
||||||
padding: theme.margin,
|
padding: theme.margin,
|
||||||
|
@ -36,8 +36,7 @@ interface Props {
|
|||||||
noteTitle: string;
|
noteTitle: string;
|
||||||
noteIsTodo: number;
|
noteIsTodo: number;
|
||||||
isProvisional: boolean;
|
isProvisional: boolean;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
titleInputRef: React.RefObject<HTMLInputElement>;
|
||||||
titleInputRef: any;
|
|
||||||
onTitleChange(event: ChangeEvent<HTMLInputElement>): void;
|
onTitleChange(event: ChangeEvent<HTMLInputElement>): void;
|
||||||
disabled: boolean;
|
disabled: boolean;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { useState, useEffect, useCallback } from 'react';
|
import { useState, useEffect, useCallback, RefObject } from 'react';
|
||||||
import { FormNote, defaultFormNote, ResourceInfos } from './types';
|
import { FormNote, defaultFormNote, ResourceInfos } from './types';
|
||||||
import { clearResourceCache, attachedResources } from './resourceHandling';
|
import { clearResourceCache, attachedResources } from './resourceHandling';
|
||||||
import AsyncActionQueue from '@joplin/lib/AsyncActionQueue';
|
import AsyncActionQueue from '@joplin/lib/AsyncActionQueue';
|
||||||
@ -25,14 +25,15 @@ export interface HookDependencies {
|
|||||||
decryptionStarted: boolean;
|
decryptionStarted: boolean;
|
||||||
noteId: string;
|
noteId: string;
|
||||||
isProvisional: boolean;
|
isProvisional: boolean;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
titleInputRef: RefObject<HTMLInputElement>;
|
||||||
titleInputRef: any;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
editorRef: any;
|
editorRef: any;
|
||||||
onBeforeLoad(event: OnLoadEvent): void;
|
onBeforeLoad(event: OnLoadEvent): void;
|
||||||
onAfterLoad(event: OnLoadEvent): void;
|
onAfterLoad(event: OnLoadEvent): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SetFormNote = ReturnType<typeof useState<FormNote>>[1];
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||||
function installResourceChangeHandler(onResourceChangeHandler: Function) {
|
function installResourceChangeHandler(onResourceChangeHandler: Function) {
|
||||||
ResourceFetcher.instance().on('downloadComplete', onResourceChangeHandler);
|
ResourceFetcher.instance().on('downloadComplete', onResourceChangeHandler);
|
||||||
|
@ -25,7 +25,6 @@ export default function useMarkupToHtml(deps: HookDependencies) {
|
|||||||
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
|
resourceBaseUrl: `file://${Setting.value('resourceDir')}/`,
|
||||||
customCss: customCss || '',
|
customCss: customCss || '',
|
||||||
});
|
});
|
||||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
|
||||||
}, [plugins, customCss]);
|
}, [plugins, customCss]);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
@ -63,6 +62,5 @@ export default function useMarkupToHtml(deps: HookDependencies) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
}, [themeId, markupToHtml, whiteBackgroundNoteRendering, deps.settingValue]);
|
||||||
}, [themeId, customCss, markupToHtml, whiteBackgroundNoteRendering]);
|
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
import { FormNote, HtmlToMarkdownHandler, MarkupToHtmlHandler } from './types';
|
import { FormNote, HtmlToMarkdownHandler, MarkupToHtmlHandler, ScrollOptions } from './types';
|
||||||
import contextMenu from './contextMenu';
|
import contextMenu from './contextMenu';
|
||||||
import CommandService from '@joplin/lib/services/CommandService';
|
import CommandService from '@joplin/lib/services/CommandService';
|
||||||
import PostMessageService from '@joplin/lib/services/PostMessageService';
|
import PostMessageService from '@joplin/lib/services/PostMessageService';
|
||||||
@ -8,7 +8,7 @@ import { reg } from '@joplin/lib/registry';
|
|||||||
const bridge = require('@electron/remote').require('./bridge').default;
|
const bridge = require('@electron/remote').require('./bridge').default;
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any -- Old code before rule was applied, Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/no-explicit-any -- Old code before rule was applied, Old code before rule was applied
|
||||||
export default function useMessageHandler(scrollWhenReady: any, setScrollWhenReady: Function, editorRef: any, setLocalSearchResultCount: Function, dispatch: Function, formNote: FormNote, htmlToMd: HtmlToMarkdownHandler, mdToHtml: MarkupToHtmlHandler) {
|
export default function useMessageHandler(scrollWhenReady: ScrollOptions|null, clearScrollWhenReady: ()=> void, editorRef: any, setLocalSearchResultCount: Function, dispatch: Function, formNote: FormNote, htmlToMd: HtmlToMarkdownHandler, mdToHtml: MarkupToHtmlHandler) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
return useCallback(async (event: any) => {
|
return useCallback(async (event: any) => {
|
||||||
const msg = event.channel ? event.channel : '';
|
const msg = event.channel ? event.channel : '';
|
||||||
@ -25,7 +25,7 @@ export default function useMessageHandler(scrollWhenReady: any, setScrollWhenRea
|
|||||||
} else if (msg === 'noteRenderComplete') {
|
} else if (msg === 'noteRenderComplete') {
|
||||||
if (scrollWhenReady) {
|
if (scrollWhenReady) {
|
||||||
const options = { ...scrollWhenReady };
|
const options = { ...scrollWhenReady };
|
||||||
setScrollWhenReady(null);
|
clearScrollWhenReady();
|
||||||
editorRef.current.scrollTo(options);
|
editorRef.current.scrollTo(options);
|
||||||
}
|
}
|
||||||
} else if (msg === 'setMarkerCount') {
|
} else if (msg === 'setMarkerCount') {
|
||||||
|
@ -0,0 +1,65 @@
|
|||||||
|
import Logger from '@joplin/utils/Logger';
|
||||||
|
import { RefObject, useCallback } from 'react';
|
||||||
|
import { FormNote, NoteBodyEditorRef } from './types';
|
||||||
|
import { formNoteToNote } from '.';
|
||||||
|
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher';
|
||||||
|
import Note from '@joplin/lib/models/Note';
|
||||||
|
import type { Dispatch } from 'redux';
|
||||||
|
import eventManager, { EventName } from '@joplin/lib/eventManager';
|
||||||
|
import type { SetFormNote } from './useFormNote';
|
||||||
|
|
||||||
|
const logger = Logger.create('useScheduleSaveCallbacks');
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
setFormNote: RefObject<SetFormNote>;
|
||||||
|
dispatch: Dispatch;
|
||||||
|
editorRef: RefObject<NoteBodyEditorRef>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useScheduleSaveCallbacks = (props: Props) => {
|
||||||
|
const scheduleSaveNote = useCallback(async (formNote: FormNote) => {
|
||||||
|
if (!formNote.saveActionQueue) throw new Error('saveActionQueue is not set!!'); // Sanity check
|
||||||
|
|
||||||
|
// reg.logger().debug('Scheduling...', formNote);
|
||||||
|
|
||||||
|
const makeAction = (formNote: FormNote) => {
|
||||||
|
return async function() {
|
||||||
|
const note = await formNoteToNote(formNote);
|
||||||
|
logger.debug('Saving note...', note);
|
||||||
|
const savedNote = await Note.save(note);
|
||||||
|
|
||||||
|
props.setFormNote.current((prev: FormNote) => {
|
||||||
|
return { ...prev, user_updated_time: savedNote.user_updated_time, hasChanged: false };
|
||||||
|
});
|
||||||
|
|
||||||
|
void ExternalEditWatcher.instance().updateNoteFile(savedNote);
|
||||||
|
|
||||||
|
props.dispatch({
|
||||||
|
type: 'EDITOR_NOTE_STATUS_REMOVE',
|
||||||
|
id: formNote.id,
|
||||||
|
});
|
||||||
|
|
||||||
|
eventManager.emit(EventName.NoteContentChange, { note: savedNote });
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
formNote.saveActionQueue.push(makeAction(formNote));
|
||||||
|
}, [props.dispatch, props.setFormNote]);
|
||||||
|
|
||||||
|
const saveNoteIfWillChange = useCallback(async (formNote: FormNote) => {
|
||||||
|
if (!formNote.id || !formNote.bodyWillChangeId) return;
|
||||||
|
|
||||||
|
const body = await props.editorRef.current.content();
|
||||||
|
|
||||||
|
void scheduleSaveNote({
|
||||||
|
...formNote,
|
||||||
|
body: body,
|
||||||
|
bodyWillChangeId: 0,
|
||||||
|
bodyChangeId: 0,
|
||||||
|
});
|
||||||
|
}, [scheduleSaveNote, props.editorRef]);
|
||||||
|
|
||||||
|
return { saveNoteIfWillChange, scheduleSaveNote };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useScheduleSaveCallbacks;
|
@ -0,0 +1,44 @@
|
|||||||
|
import { RefObject, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
|
import { NoteBodyEditorRef, ScrollOptions, ScrollOptionTypes } from './types';
|
||||||
|
import usePrevious from '@joplin/lib/hooks/usePrevious';
|
||||||
|
import ResourceEditWatcher from '@joplin/lib/services/ResourceEditWatcher';
|
||||||
|
import type { EditorScrollPercents } from '../../../app.reducer';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
noteId: string;
|
||||||
|
selectedNoteHash: string;
|
||||||
|
lastEditorScrollPercents: EditorScrollPercents;
|
||||||
|
editorRef: RefObject<NoteBodyEditorRef>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const useScrollWhenReadyOptions = ({ noteId, selectedNoteHash, lastEditorScrollPercents, editorRef }: Props) => {
|
||||||
|
const [scrollWhenReady, setScrollWhenReady] = useState<ScrollOptions|null>(null);
|
||||||
|
|
||||||
|
const previousNoteId = usePrevious(noteId);
|
||||||
|
const lastScrollPercentsRef = useRef<EditorScrollPercents>();
|
||||||
|
lastScrollPercentsRef.current = lastEditorScrollPercents;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (noteId === previousNoteId) return;
|
||||||
|
|
||||||
|
if (editorRef.current) {
|
||||||
|
editorRef.current.resetScroll();
|
||||||
|
}
|
||||||
|
|
||||||
|
const lastScrollPercent = lastScrollPercentsRef.current[noteId] || 0;
|
||||||
|
setScrollWhenReady({
|
||||||
|
type: selectedNoteHash ? ScrollOptionTypes.Hash : ScrollOptionTypes.Percent,
|
||||||
|
value: selectedNoteHash ? selectedNoteHash : lastScrollPercent,
|
||||||
|
});
|
||||||
|
|
||||||
|
void ResourceEditWatcher.instance().stopWatchingAll();
|
||||||
|
}, [noteId, previousNoteId, selectedNoteHash, editorRef]);
|
||||||
|
|
||||||
|
const clearScrollWhenReady = useCallback(() => {
|
||||||
|
setScrollWhenReady(null);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { scrollWhenReady, clearScrollWhenReady };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useScrollWhenReadyOptions;
|
@ -15,7 +15,6 @@ const commandsWithDependencies = [
|
|||||||
type SetFormNoteCallback = (callback: (prev: FormNote)=> FormNote)=> void;
|
type SetFormNoteCallback = (callback: (prev: FormNote)=> FormNote)=> void;
|
||||||
|
|
||||||
interface HookDependencies {
|
interface HookDependencies {
|
||||||
formNote: FormNote;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||||
setShowLocalSearch: Function;
|
setShowLocalSearch: Function;
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
||||||
@ -23,10 +22,7 @@ interface HookDependencies {
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
||||||
noteSearchBarRef: any;
|
noteSearchBarRef: any;
|
||||||
editorRef: RefObject<NoteBodyEditorRef>;
|
editorRef: RefObject<NoteBodyEditorRef>;
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
titleInputRef: RefObject<HTMLInputElement>;
|
||||||
titleInputRef: any;
|
|
||||||
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
|
|
||||||
saveNoteAndWait: Function;
|
|
||||||
setFormNote: SetFormNoteCallback;
|
setFormNote: SetFormNoteCallback;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,6 +105,5 @@ export default function useWindowCommandHandler(dependencies: HookDependencies)
|
|||||||
CommandService.instance().unregisterRuntime(command.declaration.name);
|
CommandService.instance().unregisterRuntime(command.declaration.name);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// eslint-disable-next-line @seiyab/react-hooks/exhaustive-deps -- Old code before rule was applied
|
}, [editorRef, setShowLocalSearch, noteSearchBarRef, titleInputRef, setFormNote]);
|
||||||
}, [editorRef, setShowLocalSearch, noteSearchBarRef, titleInputRef]);
|
|
||||||
}
|
}
|
||||||
|
@ -83,7 +83,8 @@ const useRerenderHandler = (props: Props) => {
|
|||||||
return accum;
|
return accum;
|
||||||
}, {});
|
}, {});
|
||||||
const onlyNoteBodyHasChanged = Object.keys(changedDeps).length === 1 && changedDeps[0];
|
const onlyNoteBodyHasChanged = Object.keys(changedDeps).length === 1 && changedDeps[0];
|
||||||
const onlyCheckboxesHaveChanged = previousDeps[0] && changedDeps[0] && onlyCheckboxHasChangedHack(previousDeps[0], props.noteBody);
|
const previousBody = previousDeps[0] as string;
|
||||||
|
const onlyCheckboxesHaveChanged = previousDeps[0] && changedDeps[0] && onlyCheckboxHasChangedHack(previousBody, props.noteBody);
|
||||||
const previousHash = usePrevious(props.noteHash, '');
|
const previousHash = usePrevious(props.noteHash, '');
|
||||||
const hashChanged = previousHash !== props.noteHash;
|
const hashChanged = previousHash !== props.noteHash;
|
||||||
|
|
||||||
|
@ -2,9 +2,8 @@ import shim from '../shim';
|
|||||||
|
|
||||||
const { useRef, useEffect } = shim.react();
|
const { useRef, useEffect } = shim.react();
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
|
const usePrevious = <T> (value: T, initialValue: T = null) => {
|
||||||
const usePrevious = (value: any, initialValue: any = null) => {
|
const ref = useRef<T>(initialValue);
|
||||||
const ref = useRef(initialValue);
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
ref.current = value;
|
ref.current = value;
|
||||||
});
|
});
|
||||||
|
Reference in New Issue
Block a user