Restructures the analytics events (#2333)
* ref: Restructures the pinned/unpinned events. * ref: Refactors the "audio only disabled" event. * ref: Refactors the "stream switch delay" event. * ref: Refactors the "select participant failed" event. * ref: Refactors the "initially muted" events. * ref: Refactors the screen sharing started/stopped events. * ref: Restructures the "device list changed" events. * ref: Restructures the "shared video" events. * ref: Restructures the "start muted" events. * ref: Restructures the "start audio only" event. * ref: Restructures the "sync track state" event. * ref: Restructures the "callkit" events. * ref: Restructures the "replace track". * ref: Restructures keyboard shortcuts events. * ref: Restructures most of the toolbar events. * ref: Refactors the API events. * ref: Restructures the video quality, profile button and invite dialog events. * ref: Refactors the "device changed" events. * ref: Refactors the page reload event. * ref: Removes an unused function. * ref: Removes a method which is needlessly exposed under a different name. * ref: Refactors the events from the remote video menu. * ref: Refactors the events from the profile pane. * ref: Restructures the recording-related events. Removes events fired when recording with something other than jibri (which isn't currently supported anyway). * ref: Cleans up AnalyticsEvents.js. * ref: Removes an unused function and adds documentation. * feat: Adds events for all API calls. * fix: Addresses feedback. * fix: Brings back mistakenly removed code. * fix: Simplifies code and fixes a bug in toggleFilmstrip when the 'visible' parameter is defined. * feat: Removes the resolution change application log. * ref: Uses consistent naming for events' attributes. Uses "_" as a separator instead of camel case or ".". * ref: Don't add the user agent and conference name as permanent properties. The library does this on its own now. * ref: Adapts the GA handler to changes in lib-jitsi-meet. * ref: Removes unused fields from the analytics handler initializaiton. * ref: Renames the google analytics file and add docs. * fix: Fixes the push-to-talk events and logs. * npm: Updates lib-jitsi-meet to 515374c8d383cb17df8ed76427e6f0fb5ea6ff1e. * fix: Fixes a recently introduced bug in the google analytics handler. * ref: Uses "value" instead of "delay" since this is friendlier to GA.
This commit is contained in:
parent
d08bbae770
commit
090f2f9ccb
2
Makefile
2
Makefile
|
@ -35,7 +35,7 @@ deploy-appbundle:
|
||||||
$(BUILD_DIR)/device_selection_popup_bundle.min.map \
|
$(BUILD_DIR)/device_selection_popup_bundle.min.map \
|
||||||
$(BUILD_DIR)/alwaysontop.min.js \
|
$(BUILD_DIR)/alwaysontop.min.js \
|
||||||
$(BUILD_DIR)/alwaysontop.min.map \
|
$(BUILD_DIR)/alwaysontop.min.map \
|
||||||
$(OUTPUT_DIR)/analytics.js \
|
$(OUTPUT_DIR)/analytics-ga.js \
|
||||||
$(DEPLOY_DIR)
|
$(DEPLOY_DIR)
|
||||||
|
|
||||||
deploy-lib-jitsi-meet:
|
deploy-lib-jitsi-meet:
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
/* global ga */
|
||||||
|
|
||||||
|
(function(ctx) {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
function Analytics() {
|
||||||
|
/* eslint-disable */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Google Analytics
|
||||||
|
*/
|
||||||
|
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||||
|
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||||
|
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||||
|
ga('create', 'UA-319188-14', 'jit.si');
|
||||||
|
ga('send', 'pageview');
|
||||||
|
|
||||||
|
/* eslint-enable */
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the integer to use for a Google Analytics event's value field
|
||||||
|
* from a lib-jitsi-meet analytics event.
|
||||||
|
* @param {Object} event - The lib-jitsi-meet analytics event.
|
||||||
|
* @returns {Object} - The integer to use for the 'value' of a Google
|
||||||
|
* Analytics event.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
Analytics.prototype._extractAction = function(event) {
|
||||||
|
// Page events have a single 'name' field.
|
||||||
|
if (event.type === 'page') {
|
||||||
|
return event.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
// All other events have action, actionSubject, and source fields. All
|
||||||
|
// three fields are required, and the often jitsi-meet and
|
||||||
|
// lib-jitsi-meet use the same value when separate values are not
|
||||||
|
// necessary (i.e. event.action == event.actionSubject).
|
||||||
|
// Here we concatenate these three fields, but avoid adding the same
|
||||||
|
// value twice, because it would only make the GA event's action harder
|
||||||
|
// to read.
|
||||||
|
let action = event.action;
|
||||||
|
|
||||||
|
if (event.actionSubject && event.actionSubject !== event.action) {
|
||||||
|
// Intentionally use string concatenation as analytics needs to
|
||||||
|
// work on IE but this file does not go through babel. For some
|
||||||
|
// reason disabling this globally for the file does not have an
|
||||||
|
// effect.
|
||||||
|
// eslint-disable-next-line prefer-template
|
||||||
|
action = event.actionSubject + '.' + action;
|
||||||
|
}
|
||||||
|
if (event.source && event.source !== event.action
|
||||||
|
&& event.source !== event.action) {
|
||||||
|
// eslint-disable-next-line prefer-template
|
||||||
|
action = event.source + '.' + action;
|
||||||
|
}
|
||||||
|
|
||||||
|
return action;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the integer to use for a Google Analytics event's value field
|
||||||
|
* from a lib-jitsi-meet analytics event.
|
||||||
|
* @param {Object} event - The lib-jitsi-meet analytics event.
|
||||||
|
* @returns {Object} - The integer to use for the 'value' of a Google
|
||||||
|
* Analytics event, or NaN if the lib-jitsi-meet event doesn't contain a
|
||||||
|
* suitable value.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
Analytics.prototype._extractValue = function(event) {
|
||||||
|
let value = event && event.attributes && event.attributes.value;
|
||||||
|
|
||||||
|
// Try to extract an integer from the "value" attribute.
|
||||||
|
value = Math.round(parseFloat(value));
|
||||||
|
|
||||||
|
return value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extracts the string to use for a Google Analytics event's label field
|
||||||
|
* from a lib-jitsi-meet analytics event.
|
||||||
|
* @param {Object} event - The lib-jitsi-meet analytics event.
|
||||||
|
* @returns {string} - The string to use for the 'label' of a Google
|
||||||
|
* Analytics event.
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
Analytics.prototype._extractLabel = function(event) {
|
||||||
|
let label = '';
|
||||||
|
|
||||||
|
// The label field is limited to 500B. We will concatenate all
|
||||||
|
// attributes of the event, except the user agent because it may be
|
||||||
|
// lengthy and is probably included from elsewhere.
|
||||||
|
for (const property in event.attributes) {
|
||||||
|
if (property !== 'permanent_user_agent'
|
||||||
|
&& event.attributes.hasOwnProperty(property)) {
|
||||||
|
// eslint-disable-next-line prefer-template
|
||||||
|
label += property + '=' + event.attributes[property] + '&';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (label.length > 0) {
|
||||||
|
label = label.slice(0, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return label;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the entry point of the API. The function sends an event to
|
||||||
|
* google analytics. The format of the event is described in
|
||||||
|
* AnalyticsAdapter in lib-jitsi-meet.
|
||||||
|
* @param {Object} event - the event in the format specified by
|
||||||
|
* lib-jitsi-meet.
|
||||||
|
*/
|
||||||
|
Analytics.prototype.sendEvent = function(event) {
|
||||||
|
if (!event) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const gaEvent = {
|
||||||
|
'eventCategory': 'jitsi-meet',
|
||||||
|
'eventAction': this._extractAction(event),
|
||||||
|
'eventLabel': this._extractLabel(event)
|
||||||
|
};
|
||||||
|
const value = this._extractValue(event);
|
||||||
|
|
||||||
|
if (!isNaN(value)) {
|
||||||
|
gaEvent.eventValue = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ga('send', 'event', gaEvent);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof ctx.JitsiMeetJS === 'undefined') {
|
||||||
|
ctx.JitsiMeetJS = {};
|
||||||
|
}
|
||||||
|
if (typeof ctx.JitsiMeetJS.app === 'undefined') {
|
||||||
|
ctx.JitsiMeetJS.app = {};
|
||||||
|
}
|
||||||
|
if (typeof ctx.JitsiMeetJS.app.analyticsHandlers === 'undefined') {
|
||||||
|
ctx.JitsiMeetJS.app.analyticsHandlers = [];
|
||||||
|
}
|
||||||
|
ctx.JitsiMeetJS.app.analyticsHandlers.push(Analytics);
|
||||||
|
})(window);
|
||||||
|
/* eslint-enable prefer-template */
|
47
analytics.js
47
analytics.js
|
@ -1,47 +0,0 @@
|
||||||
/* global ga */
|
|
||||||
|
|
||||||
(function(ctx) {
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
function Analytics() {
|
|
||||||
/* eslint-disable */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Google Analytics
|
|
||||||
*/
|
|
||||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
|
||||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
|
||||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
|
||||||
ga('create', 'UA-319188-14', 'jit.si');
|
|
||||||
ga('send', 'pageview');
|
|
||||||
|
|
||||||
/* eslint-enable */
|
|
||||||
}
|
|
||||||
|
|
||||||
Analytics.prototype.sendEvent = function(action, data) {
|
|
||||||
// empty label if missing value for it and add the value,
|
|
||||||
// the value should be integer or null
|
|
||||||
let value = data.value;
|
|
||||||
|
|
||||||
value = value ? Math.round(parseFloat(value)) : null;
|
|
||||||
const label = data.label || '';
|
|
||||||
|
|
||||||
// Intentionally use string concatenation as analytics needs to work on
|
|
||||||
// IE but this file does not go through babel.
|
|
||||||
// eslint-disable-next-line prefer-template
|
|
||||||
ga('send', 'event', 'jit.si', action + '.' + data.browserName,
|
|
||||||
label, value);
|
|
||||||
};
|
|
||||||
|
|
||||||
if (typeof ctx.JitsiMeetJS === 'undefined') {
|
|
||||||
ctx.JitsiMeetJS = {};
|
|
||||||
}
|
|
||||||
if (typeof ctx.JitsiMeetJS.app === 'undefined') {
|
|
||||||
ctx.JitsiMeetJS.app = {};
|
|
||||||
}
|
|
||||||
if (typeof ctx.JitsiMeetJS.app.analyticsHandlers === 'undefined') {
|
|
||||||
ctx.JitsiMeetJS.app.analyticsHandlers = [];
|
|
||||||
}
|
|
||||||
ctx.JitsiMeetJS.app.analyticsHandlers.push(Analytics);
|
|
||||||
})(window);
|
|
113
conference.js
113
conference.js
|
@ -16,19 +16,13 @@ import UIUtil from './modules/UI/util/UIUtil';
|
||||||
import * as JitsiMeetConferenceEvents from './ConferenceEvents';
|
import * as JitsiMeetConferenceEvents from './ConferenceEvents';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CONFERENCE_AUDIO_INITIALLY_MUTED,
|
createDeviceChangedEvent,
|
||||||
CONFERENCE_SHARING_DESKTOP_START,
|
createScreenSharingEvent,
|
||||||
CONFERENCE_SHARING_DESKTOP_STOP,
|
createSelectParticipantFailedEvent,
|
||||||
CONFERENCE_VIDEO_INITIALLY_MUTED,
|
createStreamSwitchDelayEvent,
|
||||||
DEVICE_LIST_CHANGED_AUDIO_MUTED,
|
createTrackMutedEvent,
|
||||||
DEVICE_LIST_CHANGED_VIDEO_MUTED,
|
|
||||||
SELECT_PARTICIPANT_FAILED,
|
|
||||||
SETTINGS_CHANGE_DEVICE_AUDIO_OUT,
|
|
||||||
SETTINGS_CHANGE_DEVICE_AUDIO_IN,
|
|
||||||
SETTINGS_CHANGE_DEVICE_VIDEO,
|
|
||||||
STREAM_SWITCH_DELAY,
|
|
||||||
initAnalytics,
|
initAnalytics,
|
||||||
sendAnalyticsEvent
|
sendAnalytics
|
||||||
} from './react/features/analytics';
|
} from './react/features/analytics';
|
||||||
|
|
||||||
import EventEmitter from 'events';
|
import EventEmitter from 'events';
|
||||||
|
@ -741,14 +735,13 @@ export default {
|
||||||
})
|
})
|
||||||
.then(([ tracks, con ]) => {
|
.then(([ tracks, con ]) => {
|
||||||
tracks.forEach(track => {
|
tracks.forEach(track => {
|
||||||
if (track.isAudioTrack() && this.isLocalAudioMuted()) {
|
if ((track.isAudioTrack() && this.isLocalAudioMuted())
|
||||||
sendAnalyticsEvent(CONFERENCE_AUDIO_INITIALLY_MUTED);
|
|| (track.isVideoTrack() && this.isLocalVideoMuted())) {
|
||||||
logger.log('Audio mute: initially muted');
|
const mediaType = track.getType();
|
||||||
track.mute();
|
|
||||||
} else if (track.isVideoTrack()
|
sendAnalytics(
|
||||||
&& this.isLocalVideoMuted()) {
|
createTrackMutedEvent(mediaType, 'initial mute'));
|
||||||
sendAnalyticsEvent(CONFERENCE_VIDEO_INITIALLY_MUTED);
|
logger.log(`${mediaType} mute: initially muted.`);
|
||||||
logger.log('Video mute: initially muted');
|
|
||||||
track.mute();
|
track.mute();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1453,8 +1446,9 @@ export default {
|
||||||
promise = createLocalTracksF({ devices: [ 'video' ] })
|
promise = createLocalTracksF({ devices: [ 'video' ] })
|
||||||
.then(([ stream ]) => this.useVideoStream(stream))
|
.then(([ stream ]) => this.useVideoStream(stream))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
sendAnalyticsEvent(CONFERENCE_SHARING_DESKTOP_STOP);
|
sendAnalytics(createScreenSharingEvent('stopped'));
|
||||||
logger.log('switched back to local video');
|
logger.log('Screen sharing stopped, switching to video.');
|
||||||
|
|
||||||
if (!this.localVideo && wasVideoMuted) {
|
if (!this.localVideo && wasVideoMuted) {
|
||||||
return Promise.reject('No local video to be muted!');
|
return Promise.reject('No local video to be muted!');
|
||||||
} else if (wasVideoMuted && this.localVideo) {
|
} else if (wasVideoMuted && this.localVideo) {
|
||||||
|
@ -1609,7 +1603,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries to switch to the screenshairng mode by disposing camera stream and
|
* Tries to switch to the screensharing mode by disposing camera stream and
|
||||||
* replacing it with a desktop one.
|
* replacing it with a desktop one.
|
||||||
*
|
*
|
||||||
* @param {Object} [options] - Screen sharing options that will be passed to
|
* @param {Object} [options] - Screen sharing options that will be passed to
|
||||||
|
@ -1632,8 +1626,8 @@ export default {
|
||||||
.then(stream => this.useVideoStream(stream))
|
.then(stream => this.useVideoStream(stream))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.videoSwitchInProgress = false;
|
this.videoSwitchInProgress = false;
|
||||||
sendAnalyticsEvent(CONFERENCE_SHARING_DESKTOP_START);
|
sendAnalytics(createScreenSharingEvent('started'));
|
||||||
logger.log('sharing local desktop');
|
logger.log('Screen sharing started');
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
this.videoSwitchInProgress = false;
|
this.videoSwitchInProgress = false;
|
||||||
|
@ -1928,7 +1922,7 @@ export default {
|
||||||
|
|
||||||
room.selectParticipant(id);
|
room.selectParticipant(id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
sendAnalyticsEvent(SELECT_PARTICIPANT_FAILED);
|
sendAnalytics(createSelectParticipantFailedEvent(e));
|
||||||
reportError(e);
|
reportError(e);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -2152,22 +2146,12 @@ export default {
|
||||||
APP.UI.addListener(
|
APP.UI.addListener(
|
||||||
UIEvents.RESOLUTION_CHANGED,
|
UIEvents.RESOLUTION_CHANGED,
|
||||||
(id, oldResolution, newResolution, delay) => {
|
(id, oldResolution, newResolution, delay) => {
|
||||||
const logObject = {
|
sendAnalytics(createStreamSwitchDelayEvent(
|
||||||
id: 'resolution_change',
|
{
|
||||||
participant: id,
|
'old_resolution': oldResolution,
|
||||||
oldValue: oldResolution,
|
'new_resolution': newResolution,
|
||||||
newValue: newResolution,
|
value: delay
|
||||||
delay
|
}));
|
||||||
};
|
|
||||||
|
|
||||||
room.sendApplicationLog(JSON.stringify(logObject));
|
|
||||||
|
|
||||||
// We only care about the delay between simulcast streams.
|
|
||||||
// Longer delays will be caused by something else and will just
|
|
||||||
// poison the data.
|
|
||||||
if (delay < 2000) {
|
|
||||||
sendAnalyticsEvent(STREAM_SWITCH_DELAY, { value: delay });
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/* eslint-enable max-params */
|
/* eslint-enable max-params */
|
||||||
|
@ -2193,7 +2177,7 @@ export default {
|
||||||
cameraDeviceId => {
|
cameraDeviceId => {
|
||||||
const videoWasMuted = this.isLocalVideoMuted();
|
const videoWasMuted = this.isLocalVideoMuted();
|
||||||
|
|
||||||
sendAnalyticsEvent(SETTINGS_CHANGE_DEVICE_VIDEO);
|
sendAnalytics(createDeviceChangedEvent('video', 'input'));
|
||||||
createLocalTracksF({
|
createLocalTracksF({
|
||||||
devices: [ 'video' ],
|
devices: [ 'video' ],
|
||||||
cameraDeviceId,
|
cameraDeviceId,
|
||||||
|
@ -2232,7 +2216,7 @@ export default {
|
||||||
micDeviceId => {
|
micDeviceId => {
|
||||||
const audioWasMuted = this.isLocalAudioMuted();
|
const audioWasMuted = this.isLocalAudioMuted();
|
||||||
|
|
||||||
sendAnalyticsEvent(SETTINGS_CHANGE_DEVICE_AUDIO_IN);
|
sendAnalytics(createDeviceChangedEvent('audio', 'input'));
|
||||||
createLocalTracksF({
|
createLocalTracksF({
|
||||||
devices: [ 'audio' ],
|
devices: [ 'audio' ],
|
||||||
cameraDeviceId: null,
|
cameraDeviceId: null,
|
||||||
|
@ -2262,7 +2246,7 @@ export default {
|
||||||
APP.UI.addListener(
|
APP.UI.addListener(
|
||||||
UIEvents.AUDIO_OUTPUT_DEVICE_CHANGED,
|
UIEvents.AUDIO_OUTPUT_DEVICE_CHANGED,
|
||||||
audioOutputDeviceId => {
|
audioOutputDeviceId => {
|
||||||
sendAnalyticsEvent(SETTINGS_CHANGE_DEVICE_AUDIO_OUT);
|
sendAnalytics(createDeviceChangedEvent('audio', 'output'));
|
||||||
APP.settings.setAudioOutputDeviceId(audioOutputDeviceId)
|
APP.settings.setAudioOutputDeviceId(audioOutputDeviceId)
|
||||||
.then(() => logger.log('changed audio output device'))
|
.then(() => logger.log('changed audio output device'))
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
@ -2528,7 +2512,9 @@ export default {
|
||||||
// If audio was muted before, or we unplugged current device
|
// If audio was muted before, or we unplugged current device
|
||||||
// and selected new one, then mute new audio track.
|
// and selected new one, then mute new audio track.
|
||||||
if (audioWasMuted) {
|
if (audioWasMuted) {
|
||||||
sendAnalyticsEvent(DEVICE_LIST_CHANGED_AUDIO_MUTED);
|
sendAnalytics(createTrackMutedEvent(
|
||||||
|
'audio',
|
||||||
|
'device list changed'));
|
||||||
logger.log('Audio mute: device list changed');
|
logger.log('Audio mute: device list changed');
|
||||||
muteLocalAudio(true);
|
muteLocalAudio(true);
|
||||||
}
|
}
|
||||||
|
@ -2536,7 +2522,9 @@ export default {
|
||||||
// If video was muted before, or we unplugged current device
|
// If video was muted before, or we unplugged current device
|
||||||
// and selected new one, then mute new video track.
|
// and selected new one, then mute new video track.
|
||||||
if (!this.isSharingScreen && videoWasMuted) {
|
if (!this.isSharingScreen && videoWasMuted) {
|
||||||
sendAnalyticsEvent(DEVICE_LIST_CHANGED_VIDEO_MUTED);
|
sendAnalytics(createTrackMutedEvent(
|
||||||
|
'video',
|
||||||
|
'device list changed'));
|
||||||
logger.log('Video mute: device list changed');
|
logger.log('Video mute: device list changed');
|
||||||
muteLocalVideo(true);
|
muteLocalVideo(true);
|
||||||
}
|
}
|
||||||
|
@ -2622,37 +2610,6 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* Log event to callstats and analytics.
|
|
||||||
* @param {string} name the event name
|
|
||||||
* @param {int} value the value (it's int because google analytics supports
|
|
||||||
* only int).
|
|
||||||
* @param {string} label short text which provides more info about the event
|
|
||||||
* which allows to distinguish between few event cases of the same name
|
|
||||||
* NOTE: Should be used after conference.init
|
|
||||||
*/
|
|
||||||
logEvent(name, value, label) {
|
|
||||||
sendAnalyticsEvent(name, {
|
|
||||||
value,
|
|
||||||
label
|
|
||||||
});
|
|
||||||
if (room) {
|
|
||||||
room.sendApplicationLog(JSON.stringify({ name,
|
|
||||||
value,
|
|
||||||
label }));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Methods logs an application event given in the JSON format.
|
|
||||||
* @param {string} logJSON an event to be logged in JSON format
|
|
||||||
*/
|
|
||||||
logJSON(logJSON) {
|
|
||||||
if (room) {
|
|
||||||
room.sendApplicationLog(logJSON);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disconnect from the conference and optionally request user feedback.
|
* Disconnect from the conference and optionally request user feedback.
|
||||||
* @param {boolean} [requestFeedback=false] if user feedback should be
|
* @param {boolean} [requestFeedback=false] if user feedback should be
|
||||||
|
|
|
@ -307,21 +307,24 @@ var config = {
|
||||||
// backToP2PDelay: 5
|
// backToP2PDelay: 5
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// A list of scripts to load as lib-jitsi-meet "analytics handlers".
|
||||||
|
// analyticsScriptUrls: [
|
||||||
|
// "libs/analytics-ga.js", // google-analytics
|
||||||
|
// "https://example.com/my-custom-analytics.js"
|
||||||
|
// ],
|
||||||
|
|
||||||
// Information about the jitsi-meet instance we are connecting to, including
|
// Information about the jitsi-meet instance we are connecting to, including
|
||||||
// the user region as seen by the server.
|
// the user region as seen by the server.
|
||||||
//
|
|
||||||
|
|
||||||
deploymentInfo: {
|
deploymentInfo: {
|
||||||
// shard: "shard1",
|
// shard: "shard1",
|
||||||
// region: "europe",
|
// region: "europe",
|
||||||
// userRegion: "asia"
|
// userRegion: "asia"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// List of undocumented settings used in jitsi-meet
|
// List of undocumented settings used in jitsi-meet
|
||||||
/**
|
/**
|
||||||
alwaysVisibleToolbar
|
alwaysVisibleToolbar
|
||||||
analyticsScriptUrls
|
|
||||||
autoEnableDesktopSharing
|
autoEnableDesktopSharing
|
||||||
autoRecord
|
autoRecord
|
||||||
autoRecordToken
|
autoRecordToken
|
||||||
|
|
|
@ -3,9 +3,8 @@
|
||||||
import * as JitsiMeetConferenceEvents from '../../ConferenceEvents';
|
import * as JitsiMeetConferenceEvents from '../../ConferenceEvents';
|
||||||
import { parseJWTFromURLParams } from '../../react/features/base/jwt';
|
import { parseJWTFromURLParams } from '../../react/features/base/jwt';
|
||||||
import {
|
import {
|
||||||
API_TOGGLE_AUDIO,
|
createApiEvent,
|
||||||
API_TOGGLE_VIDEO,
|
sendAnalytics
|
||||||
sendAnalyticsEvent
|
|
||||||
} from '../../react/features/analytics';
|
} from '../../react/features/analytics';
|
||||||
import { getJitsiMeetTransport } from '../transport';
|
import { getJitsiMeetTransport } from '../transport';
|
||||||
|
|
||||||
|
@ -56,25 +55,48 @@ let videoAvailable = true;
|
||||||
*/
|
*/
|
||||||
function initCommands() {
|
function initCommands() {
|
||||||
commands = {
|
commands = {
|
||||||
'display-name':
|
'display-name': displayName => {
|
||||||
APP.conference.changeLocalDisplayName.bind(APP.conference),
|
sendAnalytics(createApiEvent('display.name.changed'));
|
||||||
|
APP.conference.changeLocalDisplayName(displayName);
|
||||||
|
},
|
||||||
'toggle-audio': () => {
|
'toggle-audio': () => {
|
||||||
sendAnalyticsEvent(API_TOGGLE_AUDIO);
|
sendAnalytics(createApiEvent('toggle-audio'));
|
||||||
logger.log('Audio toggle: API command received');
|
logger.log('Audio toggle: API command received');
|
||||||
APP.conference.toggleAudioMuted(false /* no UI */);
|
APP.conference.toggleAudioMuted(false /* no UI */);
|
||||||
},
|
},
|
||||||
'toggle-video': () => {
|
'toggle-video': () => {
|
||||||
sendAnalyticsEvent(API_TOGGLE_VIDEO);
|
sendAnalytics(createApiEvent('toggle-video'));
|
||||||
logger.log('Video toggle: API command received');
|
logger.log('Video toggle: API command received');
|
||||||
APP.conference.toggleVideoMuted(false /* no UI */);
|
APP.conference.toggleVideoMuted(false /* no UI */);
|
||||||
},
|
},
|
||||||
'toggle-film-strip': APP.UI.toggleFilmstrip,
|
'toggle-film-strip': () => {
|
||||||
'toggle-chat': APP.UI.toggleChat,
|
sendAnalytics(createApiEvent('film.strip.toggled'));
|
||||||
'toggle-contact-list': APP.UI.toggleContactList,
|
APP.UI.toggleFilmstrip();
|
||||||
'toggle-share-screen': toggleScreenSharing,
|
},
|
||||||
'video-hangup': () => APP.conference.hangup(true),
|
'toggle-chat': () => {
|
||||||
'email': APP.conference.changeLocalEmail,
|
sendAnalytics(createApiEvent('chat.toggled'));
|
||||||
'avatar-url': APP.conference.changeLocalAvatarUrl
|
APP.UI.toggleChat();
|
||||||
|
},
|
||||||
|
'toggle-contact-list': () => {
|
||||||
|
sendAnalytics(createApiEvent('contact.list.toggled'));
|
||||||
|
APP.UI.toggleContactList();
|
||||||
|
},
|
||||||
|
'toggle-share-screen': () => {
|
||||||
|
sendAnalytics(createApiEvent('screen.sharing.toggled'));
|
||||||
|
toggleScreenSharing();
|
||||||
|
},
|
||||||
|
'video-hangup': () => {
|
||||||
|
sendAnalytics(createApiEvent('video.hangup'));
|
||||||
|
APP.conference.hangup(true);
|
||||||
|
},
|
||||||
|
'email': email => {
|
||||||
|
sendAnalytics(createApiEvent('email.changed'));
|
||||||
|
APP.conference.changeLocalEmail(email);
|
||||||
|
},
|
||||||
|
'avatar-url': avatarUrl => {
|
||||||
|
sendAnalytics(createApiEvent('avatar.url.changed'));
|
||||||
|
APP.conference.changeLocalAvatarUrl(avatarUrl);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
transport.on('event', ({ data, name }) => {
|
transport.on('event', ({ data, name }) => {
|
||||||
if (name && commands[name]) {
|
if (name && commands[name]) {
|
||||||
|
|
|
@ -142,6 +142,32 @@ UI.toggleFullScreen = function() {
|
||||||
UIUtil.isFullScreen() ? UIUtil.exitFullScreen() : UIUtil.enterFullScreen();
|
UIUtil.isFullScreen() ? UIUtil.exitFullScreen() : UIUtil.enterFullScreen();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates if we're currently in full screen mode.
|
||||||
|
*
|
||||||
|
* @return {boolean} {true} to indicate that we're currently in full screen
|
||||||
|
* mode, {false} otherwise
|
||||||
|
*/
|
||||||
|
UI.isFullScreen = function() {
|
||||||
|
return UIUtil.isFullScreen();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the etherpad window is currently visible.
|
||||||
|
* @returns {Boolean} - true if the etherpad window is currently visible.
|
||||||
|
*/
|
||||||
|
UI.isEtherpadVisible = function() {
|
||||||
|
return Boolean(etherpadManager && etherpadManager.isVisible());
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if there is a shared video which is being shown (?).
|
||||||
|
* @returns {boolean} - true if there is a shared video which is being shown.
|
||||||
|
*/
|
||||||
|
UI.isSharedVideoShown = function() {
|
||||||
|
return Boolean(sharedVideoManager && sharedVideoManager.isSharedVideoShown);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify user that server has shut down.
|
* Notify user that server has shut down.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -24,11 +24,9 @@ import {
|
||||||
JitsiRecordingStatus
|
JitsiRecordingStatus
|
||||||
} from '../../../react/features/base/lib-jitsi-meet';
|
} from '../../../react/features/base/lib-jitsi-meet';
|
||||||
import {
|
import {
|
||||||
RECORDING_CANCELED,
|
createToolbarEvent,
|
||||||
RECORDING_CLICKED,
|
createRecordingDialogEvent,
|
||||||
RECORDING_STARTED,
|
sendAnalytics
|
||||||
RECORDING_STOPPED,
|
|
||||||
sendAnalyticsEvent
|
|
||||||
} from '../../../react/features/analytics';
|
} from '../../../react/features/analytics';
|
||||||
import { setToolboxEnabled } from '../../../react/features/toolbox';
|
import { setToolboxEnabled } from '../../../react/features/toolbox';
|
||||||
import { setNotificationsEnabled } from '../../../react/features/notifications';
|
import { setNotificationsEnabled } from '../../../react/features/notifications';
|
||||||
|
@ -452,12 +450,13 @@ const Recording = {
|
||||||
},
|
},
|
||||||
|
|
||||||
// checks whether recording is enabled and whether we have params
|
// checks whether recording is enabled and whether we have params
|
||||||
// to start automatically recording
|
// to start automatically recording (XXX: No, it doesn't do that).
|
||||||
checkAutoRecord() {
|
checkAutoRecord() {
|
||||||
if (_isRecordingButtonEnabled && config.autoRecord) {
|
if (_isRecordingButtonEnabled && config.autoRecord) {
|
||||||
this.predefinedToken = UIUtil.escapeHtml(config.autoRecordToken);
|
this.predefinedToken = UIUtil.escapeHtml(config.autoRecordToken);
|
||||||
this.eventEmitter.emit(UIEvents.RECORDING_TOGGLED,
|
this.eventEmitter.emit(
|
||||||
this.predefinedToken);
|
UIEvents.RECORDING_TOGGLED,
|
||||||
|
{ token: this.predefinedToken });
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -467,11 +466,16 @@ const Recording = {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_onToolbarButtonClick() {
|
_onToolbarButtonClick() {
|
||||||
|
sendAnalytics(createToolbarEvent(
|
||||||
|
'recording.button',
|
||||||
|
{
|
||||||
|
'dialog_present': Boolean(dialog)
|
||||||
|
}));
|
||||||
|
|
||||||
if (dialog) {
|
if (dialog) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendAnalyticsEvent(RECORDING_CLICKED);
|
|
||||||
switch (this.currentState) {
|
switch (this.currentState) {
|
||||||
case JitsiRecordingStatus.ON:
|
case JitsiRecordingStatus.ON:
|
||||||
case JitsiRecordingStatus.RETRYING:
|
case JitsiRecordingStatus.RETRYING:
|
||||||
|
@ -479,7 +483,13 @@ const Recording = {
|
||||||
_showStopRecordingPrompt(this.recordingType).then(
|
_showStopRecordingPrompt(this.recordingType).then(
|
||||||
() => {
|
() => {
|
||||||
this.eventEmitter.emit(UIEvents.RECORDING_TOGGLED);
|
this.eventEmitter.emit(UIEvents.RECORDING_TOGGLED);
|
||||||
sendAnalyticsEvent(RECORDING_STOPPED);
|
|
||||||
|
// The confirm button on the stop recording dialog was
|
||||||
|
// clicked
|
||||||
|
sendAnalytics(
|
||||||
|
createRecordingDialogEvent(
|
||||||
|
'stop',
|
||||||
|
'confirm.button'));
|
||||||
},
|
},
|
||||||
() => {}); // eslint-disable-line no-empty-function
|
() => {}); // eslint-disable-line no-empty-function
|
||||||
break;
|
break;
|
||||||
|
@ -492,21 +502,32 @@ const Recording = {
|
||||||
this.eventEmitter.emit(
|
this.eventEmitter.emit(
|
||||||
UIEvents.RECORDING_TOGGLED,
|
UIEvents.RECORDING_TOGGLED,
|
||||||
{ streamId });
|
{ streamId });
|
||||||
sendAnalyticsEvent(RECORDING_STARTED);
|
|
||||||
|
// The confirm button on the start recording dialog was
|
||||||
|
// clicked
|
||||||
|
sendAnalytics(
|
||||||
|
createRecordingDialogEvent(
|
||||||
|
'start',
|
||||||
|
'confirm.button'));
|
||||||
})
|
})
|
||||||
.catch(reason => {
|
.catch(reason => {
|
||||||
if (reason === APP.UI.messageHandler.CANCEL) {
|
if (reason === APP.UI.messageHandler.CANCEL) {
|
||||||
sendAnalyticsEvent(RECORDING_CANCELED);
|
// The cancel button on the start recording dialog was
|
||||||
|
// clicked
|
||||||
|
sendAnalytics(
|
||||||
|
createRecordingDialogEvent(
|
||||||
|
'start',
|
||||||
|
'cancel.button'));
|
||||||
} else {
|
} else {
|
||||||
logger.error(reason);
|
logger.error(reason);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
// Note that we only fire analytics events for Jibri.
|
||||||
if (this.predefinedToken) {
|
if (this.predefinedToken) {
|
||||||
this.eventEmitter.emit(
|
this.eventEmitter.emit(
|
||||||
UIEvents.RECORDING_TOGGLED,
|
UIEvents.RECORDING_TOGGLED,
|
||||||
{ token: this.predefinedToken });
|
{ token: this.predefinedToken });
|
||||||
sendAnalyticsEvent(RECORDING_STARTED);
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -515,12 +536,9 @@ const Recording = {
|
||||||
this.eventEmitter.emit(
|
this.eventEmitter.emit(
|
||||||
UIEvents.RECORDING_TOGGLED,
|
UIEvents.RECORDING_TOGGLED,
|
||||||
{ token });
|
{ token });
|
||||||
sendAnalyticsEvent(RECORDING_STARTED);
|
|
||||||
})
|
})
|
||||||
.catch(reason => {
|
.catch(reason => {
|
||||||
if (reason === APP.UI.messageHandler.CANCEL) {
|
if (reason !== APP.UI.messageHandler.CANCEL) {
|
||||||
sendAnalyticsEvent(RECORDING_CANCELED);
|
|
||||||
} else {
|
|
||||||
logger.error(reason);
|
logger.error(reason);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -11,15 +11,8 @@ import LargeContainer from '../videolayout/LargeContainer';
|
||||||
import Filmstrip from '../videolayout/Filmstrip';
|
import Filmstrip from '../videolayout/Filmstrip';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SHARED_VIDEO_ALREADY_SHARED,
|
createSharedVideoEvent as createEvent,
|
||||||
SHARED_VIDEO_AUDIO_MUTED,
|
sendAnalytics
|
||||||
SHARED_VIDEO_AUDIO_UNMUTED,
|
|
||||||
SHARED_VIDEO_CANCELED,
|
|
||||||
SHARED_VIDEO_PAUSED,
|
|
||||||
SHARED_VIDEO_STARTED,
|
|
||||||
SHARED_VIDEO_STOPPED,
|
|
||||||
SHARED_VIDEO_VOLUME_CHANGED,
|
|
||||||
sendAnalyticsEvent
|
|
||||||
} from '../../../react/features/analytics';
|
} from '../../../react/features/analytics';
|
||||||
import {
|
import {
|
||||||
participantJoined,
|
participantJoined,
|
||||||
|
@ -95,11 +88,11 @@ export default class SharedVideoManager {
|
||||||
url => {
|
url => {
|
||||||
this.emitter.emit(
|
this.emitter.emit(
|
||||||
UIEvents.UPDATE_SHARED_VIDEO, url, 'start');
|
UIEvents.UPDATE_SHARED_VIDEO, url, 'start');
|
||||||
sendAnalyticsEvent(SHARED_VIDEO_STARTED);
|
sendAnalytics(createEvent('started'));
|
||||||
},
|
},
|
||||||
err => {
|
err => {
|
||||||
logger.log('SHARED VIDEO CANCELED', err);
|
logger.log('SHARED VIDEO CANCELED', err);
|
||||||
sendAnalyticsEvent(SHARED_VIDEO_CANCELED);
|
sendAnalytics(createEvent('canceled'));
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -119,7 +112,7 @@ export default class SharedVideoManager {
|
||||||
}
|
}
|
||||||
this.emitter.emit(
|
this.emitter.emit(
|
||||||
UIEvents.UPDATE_SHARED_VIDEO, this.url, 'stop');
|
UIEvents.UPDATE_SHARED_VIDEO, this.url, 'stop');
|
||||||
sendAnalyticsEvent(SHARED_VIDEO_STOPPED);
|
sendAnalytics(createEvent('stopped'));
|
||||||
},
|
},
|
||||||
() => {}); // eslint-disable-line no-empty-function
|
() => {}); // eslint-disable-line no-empty-function
|
||||||
} else {
|
} else {
|
||||||
|
@ -127,7 +120,7 @@ export default class SharedVideoManager {
|
||||||
descriptionKey: 'dialog.alreadySharedVideoMsg',
|
descriptionKey: 'dialog.alreadySharedVideoMsg',
|
||||||
titleKey: 'dialog.alreadySharedVideoTitle'
|
titleKey: 'dialog.alreadySharedVideoTitle'
|
||||||
});
|
});
|
||||||
sendAnalyticsEvent(SHARED_VIDEO_ALREADY_SHARED);
|
sendAnalytics(createEvent('already.shared'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -236,7 +229,7 @@ export default class SharedVideoManager {
|
||||||
// eslint-disable-next-line eqeqeq
|
// eslint-disable-next-line eqeqeq
|
||||||
} else if (event.data == YT.PlayerState.PAUSED) {
|
} else if (event.data == YT.PlayerState.PAUSED) {
|
||||||
self.smartAudioUnmute();
|
self.smartAudioUnmute();
|
||||||
sendAnalyticsEvent(SHARED_VIDEO_PAUSED);
|
sendAnalytics(createEvent('paused'));
|
||||||
}
|
}
|
||||||
// eslint-disable-next-line eqeqeq
|
// eslint-disable-next-line eqeqeq
|
||||||
self.fireSharedVideoEvent(event.data == YT.PlayerState.PAUSED);
|
self.fireSharedVideoEvent(event.data == YT.PlayerState.PAUSED);
|
||||||
|
@ -268,7 +261,12 @@ export default class SharedVideoManager {
|
||||||
} else if (event.data.volume <= 0 || event.data.muted) {
|
} else if (event.data.volume <= 0 || event.data.muted) {
|
||||||
self.smartAudioUnmute();
|
self.smartAudioUnmute();
|
||||||
}
|
}
|
||||||
sendAnalyticsEvent(SHARED_VIDEO_VOLUME_CHANGED);
|
sendAnalytics(createEvent(
|
||||||
|
'volume.changed',
|
||||||
|
{
|
||||||
|
volume: event.data.volume,
|
||||||
|
muted: event.data.muted
|
||||||
|
}));
|
||||||
};
|
};
|
||||||
|
|
||||||
window.onPlayerReady = function(event) {
|
window.onPlayerReady = function(event) {
|
||||||
|
@ -434,8 +432,8 @@ export default class SharedVideoManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates video, if its not playing and needs starting or
|
* Updates video, if it's not playing and needs starting or if it's playing
|
||||||
* if its playing and needs to be paysed
|
* and needs to be paused.
|
||||||
* @param id the id of the sender of the command
|
* @param id the id of the sender of the command
|
||||||
* @param url the video url
|
* @param url the video url
|
||||||
* @param attributes
|
* @param attributes
|
||||||
|
@ -574,7 +572,7 @@ export default class SharedVideoManager {
|
||||||
if (APP.conference.isLocalAudioMuted()
|
if (APP.conference.isLocalAudioMuted()
|
||||||
&& !this.mutedWithUserInteraction
|
&& !this.mutedWithUserInteraction
|
||||||
&& !this.isSharedVideoVolumeOn()) {
|
&& !this.isSharedVideoVolumeOn()) {
|
||||||
sendAnalyticsEvent(SHARED_VIDEO_AUDIO_UNMUTED);
|
sendAnalytics(createEvent('audio.unmuted'));
|
||||||
logger.log('Shared video: audio unmuted');
|
logger.log('Shared video: audio unmuted');
|
||||||
this.emitter.emit(UIEvents.AUDIO_MUTED, false, false);
|
this.emitter.emit(UIEvents.AUDIO_MUTED, false, false);
|
||||||
this.showMicMutedPopup(false);
|
this.showMicMutedPopup(false);
|
||||||
|
@ -588,7 +586,7 @@ export default class SharedVideoManager {
|
||||||
smartAudioMute() {
|
smartAudioMute() {
|
||||||
if (!APP.conference.isLocalAudioMuted()
|
if (!APP.conference.isLocalAudioMuted()
|
||||||
&& this.isSharedVideoVolumeOn()) {
|
&& this.isSharedVideoVolumeOn()) {
|
||||||
sendAnalyticsEvent(SHARED_VIDEO_AUDIO_MUTED);
|
sendAnalytics(createEvent('audio.muted'));
|
||||||
logger.log('Shared video: audio muted');
|
logger.log('Shared video: audio muted');
|
||||||
this.emitter.emit(UIEvents.AUDIO_MUTED, true, false);
|
this.emitter.emit(UIEvents.AUDIO_MUTED, true, false);
|
||||||
this.showMicMutedPopup(true);
|
this.showMicMutedPopup(true);
|
||||||
|
|
|
@ -4,9 +4,8 @@ import UIEvents from '../../../../service/UI/UIEvents';
|
||||||
import Settings from '../../../settings/Settings';
|
import Settings from '../../../settings/Settings';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
AUTHENTICATE_LOGIN_CLICKED,
|
createProfilePanelButtonEvent,
|
||||||
AUTHENTICATE_LOGOUT_CLICKED,
|
sendAnalytics
|
||||||
sendAnalyticsEvent
|
|
||||||
} from '../../../../react/features/analytics';
|
} from '../../../../react/features/analytics';
|
||||||
|
|
||||||
const sidePanelsContainerId = 'sideToolbarContainer';
|
const sidePanelsContainerId = 'sideToolbarContainer';
|
||||||
|
@ -95,7 +94,7 @@ export default {
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
function loginClicked() {
|
function loginClicked() {
|
||||||
sendAnalyticsEvent(AUTHENTICATE_LOGIN_CLICKED);
|
sendAnalytics(createProfilePanelButtonEvent('login.button'));
|
||||||
emitter.emit(UIEvents.AUTH_CLICKED);
|
emitter.emit(UIEvents.AUTH_CLICKED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,7 +107,7 @@ export default {
|
||||||
const titleKey = 'dialog.logoutTitle';
|
const titleKey = 'dialog.logoutTitle';
|
||||||
const msgKey = 'dialog.logoutQuestion';
|
const msgKey = 'dialog.logoutQuestion';
|
||||||
|
|
||||||
sendAnalyticsEvent(AUTHENTICATE_LOGOUT_CLICKED);
|
sendAnalytics(createProfilePanelButtonEvent('logout.button'));
|
||||||
|
|
||||||
// Ask for confirmation
|
// Ask for confirmation
|
||||||
APP.UI.messageHandler.openTwoButtonDialog({
|
APP.UI.messageHandler.openTwoButtonDialog({
|
||||||
|
|
|
@ -6,8 +6,9 @@ import UIEvents from '../../../service/UI/UIEvents';
|
||||||
import UIUtil from '../util/UIUtil';
|
import UIUtil from '../util/UIUtil';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TOOLBAR_FILMSTRIP_TOGGLED,
|
createShortcutEvent,
|
||||||
sendAnalyticsEvent
|
createToolbarEvent,
|
||||||
|
sendAnalytics
|
||||||
} from '../../../react/features/analytics';
|
} from '../../../react/features/analytics';
|
||||||
|
|
||||||
const Filmstrip = {
|
const Filmstrip = {
|
||||||
|
@ -75,8 +76,18 @@ const Filmstrip = {
|
||||||
// Firing the event instead of executing toggleFilmstrip method because
|
// Firing the event instead of executing toggleFilmstrip method because
|
||||||
// it's important to hide the filmstrip by UI.toggleFilmstrip in order
|
// it's important to hide the filmstrip by UI.toggleFilmstrip in order
|
||||||
// to correctly resize the video area.
|
// to correctly resize the video area.
|
||||||
$('#toggleFilmstripButton').on('click',
|
$('#toggleFilmstripButton').on(
|
||||||
() => this.eventEmitter.emit(UIEvents.TOGGLE_FILMSTRIP));
|
'click',
|
||||||
|
() => {
|
||||||
|
// The 'enable' parameter is set to true if the action results
|
||||||
|
// in the filmstrip being hidden.
|
||||||
|
sendAnalytics(createToolbarEvent(
|
||||||
|
'toggle.filmstrip.button',
|
||||||
|
{
|
||||||
|
enable: this.isFilmstripVisible()
|
||||||
|
}));
|
||||||
|
this.eventEmitter.emit(UIEvents.TOGGLE_FILMSTRIP);
|
||||||
|
});
|
||||||
|
|
||||||
this._registerToggleFilmstripShortcut();
|
this._registerToggleFilmstripShortcut();
|
||||||
},
|
},
|
||||||
|
@ -94,7 +105,14 @@ const Filmstrip = {
|
||||||
// Firing the event instead of executing toggleFilmstrip method because
|
// Firing the event instead of executing toggleFilmstrip method because
|
||||||
// it's important to hide the filmstrip by UI.toggleFilmstrip in order
|
// it's important to hide the filmstrip by UI.toggleFilmstrip in order
|
||||||
// to correctly resize the video area.
|
// to correctly resize the video area.
|
||||||
const handler = () => this.eventEmitter.emit(UIEvents.TOGGLE_FILMSTRIP);
|
const handler = () => {
|
||||||
|
sendAnalytics(createShortcutEvent(
|
||||||
|
'toggle.filmstrip',
|
||||||
|
{
|
||||||
|
enable: this.isFilmstripVisible()
|
||||||
|
}));
|
||||||
|
this.eventEmitter.emit(UIEvents.TOGGLE_FILMSTRIP);
|
||||||
|
};
|
||||||
|
|
||||||
APP.keyboardshortcut.registerShortcut(
|
APP.keyboardshortcut.registerShortcut(
|
||||||
shortcut,
|
shortcut,
|
||||||
|
@ -129,50 +147,43 @@ const Filmstrip = {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Toggles the visibility of the filmstrip.
|
* Toggles the visibility of the filmstrip, or sets it to a specific value
|
||||||
|
* if the 'visible' parameter is specified.
|
||||||
*
|
*
|
||||||
* @param visible optional {Boolean} which specifies the desired visibility
|
* @param visible optional {Boolean} which specifies the desired visibility
|
||||||
* of the filmstrip. If not specified, the visibility will be flipped
|
* of the filmstrip. If not specified, the visibility will be flipped
|
||||||
* (i.e. toggled); otherwise, the visibility will be set to the specified
|
* (i.e. toggled); otherwise, the visibility will be set to the specified
|
||||||
* value.
|
* value.
|
||||||
* @param {Boolean} sendAnalytics - True to send an analytics event. The
|
|
||||||
* default value is true.
|
|
||||||
*
|
*
|
||||||
* Note:
|
* Note:
|
||||||
* This method shouldn't be executed directly to hide the filmstrip.
|
* This method shouldn't be executed directly to hide the filmstrip.
|
||||||
* It's important to hide the filmstrip with UI.toggleFilmstrip in order
|
* It's important to hide the filmstrip with UI.toggleFilmstrip in order
|
||||||
* to correctly resize the video area.
|
* to correctly resize the video area.
|
||||||
*/
|
*/
|
||||||
toggleFilmstrip(visible, sendAnalytics = true) {
|
toggleFilmstrip(visible) {
|
||||||
const isVisibleDefined = typeof visible === 'boolean';
|
const wasFilmstripVisible = this.isFilmstripVisible();
|
||||||
|
|
||||||
if (!isVisibleDefined) {
|
// If 'visible' is defined and matches the current state, we have
|
||||||
// eslint-disable-next-line no-param-reassign
|
// nothing to do. Otherwise (regardless of whether 'visible' is defined)
|
||||||
visible = this.isFilmstripVisible();
|
// we need to toggle the state.
|
||||||
} else if (this.isFilmstripVisible() === visible) {
|
if (visible === wasFilmstripVisible) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (sendAnalytics) {
|
|
||||||
sendAnalyticsEvent(TOOLBAR_FILMSTRIP_TOGGLED);
|
|
||||||
}
|
|
||||||
this.filmstrip.toggleClass('hidden');
|
this.filmstrip.toggleClass('hidden');
|
||||||
|
|
||||||
if (visible) {
|
if (wasFilmstripVisible) {
|
||||||
this.showMenuUpIcon();
|
this.showMenuUpIcon();
|
||||||
} else {
|
} else {
|
||||||
this.showMenuDownIcon();
|
this.showMenuDownIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Emit/fire UIEvents.TOGGLED_FILMSTRIP.
|
if (this.eventEmitter) {
|
||||||
const eventEmitter = this.eventEmitter;
|
this.eventEmitter.emit(
|
||||||
const isFilmstripVisible = this.isFilmstripVisible();
|
|
||||||
|
|
||||||
if (eventEmitter) {
|
|
||||||
eventEmitter.emit(
|
|
||||||
UIEvents.TOGGLED_FILMSTRIP,
|
UIEvents.TOGGLED_FILMSTRIP,
|
||||||
this.isFilmstripVisible());
|
!wasFilmstripVisible);
|
||||||
}
|
}
|
||||||
APP.store.dispatch(setFilmstripVisibility(isFilmstripVisible));
|
APP.store.dispatch(setFilmstripVisibility(!wasFilmstripVisible));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -2,11 +2,10 @@
|
||||||
|
|
||||||
import { toggleDialog } from '../../react/features/base/dialog';
|
import { toggleDialog } from '../../react/features/base/dialog';
|
||||||
import {
|
import {
|
||||||
SHORTCUT_HELP,
|
ACTION_SHORTCUT_PRESSED as PRESSED,
|
||||||
SHORTCUT_SPEAKER_STATS_CLICKED,
|
ACTION_SHORTCUT_RELEASED as RELEASED,
|
||||||
SHORTCUT_TALK_CLICKED,
|
createShortcutEvent,
|
||||||
SHORTCUT_TALK_RELEASED,
|
sendAnalytics
|
||||||
sendAnalyticsEvent
|
|
||||||
} from '../../react/features/analytics';
|
} from '../../react/features/analytics';
|
||||||
import { KeyboardShortcutsDialog }
|
import { KeyboardShortcutsDialog }
|
||||||
from '../../react/features/keyboard-shortcuts';
|
from '../../react/features/keyboard-shortcuts';
|
||||||
|
@ -72,8 +71,10 @@ const KeyboardShortcut = {
|
||||||
|| $(':focus').is('textarea'))) {
|
|| $(':focus').is('textarea'))) {
|
||||||
if (this._getKeyboardKey(e).toUpperCase() === ' ') {
|
if (this._getKeyboardKey(e).toUpperCase() === ' ') {
|
||||||
if (APP.conference.isLocalAudioMuted()) {
|
if (APP.conference.isLocalAudioMuted()) {
|
||||||
sendAnalyticsEvent(SHORTCUT_TALK_RELEASED);
|
sendAnalytics(createShortcutEvent(
|
||||||
logger.log('Talk shortcut released');
|
'push.to.talk',
|
||||||
|
PRESSED));
|
||||||
|
logger.log('Talk shortcut pressed');
|
||||||
APP.conference.muteAudio(false);
|
APP.conference.muteAudio(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +94,7 @@ const KeyboardShortcut = {
|
||||||
* Registers a new shortcut.
|
* Registers a new shortcut.
|
||||||
*
|
*
|
||||||
* @param shortcutChar the shortcut character triggering the action
|
* @param shortcutChar the shortcut character triggering the action
|
||||||
* @param shortcutAttr the "shortcut" html element attribute mappring an
|
* @param shortcutAttr the "shortcut" html element attribute mapping an
|
||||||
* element to this shortcut and used to show the shortcut character on the
|
* element to this shortcut and used to show the shortcut character on the
|
||||||
* element tooltip
|
* element tooltip
|
||||||
* @param exec the function to be executed when the shortcut is pressed
|
* @param exec the function to be executed when the shortcut is pressed
|
||||||
|
@ -175,7 +176,7 @@ const KeyboardShortcut = {
|
||||||
*/
|
*/
|
||||||
_initGlobalShortcuts() {
|
_initGlobalShortcuts() {
|
||||||
this.registerShortcut('?', null, () => {
|
this.registerShortcut('?', null, () => {
|
||||||
sendAnalyticsEvent(SHORTCUT_HELP);
|
sendAnalytics(createShortcutEvent('help'));
|
||||||
APP.store.dispatch(toggleDialog(KeyboardShortcutsDialog, {
|
APP.store.dispatch(toggleDialog(KeyboardShortcutsDialog, {
|
||||||
shortcutDescriptions: _shortcutsHelp
|
shortcutDescriptions: _shortcutsHelp
|
||||||
}));
|
}));
|
||||||
|
@ -184,15 +185,15 @@ const KeyboardShortcut = {
|
||||||
// register SPACE shortcut in two steps to insure visibility of help
|
// register SPACE shortcut in two steps to insure visibility of help
|
||||||
// message
|
// message
|
||||||
this.registerShortcut(' ', null, () => {
|
this.registerShortcut(' ', null, () => {
|
||||||
sendAnalyticsEvent(SHORTCUT_TALK_CLICKED);
|
sendAnalytics(createShortcutEvent('push.to.talk', RELEASED));
|
||||||
logger.log('Talk shortcut pressed');
|
logger.log('Talk shortcut released');
|
||||||
APP.conference.muteAudio(true);
|
APP.conference.muteAudio(true);
|
||||||
});
|
});
|
||||||
this._addShortcutToHelp('SPACE', 'keyboardShortcuts.pushToTalk');
|
this._addShortcutToHelp('SPACE', 'keyboardShortcuts.pushToTalk');
|
||||||
|
|
||||||
if (!interfaceConfig.filmStripOnly) {
|
if (!interfaceConfig.filmStripOnly) {
|
||||||
this.registerShortcut('T', null, () => {
|
this.registerShortcut('T', null, () => {
|
||||||
sendAnalyticsEvent(SHORTCUT_SPEAKER_STATS_CLICKED);
|
sendAnalytics(createShortcutEvent('speaker.stats'));
|
||||||
APP.store.dispatch(toggleDialog(SpeakerStats, {
|
APP.store.dispatch(toggleDialog(SpeakerStats, {
|
||||||
conference: APP.conference
|
conference: APP.conference
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -37,20 +37,20 @@ export default class JitsiMeetLogStorage {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let logJSON = `{"log${this.counter}":"\n`;
|
let logMessage = `{"log${this.counter}":"\n`;
|
||||||
|
|
||||||
for (let i = 0, len = logEntries.length; i < len; i++) {
|
for (let i = 0, len = logEntries.length; i < len; i++) {
|
||||||
const logEntry = logEntries[i];
|
const logEntry = logEntries[i];
|
||||||
|
|
||||||
if (typeof logEntry === 'object') {
|
if (typeof logEntry === 'object') {
|
||||||
// Aggregated message
|
// Aggregated message
|
||||||
logJSON += `(${logEntry.count}) ${logEntry.text}\n`;
|
logMessage += `(${logEntry.count}) ${logEntry.text}\n`;
|
||||||
} else {
|
} else {
|
||||||
// Regular message
|
// Regular message
|
||||||
logJSON += `${logEntry}\n`;
|
logMessage += `${logEntry}\n`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logJSON += '"}';
|
logMessage += '"}';
|
||||||
|
|
||||||
this.counter += 1;
|
this.counter += 1;
|
||||||
|
|
||||||
|
@ -58,11 +58,11 @@ export default class JitsiMeetLogStorage {
|
||||||
// on the way that could be uninitialized if the storeLogs
|
// on the way that could be uninitialized if the storeLogs
|
||||||
// attempt would be made very early (which is unlikely)
|
// attempt would be made very early (which is unlikely)
|
||||||
try {
|
try {
|
||||||
APP.conference.logJSON(logJSON);
|
APP.conference.room.sendApplicationLog(logMessage);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// NOTE console is intentional here
|
// NOTE console is intentional here
|
||||||
console.error(
|
console.error(
|
||||||
'Failed to store the logs: ', logJSON, error);
|
'Failed to store the logs: ', logMessage, error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6386,7 +6386,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lib-jitsi-meet": {
|
"lib-jitsi-meet": {
|
||||||
"version": "github:jitsi/lib-jitsi-meet#c7d6d158b9ab87f47b2bb8484565bcb17e687f7e",
|
"version": "github:jitsi/lib-jitsi-meet#515374c8d383cb17df8ed76427e6f0fb5ea6ff1e",
|
||||||
"requires": {
|
"requires": {
|
||||||
"async": "0.9.0",
|
"async": "0.9.0",
|
||||||
"current-executing-script": "0.1.3",
|
"current-executing-script": "0.1.3",
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
"jquery-i18next": "1.2.0",
|
"jquery-i18next": "1.2.0",
|
||||||
"js-md5": "0.6.1",
|
"js-md5": "0.6.1",
|
||||||
"jwt-decode": "2.2.0",
|
"jwt-decode": "2.2.0",
|
||||||
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#c7d6d158b9ab87f47b2bb8484565bcb17e687f7e",
|
"lib-jitsi-meet": "github:jitsi/lib-jitsi-meet#515374c8d383cb17df8ed76427e6f0fb5ea6ff1e",
|
||||||
"lodash": "4.17.4",
|
"lodash": "4.17.4",
|
||||||
"moment": "2.19.4",
|
"moment": "2.19.4",
|
||||||
"nuclear-js": "1.4.0",
|
"nuclear-js": "1.4.0",
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -9,12 +9,14 @@ import { getJitsiMeetGlobalNS, loadScript } from '../base/util';
|
||||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sends an analytics event.
|
* Sends an event through the lib-jitsi-meet AnalyticsAdapter interface.
|
||||||
*
|
*
|
||||||
* @inheritdoc
|
* @param {Object} event - The event to send. It should be formatted as
|
||||||
|
* described in AnalyticsAdapter.js in lib-jitsi-meet.
|
||||||
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
export function sendAnalyticsEvent(...args: Array<any>) {
|
export function sendAnalytics(event: Object) {
|
||||||
analytics.sendEvent(...args);
|
analytics.sendEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,23 +40,17 @@ export function initAnalytics({ getState }: { getState: Function }) {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const config = state['features/base/config'];
|
const config = state['features/base/config'];
|
||||||
const { analyticsScriptUrls } = config;
|
const { analyticsScriptUrls } = config;
|
||||||
const machineId = JitsiMeetJS.getMachineId();
|
|
||||||
const { user } = state['features/base/jwt'];
|
const { user } = state['features/base/jwt'];
|
||||||
const handlerConstructorOptions = {
|
const handlerConstructorOptions = {
|
||||||
product: 'lib-jitsi-meet',
|
|
||||||
version: JitsiMeetJS.version,
|
version: JitsiMeetJS.version,
|
||||||
session: machineId,
|
user
|
||||||
user: user ? user.id : `uid-${machineId}`,
|
|
||||||
server: state['features/base/connection'].locationURL.host
|
|
||||||
};
|
};
|
||||||
|
|
||||||
_loadHandlers(analyticsScriptUrls, handlerConstructorOptions)
|
_loadHandlers(analyticsScriptUrls, handlerConstructorOptions)
|
||||||
.then(handlers => {
|
.then(handlers => {
|
||||||
const permanentProperties: Object = {
|
const roomName = state['features/base/conference'].room;
|
||||||
roomName: state['features/base/conference'].room,
|
|
||||||
userAgent: navigator.userAgent
|
|
||||||
};
|
|
||||||
const { group, server } = state['features/base/jwt'];
|
const { group, server } = state['features/base/jwt'];
|
||||||
|
const permanentProperties = {};
|
||||||
|
|
||||||
if (server) {
|
if (server) {
|
||||||
permanentProperties.server = server;
|
permanentProperties.server = server;
|
||||||
|
@ -76,6 +72,9 @@ export function initAnalytics({ getState }: { getState: Function }) {
|
||||||
}
|
}
|
||||||
|
|
||||||
analytics.addPermanentProperties(permanentProperties);
|
analytics.addPermanentProperties(permanentProperties);
|
||||||
|
analytics.setConferenceName(roomName);
|
||||||
|
|
||||||
|
// Set the handlers last, since this triggers emptying of the cache
|
||||||
analytics.setAnalyticsHandlers(handlers);
|
analytics.setAnalyticsHandlers(handlers);
|
||||||
},
|
},
|
||||||
error => analytics.dispose() && logger.error(error));
|
error => analytics.dispose() && logger.error(error));
|
||||||
|
|
|
@ -3,9 +3,8 @@
|
||||||
import UIEvents from '../../../../service/UI/UIEvents';
|
import UIEvents from '../../../../service/UI/UIEvents';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
START_MUTED_SERVER_AUDIO_,
|
createStartMutedConfigurationEvent,
|
||||||
START_MUTED_SERVER_VIDEO_,
|
sendAnalytics
|
||||||
sendAnalyticsEvent
|
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import { getName } from '../../app';
|
import { getName } from '../../app';
|
||||||
import { JitsiConferenceEvents } from '../lib-jitsi-meet';
|
import { JitsiConferenceEvents } from '../lib-jitsi-meet';
|
||||||
|
@ -90,12 +89,8 @@ function _addConferenceListeners(conference, dispatch) {
|
||||||
const audioMuted = Boolean(conference.startAudioMuted);
|
const audioMuted = Boolean(conference.startAudioMuted);
|
||||||
const videoMuted = Boolean(conference.startVideoMuted);
|
const videoMuted = Boolean(conference.startVideoMuted);
|
||||||
|
|
||||||
sendAnalyticsEvent(
|
sendAnalytics(createStartMutedConfigurationEvent(
|
||||||
`${START_MUTED_SERVER_AUDIO_}.${
|
'remote', audioMuted, videoMuted));
|
||||||
audioMuted ? 'muted' : 'unmuted'}`);
|
|
||||||
sendAnalyticsEvent(
|
|
||||||
`${START_MUTED_SERVER_VIDEO_}.${
|
|
||||||
videoMuted ? 'muted' : 'unmuted'}`);
|
|
||||||
logger.log(`Start muted: ${audioMuted ? 'audio, ' : ''}${
|
logger.log(`Start muted: ${audioMuted ? 'audio, ' : ''}${
|
||||||
videoMuted ? 'video' : ''}`);
|
videoMuted ? 'video' : ''}`);
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,11 @@
|
||||||
import UIEvents from '../../../../service/UI/UIEvents';
|
import UIEvents from '../../../../service/UI/UIEvents';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
_LOCAL,
|
ACTION_PINNED,
|
||||||
_REMOTE,
|
ACTION_UNPINNED,
|
||||||
AUDIO_ONLY_DISABLED,
|
createAudioOnlyDisableEvent,
|
||||||
PINNED_,
|
createPinnedEvent,
|
||||||
UNPINNED_,
|
sendAnalytics
|
||||||
sendAnalyticsEvent
|
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import { CONNECTION_ESTABLISHED } from '../connection';
|
import { CONNECTION_ESTABLISHED } from '../connection';
|
||||||
import { setVideoMuted, VIDEO_MUTISM_AUTHORITY } from '../media';
|
import { setVideoMuted, VIDEO_MUTISM_AUTHORITY } from '../media';
|
||||||
|
@ -131,7 +130,7 @@ function _conferenceFailedOrLeft({ dispatch, getState }, next, action) {
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
|
||||||
if (getState()['features/base/conference'].audioOnly) {
|
if (getState()['features/base/conference'].audioOnly) {
|
||||||
sendAnalyticsEvent(AUDIO_ONLY_DISABLED);
|
sendAnalytics(createAudioOnlyDisableEvent());
|
||||||
logger.log('Audio only disabled');
|
logger.log('Audio only disabled');
|
||||||
dispatch(setAudioOnly(false));
|
dispatch(setAudioOnly(false));
|
||||||
}
|
}
|
||||||
|
@ -193,19 +192,19 @@ function _pinParticipant(store, next, action) {
|
||||||
|
|
||||||
if (typeof APP !== 'undefined') {
|
if (typeof APP !== 'undefined') {
|
||||||
const pinnedParticipant = getPinnedParticipant(participants);
|
const pinnedParticipant = getPinnedParticipant(participants);
|
||||||
const actionName = action.participant.id ? PINNED_ : UNPINNED_;
|
const actionName
|
||||||
let videoType;
|
= action.participant.id ? ACTION_PINNED : ACTION_UNPINNED;
|
||||||
|
const local = (participantById && participantById.local)
|
||||||
|
|| (!id && pinnedParticipant && pinnedParticipant.local);
|
||||||
|
|
||||||
if ((participantById && participantById.local)
|
sendAnalytics(createPinnedEvent(
|
||||||
|| (!id && pinnedParticipant && pinnedParticipant.local)) {
|
actionName,
|
||||||
videoType = _LOCAL;
|
local ? 'local' : id,
|
||||||
} else {
|
{
|
||||||
videoType = _REMOTE;
|
'participant_count': conference.getParticipantCount(),
|
||||||
}
|
local
|
||||||
|
}));
|
||||||
|
|
||||||
sendAnalyticsEvent(
|
|
||||||
`${actionName}.${videoType}`,
|
|
||||||
{ value: conference.getParticipantCount() });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following condition prevents signaling to pin local participant and
|
// The following condition prevents signaling to pin local participant and
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
import {
|
import {
|
||||||
START_AUDIO_ONLY_,
|
createStartAudioOnlyEvent,
|
||||||
START_MUTED_CLIENT_AUDIO_,
|
createStartMutedConfigurationEvent,
|
||||||
START_MUTED_CLIENT_VIDEO_,
|
createSyncTrackStateEvent,
|
||||||
SYNC_TRACK_STATE_,
|
sendAnalytics
|
||||||
sendAnalyticsEvent
|
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import { SET_ROOM, setAudioOnly } from '../conference';
|
import { SET_ROOM, setAudioOnly } from '../conference';
|
||||||
import { parseURLParams } from '../config';
|
import { parseURLParams } from '../config';
|
||||||
|
@ -90,12 +89,8 @@ function _setRoom({ dispatch, getState }, next, action) {
|
||||||
audioMuted = Boolean(audioMuted);
|
audioMuted = Boolean(audioMuted);
|
||||||
videoMuted = Boolean(videoMuted);
|
videoMuted = Boolean(videoMuted);
|
||||||
|
|
||||||
// Apply the config.
|
sendAnalytics(createStartMutedConfigurationEvent(
|
||||||
|
'local', audioMuted, videoMuted));
|
||||||
sendAnalyticsEvent(
|
|
||||||
`${START_MUTED_CLIENT_AUDIO_}.${audioMuted ? 'muted' : 'unmuted'}`);
|
|
||||||
sendAnalyticsEvent(
|
|
||||||
`${START_MUTED_CLIENT_VIDEO_}.${videoMuted ? 'muted' : 'unmuted'}`);
|
|
||||||
|
|
||||||
logger.log(`Start muted: ${audioMuted ? 'audio, ' : ''}${
|
logger.log(`Start muted: ${audioMuted ? 'audio, ' : ''}${
|
||||||
videoMuted ? 'video' : ''}`);
|
videoMuted ? 'video' : ''}`);
|
||||||
|
@ -128,8 +123,7 @@ function _setRoom({ dispatch, getState }, next, action) {
|
||||||
audioOnly = true;
|
audioOnly = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendAnalyticsEvent(
|
sendAnalytics(createStartAudioOnlyEvent(audioOnly));
|
||||||
`${START_AUDIO_ONLY_}.${audioOnly ? 'enabled' : 'disabled'}`);
|
|
||||||
logger.log(`Start audio only set to ${audioOnly.toString()}`);
|
logger.log(`Start audio only set to ${audioOnly.toString()}`);
|
||||||
dispatch(setAudioOnly(audioOnly));
|
dispatch(setAudioOnly(audioOnly));
|
||||||
}
|
}
|
||||||
|
@ -155,9 +149,7 @@ function _syncTrackMutedState({ getState }, track) {
|
||||||
// not yet in redux state and JitsiTrackEvents.TRACK_MUTE_CHANGED may be
|
// not yet in redux state and JitsiTrackEvents.TRACK_MUTE_CHANGED may be
|
||||||
// fired before track gets to state.
|
// fired before track gets to state.
|
||||||
if (track.muted !== muted) {
|
if (track.muted !== muted) {
|
||||||
sendAnalyticsEvent(
|
sendAnalytics(createSyncTrackStateEvent(track.mediaType, muted));
|
||||||
`${SYNC_TRACK_STATE_}.${track.mediaType}.${
|
|
||||||
muted ? 'muted' : 'unmuted'}`);
|
|
||||||
logger.log(`Sync ${track.mediaType} track muted state to ${
|
logger.log(`Sync ${track.mediaType} track muted state to ${
|
||||||
muted ? 'muted' : 'unmuted'}`);
|
muted ? 'muted' : 'unmuted'}`);
|
||||||
track.muted = muted;
|
track.muted = muted;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import {
|
import {
|
||||||
REPLACE_TRACK_,
|
createTrackMutedEvent,
|
||||||
sendAnalyticsEvent
|
sendAnalytics
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import { JitsiTrackErrors, JitsiTrackEvents } from '../lib-jitsi-meet';
|
import { JitsiTrackErrors, JitsiTrackEvents } from '../lib-jitsi-meet';
|
||||||
import {
|
import {
|
||||||
|
@ -220,9 +220,10 @@ export function replaceLocalTrack(oldTrack, newTrack, conference) {
|
||||||
: setAudioMuted;
|
: setAudioMuted;
|
||||||
const isMuted = newTrack.isMuted();
|
const isMuted = newTrack.isMuted();
|
||||||
|
|
||||||
sendAnalyticsEvent(`${REPLACE_TRACK_}.${
|
sendAnalytics(createTrackMutedEvent(
|
||||||
newTrack.getType()}.${
|
newTrack.getType(),
|
||||||
isMuted ? 'muted' : 'unmuted'}`);
|
'track.replaced',
|
||||||
|
isMuted));
|
||||||
logger.log(`Replace ${newTrack.getType()} track - ${
|
logger.log(`Replace ${newTrack.getType()} track - ${
|
||||||
isMuted ? 'muted' : 'unmuted'}`);
|
isMuted ? 'muted' : 'unmuted'}`);
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,8 @@ import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
FEEDBACK_OPEN,
|
createFeedbackOpenEvent,
|
||||||
sendAnalyticsEvent
|
sendAnalytics
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import { Dialog } from '../../base/dialog';
|
import { Dialog } from '../../base/dialog';
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
|
@ -148,7 +148,7 @@ class FeedbackDialog extends Component {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
sendAnalyticsEvent(FEEDBACK_OPEN);
|
sendAnalytics(createFeedbackOpenEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -26,7 +26,7 @@ MiddlewareRegistry.register(({ getState }) => next => action => {
|
||||||
// not need the middleware implemented here, Filmstrip.init, and
|
// not need the middleware implemented here, Filmstrip.init, and
|
||||||
// UI.start.
|
// UI.start.
|
||||||
|| (Filmstrip.filmstrip
|
|| (Filmstrip.filmstrip
|
||||||
&& Filmstrip.toggleFilmstrip(!newValue, false));
|
&& Filmstrip.toggleFilmstrip(!newValue));
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,8 +3,8 @@ import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TOOLBAR_INVITE_CLOSE,
|
createInviteDialogClosedEvent,
|
||||||
sendAnalyticsEvent
|
sendAnalytics
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import { getInviteURL } from '../../base/connection';
|
import { getInviteURL } from '../../base/connection';
|
||||||
import { Dialog } from '../../base/dialog';
|
import { Dialog } from '../../base/dialog';
|
||||||
|
@ -54,7 +54,7 @@ class InviteDialog extends Component {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
sendAnalyticsEvent(TOOLBAR_INVITE_CLOSE);
|
sendAnalytics(createInviteDialogClosedEvent());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
/* @flow */
|
/* @flow */
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CALLKIT_BACKGROUND_VIDEO_MUTED,
|
createTrackMutedEvent,
|
||||||
sendAnalyticsEvent
|
sendAnalytics
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import { setLastN } from '../../base/conference';
|
import { setLastN } from '../../base/conference';
|
||||||
import { setVideoMuted, VIDEO_MUTISM_AUTHORITY } from '../../base/media';
|
import { setVideoMuted, VIDEO_MUTISM_AUTHORITY } from '../../base/media';
|
||||||
|
@ -46,7 +46,9 @@ export function _setBackgroundVideoMuted(muted: boolean) {
|
||||||
|
|
||||||
audioOnly || dispatch(setLastN(muted ? 0 : undefined));
|
audioOnly || dispatch(setLastN(muted ? 0 : undefined));
|
||||||
|
|
||||||
sendAnalyticsEvent(CALLKIT_BACKGROUND_VIDEO_MUTED);
|
sendAnalytics(createTrackMutedEvent(
|
||||||
|
'video',
|
||||||
|
'callkit.background.video'));
|
||||||
|
|
||||||
dispatch(setVideoMuted(muted, VIDEO_MUTISM_AUTHORITY.BACKGROUND));
|
dispatch(setVideoMuted(muted, VIDEO_MUTISM_AUTHORITY.BACKGROUND));
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,8 +4,8 @@ import { NativeModules } from 'react-native';
|
||||||
import uuid from 'uuid';
|
import uuid from 'uuid';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CALLKIT_AUDIO_,
|
createTrackMutedEvent,
|
||||||
sendAnalyticsEvent
|
sendAnalytics
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT, appNavigate } from '../../app';
|
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT, appNavigate } from '../../app';
|
||||||
import {
|
import {
|
||||||
|
@ -279,8 +279,7 @@ function _onPerformSetMutedCallAction({ callUUID, muted: newValue }) {
|
||||||
if (oldValue !== newValue) {
|
if (oldValue !== newValue) {
|
||||||
const value = Boolean(newValue);
|
const value = Boolean(newValue);
|
||||||
|
|
||||||
sendAnalyticsEvent(`${CALLKIT_AUDIO_}.${
|
sendAnalytics(createTrackMutedEvent('audio', 'callkit', value));
|
||||||
value ? 'muted' : 'unmuted'}`);
|
|
||||||
dispatch(setAudioMuted(value));
|
dispatch(setAudioMuted(value));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,10 @@
|
||||||
import PropTypes from 'prop-types';
|
import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
import { PAGE_RELOAD } from '../../analytics';
|
import {
|
||||||
|
createPageReloadScheduledEvent,
|
||||||
|
sendAnalytics
|
||||||
|
} from '../../analytics';
|
||||||
import {
|
import {
|
||||||
isFatalJitsiConferenceError,
|
isFatalJitsiConferenceError,
|
||||||
isFatalJitsiConnectionError
|
isFatalJitsiConnectionError
|
||||||
|
@ -159,12 +162,18 @@ export default class AbstractPageReloadOverlay extends Component<*, *> {
|
||||||
// sent to the backed.
|
// sent to the backed.
|
||||||
// FIXME: We should dispatch action for this.
|
// FIXME: We should dispatch action for this.
|
||||||
if (typeof APP !== 'undefined') {
|
if (typeof APP !== 'undefined') {
|
||||||
APP.conference.logEvent(
|
if (APP.conference && APP.conference.room) {
|
||||||
PAGE_RELOAD,
|
APP.conference.room.sendApplicationLog(JSON.stringify(
|
||||||
/* value */ undefined,
|
{
|
||||||
/* label */ this.props.reason);
|
name: 'page.reload',
|
||||||
|
label: this.props.reason
|
||||||
|
}));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sendAnalytics(createPageReloadScheduledEvent(
|
||||||
|
this.props.reason, this.state.timeoutSeconds));
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
`The conference will be reloaded after ${
|
`The conference will be reloaded after ${
|
||||||
this.state.timeoutSeconds} seconds.`);
|
this.state.timeoutSeconds} seconds.`);
|
||||||
|
|
|
@ -3,8 +3,8 @@ import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
REMOTE_VIDEO_MENU_KICK,
|
createRemoteVideoMenuButtonEvent,
|
||||||
sendAnalyticsEvent
|
sendAnalytics
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
import { kickParticipant } from '../../base/participants';
|
import { kickParticipant } from '../../base/participants';
|
||||||
|
@ -86,13 +86,12 @@ class KickButton extends Component {
|
||||||
_onClick() {
|
_onClick() {
|
||||||
const { dispatch, onClick, participantID } = this.props;
|
const { dispatch, onClick, participantID } = this.props;
|
||||||
|
|
||||||
sendAnalyticsEvent(
|
sendAnalytics(createRemoteVideoMenuButtonEvent(
|
||||||
REMOTE_VIDEO_MENU_KICK,
|
'kick.button',
|
||||||
{
|
{
|
||||||
value: 1,
|
'participant_id': participantID
|
||||||
label: participantID
|
}));
|
||||||
}
|
|
||||||
);
|
|
||||||
dispatch(kickParticipant(participantID));
|
dispatch(kickParticipant(participantID));
|
||||||
|
|
||||||
if (onClick) {
|
if (onClick) {
|
||||||
|
|
|
@ -3,8 +3,8 @@ import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
REMOTE_VIDEO_MENU_MUTE_CLICKED,
|
createRemoteVideoMenuButtonEvent,
|
||||||
sendAnalyticsEvent
|
sendAnalytics
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
import { openDialog } from '../../base/dialog';
|
import { openDialog } from '../../base/dialog';
|
||||||
|
@ -101,13 +101,11 @@ class MuteButton extends Component {
|
||||||
_onClick() {
|
_onClick() {
|
||||||
const { dispatch, onClick, participantID } = this.props;
|
const { dispatch, onClick, participantID } = this.props;
|
||||||
|
|
||||||
sendAnalyticsEvent(
|
sendAnalytics(createRemoteVideoMenuButtonEvent(
|
||||||
REMOTE_VIDEO_MENU_MUTE_CLICKED,
|
'mute.button',
|
||||||
{
|
{
|
||||||
value: 1,
|
'participant_id': participantID
|
||||||
label: participantID
|
}));
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
dispatch(openDialog(MuteRemoteParticipantDialog, { participantID }));
|
dispatch(openDialog(MuteRemoteParticipantDialog, { participantID }));
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@ import { Dialog } from '../../base/dialog';
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
REMOTE_VIDEO_MENU_MUTE_CONFIRMED,
|
createRemoteMuteConfirmedEvent,
|
||||||
sendAnalyticsEvent
|
sendAnalytics
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import { muteRemoteParticipant } from '../../base/participants';
|
import { muteRemoteParticipant } from '../../base/participants';
|
||||||
|
|
||||||
|
@ -77,18 +77,12 @@ class MuteRemoteParticipantDialog extends Component {
|
||||||
* Handles the submit button action.
|
* Handles the submit button action.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {void}
|
* @returns {boolean} - True (to note that the modal should be closed).
|
||||||
*/
|
*/
|
||||||
_onSubmit() {
|
_onSubmit() {
|
||||||
const { dispatch, participantID } = this.props;
|
const { dispatch, participantID } = this.props;
|
||||||
|
|
||||||
sendAnalyticsEvent(
|
sendAnalytics(createRemoteMuteConfirmedEvent(participantID));
|
||||||
REMOTE_VIDEO_MENU_MUTE_CONFIRMED,
|
|
||||||
{
|
|
||||||
value: 1,
|
|
||||||
label: participantID
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
dispatch(muteRemoteParticipant(participantID));
|
dispatch(muteRemoteParticipant(participantID));
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
REMOTE_VIDEO_MENU_REMOTE_CONTROL_,
|
createRemoteVideoMenuButtonEvent,
|
||||||
sendAnalyticsEvent
|
sendAnalytics
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
|
|
||||||
|
@ -122,24 +122,19 @@ class RemoteControlButton extends Component {
|
||||||
_onClick() {
|
_onClick() {
|
||||||
const { onClick, participantID, remoteControlState } = this.props;
|
const { onClick, participantID, remoteControlState } = this.props;
|
||||||
|
|
||||||
let eventName;
|
// TODO: What do we do in case the state is e.g. "requesting"?
|
||||||
|
if (remoteControlState === REMOTE_CONTROL_MENU_STATES.STARTED
|
||||||
|
|| remoteControlState === REMOTE_CONTROL_MENU_STATES.NOT_STARTED) {
|
||||||
|
|
||||||
if (remoteControlState === REMOTE_CONTROL_MENU_STATES.STARTED) {
|
const enable
|
||||||
eventName = 'stop';
|
= remoteControlState === REMOTE_CONTROL_MENU_STATES.NOT_STARTED;
|
||||||
}
|
|
||||||
|
|
||||||
if (remoteControlState === REMOTE_CONTROL_MENU_STATES.NOT_STARTED) {
|
sendAnalytics(createRemoteVideoMenuButtonEvent(
|
||||||
eventName = 'start';
|
'remote.control.button',
|
||||||
}
|
|
||||||
|
|
||||||
if (eventName) {
|
|
||||||
sendAnalyticsEvent(
|
|
||||||
`${REMOTE_VIDEO_MENU_REMOTE_CONTROL_}.${eventName}`,
|
|
||||||
{
|
{
|
||||||
value: 1,
|
enable,
|
||||||
label: participantID
|
'participant_id': participantID
|
||||||
}
|
}));
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onClick) {
|
if (onClick) {
|
||||||
|
|
|
@ -4,7 +4,10 @@ import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import { TOOLBAR_PROFILE_TOGGLED, sendAnalyticsEvent } from '../../analytics';
|
import {
|
||||||
|
createToolbarEvent,
|
||||||
|
sendAnalytics
|
||||||
|
} from '../../analytics';
|
||||||
import {
|
import {
|
||||||
getAvatarURL,
|
getAvatarURL,
|
||||||
getLocalParticipant
|
getLocalParticipant
|
||||||
|
@ -115,7 +118,9 @@ class ProfileButton extends Component<*> {
|
||||||
*/
|
*/
|
||||||
_onClick() {
|
_onClick() {
|
||||||
if (!this.props._unclickable) {
|
if (!this.props._unclickable) {
|
||||||
sendAnalyticsEvent(TOOLBAR_PROFILE_TOGGLED);
|
// TODO: Include an 'enable' attribute, which specifies whether
|
||||||
|
// the profile panel was opened or closed.
|
||||||
|
sendAnalytics(createToolbarEvent('profile'));
|
||||||
APP.UI.emitEvent(UIEvents.TOGGLE_PROFILE);
|
APP.UI.emitEvent(UIEvents.TOGGLE_PROFILE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,10 @@ import { View } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TOOLBAR_AUDIO_MUTED,
|
AUDIO_MUTE,
|
||||||
TOOLBAR_AUDIO_UNMUTED,
|
VIDEO_MUTE,
|
||||||
TOOLBAR_VIDEO_,
|
createToolbarEvent,
|
||||||
sendAnalyticsEvent
|
sendAnalytics
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import {
|
import {
|
||||||
isNarrowAspectRatio,
|
isNarrowAspectRatio,
|
||||||
|
@ -188,7 +188,11 @@ class Toolbox extends Component {
|
||||||
_onToggleAudio() {
|
_onToggleAudio() {
|
||||||
const mute = !this.props._audioMuted;
|
const mute = !this.props._audioMuted;
|
||||||
|
|
||||||
sendAnalyticsEvent(mute ? TOOLBAR_AUDIO_MUTED : TOOLBAR_AUDIO_UNMUTED);
|
sendAnalytics(createToolbarEvent(
|
||||||
|
AUDIO_MUTE,
|
||||||
|
{
|
||||||
|
enable: mute
|
||||||
|
}));
|
||||||
|
|
||||||
// The user sees the reality i.e. the state of base/tracks and intends
|
// The user sees the reality i.e. the state of base/tracks and intends
|
||||||
// to change reality by tapping on the respective button i.e. the user
|
// to change reality by tapping on the respective button i.e. the user
|
||||||
|
@ -211,7 +215,11 @@ class Toolbox extends Component {
|
||||||
_onToggleVideo() {
|
_onToggleVideo() {
|
||||||
const mute = !this.props._videoMuted;
|
const mute = !this.props._videoMuted;
|
||||||
|
|
||||||
sendAnalyticsEvent(`${TOOLBAR_VIDEO_}.${mute ? 'muted' : 'unmuted'}`);
|
sendAnalytics(createToolbarEvent(
|
||||||
|
VIDEO_MUTE,
|
||||||
|
{
|
||||||
|
enable: mute
|
||||||
|
}));
|
||||||
|
|
||||||
// The user sees the reality i.e. the state of base/tracks and intends
|
// The user sees the reality i.e. the state of base/tracks and intends
|
||||||
// to change reality by tapping on the respective button i.e. the user
|
// to change reality by tapping on the respective button i.e. the user
|
||||||
|
|
|
@ -3,29 +3,12 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
SHORTCUT_AUDIO_MUTE_TOGGLED,
|
ACTION_SHORTCUT_TRIGGERED as TRIGGERED,
|
||||||
SHORTCUT_CHAT_TOGGLED,
|
AUDIO_MUTE,
|
||||||
SHORTCUT_RAISE_HAND_CLICKED,
|
VIDEO_MUTE,
|
||||||
SHORTCUT_SCREEN_TOGGLED,
|
createShortcutEvent,
|
||||||
SHORTCUT_VIDEO_MUTE_TOGGLED,
|
createToolbarEvent,
|
||||||
TOOLBAR_AUDIO_MUTED,
|
sendAnalytics
|
||||||
TOOLBAR_AUDIO_UNMUTED,
|
|
||||||
TOOLBAR_CHAT_TOGGLED,
|
|
||||||
TOOLBAR_CONTACTS_TOGGLED,
|
|
||||||
TOOLBAR_ETHERPACK_CLICKED,
|
|
||||||
TOOLBAR_FILMSTRIP_ONLY_DEVICE_SELECTION_TOGGLED,
|
|
||||||
TOOLBAR_FULLSCREEN_ENABLED,
|
|
||||||
TOOLBAR_HANGUP,
|
|
||||||
TOOLBAR_INVITE_CLICKED,
|
|
||||||
TOOLBAR_RAISE_HAND_CLICKED,
|
|
||||||
TOOLBAR_SCREEN_DISABLED,
|
|
||||||
TOOLBAR_SCREEN_ENABLED,
|
|
||||||
TOOLBAR_SETTINGS_TOGGLED,
|
|
||||||
TOOLBAR_SHARED_VIDEO_CLICKED,
|
|
||||||
TOOLBAR_SIP_DIALPAD_CLICKED,
|
|
||||||
TOOLBAR_VIDEO_DISABLED,
|
|
||||||
TOOLBAR_VIDEO_ENABLED,
|
|
||||||
sendAnalyticsEvent
|
|
||||||
} from '../analytics';
|
} from '../analytics';
|
||||||
import { ParticipantCounter } from '../contact-list';
|
import { ParticipantCounter } from '../contact-list';
|
||||||
import { openDeviceSelectionDialog } from '../device-selection';
|
import { openDeviceSelectionDialog } from '../device-selection';
|
||||||
|
@ -63,13 +46,18 @@ export default function getDefaultButtons() {
|
||||||
isDisplayed: () => true,
|
isDisplayed: () => true,
|
||||||
id: 'toolbar_button_camera',
|
id: 'toolbar_button_camera',
|
||||||
onClick() {
|
onClick() {
|
||||||
|
// TODO: Why is this different from the code which handles
|
||||||
|
// a keyboard shortcut?
|
||||||
const newVideoMutedState = !APP.conference.isLocalVideoMuted();
|
const newVideoMutedState = !APP.conference.isLocalVideoMuted();
|
||||||
|
|
||||||
if (newVideoMutedState) {
|
// The 'enable' attribute in the event is set to true if the
|
||||||
sendAnalyticsEvent(TOOLBAR_VIDEO_ENABLED);
|
// button click triggered a mute action, and set to false if it
|
||||||
} else {
|
// triggered an unmute action.
|
||||||
sendAnalyticsEvent(TOOLBAR_VIDEO_DISABLED);
|
sendAnalytics(createToolbarEvent(
|
||||||
}
|
VIDEO_MUTE,
|
||||||
|
{
|
||||||
|
enable: newVideoMutedState
|
||||||
|
}));
|
||||||
APP.UI.emitEvent(UIEvents.VIDEO_MUTED, newVideoMutedState);
|
APP.UI.emitEvent(UIEvents.VIDEO_MUTED, newVideoMutedState);
|
||||||
},
|
},
|
||||||
popups: [
|
popups: [
|
||||||
|
@ -88,7 +76,13 @@ export default function getDefaultButtons() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
sendAnalyticsEvent(SHORTCUT_VIDEO_MUTE_TOGGLED);
|
// The 'enable' attribute in the event is set to true if the
|
||||||
|
// shortcut triggered a mute action, and set to false if it
|
||||||
|
// triggered an unmute action.
|
||||||
|
sendAnalytics(createShortcutEvent(
|
||||||
|
VIDEO_MUTE,
|
||||||
|
TRIGGERED,
|
||||||
|
{ enable: !APP.conference.isLocalVideoMuted() }));
|
||||||
APP.conference.toggleVideoMuted();
|
APP.conference.toggleVideoMuted();
|
||||||
},
|
},
|
||||||
shortcutDescription: 'keyboardShortcuts.videoMute',
|
shortcutDescription: 'keyboardShortcuts.videoMute',
|
||||||
|
@ -105,13 +99,26 @@ export default function getDefaultButtons() {
|
||||||
<span id = 'unreadMessages' /></span>,
|
<span id = 'unreadMessages' /></span>,
|
||||||
id: 'toolbar_button_chat',
|
id: 'toolbar_button_chat',
|
||||||
onClick() {
|
onClick() {
|
||||||
sendAnalyticsEvent(TOOLBAR_CHAT_TOGGLED);
|
// The 'enable' attribute is set to true if the click resulted
|
||||||
|
// in the chat panel being shown, and to false if it was hidden.
|
||||||
|
sendAnalytics(createToolbarEvent(
|
||||||
|
'toggle.chat',
|
||||||
|
{
|
||||||
|
enable: !APP.UI.Chat.isVisible()
|
||||||
|
}));
|
||||||
APP.UI.emitEvent(UIEvents.TOGGLE_CHAT);
|
APP.UI.emitEvent(UIEvents.TOGGLE_CHAT);
|
||||||
},
|
},
|
||||||
shortcut: 'C',
|
shortcut: 'C',
|
||||||
shortcutAttr: 'toggleChatPopover',
|
shortcutAttr: 'toggleChatPopover',
|
||||||
shortcutFunc() {
|
shortcutFunc() {
|
||||||
sendAnalyticsEvent(SHORTCUT_CHAT_TOGGLED);
|
// The 'enable' attribute is set to true if the shortcut
|
||||||
|
// resulted in the chat panel being shown, and to false if it
|
||||||
|
// was hidden.
|
||||||
|
sendAnalytics(createShortcutEvent(
|
||||||
|
'toggle.chat',
|
||||||
|
{
|
||||||
|
enable: !APP.UI.Chat.isVisible()
|
||||||
|
}));
|
||||||
APP.UI.toggleChat();
|
APP.UI.toggleChat();
|
||||||
},
|
},
|
||||||
shortcutDescription: 'keyboardShortcuts.toggleChat',
|
shortcutDescription: 'keyboardShortcuts.toggleChat',
|
||||||
|
@ -128,7 +135,9 @@ export default function getDefaultButtons() {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
id: 'toolbar_contact_list',
|
id: 'toolbar_contact_list',
|
||||||
onClick() {
|
onClick() {
|
||||||
sendAnalyticsEvent(TOOLBAR_CONTACTS_TOGGLED);
|
// TODO: Include an 'enable' attribute which specifies whether
|
||||||
|
// the contacts panel was shown or hidden.
|
||||||
|
sendAnalytics(createToolbarEvent('contacts'));
|
||||||
APP.UI.emitEvent(UIEvents.TOGGLE_CONTACT_LIST);
|
APP.UI.emitEvent(UIEvents.TOGGLE_CONTACT_LIST);
|
||||||
},
|
},
|
||||||
sideContainerId: 'contacts_container',
|
sideContainerId: 'contacts_container',
|
||||||
|
@ -143,11 +152,14 @@ export default function getDefaultButtons() {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
id: 'toolbar_button_desktopsharing',
|
id: 'toolbar_button_desktopsharing',
|
||||||
onClick() {
|
onClick() {
|
||||||
if (APP.conference.isSharingScreen) {
|
// TODO: Why is the button clicked handled differently that
|
||||||
sendAnalyticsEvent(TOOLBAR_SCREEN_DISABLED);
|
// a keyboard shortcut press (firing a TOGGLE_SCREENSHARING
|
||||||
} else {
|
// event vs. directly calling toggleScreenSharing())?
|
||||||
sendAnalyticsEvent(TOOLBAR_SCREEN_ENABLED);
|
sendAnalytics(createToolbarEvent(
|
||||||
}
|
'screen.sharing',
|
||||||
|
{
|
||||||
|
enable: !APP.conference.isSharingScreen
|
||||||
|
}));
|
||||||
APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING);
|
APP.UI.emitEvent(UIEvents.TOGGLE_SCREENSHARING);
|
||||||
},
|
},
|
||||||
popups: [
|
popups: [
|
||||||
|
@ -160,7 +172,13 @@ export default function getDefaultButtons() {
|
||||||
shortcut: 'D',
|
shortcut: 'D',
|
||||||
shortcutAttr: 'toggleDesktopSharingPopover',
|
shortcutAttr: 'toggleDesktopSharingPopover',
|
||||||
shortcutFunc() {
|
shortcutFunc() {
|
||||||
sendAnalyticsEvent(SHORTCUT_SCREEN_TOGGLED);
|
// The 'enable' attribute is set to true if pressing the
|
||||||
|
// shortcut resulted in screen sharing being enabled, and false
|
||||||
|
// if it resulted in screen sharing being disabled.
|
||||||
|
sendAnalytics(createShortcutEvent(
|
||||||
|
'toggle.screen.sharing',
|
||||||
|
TRIGGERED,
|
||||||
|
{ enable: !APP.conference.isSharingScreen }));
|
||||||
|
|
||||||
// eslint-disable-next-line no-empty-function
|
// eslint-disable-next-line no-empty-function
|
||||||
APP.conference.toggleScreenSharing().catch(() => {});
|
APP.conference.toggleScreenSharing().catch(() => {});
|
||||||
|
@ -180,8 +198,8 @@ export default function getDefaultButtons() {
|
||||||
},
|
},
|
||||||
id: 'toolbar_button_fodeviceselection',
|
id: 'toolbar_button_fodeviceselection',
|
||||||
onClick(dispatch: Function) {
|
onClick(dispatch: Function) {
|
||||||
sendAnalyticsEvent(
|
sendAnalytics(
|
||||||
TOOLBAR_FILMSTRIP_ONLY_DEVICE_SELECTION_TOGGLED);
|
createToolbarEvent('filmstrip.only.device.selection'));
|
||||||
|
|
||||||
dispatch(openDeviceSelectionDialog());
|
dispatch(openDeviceSelectionDialog());
|
||||||
},
|
},
|
||||||
|
@ -200,7 +218,7 @@ export default function getDefaultButtons() {
|
||||||
hidden: true,
|
hidden: true,
|
||||||
id: 'toolbar_button_dialpad',
|
id: 'toolbar_button_dialpad',
|
||||||
onClick() {
|
onClick() {
|
||||||
sendAnalyticsEvent(TOOLBAR_SIP_DIALPAD_CLICKED);
|
sendAnalytics(createToolbarEvent('dialpad'));
|
||||||
},
|
},
|
||||||
tooltipKey: 'toolbar.dialpad'
|
tooltipKey: 'toolbar.dialpad'
|
||||||
},
|
},
|
||||||
|
@ -214,7 +232,13 @@ export default function getDefaultButtons() {
|
||||||
hidden: true,
|
hidden: true,
|
||||||
id: 'toolbar_button_etherpad',
|
id: 'toolbar_button_etherpad',
|
||||||
onClick() {
|
onClick() {
|
||||||
sendAnalyticsEvent(TOOLBAR_ETHERPACK_CLICKED);
|
// The 'enable' attribute is set to true if the click resulted
|
||||||
|
// in the etherpad panel being shown, or false it it was hidden.
|
||||||
|
sendAnalytics(createToolbarEvent(
|
||||||
|
'toggle.etherpad',
|
||||||
|
{
|
||||||
|
enable: !APP.UI.isEtherpadVisible()
|
||||||
|
}));
|
||||||
APP.UI.emitEvent(UIEvents.ETHERPAD_CLICKED);
|
APP.UI.emitEvent(UIEvents.ETHERPAD_CLICKED);
|
||||||
},
|
},
|
||||||
tooltipKey: 'toolbar.etherpad'
|
tooltipKey: 'toolbar.etherpad'
|
||||||
|
@ -228,7 +252,18 @@ export default function getDefaultButtons() {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
id: 'toolbar_button_fullScreen',
|
id: 'toolbar_button_fullScreen',
|
||||||
onClick() {
|
onClick() {
|
||||||
sendAnalyticsEvent(TOOLBAR_FULLSCREEN_ENABLED);
|
// TODO: why is the fullscreen button handled differently than
|
||||||
|
// the fullscreen keyboard shortcut (one results in a direct
|
||||||
|
// call to toggleFullScreen, while the other fires an
|
||||||
|
// UIEvents.TOGGLE_FULLSCREEN event)?
|
||||||
|
|
||||||
|
// The 'enable' attribute is set to true if the action resulted
|
||||||
|
// in fullscreen mode being enabled.
|
||||||
|
sendAnalytics(createToolbarEvent(
|
||||||
|
'toggle.fullscreen',
|
||||||
|
{
|
||||||
|
enable: !APP.UI.isFullScreen()
|
||||||
|
}));
|
||||||
|
|
||||||
APP.UI.emitEvent(UIEvents.TOGGLE_FULLSCREEN);
|
APP.UI.emitEvent(UIEvents.TOGGLE_FULLSCREEN);
|
||||||
},
|
},
|
||||||
|
@ -236,7 +271,13 @@ export default function getDefaultButtons() {
|
||||||
shortcutAttr: 'toggleFullscreenPopover',
|
shortcutAttr: 'toggleFullscreenPopover',
|
||||||
shortcutDescription: 'keyboardShortcuts.fullScreen',
|
shortcutDescription: 'keyboardShortcuts.fullScreen',
|
||||||
shortcutFunc() {
|
shortcutFunc() {
|
||||||
sendAnalyticsEvent('shortcut.fullscreen.toggled');
|
// The 'enable' attribute is set to true if the action resulted
|
||||||
|
// in fullscreen mode being enabled.
|
||||||
|
sendAnalytics(createShortcutEvent(
|
||||||
|
'toggle.fullscreen',
|
||||||
|
{
|
||||||
|
enable: !APP.UI.isFullScreen()
|
||||||
|
}));
|
||||||
APP.UI.toggleFullScreen();
|
APP.UI.toggleFullScreen();
|
||||||
},
|
},
|
||||||
tooltipKey: 'toolbar.fullscreen'
|
tooltipKey: 'toolbar.fullscreen'
|
||||||
|
@ -252,7 +293,7 @@ export default function getDefaultButtons() {
|
||||||
isDisplayed: () => true,
|
isDisplayed: () => true,
|
||||||
id: 'toolbar_button_hangup',
|
id: 'toolbar_button_hangup',
|
||||||
onClick() {
|
onClick() {
|
||||||
sendAnalyticsEvent(TOOLBAR_HANGUP);
|
sendAnalytics(createToolbarEvent('hangup'));
|
||||||
APP.UI.emitEvent(UIEvents.HANGUP);
|
APP.UI.emitEvent(UIEvents.HANGUP);
|
||||||
},
|
},
|
||||||
tooltipKey: 'toolbar.hangup'
|
tooltipKey: 'toolbar.hangup'
|
||||||
|
@ -275,7 +316,7 @@ export default function getDefaultButtons() {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
id: 'toolbar_button_link',
|
id: 'toolbar_button_link',
|
||||||
onClick(dispatch: Function) {
|
onClick(dispatch: Function) {
|
||||||
sendAnalyticsEvent(TOOLBAR_INVITE_CLICKED);
|
sendAnalytics(createToolbarEvent('invite'));
|
||||||
|
|
||||||
dispatch(openInviteDialog());
|
dispatch(openInviteDialog());
|
||||||
},
|
},
|
||||||
|
@ -293,6 +334,13 @@ export default function getDefaultButtons() {
|
||||||
onClick() {
|
onClick() {
|
||||||
const sharedVideoManager = APP.UI.getSharedVideoManager();
|
const sharedVideoManager = APP.UI.getSharedVideoManager();
|
||||||
|
|
||||||
|
// TODO: Clicking the mute button and pressing the mute shortcut
|
||||||
|
// could be handled in a uniform manner. The code below checks
|
||||||
|
// the mute status and fires the appropriate event (MUTED or
|
||||||
|
// UNMUTED), while the code which handles the keyboard shortcut
|
||||||
|
// calls toggleAudioMuted(). Also strangely the the user is
|
||||||
|
// only warned if they click the button (and not if they use
|
||||||
|
// the shortcut).
|
||||||
if (APP.conference.isLocalAudioMuted()) {
|
if (APP.conference.isLocalAudioMuted()) {
|
||||||
// If there's a shared video with the volume "on" and we
|
// If there's a shared video with the volume "on" and we
|
||||||
// aren't the video owner, we warn the user
|
// aren't the video owner, we warn the user
|
||||||
|
@ -303,11 +351,15 @@ export default function getDefaultButtons() {
|
||||||
APP.UI.showCustomToolbarPopup(
|
APP.UI.showCustomToolbarPopup(
|
||||||
'microphone', 'unableToUnmutePopup', true, 5000);
|
'microphone', 'unableToUnmutePopup', true, 5000);
|
||||||
} else {
|
} else {
|
||||||
sendAnalyticsEvent(TOOLBAR_AUDIO_UNMUTED);
|
sendAnalytics(createToolbarEvent(
|
||||||
|
AUDIO_MUTE,
|
||||||
|
{ enable: false }));
|
||||||
APP.UI.emitEvent(UIEvents.AUDIO_MUTED, false, true);
|
APP.UI.emitEvent(UIEvents.AUDIO_MUTED, false, true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sendAnalyticsEvent(TOOLBAR_AUDIO_MUTED);
|
sendAnalytics(createToolbarEvent(
|
||||||
|
AUDIO_MUTE,
|
||||||
|
{ enable: true }));
|
||||||
APP.UI.emitEvent(UIEvents.AUDIO_MUTED, true, true);
|
APP.UI.emitEvent(UIEvents.AUDIO_MUTED, true, true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -328,7 +380,13 @@ export default function getDefaultButtons() {
|
||||||
shortcut: 'M',
|
shortcut: 'M',
|
||||||
shortcutAttr: 'mutePopover',
|
shortcutAttr: 'mutePopover',
|
||||||
shortcutFunc() {
|
shortcutFunc() {
|
||||||
sendAnalyticsEvent(SHORTCUT_AUDIO_MUTE_TOGGLED);
|
// The 'enable' attribute in the event is set to true if the
|
||||||
|
// shortcut triggered a mute action, and set to false if it
|
||||||
|
// triggered an unmute action.
|
||||||
|
sendAnalytics(createShortcutEvent(
|
||||||
|
AUDIO_MUTE,
|
||||||
|
TRIGGERED,
|
||||||
|
{ enable: !APP.conference.isLocalAudioMuted() }));
|
||||||
APP.conference.toggleAudioMuted();
|
APP.conference.toggleAudioMuted();
|
||||||
},
|
},
|
||||||
shortcutDescription: 'keyboardShortcuts.mute',
|
shortcutDescription: 'keyboardShortcuts.mute',
|
||||||
|
@ -351,14 +409,27 @@ export default function getDefaultButtons() {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
id: 'toolbar_button_raisehand',
|
id: 'toolbar_button_raisehand',
|
||||||
onClick() {
|
onClick() {
|
||||||
sendAnalyticsEvent(TOOLBAR_RAISE_HAND_CLICKED);
|
// TODO: reduce duplication with shortcutFunc below.
|
||||||
|
|
||||||
|
// The 'enable' attribute is set to true if the pressing of the
|
||||||
|
// shortcut resulted in the hand being raised, and to false
|
||||||
|
// if it resulted in the hand being 'lowered'.
|
||||||
|
sendAnalytics(createToolbarEvent(
|
||||||
|
'raise.hand',
|
||||||
|
{ enable: !APP.conference.isHandRaised }));
|
||||||
APP.conference.maybeToggleRaisedHand();
|
APP.conference.maybeToggleRaisedHand();
|
||||||
},
|
},
|
||||||
shortcut: 'R',
|
shortcut: 'R',
|
||||||
shortcutAttr: 'raiseHandPopover',
|
shortcutAttr: 'raiseHandPopover',
|
||||||
shortcutDescription: 'keyboardShortcuts.raiseHand',
|
shortcutDescription: 'keyboardShortcuts.raiseHand',
|
||||||
shortcutFunc() {
|
shortcutFunc() {
|
||||||
sendAnalyticsEvent(SHORTCUT_RAISE_HAND_CLICKED);
|
// The 'enable' attribute is set to true if the pressing of the
|
||||||
|
// shortcut resulted in the hand being raised, and to false
|
||||||
|
// if it resulted in the hand being 'lowered'.
|
||||||
|
sendAnalytics(createShortcutEvent(
|
||||||
|
'toggle.raise.hand',
|
||||||
|
TRIGGERED,
|
||||||
|
{ enable: !APP.conference.isHandRaised }));
|
||||||
APP.conference.maybeToggleRaisedHand();
|
APP.conference.maybeToggleRaisedHand();
|
||||||
},
|
},
|
||||||
tooltipKey: 'toolbar.raiseHand'
|
tooltipKey: 'toolbar.raiseHand'
|
||||||
|
@ -386,7 +457,9 @@ export default function getDefaultButtons() {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
id: 'toolbar_button_settings',
|
id: 'toolbar_button_settings',
|
||||||
onClick() {
|
onClick() {
|
||||||
sendAnalyticsEvent(TOOLBAR_SETTINGS_TOGGLED);
|
// TODO: Include an 'enable' attribute which specifies whether
|
||||||
|
// the settings panel was shown or hidden.
|
||||||
|
sendAnalytics(createToolbarEvent('settings'));
|
||||||
APP.UI.emitEvent(UIEvents.TOGGLE_SETTINGS);
|
APP.UI.emitEvent(UIEvents.TOGGLE_SETTINGS);
|
||||||
},
|
},
|
||||||
sideContainerId: 'settings_container',
|
sideContainerId: 'settings_container',
|
||||||
|
@ -401,7 +474,15 @@ export default function getDefaultButtons() {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
id: 'toolbar_button_sharedvideo',
|
id: 'toolbar_button_sharedvideo',
|
||||||
onClick() {
|
onClick() {
|
||||||
sendAnalyticsEvent(TOOLBAR_SHARED_VIDEO_CLICKED);
|
// The 'enable' attribute is set to true if the click resulted
|
||||||
|
// in the "start sharing video" dialog being shown, and false
|
||||||
|
// if it resulted in the "stop sharing video" dialog being
|
||||||
|
// shown.
|
||||||
|
sendAnalytics(createToolbarEvent(
|
||||||
|
'shared.video.toggled',
|
||||||
|
{
|
||||||
|
enable: !APP.UI.isSharedVideoShown()
|
||||||
|
}));
|
||||||
APP.UI.emitEvent(UIEvents.SHARED_VIDEO_CLICKED);
|
APP.UI.emitEvent(UIEvents.SHARED_VIDEO_CLICKED);
|
||||||
},
|
},
|
||||||
popups: [
|
popups: [
|
||||||
|
|
|
@ -4,16 +4,13 @@ import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
TOOLBAR_AUDIO_ONLY_ENABLED,
|
createToolbarEvent,
|
||||||
TOOLBAR_VIDEO_QUALITY_HIGH,
|
sendAnalytics
|
||||||
TOOLBAR_VIDEO_QUALITY_LOW,
|
|
||||||
TOOLBAR_VIDEO_QUALITY_STANDARD,
|
|
||||||
sendAnalyticsEvent
|
|
||||||
} from '../../analytics';
|
} from '../../analytics';
|
||||||
import {
|
import {
|
||||||
|
VIDEO_QUALITY_LEVELS,
|
||||||
setAudioOnly,
|
setAudioOnly,
|
||||||
setReceiveVideoQuality,
|
setReceiveVideoQuality
|
||||||
VIDEO_QUALITY_LEVELS
|
|
||||||
} from '../../base/conference';
|
} from '../../base/conference';
|
||||||
import { translate } from '../../base/i18n';
|
import { translate } from '../../base/i18n';
|
||||||
import JitsiMeetJS from '../../base/lib-jitsi-meet';
|
import JitsiMeetJS from '../../base/lib-jitsi-meet';
|
||||||
|
@ -26,6 +23,22 @@ const {
|
||||||
LOW
|
LOW
|
||||||
} = VIDEO_QUALITY_LEVELS;
|
} = VIDEO_QUALITY_LEVELS;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an analytics event for a press of one of the buttons in the video
|
||||||
|
* quality dialog.
|
||||||
|
*
|
||||||
|
* @param {string} quality - The quality which was selected.
|
||||||
|
* @returns {Object} The event in a format suitable for sending via
|
||||||
|
* sendAnalytics.
|
||||||
|
*/
|
||||||
|
const createEvent = function(quality) {
|
||||||
|
return createToolbarEvent(
|
||||||
|
'video.quality',
|
||||||
|
{
|
||||||
|
quality
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Implements a React {@link Component} which displays a dialog with a slider
|
* Implements a React {@link Component} which displays a dialog with a slider
|
||||||
* for selecting a new receive video quality.
|
* for selecting a new receive video quality.
|
||||||
|
@ -255,12 +268,13 @@ class VideoQualityDialog extends Component {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_enableAudioOnly() {
|
_enableAudioOnly() {
|
||||||
sendAnalyticsEvent(TOOLBAR_AUDIO_ONLY_ENABLED);
|
sendAnalytics(createEvent('audio.only'));
|
||||||
logger.log('Video quality: audio only enabled');
|
logger.log('Video quality: audio only enabled');
|
||||||
this.props.dispatch(setAudioOnly(true));
|
this.props.dispatch(setAudioOnly(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Handles the action of the high definition video being selected.
|
||||||
* Dispatches an action to receive high quality video from remote
|
* Dispatches an action to receive high quality video from remote
|
||||||
* participants.
|
* participants.
|
||||||
*
|
*
|
||||||
|
@ -268,7 +282,7 @@ class VideoQualityDialog extends Component {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_enableHighDefinition() {
|
_enableHighDefinition() {
|
||||||
sendAnalyticsEvent(TOOLBAR_VIDEO_QUALITY_HIGH);
|
sendAnalytics(createEvent('high'));
|
||||||
logger.log('Video quality: high enabled');
|
logger.log('Video quality: high enabled');
|
||||||
this.props.dispatch(setReceiveVideoQuality(HIGH));
|
this.props.dispatch(setReceiveVideoQuality(HIGH));
|
||||||
}
|
}
|
||||||
|
@ -281,7 +295,7 @@ class VideoQualityDialog extends Component {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_enableLowDefinition() {
|
_enableLowDefinition() {
|
||||||
sendAnalyticsEvent(TOOLBAR_VIDEO_QUALITY_LOW);
|
sendAnalytics(createEvent('low'));
|
||||||
logger.log('Video quality: low enabled');
|
logger.log('Video quality: low enabled');
|
||||||
this.props.dispatch(setReceiveVideoQuality(LOW));
|
this.props.dispatch(setReceiveVideoQuality(LOW));
|
||||||
}
|
}
|
||||||
|
@ -294,7 +308,7 @@ class VideoQualityDialog extends Component {
|
||||||
* @returns {void}
|
* @returns {void}
|
||||||
*/
|
*/
|
||||||
_enableStandardDefinition() {
|
_enableStandardDefinition() {
|
||||||
sendAnalyticsEvent(TOOLBAR_VIDEO_QUALITY_STANDARD);
|
sendAnalytics(createEvent('standard'));
|
||||||
logger.log('Video quality: standard enabled');
|
logger.log('Video quality: standard enabled');
|
||||||
this.props.dispatch(setReceiveVideoQuality(STANDARD));
|
this.props.dispatch(setReceiveVideoQuality(STANDARD));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue