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.

middleware.js 5.1KB

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