| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 | 
							- // @flow
 - import { APP_WILL_MOUNT } from '../base/app';
 - import { getURLWithoutParamsNormalized } from '../base/connection';
 - import { ReducerRegistry } from '../base/redux';
 - import { PersistenceRegistry } from '../base/storage';
 - 
 - import {
 -     _STORE_CURRENT_CONFERENCE,
 -     _UPDATE_CONFERENCE_DURATION,
 -     DELETE_RECENT_LIST_ENTRY
 - } from './actionTypes';
 - import { isRecentListEnabled } from './functions';
 - 
 - const logger = require('jitsi-meet-logger').getLogger(__filename);
 - 
 - /**
 -  * The default/initial redux state of the feature {@code recent-list}.
 -  *
 -  * @type {Array<Object>}
 -  */
 - const DEFAULT_STATE = [];
 - 
 - /**
 -  * The name of the {@code window.localStorage} item where recent rooms are
 -  * stored.
 -  *
 -  * @type {string}
 -  */
 - const LEGACY_STORAGE_KEY = 'recentURLs';
 - 
 - /**
 -  * The max size of the list.
 -  *
 -  * @type {number}
 -  */
 - export const MAX_LIST_SIZE = 30;
 - 
 - /**
 -  * The redux subtree of this feature.
 -  */
 - const STORE_NAME = 'features/recent-list';
 - 
 - /**
 -  * Sets up the persistence of the feature {@code recent-list}.
 -  */
 - PersistenceRegistry.register(STORE_NAME);
 - 
 - /**
 -  * Reduces redux actions for the purposes of the feature {@code recent-list}.
 -  */
 - ReducerRegistry.register(
 -     STORE_NAME,
 -     (state = _getLegacyRecentRoomList(), action) => {
 -         if (isRecentListEnabled()) {
 -             switch (action.type) {
 -             case APP_WILL_MOUNT:
 -                 return _appWillMount(state);
 -             case DELETE_RECENT_LIST_ENTRY:
 -                 return _deleteRecentListEntry(state, action.entryId);
 -             case _STORE_CURRENT_CONFERENCE:
 -                 return _storeCurrentConference(state, action);
 - 
 -             case _UPDATE_CONFERENCE_DURATION:
 -                 return _updateConferenceDuration(state, action);
 -             default:
 -                 return state;
 -             }
 -         }
 - 
 -         return state;
 -     });
 - 
 - /**
 -  * Deletes a recent list entry based on the url and date of the item.
 -  *
 -  * @param {Array<Object>} state - The Redux state.
 -  * @param {Object} entryId - The ID object of the entry.
 -  * @returns {Array<Object>}
 -  */
 - function _deleteRecentListEntry(
 -         state: Array<Object>, entryId: Object): Array<Object> {
 -     return state.filter(entry =>
 -         entry.conference !== entryId.url || entry.date !== entryId.date);
 - }
 - 
 - /**
 -  * Reduces the redux action {@link APP_WILL_MOUNT}.
 -  *
 -  * @param {Object} state - The redux state of the feature {@code recent-list}.
 -  * @param {Action} action - The redux action {@code APP_WILL_MOUNT}.
 -  * @returns {Array<Object>} The next redux state of the feature
 -  * {@code recent-list}.
 -  */
 - function _appWillMount(state) {
 -     // XXX APP_WILL_MOUNT is the earliest redux action of ours dispatched in the
 -     // store. For the purposes of legacy support, make sure that the
 -     // deserialized recent-list's state is in the format deemed current by the
 -     // current app revision.
 -     if (state && typeof state === 'object') {
 -         if (Array.isArray(state)) {
 -             return state;
 -         }
 - 
 -         // In an enterprise/internal build of Jitsi Meet for Android and iOS we
 -         // had recent-list's state as an object with property list.
 -         const { list } = state;
 - 
 -         if (Array.isArray(list) && list.length) {
 -             return list.slice();
 -         }
 -     }
 - 
 -     // In the weird case that we have previously persisted/serialized null.
 -     return DEFAULT_STATE;
 - }
 - 
 - /**
 -  * Retrieves the recent room list that was stored using the legacy way.
 -  *
 -  * @returns {Array<Object>}
 -  */
 - function _getLegacyRecentRoomList(): Array<Object> {
 -     try {
 -         const str = window.localStorage.getItem(LEGACY_STORAGE_KEY);
 - 
 -         if (str) {
 -             return JSON.parse(str);
 -         }
 -     } catch (error) {
 -         logger.warn('Failed to parse legacy recent-room list!');
 -     }
 - 
 -     return [];
 - }
 - 
 - /**
 -  * Adds a new list entry to the redux store.
 -  *
 -  * @param {Object} state - The redux state of the feature {@code recent-list}.
 -  * @param {Object} action - The redux action.
 -  * @returns {Object}
 -  */
 - function _storeCurrentConference(state, { locationURL }) {
 -     const conference = locationURL.href;
 - 
 -     // If the current conference is already in the list, we remove it to re-add
 -     // it to the top.
 -     const nextState
 -         = state.filter(e => !_urlStringEquals(e.conference, conference));
 - 
 -     // The list is a reverse-sorted (i.e. the newer elements are at the end).
 -     nextState.push({
 -         conference,
 -         date: Date.now(),
 -         duration: 0 // We don't have the duration yet!
 -     });
 - 
 -     // Ensure the list doesn't exceed a/the maximum size.
 -     nextState.splice(0, nextState.length - MAX_LIST_SIZE);
 - 
 -     return nextState;
 - }
 - 
 - /**
 -  * Updates the conference length when left.
 -  *
 -  * @param {Object} state - The redux state of the feature {@code recent-list}.
 -  * @param {Object} action - The redux action.
 -  * @returns {Object} The next redux state of the feature {@code recent-list}.
 -  */
 - function _updateConferenceDuration(state, { locationURL }) {
 -     if (locationURL && locationURL.href && state.length) {
 -         const mostRecentIndex = state.length - 1;
 -         const mostRecent = state[mostRecentIndex];
 - 
 -         if (_urlStringEquals(mostRecent.conference, locationURL.href)) {
 -             // The last conference start was stored so we need to update the
 -             // length.
 -             const nextMostRecent = {
 -                 ...mostRecent,
 -                 duration: Date.now() - mostRecent.date
 -             };
 - 
 -             delete nextMostRecent.conferenceDuration; // legacy
 - 
 -             // Shallow copy to avoid in-place modification.
 -             const nextState = state.slice();
 - 
 -             nextState[mostRecentIndex] = nextMostRecent;
 - 
 -             return nextState;
 -         }
 -     }
 - 
 -     return state;
 - }
 - 
 - /**
 -  * Determines whether two specific URL {@code strings} are equal in the sense
 -  * that they identify one and the same conference resource (irrespective of
 -  * time) for the purposes of the feature {@code recent-list}.
 -  *
 -  * @param {string} a - The URL {@code string} to test for equality to {@code b}.
 -  * @param {string} b - The URL {@code string} to test for equality to {@code a}.
 -  * @returns {boolean}
 -  */
 - function _urlStringEquals(a: string, b: string) {
 -     const aHref = getURLWithoutParamsNormalized(new URL(a));
 -     const bHref = getURLWithoutParamsNormalized(new URL(b));
 - 
 -     return aHref === bHref;
 - }
 
 
  |