Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

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