您最多选择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 { isRecentListEnabled } from './functions';
  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 (isRecentListEnabled()) {
  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. }
  57. return state;
  58. });
  59. /**
  60. * Reduces the redux action {@link APP_WILL_MOUNT}.
  61. *
  62. * @param {Object} state - The redux state of the feature {@code recent-list}.
  63. * @param {Action} action - The redux action {@code APP_WILL_MOUNT}.
  64. * @returns {Array<Object>} The next redux state of the feature
  65. * {@code recent-list}.
  66. */
  67. function _appWillMount(state) {
  68. // XXX APP_WILL_MOUNT is the earliest redux action of ours dispatched in the
  69. // store. For the purposes of legacy support, make sure that the
  70. // deserialized recent-list's state is in the format deemed current by the
  71. // current app revision.
  72. if (state && typeof state === 'object') {
  73. if (Array.isArray(state)) {
  74. return state;
  75. }
  76. // In an enterprise/internal build of Jitsi Meet for Android and iOS we
  77. // had recent-list's state as an object with property list.
  78. const { list } = state;
  79. if (Array.isArray(list) && list.length) {
  80. return list.slice();
  81. }
  82. }
  83. // In the weird case that we have previously persisted/serialized null.
  84. return DEFAULT_STATE;
  85. }
  86. /**
  87. * Retrieves the recent room list that was stored using the legacy way.
  88. *
  89. * @returns {Array<Object>}
  90. */
  91. function _getLegacyRecentRoomList(): Array<Object> {
  92. try {
  93. const str = window.localStorage.getItem(LEGACY_STORAGE_KEY);
  94. if (str) {
  95. return JSON.parse(str);
  96. }
  97. } catch (error) {
  98. logger.warn('Failed to parse legacy recent-room list!');
  99. }
  100. return [];
  101. }
  102. /**
  103. * Adds a new list entry to the redux store.
  104. *
  105. * @param {Object} state - The redux state of the feature {@code recent-list}.
  106. * @param {Object} action - The redux action.
  107. * @returns {Object}
  108. */
  109. function _storeCurrentConference(state, { locationURL }) {
  110. const conference = locationURL.href;
  111. // If the current conference is already in the list, we remove it to re-add
  112. // it to the top.
  113. const nextState
  114. = state.filter(e => !_urlStringEquals(e.conference, conference));
  115. // The list is a reverse-sorted (i.e. the newer elements are at the end).
  116. nextState.push({
  117. conference,
  118. date: Date.now(),
  119. duration: 0 // We don't have the duration yet!
  120. });
  121. // Ensure the list doesn't exceed a/the maximum size.
  122. nextState.splice(0, nextState.length - MAX_LIST_SIZE);
  123. return nextState;
  124. }
  125. /**
  126. * Updates the conference length when left.
  127. *
  128. * @param {Object} state - The redux state of the feature {@code recent-list}.
  129. * @param {Object} action - The redux action.
  130. * @returns {Object} The next redux state of the feature {@code recent-list}.
  131. */
  132. function _updateConferenceDuration(state, { locationURL }) {
  133. if (locationURL && locationURL.href && state.length) {
  134. const mostRecentIndex = state.length - 1;
  135. const mostRecent = state[mostRecentIndex];
  136. if (_urlStringEquals(mostRecent.conference, locationURL.href)) {
  137. // The last conference start was stored so we need to update the
  138. // length.
  139. const nextMostRecent = {
  140. ...mostRecent,
  141. duration: Date.now() - mostRecent.date
  142. };
  143. delete nextMostRecent.conferenceDuration; // legacy
  144. // Shallow copy to avoid in-place modification.
  145. const nextState = state.slice();
  146. nextState[mostRecentIndex] = nextMostRecent;
  147. return nextState;
  148. }
  149. }
  150. return state;
  151. }
  152. /**
  153. * Determines whether two specific URL {@code strings} are equal in the sense
  154. * that they identify one and the same conference resource (irrespective of
  155. * time) for the purposes of the feature {@code recent-list}.
  156. *
  157. * @param {string} a - The URL {@code string} to test for equality to {@code b}.
  158. * @param {string} b - The URL {@code string} to test for equality to {@code a}.
  159. * @returns {boolean}
  160. */
  161. function _urlStringEquals(a: string, b: string) {
  162. const aHref = getURLWithoutParamsNormalized(new URL(a));
  163. const bHref = getURLWithoutParamsNormalized(new URL(b));
  164. return aHref === bHref;
  165. }