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;
- }
|