選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

middleware.ts 6.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. import { IReduxState } from '../app/types';
  2. import {
  3. CONFERENCE_JOINED,
  4. CONFERENCE_WILL_LEAVE,
  5. SET_ROOM
  6. } from '../base/conference/actionTypes';
  7. import { SET_CONFIG } from '../base/config/actionTypes';
  8. import { SET_NETWORK_INFO } from '../base/net-info/actionTypes';
  9. import MiddlewareRegistry from '../base/redux/MiddlewareRegistry';
  10. import {
  11. TRACK_ADDED,
  12. TRACK_REMOVED,
  13. TRACK_UPDATED
  14. } from '../base/tracks/actionTypes';
  15. import {
  16. getLocalAudioTrack,
  17. getLocalVideoTrack
  18. } from '../base/tracks/functions';
  19. import { SET_LOBBY_VISIBILITY } from '../lobby/actionTypes';
  20. import { getIsLobbyVisible } from '../lobby/functions';
  21. import { I_AM_VISITOR_MODE } from '../visitors/actionTypes';
  22. import { iAmVisitor } from '../visitors/functions';
  23. import { createLocalTracksDurationEvent, createNetworkInfoEvent } from './AnalyticsEvents';
  24. import { SET_INITIALIZED, UPDATE_LOCAL_TRACKS_DURATION } from './actionTypes';
  25. import { setPermanentProperty } from './actions';
  26. import { createHandlers, initAnalytics, resetAnalytics, sendAnalytics } from './functions';
  27. /**
  28. * Calculates the duration of the local tracks.
  29. *
  30. * @param {Object} state - The redux state.
  31. * @returns {Object} - The local tracks duration.
  32. */
  33. function calculateLocalTrackDuration(state: IReduxState) {
  34. const now = Date.now();
  35. const { localTracksDuration } = state['features/analytics'];
  36. const { conference } = state['features/base/conference'];
  37. const { audio, video } = localTracksDuration;
  38. const { camera, desktop } = video;
  39. const tracks = state['features/base/tracks'];
  40. const audioTrack = getLocalAudioTrack(tracks);
  41. const videoTrack = getLocalVideoTrack(tracks);
  42. const newDuration = { ...localTracksDuration };
  43. if (!audioTrack || audioTrack.muted || !conference) {
  44. newDuration.audio = {
  45. startedTime: -1,
  46. value: audio.value + (audio.startedTime === -1 ? 0 : now - audio.startedTime)
  47. };
  48. } else if (audio.startedTime === -1) {
  49. newDuration.audio.startedTime = now;
  50. }
  51. if (!videoTrack || videoTrack.muted || !conference) {
  52. newDuration.video = {
  53. camera: {
  54. startedTime: -1,
  55. value: camera.value + (camera.startedTime === -1 ? 0 : now - camera.startedTime)
  56. },
  57. desktop: {
  58. startedTime: -1,
  59. value: desktop.value + (desktop.startedTime === -1 ? 0 : now - desktop.startedTime)
  60. }
  61. };
  62. } else {
  63. const { videoType } = videoTrack;
  64. if (video[videoType as keyof typeof video].startedTime === -1) {
  65. newDuration.video[videoType as keyof typeof video].startedTime = now;
  66. }
  67. }
  68. return {
  69. ...localTracksDuration,
  70. ...newDuration
  71. };
  72. }
  73. /**
  74. * Middleware which intercepts config actions to handle evaluating analytics
  75. * config based on the config stored in the store.
  76. *
  77. * @param {Store} store - The redux store.
  78. * @returns {Function}
  79. */
  80. MiddlewareRegistry.register(store => next => action => {
  81. switch (action.type) {
  82. case I_AM_VISITOR_MODE: {
  83. const oldIAmVisitor = iAmVisitor(store.getState());
  84. const result = next(action);
  85. const newIAmVisitor = iAmVisitor(store.getState());
  86. store.dispatch(setPermanentProperty({
  87. isVisitor: newIAmVisitor,
  88. isPromotedFromVisitor: oldIAmVisitor && !newIAmVisitor
  89. }));
  90. return result;
  91. }
  92. case SET_CONFIG:
  93. if (navigator.product === 'ReactNative') {
  94. // Resetting the analytics is currently not needed for web because
  95. // the user will be redirected to another page and new instance of
  96. // Analytics will be created and initialized.
  97. resetAnalytics();
  98. const { dispatch } = store;
  99. dispatch({
  100. type: SET_INITIALIZED,
  101. value: false
  102. });
  103. }
  104. break;
  105. case SET_ROOM: {
  106. // createHandlers is called before the SET_ROOM action is executed in order for Amplitude to initialize before
  107. // the deeplinking logic is executed (after the SET_ROOM action) so that the Amplitude device id is available
  108. // if needed.
  109. const createHandlersPromise = createHandlers(store);
  110. const result = next(action);
  111. createHandlersPromise.then(handlers => {
  112. if (initAnalytics(store, handlers)) {
  113. store.dispatch({
  114. type: SET_INITIALIZED,
  115. value: true
  116. });
  117. }
  118. });
  119. return result;
  120. }
  121. }
  122. const result = next(action);
  123. switch (action.type) {
  124. case CONFERENCE_JOINED: {
  125. const { dispatch, getState } = store;
  126. const state = getState();
  127. dispatch({
  128. type: UPDATE_LOCAL_TRACKS_DURATION,
  129. localTracksDuration: {
  130. ...calculateLocalTrackDuration(state),
  131. conference: {
  132. startedTime: Date.now(),
  133. value: 0
  134. }
  135. }
  136. });
  137. break;
  138. }
  139. case CONFERENCE_WILL_LEAVE: {
  140. const { dispatch, getState } = store;
  141. const state = getState();
  142. const { localTracksDuration } = state['features/analytics'];
  143. const newLocalTracksDuration = {
  144. ...calculateLocalTrackDuration(state),
  145. conference: {
  146. startedTime: -1,
  147. value: Date.now() - localTracksDuration.conference.startedTime
  148. }
  149. };
  150. sendAnalytics(createLocalTracksDurationEvent(newLocalTracksDuration));
  151. dispatch({
  152. type: UPDATE_LOCAL_TRACKS_DURATION,
  153. localTracksDuration: newLocalTracksDuration
  154. });
  155. break;
  156. }
  157. case SET_LOBBY_VISIBILITY:
  158. if (getIsLobbyVisible(store.getState())) {
  159. store.dispatch(setPermanentProperty({
  160. wasLobbyVisible: true
  161. }));
  162. }
  163. break;
  164. case SET_NETWORK_INFO:
  165. sendAnalytics(
  166. createNetworkInfoEvent({
  167. isOnline: action.isOnline,
  168. details: action.details,
  169. networkType: action.networkType
  170. }));
  171. break;
  172. case TRACK_ADDED:
  173. case TRACK_REMOVED:
  174. case TRACK_UPDATED: {
  175. const { dispatch, getState } = store;
  176. const state = getState();
  177. const { localTracksDuration } = state['features/analytics'];
  178. if (localTracksDuration.conference.startedTime === -1) {
  179. // We don't want to track the media duration if the conference is not joined yet because otherwise we won't
  180. // be able to compare them with the conference duration (from conference join to conference will leave).
  181. break;
  182. }
  183. dispatch({
  184. type: UPDATE_LOCAL_TRACKS_DURATION,
  185. localTracksDuration: {
  186. ...localTracksDuration,
  187. ...calculateLocalTrackDuration(state)
  188. }
  189. });
  190. break;
  191. }
  192. }
  193. return result;
  194. });