Introduce base/storage to represent the Web Storage API and persistence-related customizations

This commit is contained in:
Lyubo Marinov 2018-02-02 13:35:49 -06:00
parent 83243d5980
commit d7dddb2509
16 changed files with 93 additions and 72 deletions

View File

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

View File

@ -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');

View File

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

View File

@ -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';

View File

@ -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
```

View File

@ -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();

View File

@ -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
```

View File

@ -0,0 +1 @@
export * from './native';

View File

View File

@ -0,0 +1,4 @@
export * from './_';
export { default as PersistenceRegistry } from './PersistenceRegistry';
import './middleware';

View File

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

View File

@ -0,0 +1 @@
import './polyfills-browser';

View File

@ -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

View File

@ -46,7 +46,7 @@ export default class AbstractRecentList extends Component<Props> {
}
/**
* Maps Redux state to component props.
* Maps redux state to component props.
*
* @param {Object} state - The redux state.
* @returns {{

View File

@ -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,