1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-08-10 22:11:50 +02:00

Chore: Desktop: Update components for compatibility with React 19 (#12184)

This commit is contained in:
Henry Heino
2025-05-19 15:02:09 -07:00
committed by GitHub
parent 80696fe324
commit cbf6d5506f
17 changed files with 39 additions and 47 deletions

View File

@@ -1,4 +1,4 @@
const React = require('react');
import * as React from 'react';
import EncryptionService from '@joplin/lib/services/e2ee/EncryptionService';
import { themeStyle } from '@joplin/lib/theme';
import { _ } from '@joplin/lib/locale';

View File

@@ -1,9 +1,9 @@
const { connect } = require('react-redux');
import * as React from 'react';
import { connect } from 'react-redux';
import { AppState } from '../app.reducer';
import { _ } from '@joplin/lib/locale';
import { clipboard } from 'electron';
import Button from './Button/Button';
import { Fragment } from 'react';
import { accountTypeToString } from '@joplin/lib/utils/joplinCloud/types';
import bridge from '../services/bridge';
@@ -47,10 +47,10 @@ const JoplinCloudConfigScreen = (props: JoplinCloudConfigScreenProps) => {
<h2>{_('Email to note')}</h2>
<p>{_('Any email sent to this address will be converted into a note and added to your collection. The note will be saved into the Inbox notebook')}</p>
{
isEmailToNoteAvailableInAccount ? <Fragment>
isEmailToNoteAvailableInAccount ? <>
<p className='inbox-email-value'>{props.inboxEmail}</p>
<Button onClick={copyToClipboard} title={_('Copy to clipboard')} />
</Fragment>
</>
: <div className='alert-warn'>
<p>{_('Your account doesn\'t have access to this feature')}</p>
</div>

View File

@@ -1,4 +1,5 @@
import { Fragment, useEffect, useMemo, useReducer, useState } from 'react';
import * as React from 'react';
import { useEffect, useMemo, useReducer, useState } from 'react';
import ButtonBar from './ConfigScreen/ButtonBar';
import { _ } from '@joplin/lib/locale';
import { clipboard } from 'electron';
@@ -81,7 +82,7 @@ const JoplinCloudScreenComponent = (props: Props) => {
<div className="login-page">
<div className="page-container">
{state.active !== 'COMPLETED' ? (
<Fragment>
<>
<p className="text">{_('To allow Joplin to synchronise with Joplin Cloud, please login using this URL:')}</p>
<div className="buttons-container">
<Button
@@ -98,7 +99,7 @@ const JoplinCloudScreenComponent = (props: Props) => {
/>
</div>
</Fragment>
</>
) : null}
<p className={state.className}>{state.message()}
{state.active === 'ERROR' ? (

View File

@@ -1,6 +1,4 @@
import { useEffect, useRef } from 'react';
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
// eslint-disable-next-line @typescript-eslint/no-explicit-any, import/prefer-default-export -- Old code before rule was applied
export function cursorPositionToTextOffset(cursorPos: any, body: string) {
if (!body) return 0;
@@ -20,12 +18,3 @@ export function cursorPositionToTextOffset(cursorPos: any, body: string) {
return pos;
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
export function usePrevious(value: any): any {
const ref = useRef();
useEffect(() => {
ref.current = value;
});
return ref.current;
}

View File

@@ -26,7 +26,7 @@ interface Props extends EditorProps {
}
const Editor = (props: Props, ref: ForwardedRef<CodeMirrorControl>) => {
const editorContainerRef = useRef<HTMLDivElement>();
const editorContainerRef = useRef<HTMLDivElement|null>(null);
const [editor, setEditor] = useState<CodeMirrorControl|null>(null);
// The editor will only be created once, so callbacks that could

View File

@@ -7,7 +7,7 @@ import { useEffect, useCallback, useRef, forwardRef, useImperativeHandle } from
import { NoteBodyEditorProps, NoteBodyEditorRef } from '../../utils/types';
const PlainEditor = (props: NoteBodyEditorProps, ref: ForwardedRef<NoteBodyEditorRef>) => {
const editorRef = useRef<HTMLTextAreaElement>();
const editorRef = useRef<HTMLTextAreaElement|null>(null);
useImperativeHandle(ref, () => {
return {

View File

@@ -1,6 +1,6 @@
import * as React from 'react';
import { useState, useEffect, useCallback, useRef, forwardRef, useImperativeHandle, useMemo } from 'react';
import { ScrollOptions, ScrollOptionTypes, EditorCommand, NoteBodyEditorProps, ResourceInfos, HtmlToMarkdownHandler, ScrollToTextValue } from '../../utils/types';
import { useState, useEffect, useCallback, useRef, forwardRef, useImperativeHandle, useMemo, Ref } from 'react';
import { ScrollOptions, ScrollOptionTypes, EditorCommand, NoteBodyEditorProps, ResourceInfos, HtmlToMarkdownHandler, ScrollToTextValue, NoteBodyEditorRef } from '../../utils/types';
import { resourcesStatus, commandAttachFileToBody, getResourcesFromPasteEvent, processPastedHtml } from '../../utils/resourceHandling';
import attachedResources from '@joplin/lib/utils/attachedResources';
import useScroll from './utils/useScroll';
@@ -95,8 +95,7 @@ interface LastOnChangeEventInfo {
let dispatchDidUpdateIID_: any = null;
let changeId_ = 1;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
const TinyMCE = (props: NoteBodyEditorProps, ref: Ref<NoteBodyEditorRef>) => {
const [editorContainer, setEditorContainer] = useState<HTMLDivElement|null>(null);
const editorContainerDom = useDocument(editorContainer);
const [editor, setEditor] = useState<Editor|null>(null);
@@ -1200,11 +1199,10 @@ const TinyMCE = (props: NoteBodyEditorProps, ref: any) => {
// we call the current one from setTimeout.
// https://github.com/facebook/react/issues/14010#issuecomment-433788147
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
const props_onChangeRef = useRef<Function>();
const props_onChangeRef = useRef<Function>(null);
props_onChangeRef.current = props.onChange;
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
const prop_htmlToMarkdownRef = useRef<HtmlToMarkdownHandler>();
const prop_htmlToMarkdownRef = useRef<HtmlToMarkdownHandler>(null);
prop_htmlToMarkdownRef.current = props.htmlToMarkdown;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied

View File

@@ -14,7 +14,7 @@ import useFormNote, { OnLoadEvent, OnSetFormNote } from './utils/useFormNote';
import useEffectiveNoteId from './utils/useEffectiveNoteId';
import useFolder from './utils/useFolder';
import styles_ from './styles';
import { NoteEditorProps, FormNote, OnChangeEvent, NoteBodyEditorProps, AllAssetsOptions, NoteBodyEditorRef } from './utils/types';
import { NoteEditorProps, FormNote, OnChangeEvent, AllAssetsOptions, NoteBodyEditorRef, NoteBodyEditorPropsAndRef } from './utils/types';
import CommandService from '@joplin/lib/services/CommandService';
import Button, { ButtonLevel } from '../Button/Button';
import eventManager, { EventName } from '@joplin/lib/eventManager';
@@ -75,8 +75,8 @@ function NoteEditorContent(props: NoteEditorProps) {
const [titleHasBeenManuallyChanged, setTitleHasBeenManuallyChanged] = useState(false);
const [isReadOnly, setIsReadOnly] = useState<boolean>(false);
const editorRef = useRef<NoteBodyEditorRef>();
const titleInputRef = useRef<HTMLInputElement>();
const editorRef = useRef<NoteBodyEditorRef|null>(null);
const titleInputRef = useRef<HTMLInputElement|null>(null);
const isMountedRef = useRef(true);
const noteSearchBarRef = useRef(null);
@@ -91,7 +91,7 @@ function NoteEditorContent(props: NoteEditorProps) {
return `editor-${editorIdCounter++}`;
}, []);
const setFormNoteRef = useRef<OnSetFormNote>();
const setFormNoteRef = useRef<OnSetFormNote>(null);
const { saveNoteIfWillChange, scheduleSaveNote } = useScheduleSaveCallbacks({
setFormNote: setFormNoteRef, dispatch: props.dispatch, editorRef, editorId,
});
@@ -130,7 +130,7 @@ function NoteEditorContent(props: NoteEditorProps) {
editorId,
});
setFormNoteRef.current = setFormNote;
const formNoteRef = useRef<FormNote>();
const formNoteRef = useRef<FormNote>(formNote);
formNoteRef.current = { ...formNote };
const formNoteFolder = useFolder({ folderId: formNote.parent_id });
@@ -423,7 +423,7 @@ function NoteEditorContent(props: NoteEditorProps) {
const searchMarkers = useSearchMarkers(showLocalSearch, localSearchMarkerOptions, props.searches, props.selectedSearchId, props.highlightedWords);
const markupLanguage = formNote.markup_language;
const editorProps: NoteBodyEditorProps = {
const editorProps: NoteBodyEditorPropsAndRef = {
ref: editorRef,
contentKey: formNote.id,
style: styles.tinyMCE,

View File

@@ -85,8 +85,6 @@ export type HtmlToMarkdownHandler = (markupLanguage: number, html: string, origi
export interface NoteBodyEditorProps {
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
style: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Old code before rule was applied
ref: any;
themeId: number;
// When this is true it means the note must always be rendered using a white
@@ -141,6 +139,10 @@ export interface NoteBodyEditorProps {
watchedNoteFiles: string[];
}
export interface NoteBodyEditorPropsAndRef extends NoteBodyEditorProps {
ref: RefObject<NoteBodyEditorRef>;
}
export interface FormNote {
id: string;
title: string;

View File

@@ -14,7 +14,7 @@ const useScrollWhenReadyOptions = ({ noteId, selectedNoteHash, lastEditorScrollP
const [scrollWhenReady, setScrollWhenReady] = useState<ScrollOptions|null>(null);
const previousNoteId = usePrevious(noteId);
const lastScrollPercentsRef = useRef<EditorScrollPercents>();
const lastScrollPercentsRef = useRef<EditorScrollPercents>(null);
lastScrollPercentsRef.current = lastEditorScrollPercents;
useEffect(() => {

View File

@@ -226,7 +226,7 @@ const NoteList = (props: Props) => {
output.push(
<NoteListItem
key={note.id}
ref={el => itemRefs.current[note.id] = el}
ref={el => { itemRefs.current[note.id] = el; }}
index={i}
dragIndex={dragOverTargetNoteIndex}
noteCount={props.notes.length}

View File

@@ -10,7 +10,7 @@ const useActiveDescendantId = (selectedFolderId: string, selectedNoteIds: string
setActiveNoteId(selectedNoteIdsRef.current?.[0] ?? '');
}, [selectedFolderId]);
const previousNoteIdsRef = useRef<string[]>();
const previousNoteIdsRef = useRef<string[]>(null);
previousNoteIdsRef.current = usePrevious(selectedNoteIds);
useEffect(() => {

View File

@@ -157,24 +157,24 @@ const NoteTextViewer = forwardRef((props: Props, ref: ForwardedRef<NoteViewerCon
return result;
}, [parentDoc]);
const webview_domReadyRef = useRef<EventListener>();
const webview_domReadyRef = useRef<EventListener>(null);
webview_domReadyRef.current = (event: Event) => {
domReadyRef.current = true;
if (props.onDomReady) props.onDomReady(event);
};
const webview_ipcMessageRef = useRef<EventListener>();
const webview_ipcMessageRef = useRef<EventListener>(null);
webview_ipcMessageRef.current = (event: Event) => {
if (props.onIpcMessage) props.onIpcMessage(event);
};
const webview_loadRef = useRef<EventListener>();
const webview_loadRef = useRef<EventListener>(null);
webview_loadRef.current = (event: Event) => {
webview_domReadyRef.current(event);
};
type MessageEventListener = (event: MessageEvent)=> void;
const webview_messageRef = useRef<MessageEventListener>();
const webview_messageRef = useRef<MessageEventListener>(null);
webview_messageRef.current = (event: MessageEvent) => {
if (event.source !== webviewRef.current?.contentWindow) return;
if (!event.data || event.data.target !== 'main') return;

View File

@@ -1,3 +1,4 @@
import * as React from 'react';
import app from '../app';
import { AppState, AppStateDialog } from '../app.reducer';
import MainScreen from './MainScreen';
@@ -10,7 +11,6 @@ import { themeStyle } from '@joplin/lib/theme';
import { Size } from './ResizableLayout/utils/types';
import MenuBar from './MenuBar';
import { _ } from '@joplin/lib/locale';
const React = require('react');
const { createRoot } = require('react-dom/client');
const { connect, Provider } = require('react-redux');
import Setting from '@joplin/lib/models/Setting';
@@ -36,6 +36,7 @@ const { ThemeProvider, StyleSheetManager, createGlobalStyle } = require('styled-
interface Props {
themeId: number;
appState: string;
profileConfigCurrentProfileId: string;
// eslint-disable-next-line @typescript-eslint/ban-types -- Old code before rule was applied
dispatch: Function;
size: Size;

View File

@@ -1,3 +1,4 @@
import * as React from 'react';
import Dialog from '../Dialog';
import DialogButtonRow, { ClickEvent, ButtonSpec } from '../DialogButtonRow';
import DialogTitle from '../DialogTitle';

View File

@@ -62,7 +62,7 @@ const FolderAndTagList: React.FC<Props> = props => {
collapsedFolderIds: props.collapsedFolderIds,
});
const itemListRef = useRef<ItemList<ListItem>>();
const itemListRef = useRef<ItemList<ListItem>|null>(null);
const { focusSidebar } = useFocusHandler({ itemListRef, selectedIndex, listItems });
useSidebarCommandHandler({ focusSidebar });

View File

@@ -29,7 +29,7 @@ const onCancelClick = async (lastDeletion: StateLastDeletion) => {
export default (props: Props) => {
const popupManager = useContext(PopupNotificationContext);
const lastDeletionNotificationTimeRef = useRef<number>();
const lastDeletionNotificationTimeRef = useRef<number>(props.lastDeletionNotificationTime);
lastDeletionNotificationTimeRef.current = props.lastDeletionNotificationTime;
useEffect(() => {