Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

middleware.ts 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. import { NativeModules, Platform } from 'react-native';
  2. import { updateApplicationContext, watchEvents } from 'react-native-watch-connectivity';
  3. import { appNavigate } from '../../app/actions';
  4. import { IStore } from '../../app/types';
  5. import { APP_WILL_MOUNT } from '../../base/app/actionTypes';
  6. import { IStateful } from '../../base/app/types';
  7. import { CONFERENCE_JOINED } from '../../base/conference/actionTypes';
  8. import { getCurrentConferenceUrl } from '../../base/connection/functions';
  9. import { setAudioMuted } from '../../base/media/actions';
  10. import MiddlewareRegistry from '../../base/redux/MiddlewareRegistry';
  11. import StateListenerRegistry from '../../base/redux/StateListenerRegistry';
  12. import { toState } from '../../base/redux/functions';
  13. import { setConferenceTimestamp, setSessionId, setWatchReachable } from './actions';
  14. import { CMD_HANG_UP, CMD_JOIN_CONFERENCE, CMD_SET_MUTED, MAX_RECENT_URLS } from './constants';
  15. import logger from './logger';
  16. const { AppInfo } = NativeModules;
  17. const watchOSEnabled = Platform.OS === 'ios' && !AppInfo.isLiteSDK;
  18. // Handles the recent URLs state sent to the watch
  19. watchOSEnabled && StateListenerRegistry.register(
  20. /* selector */ state => state['features/recent-list'],
  21. /* listener */ (recentListState, { getState }) => {
  22. _updateApplicationContext(getState);
  23. });
  24. // Handles the mic muted state sent to the watch
  25. watchOSEnabled && StateListenerRegistry.register(
  26. /* selector */ state => _isAudioMuted(state),
  27. /* listener */ (isAudioMuted, { getState }) => {
  28. _updateApplicationContext(getState);
  29. });
  30. // Handles the conference URL state sent to the watch
  31. watchOSEnabled && StateListenerRegistry.register(
  32. /* selector */ state => getCurrentConferenceUrl(state),
  33. /* listener */ (currentUrl, { dispatch, getState }) => {
  34. dispatch(setSessionId());
  35. _updateApplicationContext(getState);
  36. });
  37. /**
  38. * Middleware that captures conference actions.
  39. *
  40. * @param {Store} store - The redux store.
  41. * @returns {Function}
  42. */
  43. watchOSEnabled && MiddlewareRegistry.register(store => next => action => {
  44. switch (action.type) {
  45. case APP_WILL_MOUNT:
  46. _appWillMount(store);
  47. break;
  48. case CONFERENCE_JOINED:
  49. store.dispatch(setConferenceTimestamp(new Date().getTime()));
  50. _updateApplicationContext(store.getState());
  51. break;
  52. }
  53. return next(action);
  54. });
  55. /**
  56. * Registers listeners to the react-native-watch-connectivity lib.
  57. *
  58. * @param {Store} store - The redux store.
  59. * @private
  60. * @returns {void}
  61. */
  62. function _appWillMount({ dispatch, getState }: IStore) {
  63. watchEvents.addListener('reachability', reachable => {
  64. dispatch(setWatchReachable(reachable));
  65. _updateApplicationContext(getState);
  66. });
  67. watchEvents.addListener('message', message => {
  68. const {
  69. command,
  70. sessionID
  71. } = message;
  72. const currentSessionID = _getSessionId(getState());
  73. if (!sessionID || sessionID !== currentSessionID) {
  74. logger.warn(
  75. `Ignoring outdated watch command: ${message.command}`
  76. + ` sessionID: ${sessionID} current session ID: ${currentSessionID}`);
  77. return;
  78. }
  79. switch (command) {
  80. case CMD_HANG_UP:
  81. if (typeof getCurrentConferenceUrl(getState()) !== 'undefined') {
  82. dispatch(appNavigate(undefined));
  83. }
  84. break;
  85. case CMD_JOIN_CONFERENCE: {
  86. const newConferenceURL: any = message.data;
  87. const oldConferenceURL = getCurrentConferenceUrl(getState());
  88. if (oldConferenceURL !== newConferenceURL) {
  89. dispatch(appNavigate(newConferenceURL));
  90. }
  91. break;
  92. }
  93. case CMD_SET_MUTED:
  94. dispatch(
  95. setAudioMuted(
  96. message.muted === 'true',
  97. /* ensureTrack */ true));
  98. break;
  99. }
  100. });
  101. }
  102. /**
  103. * Gets the current Apple Watch session's ID. A new session is started whenever the conference URL has changed. It is
  104. * used to filter out outdated commands which may arrive very later if the Apple Watch loses the connectivity.
  105. *
  106. * @param {Object|Function} stateful - Either the whole Redux state object or the Redux store's {@code getState} method.
  107. * @returns {number}
  108. * @private
  109. */
  110. function _getSessionId(stateful: IStateful) {
  111. const state = toState(stateful);
  112. return state['features/mobile/watchos'].sessionID;
  113. }
  114. /**
  115. * Gets the list of recent URLs to be passed over to the Watch app.
  116. *
  117. * @param {Object|Function} stateful - Either the whole Redux state object or the Redux store's {@code getState} method.
  118. * @returns {Array<Object>}
  119. * @private
  120. */
  121. function _getRecentUrls(stateful: IStateful) {
  122. const state = toState(stateful);
  123. const recentURLs = state['features/recent-list'];
  124. // Trim to MAX_RECENT_URLS and reverse the list
  125. const reversedList = recentURLs.slice(-MAX_RECENT_URLS);
  126. reversedList.reverse();
  127. return reversedList;
  128. }
  129. /**
  130. * Determines the audio muted state to be sent to the apple watch.
  131. *
  132. * @param {Object|Function} stateful - Either the whole Redux state object or the Redux store's {@code getState} method.
  133. * @returns {boolean}
  134. * @private
  135. */
  136. function _isAudioMuted(stateful: IStateful) {
  137. const state = toState(stateful);
  138. const { audio } = state['features/base/media'];
  139. return audio.muted;
  140. }
  141. /**
  142. * Sends the context to the watch os app. At the time of this writing it's the entire state of
  143. * the 'features/mobile/watchos' reducer.
  144. *
  145. * @param {Object|Function} stateful - Either the whole Redux state object or the Redux store's {@code getState} method.
  146. * @private
  147. * @returns {void}
  148. */
  149. function _updateApplicationContext(stateful: IStateful) {
  150. const state = toState(stateful);
  151. const { conferenceTimestamp, sessionID, watchReachable } = state['features/mobile/watchos'];
  152. if (!watchReachable) {
  153. return;
  154. }
  155. try {
  156. updateApplicationContext({
  157. conferenceTimestamp,
  158. conferenceURL: getCurrentConferenceUrl(state),
  159. micMuted: _isAudioMuted(state),
  160. recentURLs: _getRecentUrls(state),
  161. sessionID
  162. });
  163. } catch (error) {
  164. logger.error('Failed to stringify or send the context', error);
  165. }
  166. }