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

reducer.js 6.2KB

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