您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

reducer.js 5.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // @flow
  2. import { APP_WILL_MOUNT } from '../base/app';
  3. import { getURLWithoutParamsNormalized } from '../base/connection';
  4. import { ReducerRegistry } from '../base/redux';
  5. import { PersistenceRegistry } from '../base/storage';
  6. import {
  7. _STORE_CURRENT_CONFERENCE,
  8. _UPDATE_CONFERENCE_DURATION
  9. } from './actionTypes';
  10. import { RECENT_LIST_ENABLED } from './featureFlag';
  11. const logger = require('jitsi-meet-logger').getLogger(__filename);
  12. /**
  13. * The default/initial redux state of the feature {@code recent-list}.
  14. *
  15. * @type {Array<Object>}
  16. */
  17. const DEFAULT_STATE = [];
  18. /**
  19. * The name of the {@code window.localStorage} item where recent rooms are
  20. * stored.
  21. *
  22. * @type {string}
  23. */
  24. const LEGACY_STORAGE_KEY = 'recentURLs';
  25. /**
  26. * The max size of the list.
  27. *
  28. * @type {number}
  29. */
  30. export const MAX_LIST_SIZE = 30;
  31. /**
  32. * The redux subtree of this feature.
  33. */
  34. const STORE_NAME = 'features/recent-list';
  35. /**
  36. * Sets up the persistence of the feature {@code recent-list}.
  37. */
  38. PersistenceRegistry.register(STORE_NAME);
  39. /**
  40. * Reduces redux actions for the purposes of the feature {@code recent-list}.
  41. */
  42. ReducerRegistry.register(
  43. STORE_NAME,
  44. (state = _getLegacyRecentRoomList(), action) => {
  45. if (RECENT_LIST_ENABLED) {
  46. switch (action.type) {
  47. case APP_WILL_MOUNT:
  48. return _appWillMount(state);
  49. case _STORE_CURRENT_CONFERENCE:
  50. return _storeCurrentConference(state, action);
  51. case _UPDATE_CONFERENCE_DURATION:
  52. return _updateConferenceDuration(state, action);
  53. default:
  54. return state;
  55. }
  56. } else {
  57. return state;
  58. }
  59. });
  60. /**
  61. * Reduces the redux action {@link APP_WILL_MOUNT}.
  62. *
  63. * @param {Object} state - The redux state of the feature {@code recent-list}.
  64. * @param {Action} action - The redux action {@code APP_WILL_MOUNT}.
  65. * @returns {Array<Object>} The next redux state of the feature
  66. * {@code recent-list}.
  67. */
  68. function _appWillMount(state) {
  69. // XXX APP_WILL_MOUNT is the earliest redux action of ours dispatched in the
  70. // store. For the purposes of legacy support, make sure that the
  71. // deserialized recent-list's state is in the format deemed current by the
  72. // current app revision.
  73. if (state && typeof state === 'object') {
  74. if (Array.isArray(state)) {
  75. return state;
  76. }
  77. // In an enterprise/internal build of Jitsi Meet for Android and iOS we
  78. // had recent-list's state as an object with property list.
  79. const { list } = state;
  80. if (Array.isArray(list) && list.length) {
  81. return list.slice();
  82. }
  83. }
  84. // In the weird case that we have previously persisted/serialized null.
  85. return DEFAULT_STATE;
  86. }
  87. /**
  88. * Retrieves the recent room list that was stored using the legacy way.
  89. *
  90. * @returns {Array<Object>}
  91. */
  92. function _getLegacyRecentRoomList(): Array<Object> {
  93. try {
  94. const str = window.localStorage.getItem(LEGACY_STORAGE_KEY);
  95. if (str) {
  96. return JSON.parse(str);
  97. }
  98. } catch (error) {
  99. logger.warn('Failed to parse legacy recent-room list!');
  100. }
  101. return [];
  102. }
  103. /**
  104. * Adds a new list entry to the redux store.
  105. *
  106. * @param {Object} state - The redux state of the feature {@code recent-list}.
  107. * @param {Object} action - The redux action.
  108. * @returns {Object}
  109. */
  110. function _storeCurrentConference(state, { locationURL }) {
  111. const conference = locationURL.href;
  112. // If the current conference is already in the list, we remove it to re-add
  113. // it to the top.
  114. const nextState
  115. = state.filter(e => !_urlStringEquals(e.conference, conference));
  116. // The list is a reverse-sorted (i.e. the newer elements are at the end).
  117. nextState.push({
  118. conference,
  119. date: Date.now(),
  120. duration: 0 // We don't have the duration yet!
  121. });
  122. // Ensure the list doesn't exceed a/the maximum size.
  123. nextState.splice(0, nextState.length - MAX_LIST_SIZE);
  124. return nextState;
  125. }
  126. /**
  127. * Updates the conference length when left.
  128. *
  129. * @param {Object} state - The redux state of the feature {@code recent-list}.
  130. * @param {Object} action - The redux action.
  131. * @returns {Object} The next redux state of the feature {@code recent-list}.
  132. */
  133. function _updateConferenceDuration(state, { locationURL }) {
  134. if (locationURL && locationURL.href && state.length) {
  135. const mostRecentIndex = state.length - 1;
  136. const mostRecent = state[mostRecentIndex];
  137. if (_urlStringEquals(mostRecent.conference, locationURL.href)) {
  138. // The last conference start was stored so we need to update the
  139. // length.
  140. const nextMostRecent = {
  141. ...mostRecent,
  142. duration: Date.now() - mostRecent.date
  143. };
  144. delete nextMostRecent.conferenceDuration; // legacy
  145. // Shallow copy to avoid in-place modification.
  146. const nextState = state.slice();
  147. nextState[mostRecentIndex] = nextMostRecent;
  148. return nextState;
  149. }
  150. }
  151. return state;
  152. }
  153. /**
  154. * Determines whether two specific URL {@code strings} are equal in the sense
  155. * that they identify one and the same conference resource (irrespective of
  156. * time) for the purposes of the feature {@code recent-list}.
  157. *
  158. * @param {string} a - The URL {@code string} to test for equality to {@code b}.
  159. * @param {string} b - The URL {@code string} to test for equality to {@code a}.
  160. * @returns {boolean}
  161. */
  162. function _urlStringEquals(a: string, b: string) {
  163. const aHref = getURLWithoutParamsNormalized(new URL(a));
  164. const bHref = getURLWithoutParamsNormalized(new URL(b));
  165. return aHref === bHref;
  166. }