From 10a74696b3c0340181f2da5c93901454fc20ec41 Mon Sep 17 00:00:00 2001 From: Bruno Windels Date: Wed, 17 Jul 2019 16:50:05 +0200 Subject: [PATCH] hack around React having its own bubbling phase --- src/components/structures/LoggedInView.js | 42 +++++++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/components/structures/LoggedInView.js b/src/components/structures/LoggedInView.js index 146af704c0..16618bb973 100644 --- a/src/components/structures/LoggedInView.js +++ b/src/components/structures/LoggedInView.js @@ -106,7 +106,7 @@ const LoggedInView = React.createClass({ CallMediaHandler.loadDevices(); - document.addEventListener('keydown', this._onKeyDown); + document.addEventListener('keydown', this._onNativeKeyDown, false); this._sessionStore = sessionStore; this._sessionStoreToken = this._sessionStore.addListener( @@ -136,7 +136,7 @@ const LoggedInView = React.createClass({ }, componentWillUnmount: function() { - document.removeEventListener('keydown', this._onKeyDown); + document.removeEventListener('keydown', this._onNativeKeyDown, false); this._matrixClient.removeListener("accountData", this.onAccountData); this._matrixClient.removeListener("sync", this.onSync); this._matrixClient.removeListener("RoomState.events", this.onRoomStateEvents); @@ -272,6 +272,42 @@ const LoggedInView = React.createClass({ }); }, + /* + SOME HACKERY BELOW: + React optimizes event handlers, by always attaching only 1 handler to the document for a given type. + It then internally determines the order in which React event handlers should be called, + emulating the capture and bubbling phases the DOM also has. + + But, as the native handler for React is always attached on the document, + it will always run last for bubbling (first for capturing) handlers, + and thus React basically has its own event phases, and will always run + after (before for capturing) any native other event handlers (as they tend to be attached last). + + So ideally one wouldn't mix React and native event handlers to have bubbling working as expected, + but we do need a native event handler here on the document, + to get keydown events when there is no focused element (target=body). + + We also do need bubbling here to give child components a chance to call `stopPropagation()`, + for keydown events it can handle itself, and shouldn't be redirected to the composer. + + So we listen with React on this component to get any events on focused elements, and get bubbling working as expected. + We also listen with a native listener on the document to get keydown events when no element is focused. + Bubbling is irrelevant here as the target is the body element. + */ + _onReactKeyDown: function(ev) { + // events caught while bubbling up on the root element + // of this component, so something must be focused. + this._onKeyDown(ev); + }, + + _onNativeKeyDown: function(ev) { + // only pass this if there is no focused element. + // if there is, _onKeyDown will be called by the + // react keydown handler that respects the react bubbling order. + if (ev.target === document.body) { + this._onKeyDown(ev); + } + }, _onKeyDown: function(ev) { /* @@ -560,7 +596,7 @@ const LoggedInView = React.createClass({ } return ( -
+
{ topBar }