From d7dddb250950a833fd6870c5511209a67a4e94e2 Mon Sep 17 00:00:00 2001 From: Lyubo Marinov Date: Fri, 2 Feb 2018 13:35:49 -0600 Subject: [PATCH] Introduce base/storage to represent the Web Storage API and persistence-related customizations --- react/features/app/components/AbstractApp.js | 9 ++--- .../native/polyfills-browser.js | 22 ++++--------- react/features/base/profile/reducer.js | 8 +++-- react/features/base/redux/index.js | 3 -- react/features/base/redux/readme.md | 30 ----------------- .../PersistenceRegistry.js} | 6 ++-- react/features/base/storage/README.md | 33 +++++++++++++++++++ react/features/base/storage/_.native.js | 1 + react/features/base/storage/_.web.js | 0 react/features/base/storage/index.js | 4 +++ .../base/{redux => storage}/middleware.js | 18 +++++----- .../native/Storage.js | 0 react/features/base/storage/native/index.js | 1 + .../base/storage/native/polyfills-browser.js | 19 +++++++++++ .../components/AbstractRecentList.js | 2 +- react/features/recent-list/reducer.js | 9 ++--- 16 files changed, 93 insertions(+), 72 deletions(-) delete mode 100644 react/features/base/redux/readme.md rename react/features/base/{redux/PersistencyRegistry.js => storage/PersistenceRegistry.js} (97%) create mode 100644 react/features/base/storage/README.md create mode 100644 react/features/base/storage/_.native.js create mode 100644 react/features/base/storage/_.web.js create mode 100644 react/features/base/storage/index.js rename react/features/base/{redux => storage}/middleware.js (51%) rename react/features/base/{lib-jitsi-meet => storage}/native/Storage.js (100%) create mode 100644 react/features/base/storage/native/index.js create mode 100644 react/features/base/storage/native/polyfills-browser.js diff --git a/react/features/app/components/AbstractApp.js b/react/features/app/components/AbstractApp.js index d29ef1992..d2c741606 100644 --- a/react/features/app/components/AbstractApp.js +++ b/react/features/app/components/AbstractApp.js @@ -14,11 +14,8 @@ import { } from '../../base/participants'; import { getProfile } from '../../base/profile'; import { Fragment, RouteRegistry } from '../../base/react'; -import { - MiddlewareRegistry, - PersistencyRegistry, - ReducerRegistry -} from '../../base/redux'; +import { MiddlewareRegistry, ReducerRegistry } from '../../base/redux'; +import { PersistenceRegistry } from '../../base/storage'; import { toURLString } from '../../base/util'; import { OverlayContainer } from '../../overlay'; import { BlankPage } from '../../welcome'; @@ -349,7 +346,7 @@ export class AbstractApp extends Component { return ( createStore( reducer, - PersistencyRegistry.getPersistedState(), + PersistenceRegistry.getPersistedState(), middleware)); } diff --git a/react/features/base/lib-jitsi-meet/native/polyfills-browser.js b/react/features/base/lib-jitsi-meet/native/polyfills-browser.js index bd7ca0ba8..24089b1c4 100644 --- a/react/features/base/lib-jitsi-meet/native/polyfills-browser.js +++ b/react/features/base/lib-jitsi-meet/native/polyfills-browser.js @@ -4,7 +4,13 @@ import 'url-polyfill'; // Polyfill for URL constructor import { Platform } from '../../react'; -import Storage from './Storage'; +// XXX The library lib-jitsi-meet utilizes window.localStorage at the time of +// this writing and, consequently, the browser-related polyfills implemented +// here by the feature base/lib-jitsi-meet for the purposes of the library +// lib-jitsi-meet are incomplete without the Web Storage API! Should the library +// lib-jitsi-meet (and its dependencies) stop utilizing window.localStorage, +// the following import may be removed: +import '../../storage'; /** * Gets the first common prototype of two specified Objects (treating the @@ -271,11 +277,6 @@ function _visitNode(node, callback) { global.document = document; } - // localStorage - if (typeof global.localStorage === 'undefined') { - global.localStorage = new Storage('@jitsi-meet/'); - } - // location if (typeof global.location === 'undefined') { global.location = { @@ -332,15 +333,6 @@ function _visitNode(node, callback) { navigator.userAgent = userAgent; } - // sessionStorage - // - // Required by: - // - herment - // - Strophe - if (typeof global.sessionStorage === 'undefined') { - global.sessionStorage = new Storage(); - } - // WebRTC require('./polyfills-webrtc'); require('react-native-callstats/csio-polyfill'); diff --git a/react/features/base/profile/reducer.js b/react/features/base/profile/reducer.js index d0b3489b5..d43bce1d5 100644 --- a/react/features/base/profile/reducer.js +++ b/react/features/base/profile/reducer.js @@ -1,6 +1,7 @@ // @flow -import { PersistencyRegistry, ReducerRegistry } from '../redux'; +import { ReducerRegistry } from '../redux'; +import { PersistenceRegistry } from '../storage'; import { PROFILE_UPDATED } from './actionTypes'; @@ -10,7 +11,10 @@ const DEFAULT_STATE = { const STORE_NAME = 'features/base/profile'; -PersistencyRegistry.register(STORE_NAME, { +/** + * Sets up the persistence of the feature base/profile. + */ +PersistenceRegistry.register(STORE_NAME, { profile: true }); diff --git a/react/features/base/redux/index.js b/react/features/base/redux/index.js index f28de691b..9658a8706 100644 --- a/react/features/base/redux/index.js +++ b/react/features/base/redux/index.js @@ -1,6 +1,3 @@ export * from './functions'; export { default as MiddlewareRegistry } from './MiddlewareRegistry'; -export { default as PersistencyRegistry } from './PersistencyRegistry'; export { default as ReducerRegistry } from './ReducerRegistry'; - -import './middleware'; diff --git a/react/features/base/redux/readme.md b/react/features/base/redux/readme.md deleted file mode 100644 index a7b7a1b63..000000000 --- a/react/features/base/redux/readme.md +++ /dev/null @@ -1,30 +0,0 @@ -Jitsi Meet - redux state persistency -==================================== -Jitsi Meet has a persistency layer that persists a subtree (or specific subtrees) into window.localStorage (on web) or -AsyncStorage (on mobile). - -Usage -===== -If a subtree of the redux store should be persisted (e.g. ``'features/base/profile'``), then persistency for that -subtree should be requested by registering the subtree (and related config) into PersistencyRegistry. - -E.g. to register the field ``profile`` of the Redux subtree ``'features/base/profile'`` to be persisted, use: - -```JavaScript -PersistencyRegistry.register('features/base/profile', { - profile: true -}); -``` - -in the ``reducer.js`` of the ``profile`` feature. - -When it's done, Jitsi Meet will automatically persist these subtrees/fields and rehidrate them on startup. - -Throttling -========== -To avoid too frequent write operations in the storage, we utilise throttling in the persistency layer, meaning that the storage -gets persisted only once in every 2 seconds, even if multiple redux state changes occur during this period. This throttling timeout -can be configured in -``` -react/features/base/redux/middleware.js#PERSIST_DELAY -``` diff --git a/react/features/base/redux/PersistencyRegistry.js b/react/features/base/storage/PersistenceRegistry.js similarity index 97% rename from react/features/base/redux/PersistencyRegistry.js rename to react/features/base/storage/PersistenceRegistry.js index a7af9eddf..8a5fefc64 100644 --- a/react/features/base/redux/PersistencyRegistry.js +++ b/react/features/base/storage/PersistenceRegistry.js @@ -19,13 +19,13 @@ declare type PersistencyConfigMap = { [name: string]: Object }; * A registry to allow features to register their redux store subtree to be * persisted and also handles the persistency calls too. */ -class PersistencyRegistry { +class PersistenceRegistry { _checksum: string; _elements: PersistencyConfigMap; /** - * Initializes a new {@ code PersistencyRegistry} instance. + * Initializes a new {@ code PersistenceRegistry} instance. */ constructor() { this._elements = {}; @@ -166,4 +166,4 @@ class PersistencyRegistry { } } -export default new PersistencyRegistry(); +export default new PersistenceRegistry(); diff --git a/react/features/base/storage/README.md b/react/features/base/storage/README.md new file mode 100644 index 000000000..6d1d4209b --- /dev/null +++ b/react/features/base/storage/README.md @@ -0,0 +1,33 @@ +Jitsi Meet - redux state persistence +==================================== +Jitsi Meet has a persistence layer that persists specific subtrees of the redux +store/state into window.localStorage (on Web) or AsyncStorage (on mobile). + +Usage +===== +If a subtree of the redux store should be persisted (e.g. +`'features/base/profile'`), then persistence for that subtree should be +requested by registering the subtree with `PersistenceRegistry`. + +For example, to register the field `profile` of the redux subtree +`'features/base/profile'` to be persisted, use: +```javascript +PersistenceRegistry.register('features/base/profile', { + profile: true +}); +``` + +in the `reducer.js` of the `base/profile` feature. + +When it's done, Jitsi Meet will automatically persist these subtrees and +rehydrate them on startup. + +Throttling +========== +To avoid too frequent write operations in the storage, we utilize throttling in +the persistence layer, meaning that the storage gets persisted only once every 2 +seconds, even if multiple redux state changes occur during this period. The +throttling timeout can be configured in +``` +react/features/base/storage/middleware.js#PERSIST_STATE_DELAY +``` diff --git a/react/features/base/storage/_.native.js b/react/features/base/storage/_.native.js new file mode 100644 index 000000000..738c4d2b8 --- /dev/null +++ b/react/features/base/storage/_.native.js @@ -0,0 +1 @@ +export * from './native'; diff --git a/react/features/base/storage/_.web.js b/react/features/base/storage/_.web.js new file mode 100644 index 000000000..e69de29bb diff --git a/react/features/base/storage/index.js b/react/features/base/storage/index.js new file mode 100644 index 000000000..a35697006 --- /dev/null +++ b/react/features/base/storage/index.js @@ -0,0 +1,4 @@ +export * from './_'; +export { default as PersistenceRegistry } from './PersistenceRegistry'; + +import './middleware'; diff --git a/react/features/base/redux/middleware.js b/react/features/base/storage/middleware.js similarity index 51% rename from react/features/base/redux/middleware.js rename to react/features/base/storage/middleware.js index 4548343c5..41b0ed536 100644 --- a/react/features/base/redux/middleware.js +++ b/react/features/base/storage/middleware.js @@ -2,13 +2,13 @@ import _ from 'lodash'; -import { toState } from './functions'; -import MiddlewareRegistry from './MiddlewareRegistry'; -import PersistencyRegistry from './PersistencyRegistry'; +import { MiddlewareRegistry, toState } from '../redux'; + +import PersistenceRegistry from './PersistenceRegistry'; /** - * The delay that passes between the last state change and the persisting of - * that state in the storage. + * The delay in milliseconds that passes between the last state change and the + * persisting of that state in the storage. */ const PERSIST_STATE_DELAY = 2000; @@ -17,21 +17,23 @@ const PERSIST_STATE_DELAY = 2000; */ const throttledPersistState = _.throttle( - state => PersistencyRegistry.persistState(state), + state => PersistenceRegistry.persistState(state), PERSIST_STATE_DELAY); /** * A master MiddleWare to selectively persist state. Please use the - * {@link persisterconfig.json} to set which subtrees of the Redux state should + * {@link persisterconfig.json} to set which subtrees of the redux state should * be persisted. * * @param {Store} store - The redux store. * @returns {Function} */ MiddlewareRegistry.register(store => next => action => { + const oldState = toState(store); const result = next(action); + const newState = toState(store); - throttledPersistState(toState(store)); + oldState === newState || throttledPersistState(newState); return result; }); diff --git a/react/features/base/lib-jitsi-meet/native/Storage.js b/react/features/base/storage/native/Storage.js similarity index 100% rename from react/features/base/lib-jitsi-meet/native/Storage.js rename to react/features/base/storage/native/Storage.js diff --git a/react/features/base/storage/native/index.js b/react/features/base/storage/native/index.js new file mode 100644 index 000000000..8caf54951 --- /dev/null +++ b/react/features/base/storage/native/index.js @@ -0,0 +1 @@ +import './polyfills-browser'; diff --git a/react/features/base/storage/native/polyfills-browser.js b/react/features/base/storage/native/polyfills-browser.js new file mode 100644 index 000000000..1518b76b0 --- /dev/null +++ b/react/features/base/storage/native/polyfills-browser.js @@ -0,0 +1,19 @@ +import Storage from './Storage'; + +(global => { + + // localStorage + if (typeof global.localStorage === 'undefined') { + global.localStorage = new Storage('@jitsi-meet/'); + } + + // sessionStorage + // + // Required by: + // - herment + // - Strophe + if (typeof global.sessionStorage === 'undefined') { + global.sessionStorage = new Storage(); + } + +})(global || window || this); // eslint-disable-line no-invalid-this diff --git a/react/features/recent-list/components/AbstractRecentList.js b/react/features/recent-list/components/AbstractRecentList.js index 968b6316a..2d7d46f5e 100644 --- a/react/features/recent-list/components/AbstractRecentList.js +++ b/react/features/recent-list/components/AbstractRecentList.js @@ -46,7 +46,7 @@ export default class AbstractRecentList extends Component { } /** - * Maps Redux state to component props. + * Maps redux state to component props. * * @param {Object} state - The redux state. * @returns {{ diff --git a/react/features/recent-list/reducer.js b/react/features/recent-list/reducer.js index 4167d42c3..6c4c48492 100644 --- a/react/features/recent-list/reducer.js +++ b/react/features/recent-list/reducer.js @@ -1,6 +1,7 @@ // @flow -import { PersistencyRegistry, ReducerRegistry } from '../base/redux'; +import { ReducerRegistry } from '../base/redux'; +import { PersistenceRegistry } from '../base/storage'; import { STORE_CURRENT_CONFERENCE, @@ -30,14 +31,14 @@ export const MAX_LIST_SIZE = 30; const STORE_NAME = 'features/recent-list'; /** - * Registers the redux store subtree of this feature for persistency. + * Sets up the persistence of the feature recent-list. */ -PersistencyRegistry.register(STORE_NAME, { +PersistenceRegistry.register(STORE_NAME, { list: true }); /** - * Reduces the redux actions of the feature features/recent-list. + * Reduces the redux actions of the feature recent-list. */ ReducerRegistry.register( STORE_NAME,