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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. // @flow
  2. import {
  3. CONFERENCE_JOINED,
  4. CONFERENCE_WILL_LEAVE,
  5. SET_ROOM
  6. } from '../base/conference';
  7. import { SET_CONFIG } from '../base/config';
  8. import { MiddlewareRegistry } from '../base/redux';
  9. import {
  10. getLocalAudioTrack,
  11. getLocalVideoTrack,
  12. TRACK_ADDED,
  13. TRACK_REMOVED,
  14. TRACK_UPDATED
  15. } from '../base/tracks';
  16. import { UPDATE_LOCAL_TRACKS_DURATION } from './actionTypes';
  17. import { createLocalTracksDurationEvent } from './AnalyticsEvents';
  18. import { initAnalytics, resetAnalytics, sendAnalytics } from './functions';
  19. /**
  20. * Calculates the duration of the local tracks.
  21. *
  22. * @param {Object} state - The redux state.
  23. * @returns {Object} - The local tracks duration.
  24. */
  25. function calculateLocalTrackDuration(state) {
  26. const now = Date.now();
  27. const { localTracksDuration } = state['features/analytics'];
  28. const { conference } = state['features/base/conference'];
  29. const { audio, video } = localTracksDuration;
  30. const { camera, desktop } = video;
  31. const tracks = state['features/base/tracks'];
  32. const audioTrack = getLocalAudioTrack(tracks);
  33. const videoTrack = getLocalVideoTrack(tracks);
  34. const newDuration = { ...localTracksDuration };
  35. if (!audioTrack || audioTrack.muted || !conference) {
  36. newDuration.audio = {
  37. startedTime: -1,
  38. value: audio.value + (audio.startedTime === -1 ? 0 : now - audio.startedTime)
  39. };
  40. } else if (audio.startedTime === -1) {
  41. newDuration.audio.startedTime = now;
  42. }
  43. if (!videoTrack || videoTrack.muted || !conference) {
  44. newDuration.video = {
  45. camera: {
  46. startedTime: -1,
  47. value: camera.value + (camera.startedTime === -1 ? 0 : now - camera.startedTime)
  48. },
  49. desktop: {
  50. startedTime: -1,
  51. value: desktop.value + (desktop.startedTime === -1 ? 0 : now - desktop.startedTime)
  52. }
  53. };
  54. } else {
  55. const { videoType } = videoTrack;
  56. if (video[videoType].startedTime === -1) {
  57. newDuration.video[videoType].startedTime = now;
  58. }
  59. }
  60. return {
  61. ...localTracksDuration,
  62. ...newDuration
  63. };
  64. }
  65. /**
  66. * Middleware which intercepts config actions to handle evaluating analytics
  67. * config based on the config stored in the store.
  68. *
  69. * @param {Store} store - The redux store.
  70. * @returns {Function}
  71. */
  72. MiddlewareRegistry.register(store => next => action => {
  73. if (action.type === SET_CONFIG) {
  74. if (navigator.product === 'ReactNative') {
  75. // Reseting the analytics is currently not needed for web because
  76. // the user will be redirected to another page and new instance of
  77. // Analytics will be created and initialized.
  78. resetAnalytics();
  79. }
  80. }
  81. const result = next(action);
  82. switch (action.type) {
  83. case CONFERENCE_JOINED: {
  84. const { dispatch, getState } = store;
  85. const state = getState();
  86. dispatch({
  87. type: UPDATE_LOCAL_TRACKS_DURATION,
  88. localTracksDuration: {
  89. ...calculateLocalTrackDuration(state),
  90. conference: {
  91. startedTime: Date.now(),
  92. value: 0
  93. }
  94. }
  95. });
  96. break;
  97. }
  98. case CONFERENCE_WILL_LEAVE: {
  99. const { dispatch, getState } = store;
  100. const state = getState();
  101. const { localTracksDuration } = state['features/analytics'];
  102. const newLocalTracksDuration = {
  103. ...calculateLocalTrackDuration(state),
  104. conference: {
  105. startedTime: -1,
  106. value: Date.now() - localTracksDuration.conference.startedTime
  107. }
  108. };
  109. sendAnalytics(createLocalTracksDurationEvent(newLocalTracksDuration));
  110. dispatch({
  111. type: UPDATE_LOCAL_TRACKS_DURATION,
  112. localTracksDuration: newLocalTracksDuration
  113. });
  114. break;
  115. }
  116. case SET_ROOM: {
  117. initAnalytics(store);
  118. break;
  119. }
  120. case TRACK_ADDED:
  121. case TRACK_REMOVED:
  122. case TRACK_UPDATED: {
  123. const { dispatch, getState } = store;
  124. const state = getState();
  125. const { localTracksDuration } = state['features/analytics'];
  126. if (localTracksDuration.conference.startedTime === -1) {
  127. // We don't want to track the media duration if the conference is not joined yet because otherwise we won't
  128. // be able to compare them with the conference duration (from conference join to conference will leave).
  129. break;
  130. }
  131. dispatch({
  132. type: UPDATE_LOCAL_TRACKS_DURATION,
  133. localTracksDuration: {
  134. ...localTracksDuration,
  135. ...calculateLocalTrackDuration(state)
  136. }
  137. });
  138. break;
  139. }
  140. }
  141. return result;
  142. });