diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a0fc6bdb..11f2709f8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ## Added ## Changed +- dev: upgrade react to v18 and react pinch pan zoom to v3 ## Fixed diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 7736fb861..53ac9f32d 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -37,13 +37,13 @@ "mime-types": "catalog:", "moment": "^2.30.1", "path-browserify": "^1.0.1", - "react": "^17.0.2", - "react-dom": "^17.0.2", + "react": "^18.0.0", + "react-dom": "^18.0.0", "react-string-replace": "^1.1.1", "react-virtualized-auto-sizer": "^1.0.24", "react-window": "^1.8.10", "react-window-infinite-loader": "^1.0.9", - "react-zoom-pan-pinch": "^2.6.1", + "react-zoom-pan-pinch": "^3.0.0", "split2": "^4.2.0", "use-debounce": "^3.3.0", "ws": "7.5.10" @@ -52,8 +52,8 @@ "@types/debounce": "^1.2.4", "@types/emoji-mart": "^3.0.14", "@types/mime-types": "catalog:", - "@types/react": "^17.0.80", - "@types/react-dom": "^17.0.25", + "@types/react": "^18.0.0", + "@types/react-dom": "^18.0.0", "@types/react-window": "^1.8.8", "@types/react-window-infinite-loader": "^1.0.9", "@typescript-eslint/eslint-plugin": "^7.18.0", diff --git a/packages/frontend/src/components/Gallery.tsx b/packages/frontend/src/components/Gallery.tsx index 6bbfbd4f4..d9e6071cd 100644 --- a/packages/frontend/src/components/Gallery.tsx +++ b/packages/frontend/src/components/Gallery.tsx @@ -78,6 +78,9 @@ export default class Gallery extends Component< galleryImageKeepAspectRatio?: boolean } > { + static contextType = DialogContext + declare context: React.ContextType + dateHeader = createRef() tabListRef = createRef() constructor(props: Props) { @@ -443,8 +446,6 @@ function GalleryTab(props: { ) } -Gallery.contextType = DialogContext - function FileTable({ width, height, diff --git a/packages/frontend/src/components/composer/Composer.tsx b/packages/frontend/src/components/composer/Composer.tsx index 459b359a8..45ca67000 100644 --- a/packages/frontend/src/components/composer/Composer.tsx +++ b/packages/frontend/src/components/composer/Composer.tsx @@ -199,8 +199,14 @@ const Composer = forwardRef< if (clickIsOutSideEmojiPicker) setShowEmojiPicker(false) } - document.addEventListener('click', onClick) + // `setTimeout` to work around the fact that otherwise we'd catch + // the "click" event that caused the emoji picker to open + // in the first place, resulting in it getting closed immediately. + const timeoutId = setTimeout(() => { + document.addEventListener('click', onClick) + }) return () => { + clearTimeout(timeoutId) document.removeEventListener('click', onClick) } }, [showEmojiPicker, emojiAndStickerRef]) diff --git a/packages/frontend/src/components/composer/ComposerMessageInput.tsx b/packages/frontend/src/components/composer/ComposerMessageInput.tsx index 64523cbaf..e3604722a 100644 --- a/packages/frontend/src/components/composer/ComposerMessageInput.tsx +++ b/packages/frontend/src/components/composer/ComposerMessageInput.tsx @@ -26,6 +26,9 @@ export default class ComposerMessageInput extends React.Component< ComposerMessageInputProps, ComposerMessageInputState > { + static contextType = DialogContext + declare context: React.ContextType + composerSize: number setCursorPosition: number | false textareaRef: React.RefObject @@ -229,5 +232,3 @@ export default class ComposerMessageInput extends React.Component< ) } } - -ComposerMessageInput.contextType = DialogContext diff --git a/packages/frontend/src/components/screens/CrashScreen.tsx b/packages/frontend/src/components/screens/CrashScreen.tsx index e9e514322..b7a50a6b2 100644 --- a/packages/frontend/src/components/screens/CrashScreen.tsx +++ b/packages/frontend/src/components/screens/CrashScreen.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { PropsWithChildren } from 'react' import { runtime } from '@deltachat-desktop/runtime-interface' import { getLogger } from '../../../../shared/logger' @@ -6,13 +6,21 @@ import { DialogContext } from '../../contexts/DialogContext' const log = getLogger('renderer/react-crashhandler') -export class CrashScreen extends React.Component { +interface CrashScreenState { + hasError: boolean + error: string +} + +export class CrashScreen extends React.Component< + PropsWithChildren<{}>, + CrashScreenState +> { state = { hasError: false, error: '', } - componentDidCatch(error: any) { + componentDidCatch(error: object | Error) { log.error('The app encountered an react error', error) this.setState({ hasError: true, @@ -20,7 +28,7 @@ export class CrashScreen extends React.Component { }) } - errorToText(error: any) { + errorToText(error: object | Error) { if (error instanceof Error) { // TODO parse the stack and map the sourcemap to provide a useful stacktrace return (error.stack || '[no stack trace provided]') diff --git a/packages/frontend/src/components/screens/RecoverableCrashScreen.tsx b/packages/frontend/src/components/screens/RecoverableCrashScreen.tsx index 28a856800..3f2128ac2 100644 --- a/packages/frontend/src/components/screens/RecoverableCrashScreen.tsx +++ b/packages/frontend/src/components/screens/RecoverableCrashScreen.tsx @@ -1,4 +1,4 @@ -import React from 'react' +import React, { PropsWithChildren } from 'react' import { runtime } from '@deltachat-desktop/runtime-interface' import { getLogger } from '../../../../shared/logger' @@ -9,9 +9,11 @@ const log = getLogger('renderer/react-crashhandler') /** * if props.reset_on_change_key changes the RecoverableCrashScreen is reset */ -export class RecoverableCrashScreen extends React.Component<{ - reset_on_change_key: string | number -}> { +export class RecoverableCrashScreen extends React.Component< + PropsWithChildren<{ + reset_on_change_key: string | number + }> +> { state = { hasError: false, error: '', diff --git a/packages/frontend/src/contexts/ContextMenuContext.tsx b/packages/frontend/src/contexts/ContextMenuContext.tsx index 950f24019..e6b43af47 100644 --- a/packages/frontend/src/contexts/ContextMenuContext.tsx +++ b/packages/frontend/src/contexts/ContextMenuContext.tsx @@ -27,7 +27,7 @@ export function ContextMenuProvider({ children }: PropsWithChildren<{}>) { } ) - const setShowFunction = useCallback(showFn => { + const setShowFunction = useCallback((showFn: OpenContextMenu) => { setOpenContextMenuFn( // Similar to above we need to wrap this into a function, otherwise React // would call `showFn` thinking this is the method creating the next diff --git a/packages/frontend/src/main.tsx b/packages/frontend/src/main.tsx index f6d940186..e49175726 100644 --- a/packages/frontend/src/main.tsx +++ b/packages/frontend/src/main.tsx @@ -1,5 +1,5 @@ import React from 'react' -import ReactDOM from 'react-dom' +import { createRoot } from 'react-dom/client' import initWasm from '@deltachat/message_parser_wasm' import App from './App' @@ -21,8 +21,12 @@ async function main() { await initWasm('./message_parser_wasm_bg.wasm') initSystemIntegration() - - ReactDOM.render(, document.querySelector('#root')) + const domNode = document.querySelector('#root') + if (!domNode) { + throw new Error('No element with ID root in the DOM. Cannot continue') + } + const root = createRoot(domNode) + root.render() } catch (error) { document.write( 'Error while initialisation, please contact developers and look into the dev console for details:' + diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 921f98734..649f5962e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -106,7 +106,7 @@ importers: version: 1.1.2 '@emoji-mart/react': specifier: 1.1.1 - version: 1.1.1(emoji-mart@5.5.2)(react@17.0.2) + version: 1.1.1(emoji-mart@5.5.2)(react@18.3.1) classnames: specifier: ^2.5.1 version: 2.5.1 @@ -138,32 +138,32 @@ importers: specifier: ^1.0.1 version: 1.0.1 react: - specifier: ^17.0.2 - version: 17.0.2 + specifier: ^18.0.0 + version: 18.3.1 react-dom: - specifier: ^17.0.2 - version: 17.0.2(react@17.0.2) + specifier: ^18.0.0 + version: 18.3.1(react@18.3.1) react-string-replace: specifier: ^1.1.1 version: 1.1.1 react-virtualized-auto-sizer: specifier: ^1.0.24 - version: 1.0.24(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + version: 1.0.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-window: specifier: ^1.8.10 - version: 1.8.10(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + version: 1.8.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-window-infinite-loader: specifier: ^1.0.9 - version: 1.0.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + version: 1.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-zoom-pan-pinch: - specifier: ^2.6.1 - version: 2.6.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2) + specifier: ^3.0.0 + version: 3.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) split2: specifier: ^4.2.0 version: 4.2.0 use-debounce: specifier: ^3.3.0 - version: 3.4.3(react@17.0.2) + version: 3.4.3(react@18.3.1) ws: specifier: 7.5.10 version: 7.5.10 @@ -178,11 +178,11 @@ importers: specifier: 'catalog:' version: 2.1.4 '@types/react': - specifier: ^17.0.80 - version: 17.0.80 + specifier: ^18.0.0 + version: 18.3.17 '@types/react-dom': - specifier: ^17.0.25 - version: 17.0.25 + specifier: ^18.0.0 + version: 18.3.5(@types/react@18.3.17) '@types/react-window': specifier: ^1.8.8 version: 1.8.8 @@ -1287,8 +1287,10 @@ packages: '@types/rc@1.2.4': resolution: {integrity: sha512-xD6+epQoMH79A1uwmJIq25D+XZ57jUzCQ1DGSvs3tGKdx7QDYOOaMh6m5KBkEIW4+Cy5++bZ7NLDfdpNiYVKYA==} - '@types/react-dom@17.0.25': - resolution: {integrity: sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA==} + '@types/react-dom@18.3.5': + resolution: {integrity: sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==} + peerDependencies: + '@types/react': ^18.0.0 '@types/react-window-infinite-loader@1.0.9': resolution: {integrity: sha512-gEInTjQwURCnDOFyIEK2+fWB5gTjqwx30O62QfxA9stE5aiB6EWkGj4UMhc0axq7/FV++Gs/TGW8FtgEx0S6Tw==} @@ -1296,8 +1298,8 @@ packages: '@types/react-window@1.8.8': resolution: {integrity: sha512-8Ls660bHR1AUA2kuRvVG9D/4XpRC6wjAaPT9dil7Ckc76eP9TKWZwwmgfq8Q1LANX3QNDnoU4Zp48A3w+zK69Q==} - '@types/react@17.0.80': - resolution: {integrity: sha512-LrgHIu2lEtIo8M7d1FcI3BdwXWoRQwMoXOZ7+dPTW0lYREjmlHl3P0U1VD0i/9tppOuv8/sam7sOjx34TxSFbA==} + '@types/react@18.3.17': + resolution: {integrity: sha512-opAQ5no6LqJNo9TqnxBKsgnkIYHozW9KSTlFVoSUJYh1Fl/sswkEoqIugRSm7tbh6pABtYjGAjW+GOS23j8qbw==} '@types/resolve-path@1.4.2': resolution: {integrity: sha512-tAt9lBclaYmgg4GErwNWM6rG3FkA1lNckmujXJ9hqWDUKhBzTrccKwAYzMK8ScaT2Rxf2m2OXOdeVxXrOlqq9A==} @@ -1305,9 +1307,6 @@ packages: '@types/responselike@1.0.0': resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} - '@types/scheduler@0.16.2': - resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} - '@types/semver@7.5.8': resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} @@ -2742,10 +2741,6 @@ packages: normalize.css@8.0.1: resolution: {integrity: sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg==} - object-assign@4.1.1: - resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} - engines: {node: '>=0.10.0'} - object-inspect@1.13.2: resolution: {integrity: sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==} engines: {node: '>= 0.4'} @@ -2976,10 +2971,10 @@ packages: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true - react-dom@17.0.2: - resolution: {integrity: sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA==} + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} peerDependencies: - react: 17.0.2 + react: ^18.3.1 react-string-replace@1.1.1: resolution: {integrity: sha512-26TUbLzLfHQ5jO5N7y3Mx88eeKo0Ml0UjCQuX4BMfOd/JX+enQqlKpL1CZnmjeBRvQE8TR+ds9j1rqx9CxhKHQ==} @@ -3005,15 +3000,15 @@ packages: react: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 react-dom: ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 - react-zoom-pan-pinch@2.6.1: - resolution: {integrity: sha512-4Cgdnn6OwN4DomY/E9NpAf0TyCtslEgwdYn96ZV/f5LKuw/FE3gcIBJiaKFmMGThDGV0yKN5mzO8noi34+UE4Q==} + react-zoom-pan-pinch@3.6.1: + resolution: {integrity: sha512-SdPqdk7QDSV7u/WulkFOi+cnza8rEZ0XX4ZpeH7vx3UZEg7DoyuAy3MCmm+BWv/idPQL2Oe73VoC0EhfCN+sZQ==} engines: {node: '>=8', npm: '>=5'} peerDependencies: react: '*' react-dom: '*' - react@17.0.2: - resolution: {integrity: sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA==} + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} read-config-file@6.3.2: @@ -3101,8 +3096,8 @@ packages: sax@1.3.0: resolution: {integrity: sha512-0s+oAmw9zLl1V1cS9BtZN7JAd0cW5e0QH4W3LWEK6a4LaLEA2OTpGYWDY+6XasBLtz6wkm3u1xRw95mRuJ59WA==} - scheduler@0.20.2: - resolution: {integrity: sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ==} + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} semver-compare@1.0.0: resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} @@ -3682,10 +3677,10 @@ snapshots: '@emoji-mart/data@1.1.2': {} - '@emoji-mart/react@1.1.1(emoji-mart@5.5.2)(react@17.0.2)': + '@emoji-mart/react@1.1.1(emoji-mart@5.5.2)(react@18.3.1)': dependencies: emoji-mart: 5.5.2 - react: 17.0.2 + react: 18.3.1 '@esbuild/aix-ppc64@0.23.0': optional: true @@ -4109,7 +4104,7 @@ snapshots: '@types/emoji-mart@3.0.14': dependencies: - '@types/react': 17.0.80 + '@types/react': 18.3.17 '@types/express-serve-static-core@4.19.5': dependencies: @@ -4189,23 +4184,22 @@ snapshots: dependencies: '@types/minimist': 1.2.2 - '@types/react-dom@17.0.25': + '@types/react-dom@18.3.5(@types/react@18.3.17)': dependencies: - '@types/react': 17.0.80 + '@types/react': 18.3.17 '@types/react-window-infinite-loader@1.0.9': dependencies: - '@types/react': 17.0.80 + '@types/react': 18.3.17 '@types/react-window': 1.8.8 '@types/react-window@1.8.8': dependencies: - '@types/react': 17.0.80 + '@types/react': 18.3.17 - '@types/react@17.0.80': + '@types/react@18.3.17': dependencies: '@types/prop-types': 15.7.5 - '@types/scheduler': 0.16.2 csstype: 3.1.1 '@types/resolve-path@1.4.2': {} @@ -4214,8 +4208,6 @@ snapshots: dependencies: '@types/node': 20.14.15 - '@types/scheduler@0.16.2': {} - '@types/semver@7.5.8': {} '@types/send@0.17.4': @@ -5946,8 +5938,6 @@ snapshots: normalize.css@8.0.1: {} - object-assign@4.1.1: {} - object-inspect@1.13.2: {} object-keys@1.1.1: @@ -6145,41 +6135,39 @@ snapshots: minimist: 1.2.6 strip-json-comments: 2.0.1 - react-dom@17.0.2(react@17.0.2): + react-dom@18.3.1(react@18.3.1): dependencies: loose-envify: 1.4.0 - object-assign: 4.1.1 - react: 17.0.2 - scheduler: 0.20.2 + react: 18.3.1 + scheduler: 0.23.2 react-string-replace@1.1.1: {} - react-virtualized-auto-sizer@1.0.24(react-dom@17.0.2(react@17.0.2))(react@17.0.2): + react-virtualized-auto-sizer@1.0.24(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) - react-window-infinite-loader@1.0.9(react-dom@17.0.2(react@17.0.2))(react@17.0.2): + react-window-infinite-loader@1.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) - react-window@1.8.10(react-dom@17.0.2(react@17.0.2))(react@17.0.2): + react-window@1.8.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.19.0 memoize-one: 5.2.1 - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) - react-zoom-pan-pinch@2.6.1(react-dom@17.0.2(react@17.0.2))(react@17.0.2): + react-zoom-pan-pinch@3.6.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: - react: 17.0.2 - react-dom: 17.0.2(react@17.0.2) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) - react@17.0.2: + react@18.3.1: dependencies: loose-envify: 1.4.0 - object-assign: 4.1.1 read-config-file@6.3.2: dependencies: @@ -6275,10 +6263,9 @@ snapshots: sax@1.3.0: {} - scheduler@0.20.2: + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 - object-assign: 4.1.1 semver-compare@1.0.0: optional: true @@ -6585,9 +6572,9 @@ snapshots: dependencies: punycode: 2.1.1 - use-debounce@3.4.3(react@17.0.2): + use-debounce@3.4.3(react@18.3.1): dependencies: - react: 17.0.2 + react: 18.3.1 utf8-byte-length@1.0.5: {}