gecko-dev/browser/components/loop/standalone/content/js/standaloneMetricsStore.js

210 lines
6.4 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
var loop = loop || {};
loop.store = loop.store || {};
/**
* The standalone metrics store is used to log activities to
* analytics.
*
* Where possible we log events via receiving actions. However, some
* combinations of actions and events require us to listen directly to
* changes in the activeRoomStore so that we gain the benefit of the logic
* in that store.
*/
loop.store.StandaloneMetricsStore = (function() {
"use strict";
var ROOM_STATES = loop.store.ROOM_STATES;
var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
loop.store.metrics = loop.store.metrics || {};
var METRICS_GA_CATEGORY = loop.store.METRICS_GA_CATEGORY = {
general: "/conversation/ interactions",
download: "Firefox Downloads"
};
var METRICS_GA_ACTIONS = loop.store.METRICS_GA_ACTIONS = {
audioMute: "audio mute",
button: "button click",
download: "download button click",
faceMute: "face mute",
link: "link click",
pageLoad: "page load messages",
success: "success",
support: "support link click"
}
var StandaloneMetricsStore = loop.store.createStore({
actions: [
"gotMediaPermission",
"joinRoom",
"leaveRoom",
"mediaConnected",
"recordClick"
],
/**
* Initializes the store and starts listening to the activeRoomStore.
*
* @param {Object} options Options for the store, should include a
* reference to the activeRoomStore.
*/
initialize: function(options) {
options = options || {};
if (!options.activeRoomStore) {
throw new Error("Missing option activeRoomStore");
}
// Don't bother listening if we're not storing metrics.
// I'd love for ga to be an option, but that messes up the function
// working properly.
if (window.ga) {
this.activeRoomStore = options.activeRoomStore;
this.listenTo(options.activeRoomStore, "change",
this._onActiveRoomStoreChange.bind(this));
}
},
/**
* Returns initial state data for this store. These are mainly reflections
* of activeRoomStore so we can match initial states for change tracking
* purposes.
*
* @return {Object} The initial store state.
*/
getInitialStoreState: function() {
return {
audioMuted: false,
roomState: ROOM_STATES.INIT,
videoMuted: false
};
},
/**
* Saves an event to ga.
*
* @param {String} category The category for the event.
* @param {String} action The type of action.
* @param {String} label The label detailing the action.
*/
_storeEvent: function(category, action, label) {
// ga might not be defined if donottrack is enabled, see index.html.
if (!window.ga) {
return;
}
// For now all we need to do is forward onto ga.
window.ga("send", "event", category, action, label);
},
/**
* Handles media permssion being obtained.
*/
gotMediaPermission: function() {
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.success,
"Media granted");
},
/**
* Handles the user clicking the join room button.
*/
joinRoom: function() {
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.button,
"Join the conversation");
},
/**
* Handles the user clicking the leave room button.
*/
leaveRoom: function() {
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.button,
"Leave conversation");
},
/**
* Handles notification that two-way media has been achieved.
*/
mediaConnected: function() {
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.success,
"Media connected")
},
/**
* Handles recording link clicks.
*
* @param {sharedActions.RecordClick} actionData The data associated with
* the link.
*/
recordClick: function(actionData) {
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.linkClick,
actionData.linkInfo);
},
/**
* Handles notifications that the activeRoomStore has changed, updating
* the metrics for room state and mute state as necessary.
*/
_onActiveRoomStoreChange: function() {
var roomStore = this.activeRoomStore.getStoreState();
this._checkRoomState(roomStore.roomState, roomStore.failureReason);
this._checkMuteState("audio", roomStore.audioMuted);
this._checkMuteState("video", roomStore.videoMuted);
},
/**
* Handles checking of the room state to look for events we need to send.
*
* @param {String} roomState The new room state.
* @param {String} failureReason Optional, if the room is in the failure
* state, this should contain the reason for
* the failure.
*/
_checkRoomState: function(roomState, failureReason) {
if (this._storeState.roomState === roomState) {
return;
}
this._storeState.roomState = roomState;
if (roomState === ROOM_STATES.FAILED &&
failureReason === FAILURE_DETAILS.EXPIRED_OR_INVALID) {
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.pageLoad,
"Link expired or invalid");
}
if (roomState === ROOM_STATES.FULL) {
this._storeEvent(METRICS_GA_CATEGORY.general, METRICS_GA_ACTIONS.pageLoad,
"Room full");
}
},
/**
* Handles check of the mute state to look for events we need to send.
*
* @param {String} type The type of mute being adjusted, i.e. "audio" or
* "video".
* @param {Boolean} muted The new state of mute
*/
_checkMuteState: function(type, muted) {
var muteItem = type + "Muted";
if (this._storeState[muteItem] === muted) {
return;
}
this._storeState[muteItem] = muted;
var muteType = type === "audio" ? METRICS_GA_ACTIONS.audioMute : METRICS_GA_ACTIONS.faceMute;
var muteState = muted ? "mute" : "unmute";
this._storeEvent(METRICS_GA_CATEGORY.general, muteType, muteState);
}
});
return StandaloneMetricsStore;
})();