You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

analytics.js 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /* global JitsiMeetJS, config, APP */
  2. /**
  3. * Load the integration of a third-party analytics API such as Google
  4. * Analytics. Since we cannot guarantee the quality of the third-party service
  5. * (e.g. their server may take noticeably long time to respond), it is in our
  6. * best interest (in the sense that the integration of the analytics API is
  7. * important to us but not enough to allow it to prevent people from joining
  8. * a conference) to download the API asynchronously. Additionally, Google
  9. * Analytics will download its implementation asynchronously anyway so it makes
  10. * sense to append the loading on our side rather than prepend it.
  11. * @param {string} url the url to be loaded
  12. * @returns {Promise} resolved with no arguments when the script is loaded and
  13. * rejected with the error from JitsiMeetJS.ScriptUtil.loadScript method
  14. */
  15. function loadScript(url) {
  16. return new Promise((resolve, reject) =>
  17. JitsiMeetJS.util.ScriptUtil.loadScript(
  18. url,
  19. /* async */ true,
  20. /* prepend */ false,
  21. /* relativeURL */ false,
  22. /* loadCallback */ () => resolve(),
  23. /* errorCallback */ error => reject(error)));
  24. }
  25. /**
  26. * Handles the initialization of analytics.
  27. */
  28. class Analytics {
  29. constructor() {
  30. this._scriptURLs = Array.isArray(config.analyticsScriptUrls)
  31. ? config.analyticsScriptUrls : [];
  32. this._enabled = !!this._scriptURLs.length
  33. && !config.disableThirdPartyRequests;
  34. window.analyticsHandlers = [];
  35. const machineId = JitsiMeetJS.getMachineId();
  36. this._handlerConstructorOptions = {
  37. product: "lib-jitsi-meet",
  38. version: JitsiMeetJS.version,
  39. session: machineId,
  40. user: "uid-" + machineId
  41. };
  42. }
  43. /**
  44. * Returns whether analytics is enabled or not.
  45. * @returns {boolean} whether analytics is enabled or not.
  46. */
  47. isEnabled() {
  48. return this._enabled;
  49. }
  50. /**
  51. * Tries to load the scripts for the analytics handlers.
  52. * @returns {Promise} resolves with the handlers that have been
  53. * successfully loaded and rejects if there are no handlers loaded or the
  54. * analytics is disabled.
  55. */
  56. _loadHandlers() {
  57. if(!this.isEnabled()) {
  58. return Promise.reject(new Error("Analytics is disabled!"));
  59. }
  60. let handlersPromises = [];
  61. this._scriptURLs.forEach(url =>
  62. handlersPromises.push(
  63. loadScript(url).then(
  64. () => {
  65. return {type: "success"};
  66. },
  67. error => {
  68. return {type: "error", error, url};
  69. }))
  70. );
  71. return new Promise((resolve, reject) =>
  72. {
  73. Promise.all(handlersPromises).then(values => {
  74. values.forEach(el => {
  75. if(el.type === "error") {
  76. console.log("Failed to load " + el.url);
  77. console.error(el.error);
  78. }
  79. });
  80. if(window.analyticsHandlers.length === 0) {
  81. reject(new Error("No analytics handlers available"));
  82. } else {
  83. let handlerInstances = [];
  84. window.analyticsHandlers.forEach(
  85. Handler => handlerInstances.push(
  86. new Handler(this._handlerConstructorOptions)));
  87. resolve(handlerInstances);
  88. }
  89. });
  90. });
  91. }
  92. /**
  93. * Loads the analytics scripts and inits JitsiMeetJS.analytics by setting
  94. * permanent properties and setting the handlers from the loaded scripts.
  95. * NOTE: Has to be used after JitsiMeetJS.init. Otherwise analytics will be
  96. * null.
  97. */
  98. init() {
  99. const { analytics } = JitsiMeetJS;
  100. if (!this.isEnabled() || !analytics)
  101. return;
  102. this._loadHandlers().then(
  103. handlers => {
  104. const permanentProperties = {
  105. roomName: APP.conference.roomName,
  106. userAgent: navigator.userAgent
  107. };
  108. const { group, server } = APP.store.getState()['features/jwt'];
  109. if (server) {
  110. permanentProperties.server = server;
  111. }
  112. if (group) {
  113. permanentProperties.group = group;
  114. }
  115. // optionally include local deployment information based on
  116. // the contents of window.config.deploymentInfo
  117. if (config.deploymentInfo) {
  118. for (let key in config.deploymentInfo) {
  119. if (config.deploymentInfo.hasOwnProperty(key)) {
  120. permanentProperties[key]
  121. = config.deploymentInfo[key];
  122. }
  123. }
  124. }
  125. analytics.addPermanentProperties(permanentProperties);
  126. analytics.setAnalyticsHandlers(handlers);
  127. },
  128. error => analytics.dispose() && console.error(error));
  129. }
  130. }
  131. export default new Analytics();