diff --git a/app/javascript/flavours/glitch/components/column.jsx b/app/javascript/flavours/glitch/components/column.jsx
index 47293ef18..fce6a6c40 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 3f17bc129..901c7fe22 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 13701bcd3..69925321b 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 06db0a003..99e1ed55c 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,
);
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 786f59307..28f316619 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 752689930..68ff37b80 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 05614de01..2d4ae72d4 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 1ac7d579e..63195518b 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(, mountNode);
+ const root = createRoot(mountNode);
+ root.render();
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 7544ae4e9..7b0917df8 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 (
-
- ), element);
+ ,
+ );
});
}).catch(error => {
console.error(error);
diff --git a/app/javascript/flavours/glitch/packs/public.jsx b/app/javascript/flavours/glitch/packs/public.jsx
index 34a8f3513..7bb85fd2f 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(, content);
+ const root = createRoot(content);
+ root.render();
document.body.appendChild(content);
scrollToDetailedStatus();
})
diff --git a/app/javascript/flavours/glitch/packs/share.jsx b/app/javascript/flavours/glitch/packs/share.jsx
index 47a51a5b0..a835058ac 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(, mountNode);
+ const root = createRoot(mountNode);
+ root.render();
}
}
diff --git a/app/javascript/flavours/glitch/utils/dom_helpers.js b/app/javascript/flavours/glitch/utils/dom_helpers.js
index d94aeb9d4..d9796954a 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;