From 3b375ee3952f98278d235fe3fad04ee836169a62 Mon Sep 17 00:00:00 2001
From: Renaud Chaput <renchap@gmail.com>
Date: Mon, 22 May 2023 15:48:01 +0200
Subject: [PATCH] [Glitch] Upgrade to React 18

Port 8d6aea33260dedeacb3d22ac1a6d2f9cc3856a5e to glitch-soc

Signed-off-by: Claire <claire.github-309c@sitedethib.com>
---
 .../flavours/glitch/components/column.jsx           | 10 ++++++----
 .../flavours/glitch/components/dropdown_menu.jsx    | 11 ++++++-----
 .../flavours/glitch/components/scrollable_list.jsx  | 10 ++++++----
 .../flavours/glitch/containers/media_container.jsx  |  4 ++--
 .../features/compose/components/dropdown_menu.jsx   | 13 +++++++------
 .../compose/components/emoji_picker_dropdown.jsx    | 10 +++++-----
 .../compose/components/language_dropdown.jsx        |  7 ++++---
 app/javascript/flavours/glitch/main.jsx             |  5 +++--
 app/javascript/flavours/glitch/packs/admin.jsx      | 10 ++++++----
 app/javascript/flavours/glitch/packs/public.jsx     |  5 +++--
 app/javascript/flavours/glitch/packs/share.jsx      |  7 ++++---
 app/javascript/flavours/glitch/utils/dom_helpers.js |  4 ----
 12 files changed, 52 insertions(+), 44 deletions(-)

diff --git a/app/javascript/flavours/glitch/components/column.jsx b/app/javascript/flavours/glitch/components/column.jsx
index 47293ef18e..fce6a6c408 100644
--- a/app/javascript/flavours/glitch/components/column.jsx
+++ b/app/javascript/flavours/glitch/components/column.jsx
@@ -3,6 +3,8 @@ import PropTypes from 'prop-types';
 import { supportsPassiveEvents } from 'detect-passive-events';
 import { scrollTop } from '../scroll';
 
