feat(tile-view): double click to pin
This commit is contained in:
parent
fc75adc6ff
commit
ebcde745ef
|
@ -595,17 +595,7 @@ UI.removeListener = function(type, listener) {
|
||||||
*/
|
*/
|
||||||
UI.emitEvent = (type, ...options) => eventEmitter.emit(type, ...options);
|
UI.emitEvent = (type, ...options) => eventEmitter.emit(type, ...options);
|
||||||
|
|
||||||
UI.clickOnVideo = function(videoNumber) {
|
UI.clickOnVideo = videoNumber => VideoLayout.togglePin(videoNumber);
|
||||||
const videos = $('#remoteVideos .videocontainer:not(#mixedstream)');
|
|
||||||
const videosLength = videos.length;
|
|
||||||
|
|
||||||
if (videosLength <= videoNumber) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const videoIndex = videoNumber === 0 ? 0 : videosLength - videoNumber;
|
|
||||||
|
|
||||||
videos[videoIndex].click();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Used by torture.
|
// Used by torture.
|
||||||
UI.showToolbar = timeout => APP.store.dispatch(showToolbox(timeout));
|
UI.showToolbar = timeout => APP.store.dispatch(showToolbox(timeout));
|
||||||
|
|
|
@ -15,11 +15,14 @@ export default function SharedVideoThumb(participant, videoType, VideoLayout) {
|
||||||
this.videoSpanId = 'sharedVideoContainer';
|
this.videoSpanId = 'sharedVideoContainer';
|
||||||
this.container = this.createContainer(this.videoSpanId);
|
this.container = this.createContainer(this.videoSpanId);
|
||||||
this.$container = $(this.container);
|
this.$container = $(this.container);
|
||||||
this.container.onclick = this.videoClick.bind(this);
|
|
||||||
this.bindHoverHandler();
|
this.bindHoverHandler();
|
||||||
SmallVideo.call(this, VideoLayout);
|
SmallVideo.call(this, VideoLayout);
|
||||||
this.isVideoMuted = true;
|
this.isVideoMuted = true;
|
||||||
this.setDisplayName(participant.name);
|
this.setDisplayName(participant.name);
|
||||||
|
|
||||||
|
this.container.onclick = this._onContainerClick;
|
||||||
|
this.container.ondblclick = this._onContainerDoubleClick;
|
||||||
}
|
}
|
||||||
SharedVideoThumb.prototype = Object.create(SmallVideo.prototype);
|
SharedVideoThumb.prototype = Object.create(SmallVideo.prototype);
|
||||||
SharedVideoThumb.prototype.constructor = SharedVideoThumb;
|
SharedVideoThumb.prototype.constructor = SharedVideoThumb;
|
||||||
|
@ -61,13 +64,6 @@ SharedVideoThumb.prototype.createContainer = function(spanId) {
|
||||||
return container;
|
return container;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* The thumb click handler.
|
|
||||||
*/
|
|
||||||
SharedVideoThumb.prototype.videoClick = function() {
|
|
||||||
this._togglePin();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes RemoteVideo from the page.
|
* Removes RemoteVideo from the page.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -61,7 +61,8 @@ function LocalVideo(VideoLayout, emitter, streamEndedCallback) {
|
||||||
this.addAudioLevelIndicator();
|
this.addAudioLevelIndicator();
|
||||||
this.updateIndicators();
|
this.updateIndicators();
|
||||||
|
|
||||||
this.container.onclick = this._onContainerClick.bind(this);
|
this.container.onclick = this._onContainerClick;
|
||||||
|
this.container.ondblclick = this._onContainerDoubleClick;
|
||||||
}
|
}
|
||||||
|
|
||||||
LocalVideo.prototype = Object.create(SmallVideo.prototype);
|
LocalVideo.prototype = Object.create(SmallVideo.prototype);
|
||||||
|
@ -253,40 +254,6 @@ LocalVideo.prototype.updateDOMLocation = function() {
|
||||||
this._updateVideoElement();
|
this._updateVideoElement();
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback invoked when the thumbnail is clicked. Will directly call
|
|
||||||
* VideoLayout to handle thumbnail click if certain elements have not been
|
|
||||||
* clicked.
|
|
||||||
*
|
|
||||||
* @param {MouseEvent} event - The click event to intercept.
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
LocalVideo.prototype._onContainerClick = function(event) {
|
|
||||||
// TODO Checking the classes is a workround to allow events to bubble into
|
|
||||||
// the DisplayName component if it was clicked. React's synthetic events
|
|
||||||
// will fire after jQuery handlers execute, so stop propogation at this
|
|
||||||
// point will prevent DisplayName from getting click events. This workaround
|
|
||||||
// should be removeable once LocalVideo is a React Component because then
|
|
||||||
// the components share the same eventing system.
|
|
||||||
const $source = $(event.target || event.srcElement);
|
|
||||||
const { classList } = event.target;
|
|
||||||
|
|
||||||
const clickedOnDisplayName
|
|
||||||
= $source.parents('.displayNameContainer').length > 0;
|
|
||||||
const clickedOnPopover = $source.parents('.popover').length > 0
|
|
||||||
|| classList.contains('popover');
|
|
||||||
const ignoreClick = clickedOnDisplayName || clickedOnPopover;
|
|
||||||
|
|
||||||
if (event.stopPropagation && !ignoreClick) {
|
|
||||||
event.stopPropagation();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ignoreClick) {
|
|
||||||
this._togglePin();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renders the React Element for displaying video in {@code LocalVideo}.
|
* Renders the React Element for displaying video in {@code LocalVideo}.
|
||||||
*
|
*
|
||||||
|
|
|
@ -22,8 +22,7 @@ import {
|
||||||
} from '../../../react/features/remote-video-menu';
|
} from '../../../react/features/remote-video-menu';
|
||||||
import {
|
import {
|
||||||
LAYOUTS,
|
LAYOUTS,
|
||||||
getCurrentLayout,
|
getCurrentLayout
|
||||||
shouldDisplayTileView
|
|
||||||
} from '../../../react/features/video-layout';
|
} from '../../../react/features/video-layout';
|
||||||
/* eslint-enable no-unused-vars */
|
/* eslint-enable no-unused-vars */
|
||||||
|
|
||||||
|
@ -89,7 +88,8 @@ function RemoteVideo(user, VideoLayout, emitter) {
|
||||||
this._setAudioVolume = this._setAudioVolume.bind(this);
|
this._setAudioVolume = this._setAudioVolume.bind(this);
|
||||||
this._stopRemoteControl = this._stopRemoteControl.bind(this);
|
this._stopRemoteControl = this._stopRemoteControl.bind(this);
|
||||||
|
|
||||||
this.container.onclick = this._onContainerClick.bind(this);
|
this.container.onclick = this._onContainerClick;
|
||||||
|
this.container.ondblclick = this._onContainerDoubleClick;
|
||||||
}
|
}
|
||||||
|
|
||||||
RemoteVideo.prototype = Object.create(SmallVideo.prototype);
|
RemoteVideo.prototype = Object.create(SmallVideo.prototype);
|
||||||
|
@ -613,36 +613,6 @@ RemoteVideo.prototype.removePresenceLabel = function() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback invoked when the thumbnail is clicked. Will directly call
|
|
||||||
* VideoLayout to handle thumbnail click if certain elements have not been
|
|
||||||
* clicked.
|
|
||||||
*
|
|
||||||
* @param {MouseEvent} event - The click event to intercept.
|
|
||||||
* @private
|
|
||||||
* @returns {void}
|
|
||||||
*/
|
|
||||||
RemoteVideo.prototype._onContainerClick = function(event) {
|
|
||||||
const $source = $(event.target || event.srcElement);
|
|
||||||
const { classList } = event.target;
|
|
||||||
|
|
||||||
const ignoreClick = $source.parents('.popover').length > 0
|
|
||||||
|| classList.contains('popover');
|
|
||||||
|
|
||||||
if (!ignoreClick) {
|
|
||||||
this._togglePin();
|
|
||||||
}
|
|
||||||
|
|
||||||
// On IE we need to populate this handler on video <object> and it does not
|
|
||||||
// give event instance as an argument, so we check here for methods.
|
|
||||||
if (event.stopPropagation && event.preventDefault && !ignoreClick) {
|
|
||||||
event.stopPropagation();
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
RemoteVideo.createContainer = function(spanId) {
|
RemoteVideo.createContainer = function(spanId) {
|
||||||
const container = document.createElement('span');
|
const container = document.createElement('span');
|
||||||
|
|
||||||
|
|
|
@ -142,6 +142,9 @@ function SmallVideo(VideoLayout) {
|
||||||
// Bind event handlers so they are only bound once for every instance.
|
// Bind event handlers so they are only bound once for every instance.
|
||||||
this._onPopoverHover = this._onPopoverHover.bind(this);
|
this._onPopoverHover = this._onPopoverHover.bind(this);
|
||||||
this.updateView = this.updateView.bind(this);
|
this.updateView = this.updateView.bind(this);
|
||||||
|
|
||||||
|
this._onContainerClick = this._onContainerClick.bind(this);
|
||||||
|
this._onContainerDoubleClick = this._onContainerDoubleClick.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -821,12 +824,71 @@ SmallVideo.prototype.updateIndicators = function() {
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pins the participant displayed by this thumbnail or unpins if already pinned.
|
* Callback invoked when the thumbnail is double clicked. Will pin the
|
||||||
|
* participant if in tile view.
|
||||||
*
|
*
|
||||||
|
* @param {MouseEvent} event - The click event to intercept.
|
||||||
* @private
|
* @private
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
SmallVideo.prototype._togglePin = function() {
|
SmallVideo.prototype._onContainerDoubleClick = function(event) {
|
||||||
|
if (this._pinningRequiresDoubleClick() && this._shouldTriggerPin(event)) {
|
||||||
|
APP.store.dispatch(pinParticipant(this.id));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback invoked when the thumbnail is clicked and potentially trigger
|
||||||
|
* pinning of the participant.
|
||||||
|
*
|
||||||
|
* @param {MouseEvent} event - The click event to intercept.
|
||||||
|
* @private
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
SmallVideo.prototype._onContainerClick = function(event) {
|
||||||
|
const triggerPin = this._shouldTriggerPin(event)
|
||||||
|
&& !this._pinningRequiresDoubleClick();
|
||||||
|
|
||||||
|
if (event.stopPropagation && triggerPin) {
|
||||||
|
event.stopPropagation();
|
||||||
|
event.preventDefault();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (triggerPin) {
|
||||||
|
this.togglePin();
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not a click event is targeted at certain elements which
|
||||||
|
* should not trigger a pin.
|
||||||
|
*
|
||||||
|
* @param {MouseEvent} event - The click event to intercept.
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
SmallVideo.prototype._shouldTriggerPin = function(event) {
|
||||||
|
// TODO Checking the classes is a workround to allow events to bubble into
|
||||||
|
// the DisplayName component if it was clicked. React's synthetic events
|
||||||
|
// will fire after jQuery handlers execute, so stop propogation at this
|
||||||
|
// point will prevent DisplayName from getting click events. This workaround
|
||||||
|
// should be removeable once LocalVideo is a React Component because then
|
||||||
|
// the components share the same eventing system.
|
||||||
|
const $source = $(event.target || event.srcElement);
|
||||||
|
|
||||||
|
return $source.parents('.displayNameContainer').length === 0
|
||||||
|
&& $source.parents('.popover').length === 0
|
||||||
|
&& !event.target.classList.contains('popover');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pins the participant displayed by this thumbnail or unpins if already pinned.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
SmallVideo.prototype.togglePin = function() {
|
||||||
const pinnedParticipant
|
const pinnedParticipant
|
||||||
= getPinnedParticipant(APP.store.getState()) || {};
|
= getPinnedParticipant(APP.store.getState()) || {};
|
||||||
const participantIdToPin
|
const participantIdToPin
|
||||||
|
@ -836,6 +898,17 @@ SmallVideo.prototype._togglePin = function() {
|
||||||
APP.store.dispatch(pinParticipant(participantIdToPin));
|
APP.store.dispatch(pinParticipant(participantIdToPin));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether or not clicking to pin the participant needs to be a double
|
||||||
|
* click instead of a single click.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
SmallVideo.prototype._pinningRequiresDoubleClick = function() {
|
||||||
|
return shouldDisplayTileView(APP.store.getState());
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes the React element responsible for showing connection status, dominant
|
* Removes the React element responsible for showing connection status, dominant
|
||||||
* speaker, and raised hand icons.
|
* speaker, and raised hand icons.
|
||||||
|
|
|
@ -391,6 +391,19 @@ const VideoLayout = {
|
||||||
return id || null;
|
return id || null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Triggers a thumbnail to pin or unpin itself.
|
||||||
|
*
|
||||||
|
* @param {number} videoNumber - The index of the video to toggle pin on.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
togglePin(videoNumber) {
|
||||||
|
const videos = getAllThumbnails();
|
||||||
|
const videoView = videos[videoNumber];
|
||||||
|
|
||||||
|
videoView && videoView.togglePin();
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Callback invoked to update display when the pin participant has changed.
|
* Callback invoked to update display when the pin participant has changed.
|
||||||
*
|
*
|
||||||
|
|
Loading…
Reference in New Issue