Skip to content

Commit

Permalink
fix(FEC-8163): UI doesn't remove its listeners when components unmount (
Browse files Browse the repository at this point in the history
#234)

Add bindMethod util.
Bind any listener context in the constructor.
  • Loading branch information
Dan Ziv committed May 6, 2018
1 parent 90c30d6 commit 13f9078
Show file tree
Hide file tree
Showing 9 changed files with 173 additions and 52 deletions.
38 changes: 25 additions & 13 deletions src/components/fullscreen/fullscreen.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {actions} from '../../reducers/shell';
import {actions as fullscreenActions} from '../../reducers/fullscreen';
import BaseComponent from '../base';
import {default as Icon, IconType} from '../icon';
import {bindMethod} from '../../utils/bind-method';

/**
* mapping state to props
Expand All @@ -31,6 +32,7 @@ const mapStateToProps = state => ({
*/
class FullscreenControl extends BaseComponent {
_targetDiv: ?HTMLElement;
fullscreenChangeHandler: Function;

/**
* Creates an instance of FullscreenControl.
Expand All @@ -39,6 +41,7 @@ class FullscreenControl extends BaseComponent {
*/
constructor(obj: Object) {
super({name: 'Fullscreen', player: obj.player});
this.fullscreenChangeHandler = bindMethod(this, this.fullscreenChangeHandler);
}

/**
Expand All @@ -52,21 +55,34 @@ class FullscreenControl extends BaseComponent {
}

/**
* after component mounted, set up event listerners to window fullscreen state change
* after component mounted, set up event listeners to window fullscreen state change
*
* @returns {void}
* @memberof FullscreenControl
*/
componentDidMount() {
document.addEventListener('webkitfullscreenchange', () => this.fullscreenChangeHandler());
document.addEventListener('mozfullscreenchange', () => this.fullscreenChangeHandler());
document.addEventListener('fullscreenchange', () => this.fullscreenChangeHandler());
document.addEventListener('MSFullscreenChange', () => this.fullscreenChangeHandler());
componentDidMount(): void {
document.addEventListener('webkitfullscreenchange', this.fullscreenChangeHandler);
document.addEventListener('mozfullscreenchange', this.fullscreenChangeHandler);
document.addEventListener('fullscreenchange', this.fullscreenChangeHandler);
document.addEventListener('MSFullscreenChange', this.fullscreenChangeHandler);
this.player.addEventListener(this.player.Event.REQUESTED_ENTER_FULLSCREEN, () => this.enterFullscreen());
this.player.addEventListener(this.player.Event.REQUESTED_EXIT_FULLSCREEN, () => this.exitFullscreen());
this.handleIosFullscreen();
}

/**
* before component unmounted, remove event listeners
*
* @returns {void}
* @memberof FullscreenControl
*/
componentWillUnmount(): void {
document.removeEventListener('webkitfullscreenchange', this.fullscreenChangeHandler);
document.removeEventListener('mozfullscreenchange', this.fullscreenChangeHandler);
document.removeEventListener('fullscreenchange', this.fullscreenChangeHandler);
document.removeEventListener('MSFullscreenChange', this.fullscreenChangeHandler);
}

/**
* Handle iOS full screen changes
*
Expand All @@ -81,12 +97,8 @@ class FullscreenControl extends BaseComponent {
*/
const attachIosFullscreenListeners = () => {
this.player.removeEventListener(this.player.Event.SOURCE_SELECTED, attachIosFullscreenListeners);
this.player.getVideoElement().addEventListener('webkitbeginfullscreen', () => {
this.fullscreenEnterHandler();
});
this.player.getVideoElement().addEventListener('webkitendfullscreen', () => {
this.fullscreenExitHandler();
});
this.player.getVideoElement().addEventListener('webkitbeginfullscreen', () => this.fullscreenEnterHandler());
this.player.getVideoElement().addEventListener('webkitendfullscreen', () => this.fullscreenExitHandler());
};
this.player.addEventListener(this.player.Event.SOURCE_SELECTED, attachIosFullscreenListeners);
}
Expand All @@ -99,7 +111,7 @@ class FullscreenControl extends BaseComponent {
* @memberof FullscreenControl
*/
fullscreenChangeHandler(): void {
let isFullscreen = typeof document.fullscreenElement !== 'undefined' && Boolean(document.fullscreenElement) ||
const isFullscreen = typeof document.fullscreenElement !== 'undefined' && Boolean(document.fullscreenElement) ||
typeof document.webkitFullscreenElement !== 'undefined' && Boolean(document.webkitFullscreenElement) ||
typeof document.mozFullScreenElement !== 'undefined' && Boolean(document.mozFullScreenElement) ||
typeof document.msFullscreenElement !== 'undefined' && Boolean(document.msFullscreenElement);
Expand Down
7 changes: 5 additions & 2 deletions src/components/language/language.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {default as Icon, IconType} from '../icon';
import {CVAAOverlay} from '../cvaa-overlay';
import Portal from 'preact-portal';
import {KeyMap} from "../../utils/key-map";
import {bindMethod} from '../../utils/bind-method';

/**
* mapping state to props
Expand All @@ -35,6 +36,7 @@ const mapStateToProps = state => ({
*/
class LanguageControl extends BaseComponent {
state: Object;
handleClickOutside: Function;
_controlLanguageElement: any;
_portal: any;

Expand All @@ -45,6 +47,7 @@ class LanguageControl extends BaseComponent {
*/
constructor(obj: Object) {
super({name: 'LanguageControl', player: obj.player});
this.handleClickOutside = bindMethod(this, this.handleClickOutside);
}

/**
Expand All @@ -64,7 +67,7 @@ class LanguageControl extends BaseComponent {
* @memberof LanguageControl
*/
componentDidMount() {
document.addEventListener('click', this.handleClickOutside.bind(this), true);
document.addEventListener('click', this.handleClickOutside, true);
}

/**
Expand All @@ -74,7 +77,7 @@ class LanguageControl extends BaseComponent {
* @memberof LanguageControl
*/
componentWillUnmount() {
document.removeEventListener('click', this.handleClickOutside.bind(this), true);
document.removeEventListener('click', this.handleClickOutside, true);
}

/**
Expand Down
21 changes: 17 additions & 4 deletions src/components/menu/menu.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import style from '../../styles/style.scss';
import {h, Component} from 'preact';
import {default as Icon, IconType} from '../icon';
import {connect} from 'preact-redux';
import {KeyMap} from "../../utils/key-map";
import {KeyMap} from '../../utils/key-map';
import {bindMethod} from '../../utils/bind-method';

/**
* mapping state to props
Expand All @@ -28,8 +29,20 @@ const mapStateToProps = state => ({
* @extends {Component}
*/
class Menu extends Component {
_menuElement: any;
state: Object;
handleClickOutside: Function;
_menuElement: any;

/**
* Creates an instance of Menu.
*
* @constructor
* @memberof Menu
*/
constructor() {
super();
this.handleClickOutside = bindMethod(this, this.handleClickOutside);
}

/**
* before component mounted, set initial state of the menu position
Expand All @@ -46,7 +59,7 @@ class Menu extends Component {
* @memberof Menu
*/
componentDidMount() {
document.addEventListener('click', this.handleClickOutside.bind(this), true);
document.addEventListener('click', this.handleClickOutside, true);
if (!this.props.isMobile) {
this.setState({position: this.getPosition()});
}
Expand All @@ -59,7 +72,7 @@ class Menu extends Component {
* @memberof Menu
*/
componentWillUnmount() {
document.removeEventListener('click', this.handleClickOutside.bind(this));
document.removeEventListener('click', this.handleClickOutside);
}

/**
Expand Down
33 changes: 25 additions & 8 deletions src/components/seekbar/seekbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {connect} from "preact-redux";
import {bindActions} from "../../utils/bind-actions";
import {actions} from "../../reducers/shell";
import {KEYBOARD_DEFAULT_SEEK_JUMP} from '../keyboard/keyboard';
import {bindMethod} from '../../utils/bind-method';

/**
* mapping state to props
Expand All @@ -27,18 +28,28 @@ const mapStateToProps = state => ({
*/
class SeekBarControl extends Component {
state: Object;
onPlayerMouseUp: Function;
_seekBarElement: HTMLElement;
_framePreviewElement: HTMLElement;
_timeBubbleElement: HTMLElement;
_movex: number;

/**
* Creates an instance of SeekBarControl.
* @memberof SeekBarControl
*/
constructor() {
super();
this.onPlayerMouseUp = bindMethod(this, this.onPlayerMouseUp);
}

/**
* before component mounted, set initial state
*
* @returns {void}
* @memberof SeekBarControl
*/
componentWillMount() {
componentWillMount(): void {
this.setState({virtualTime: 0});
}

Expand All @@ -48,14 +59,20 @@ class SeekBarControl extends Component {
* @returns {void}
* @memberof SeekBarControl
*/
componentDidMount() {
document.addEventListener('mouseup', (e: Event) => {
this.onPlayerMouseUp(e);
});
componentDidMount(): void {
document.addEventListener('mouseup', this.onPlayerMouseUp);
document.addEventListener('mousemove', this.onPlayerMouseUp);
}

document.addEventListener('mousemove', (e: Event) => {
this.onPlayerMouseMove(e);
});
/**
* before component unmounted, remove event listeners
*
* @returns {void}
* @memberof SeekBarControl
*/
componentWillUnmount(): void {
document.removeEventListener('mouseup', this.onPlayerMouseUp);
document.removeEventListener('mousemove', this.onPlayerMouseUp);
}

/**
Expand Down
7 changes: 5 additions & 2 deletions src/components/settings/settings.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import BaseComponent from '../base';
import {SmartContainer} from '../smart-container';
import {SmartContainerItem} from '../smart-container/smart-container-item';
import {default as Icon, IconType} from '../icon';
import {bindMethod} from '../../utils/bind-method';

/**
* mapping state to props
Expand All @@ -31,6 +32,7 @@ const mapStateToProps = state => ({
*/
class SettingsControl extends BaseComponent {
state: Object;
handleClickOutside: Function;
_controlSettingsElement: any;

/**
Expand All @@ -40,6 +42,7 @@ class SettingsControl extends BaseComponent {
*/
constructor(obj: Object) {
super({name: 'Settings', player: obj.player});
this.handleClickOutside = bindMethod(this, this.handleClickOutside);
}

/**
Expand All @@ -59,7 +62,7 @@ class SettingsControl extends BaseComponent {
* @memberof SettingsControl
*/
componentDidMount() {
document.addEventListener('click', this.handleClickOutside.bind(this), true);
document.addEventListener('click', this.handleClickOutside, true);
}

/**
Expand All @@ -69,7 +72,7 @@ class SettingsControl extends BaseComponent {
* @memberof SettingsControl
*/
componentWillUnmount() {
document.removeEventListener('click', this.handleClickOutside.bind(this));
document.removeEventListener('click', this.handleClickOutside, true);
}

/**
Expand Down
41 changes: 28 additions & 13 deletions src/components/shell/shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import BaseComponent from '../base';
import {connect} from 'preact-redux';
import {bindActions} from '../../utils/bind-actions';
import {actions} from '../../reducers/shell';
import {KeyMap} from "../../utils/key-map";
import {KeyMap} from '../../utils/key-map';
import {bindMethod} from '../../utils/bind-method';

/**
* mapping state to props
Expand Down Expand Up @@ -52,6 +53,7 @@ class Shell extends BaseComponent {
hoverTimeout: ?number;
_fallbackToMutedAutoPlayMode: boolean;
_environmentClasses: Array<string>;
_onWindowResize: Function;

/**
* Creates an instance of Shell.
Expand All @@ -60,6 +62,7 @@ class Shell extends BaseComponent {
*/
constructor(obj: Object) {
super({name: 'Shell', player: obj.player});
this._onWindowResize = bindMethod(this, this._onWindowResize);
this._fallbackToMutedAutoPlayMode = false;
this.player.addEventListener(this.player.Event.FALLBACK_TO_MUTED_AUTOPLAY, () => {
this._fallbackToMutedAutoPlayMode = true
Expand Down Expand Up @@ -172,22 +175,34 @@ class Shell extends BaseComponent {
*/
componentDidMount() {
this.props.updateIsMobile(!!this.player.env.device.type || this.props.forceTouchUI);
if (document.body) {
this.props.updateDocumentWidth(document.body.clientWidth);
}
this._onWindowResize();
window.addEventListener('resize', this._onWindowResize);
}

/**
* window resize handler
*
* @returns {void}
* @memberof Shell
*/
_onWindowResize(): void {
const playerContainer = document.getElementById(this.props.targetId);
if (playerContainer) {
this.props.updatePlayerClientRect(playerContainer.getBoundingClientRect());
}
window.addEventListener('resize', () => {
const playerContainer = document.getElementById(this.props.targetId);
if (playerContainer) {
this.props.updatePlayerClientRect(playerContainer.getBoundingClientRect());
}
if (document.body) {
this.props.updateDocumentWidth(document.body.clientWidth);
}
});
if (document.body) {
this.props.updateDocumentWidth(document.body.clientWidth);
}
}

/**
* before component mounted, remove event listeners
*
* @returns {void}
* @memberof Shell
*/
componentWillUnmount(): void {
window.removeEventListener('resize', this._onWindowResize);
}

/**
Expand Down

0 comments on commit 13f9078

Please sign in to comment.