You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

reducer.js 5.5KB

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