+const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
+
 export default class Column extends React.PureComponent {
 
   static propTypes = {
@@ -37,17 +39,17 @@ export default class Column extends React.PureComponent {
 
   componentDidMount () {
     if (this.props.bindToDocument) {
-      document.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false);
+      document.addEventListener('wheel', this.handleWheel, listenerOptions);
     } else {
-      this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false);
+      this.node.addEventListener('wheel', this.handleWheel, listenerOptions);
     }
   }
 
   componentWillUnmount () {
     if (this.props.bindToDocument) {
-      document.removeEventListener('wheel', this.handleWheel);
+      document.removeEventListener('wheel', this.handleWheel, listenerOptions);
     } else {
-      this.node.removeEventListener('wheel', this.handleWheel);
+      this.node.removeEventListener('wheel', this.handleWheel, listenerOptions);
     }
   }
 
diff --git a/app/javascript/flavours/glitch/components/dropdown_menu.jsx b/app/javascript/flavours/glitch/components/dropdown_menu.jsx
index 3f17bc1299..901c7fe22f 100644
--- a/app/javascript/flavours/glitch/components/dropdown_menu.jsx
+++ b/app/javascript/flavours/glitch/components/dropdown_menu.jsx
@@ -7,7 +7,7 @@ import { supportsPassiveEvents } from 'detect-passive-events';
 import classNames from 'classnames';
 import { CircularProgress } from 'flavours/glitch/components/loading_indicator';
 
-const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
+const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
 let id = 0;
 
 class DropdownMenu extends React.PureComponent {
@@ -35,12 +35,13 @@ class DropdownMenu extends React.PureComponent {
   handleDocumentClick = e => {
     if (this.node && !this.node.contains(e.target)) {
       this.props.onClose();
+      e.stopPropagation();
     }
   };
 
   componentDidMount () {
-    document.addEventListener('click', this.handleDocumentClick, false);
-    document.addEventListener('keydown', this.handleKeyDown, false);
+    document.addEventListener('click', this.handleDocumentClick, { capture: true });
+    document.addEventListener('keydown', this.handleKeyDown, { capture: true });
     document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
 
     if (this.focusedItem && this.props.openedViaKeyboard) {
@@ -49,8 +50,8 @@ class DropdownMenu extends React.PureComponent {
   }
 
   componentWillUnmount () {
-    document.removeEventListener('click', this.handleDocumentClick, false);
-    document.removeEventListener('keydown', this.handleKeyDown, false);
+    document.removeEventListener('click', this.handleDocumentClick, { capture: true });
+    document.removeEventListener('keydown', this.handleKeyDown, { capture: true });
     document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
   }
 
diff --git a/app/javascript/flavours/glitch/components/scrollable_list.jsx b/app/javascript/flavours/glitch/components/scrollable_list.jsx
index 13701bcd3e..69925321bd 100644
--- a/app/javascript/flavours/glitch/components/scrollable_list.jsx
+++ b/app/javascript/flavours/glitch/components/scrollable_list.jsx
@@ -15,6 +15,8 @@ import { connect } from 'react-redux';
 
 const MOUSE_IDLE_DELAY = 300;
 
+const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
+
 const mapStateToProps = (state, { scrollKey }) => {
   return {
     preventScroll: scrollKey === state.getIn(['dropdown_menu', 'scroll_key']),
@@ -237,20 +239,20 @@ class ScrollableList extends PureComponent {
   attachScrollListener () {
     if (this.props.bindToDocument) {
       document.addEventListener('scroll', this.handleScroll);
-      document.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : undefined);
+      document.addEventListener('wheel', this.handleWheel,  listenerOptions);
     } else {
       this.node.addEventListener('scroll', this.handleScroll);
-      this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : undefined);
+      this.node.addEventListener('wheel', this.handleWheel, listenerOptions);
     }
   }
 
   detachScrollListener () {
     if (this.props.bindToDocument) {
       document.removeEventListener('scroll', this.handleScroll);
-      document.removeEventListener('wheel', this.handleWheel);
+      document.removeEventListener('wheel', this.handleWheel, listenerOptions);
     } else {
       this.node.removeEventListener('scroll', this.handleScroll);
-      this.node.removeEventListener('wheel', this.handleWheel);
+      this.node.removeEventListener('wheel', this.handleWheel, listenerOptions);
     }
   }
 
diff --git a/app/javascript/flavours/glitch/containers/media_container.jsx b/app/javascript/flavours/glitch/containers/media_container.jsx
index 06db0a0030..99e1ed55c3 100644
--- a/app/javascript/flavours/glitch/containers/media_container.jsx
+++ b/app/javascript/flavours/glitch/containers/media_container.jsx
@@ -1,5 +1,5 @@
 import React, { PureComponent, Fragment } from 'react';
-import ReactDOM from 'react-dom';
+import { createPortal } from 'react-dom';
 import PropTypes from 'prop-types';
 import { IntlProvider, addLocaleData } from 'react-intl';
 import { fromJS } from 'immutable';
@@ -95,7 +95,7 @@ export default class MediaContainer extends PureComponent {
               }),
             });
 
-            return ReactDOM.createPortal(
+            return createPortal(
               <Component {...props} key={`media-${i}`} />,
               component,
             );
diff --git a/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.jsx b/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.jsx
index 786f593070..28f316619c 100644
--- a/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.jsx
+++ b/app/javascript/flavours/glitch/features/compose/components/dropdown_menu.jsx
@@ -2,12 +2,12 @@
 import PropTypes from 'prop-types';
 import React from 'react';
 import classNames from 'classnames';
+import { supportsPassiveEvents } from 'detect-passive-events';
 
 //  Components.
 import { Icon } from 'flavours/glitch/components/icon';
 
-//  Utils.
-import { withPassive } from 'flavours/glitch/utils/dom_helpers';
+const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
 
 //  The component.
 export default class ComposerOptionsDropdownContent extends React.PureComponent {
@@ -41,6 +41,7 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
   handleDocumentClick = (e) => {
     if (this.node && !this.node.contains(e.target)) {
       this.props.onClose();
+      e.stopPropagation();
     }
   };
 
@@ -51,8 +52,8 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
 
   //  On mounting, we add our listeners.
   componentDidMount () {
-    document.addEventListener('click', this.handleDocumentClick, false);
-    document.addEventListener('touchend', this.handleDocumentClick, withPassive);
+    document.addEventListener('click', this.handleDocumentClick, { capture: true });
+    document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
     if (this.focusedItem) {
       this.focusedItem.focus({ preventScroll: true });
     } else {
@@ -62,8 +63,8 @@ export default class ComposerOptionsDropdownContent extends React.PureComponent
 
   //  On unmounting, we remove our listeners.
   componentWillUnmount () {
-    document.removeEventListener('click', this.handleDocumentClick, false);
-    document.removeEventListener('touchend', this.handleDocumentClick, withPassive);
+    document.removeEventListener('click', this.handleDocumentClick, { capture: true });
+    document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
   }
 
   handleClick = (e) => {
diff --git a/app/javascript/flavours/glitch/features/compose/components/emoji_picker_dropdown.jsx b/app/javascript/flavours/glitch/features/compose/components/emoji_picker_dropdown.jsx
index 7526899305..68ff37b80e 100644
--- a/app/javascript/flavours/glitch/features/compose/components/emoji_picker_dropdown.jsx
+++ b/app/javascript/flavours/glitch/features/compose/components/emoji_picker_dropdown.jsx
@@ -28,7 +28,7 @@ const messages = defineMessages({
 
 let EmojiPicker, Emoji; // load asynchronously
 
-const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
+const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
 
 const backgroundImageFn = () => `${assetHost}/emoji/sheet_13.png`;
 
@@ -79,12 +79,12 @@ class ModifierPickerMenu extends React.PureComponent {
   };
 
   attachListeners () {
-    document.addEventListener('click', this.handleDocumentClick, false);
+    document.addEventListener('click', this.handleDocumentClick, { capture: true });
     document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
   }
 
   removeListeners () {
-    document.removeEventListener('click', this.handleDocumentClick, false);
+    document.removeEventListener('click', this.handleDocumentClick, { capture: true });
     document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
   }
 
@@ -177,7 +177,7 @@ class EmojiPickerMenuImpl extends React.PureComponent {
   };
 
   componentDidMount () {
-    document.addEventListener('click', this.handleDocumentClick, false);
+    document.addEventListener('click', this.handleDocumentClick, { capture: true });
     document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
 
     // Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need
@@ -192,7 +192,7 @@ class EmojiPickerMenuImpl extends React.PureComponent {
   }
 
   componentWillUnmount () {
-    document.removeEventListener('click', this.handleDocumentClick, false);
+    document.removeEventListener('click', this.handleDocumentClick, { capture: true });
     document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
   }
 
diff --git a/app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx b/app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx
index 05614de011..2d4ae72d40 100644
--- a/app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx
+++ b/app/javascript/flavours/glitch/features/compose/components/language_dropdown.jsx
@@ -15,7 +15,7 @@ const messages = defineMessages({
   clear: { id: 'emoji_button.clear', defaultMessage: 'Clear' },
 });
 
-const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
+const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
 
 class LanguageDropdownMenu extends React.PureComponent {
 
@@ -39,11 +39,12 @@ class LanguageDropdownMenu extends React.PureComponent {
   handleDocumentClick = e => {
     if (this.node && !this.node.contains(e.target)) {
       this.props.onClose();
+      e.stopPropagation();
     }
   };
 
   componentDidMount () {
-    document.addEventListener('click', this.handleDocumentClick, false);
+    document.addEventListener('click', this.handleDocumentClick, { capture: true });
     document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
 
     // Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need
@@ -57,7 +58,7 @@ class LanguageDropdownMenu extends React.PureComponent {
   }
 
   componentWillUnmount () {
-    document.removeEventListener('click', this.handleDocumentClick, false);
+    document.removeEventListener('click', this.handleDocumentClick, { capture: true });
     document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
   }
 
diff --git a/app/javascript/flavours/glitch/main.jsx b/app/javascript/flavours/glitch/main.jsx
index 1ac7d579ec..63195518be 100644
--- a/app/javascript/flavours/glitch/main.jsx
+++ b/app/javascript/flavours/glitch/main.jsx
@@ -1,5 +1,5 @@
 import React from 'react';
-import ReactDOM from 'react-dom';
+import { createRoot } from 'react-dom/client';
 import { setupBrowserNotifications } from 'flavours/glitch/actions/notifications';
 import Mastodon from 'flavours/glitch/containers/mastodon';
 import { store } from 'flavours/glitch/store';
@@ -18,7 +18,8 @@ function main() {
     const mountNode = document.getElementById('mastodon');
     const props = JSON.parse(mountNode.getAttribute('data-props'));
 
-    ReactDOM.render(<Mastodon {...props} />, mountNode);
+    const root = createRoot(mountNode);
+    root.render(<Mastodon {...props} />);
     store.dispatch(setupBrowserNotifications());
 
     if (process.env.NODE_ENV === 'production' && me && 'serviceWorker' in navigator) {
diff --git a/app/javascript/flavours/glitch/packs/admin.jsx b/app/javascript/flavours/glitch/packs/admin.jsx
index 7544ae4e97..7b0917df85 100644
--- a/app/javascript/flavours/glitch/packs/admin.jsx
+++ b/app/javascript/flavours/glitch/packs/admin.jsx
@@ -1,7 +1,7 @@
 import 'packs/public-path';
 import ready from 'flavours/glitch/ready';
 import React from 'react';
-import ReactDOM from 'react-dom';
+import { createRoot } from 'react-dom/client';
 
 ready(() => {
   [].forEach.call(document.querySelectorAll('[data-admin-component]'), element => {
@@ -10,11 +10,13 @@ ready(() => {
 
     import('flavours/glitch/containers/admin_component').then(({ default: AdminComponent }) => {
       return import('flavours/glitch/components/admin/' + componentName).then(({ default: Component }) => {
-        ReactDOM.render((
+        const root = createRoot(element);
+
+        root.render (
           <AdminComponent locale={locale}>
             <Component {...componentProps} />
-          </AdminComponent>
-        ), element);
+          </AdminComponent>,
+        );
       });
     }).catch(error => {
       console.error(error);
diff --git a/app/javascript/flavours/glitch/packs/public.jsx b/app/javascript/flavours/glitch/packs/public.jsx
index 34a8f35133..7bb85fd2f9 100644
--- a/app/javascript/flavours/glitch/packs/public.jsx
+++ b/app/javascript/flavours/glitch/packs/public.jsx
@@ -11,7 +11,7 @@ import { delegate }  from '@rails/ujs';
 import emojify  from 'flavours/glitch/features/emoji/emoji';
 import { getLocale }  from 'locales';
 import React  from 'react';
-import ReactDOM  from 'react-dom';
+import { createRoot }  from 'react-dom/client';
 import { createBrowserHistory }  from 'history';
 
 const messages = defineMessages({
@@ -130,7 +130,8 @@ function main() {
 
           const content = document.createElement('div');
 
-          ReactDOM.render(<MediaContainer locale={locale} components={reactComponents} />, content);
+          const root = createRoot(content);
+          root.render(<MediaContainer locale={locale} components={reactComponents} />);
           document.body.appendChild(content);
           scrollToDetailedStatus();
         })
diff --git a/app/javascript/flavours/glitch/packs/share.jsx b/app/javascript/flavours/glitch/packs/share.jsx
index 47a51a5b01..a835058ac8 100644
--- a/app/javascript/flavours/glitch/packs/share.jsx
+++ b/app/javascript/flavours/glitch/packs/share.jsx
@@ -1,9 +1,9 @@
 import 'packs/public-path';
 import { loadPolyfills } from 'flavours/glitch/polyfills';
+import ready from 'flavours/glitch/ready';
 import ComposeContainer from 'flavours/glitch/containers/compose_container';
 import React from 'react';
-import ReactDOM from 'react-dom';
-import ready from 'flavours/glitch/ready';
+import { createRoot } from 'react-dom/client';
 
 function loaded() {
   const mountNode = document.getElementById('mastodon-compose');
@@ -13,7 +13,8 @@ function loaded() {
     if(!attr) return;
 
     const props = JSON.parse(attr);
-    ReactDOM.render(<ComposeContainer {...props} />, mountNode);
+    const root = createRoot(mountNode);
+    root.render(<ComposeContainer {...props} />);
   }
 }
 
diff --git a/app/javascript/flavours/glitch/utils/dom_helpers.js b/app/javascript/flavours/glitch/utils/dom_helpers.js
index d94aeb9d47..d9796954a3 100644
--- a/app/javascript/flavours/glitch/utils/dom_helpers.js
+++ b/app/javascript/flavours/glitch/utils/dom_helpers.js
@@ -1,10 +1,6 @@
 //  Package imports.
 import { supportsPassiveEvents } from 'detect-passive-events';
 
-//  This will either be a passive lister options object (if passive
-//  events are supported), or `false`.
-export const withPassive = supportsPassiveEvents ? { passive: true } : false;
-
 //  Focuses the root element.
 export function focusRoot () {
   let e;