Introduce base/storage to represent the Web Storage API and persistence-related customizations
This commit is contained in:
parent
83243d5980
commit
d7dddb2509
|
@ -14,11 +14,8 @@ import {
|
||||||
} from '../../base/participants';
|
} from '../../base/participants';
|
||||||
import { getProfile } from '../../base/profile';
|
import { getProfile } from '../../base/profile';
|
||||||
import { Fragment, RouteRegistry } from '../../base/react';
|
import { Fragment, RouteRegistry } from '../../base/react';
|
||||||
import {
|
import { MiddlewareRegistry, ReducerRegistry } from '../../base/redux';
|
||||||
MiddlewareRegistry,
|
import { PersistenceRegistry } from '../../base/storage';
|
||||||
PersistencyRegistry,
|
|
||||||
ReducerRegistry
|
|
||||||
} from '../../base/redux';
|
|
||||||
import { toURLString } from '../../base/util';
|
import { toURLString } from '../../base/util';
|
||||||
import { OverlayContainer } from '../../overlay';
|
import { OverlayContainer } from '../../overlay';
|
||||||
import { BlankPage } from '../../welcome';
|
import { BlankPage } from '../../welcome';
|
||||||
|
@ -349,7 +346,7 @@ export class AbstractApp extends Component {
|
||||||
return (
|
return (
|
||||||
createStore(
|
createStore(
|
||||||
reducer,
|
reducer,
|
||||||
PersistencyRegistry.getPersistedState(),
|
PersistenceRegistry.getPersistedState(),
|
||||||
middleware));
|
middleware));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,7 +4,13 @@ import 'url-polyfill'; // Polyfill for URL constructor
|
||||||
|
|
||||||
import { Platform } from '../../react';
|
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
|
* Gets the first common prototype of two specified Objects (treating the
|
||||||
|
@ -271,11 +277,6 @@ function _visitNode(node, callback) {
|
||||||
global.document = document;
|
global.document = document;
|
||||||
}
|
}
|
||||||
|
|
||||||
// localStorage
|
|
||||||
if (typeof global.localStorage === 'undefined') {
|
|
||||||
global.localStorage = new Storage('@jitsi-meet/');
|
|
||||||
}
|
|
||||||
|
|
||||||
// location
|
// location
|
||||||
if (typeof global.location === 'undefined') {
|
if (typeof global.location === 'undefined') {
|
||||||
global.location = {
|
global.location = {
|
||||||
|
@ -332,15 +333,6 @@ function _visitNode(node, callback) {
|
||||||
navigator.userAgent = userAgent;
|
navigator.userAgent = userAgent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// sessionStorage
|
|
||||||
//
|
|
||||||
// Required by:
|
|
||||||
// - herment
|
|
||||||
// - Strophe
|
|
||||||
if (typeof global.sessionStorage === 'undefined') {
|
|
||||||
global.sessionStorage = new Storage();
|
|
||||||
}
|
|
||||||
|
|
||||||
// WebRTC
|
// WebRTC
|
||||||
require('./polyfills-webrtc');
|
require('./polyfills-webrtc');
|
||||||
require('react-native-callstats/csio-polyfill');
|
require('react-native-callstats/csio-polyfill');
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import { PersistencyRegistry, ReducerRegistry } from '../redux';
|
import { ReducerRegistry } from '../redux';
|
||||||
|
import { PersistenceRegistry } from '../storage';
|
||||||
|
|
||||||
import { PROFILE_UPDATED } from './actionTypes';
|
import { PROFILE_UPDATED } from './actionTypes';
|
||||||
|
|
||||||
|
@ -10,7 +11,10 @@ const DEFAULT_STATE = {
|
||||||
|
|
||||||
const STORE_NAME = 'features/base/profile';
|
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
|
profile: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
export * from './functions';
|
export * from './functions';
|
||||||
export { default as MiddlewareRegistry } from './MiddlewareRegistry';
|
export { default as MiddlewareRegistry } from './MiddlewareRegistry';
|
||||||
export { default as PersistencyRegistry } from './PersistencyRegistry';
|
|
||||||
export { default as ReducerRegistry } from './ReducerRegistry';
|
export { default as ReducerRegistry } from './ReducerRegistry';
|
||||||
|
|
||||||
import './middleware';
|
|
||||||
|
|
|
@ -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
|
|
||||||
```
|
|
|
@ -19,13 +19,13 @@ declare type PersistencyConfigMap = { [name: string]: Object };
|
||||||
* A registry to allow features to register their redux store subtree to be
|
* A registry to allow features to register their redux store subtree to be
|
||||||
* persisted and also handles the persistency calls too.
|
* persisted and also handles the persistency calls too.
|
||||||
*/
|
*/
|
||||||
class PersistencyRegistry {
|
class PersistenceRegistry {
|
||||||
_checksum: string;
|
_checksum: string;
|
||||||
|
|
||||||
_elements: PersistencyConfigMap;
|
_elements: PersistencyConfigMap;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes a new {@ code PersistencyRegistry} instance.
|
* Initializes a new {@ code PersistenceRegistry} instance.
|
||||||
*/
|
*/
|
||||||
constructor() {
|
constructor() {
|
||||||
this._elements = {};
|
this._elements = {};
|
||||||
|
@ -166,4 +166,4 @@ class PersistencyRegistry {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default new PersistencyRegistry();
|
export default new PersistenceRegistry();
|
|
@ -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
|
||||||
|
```
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './native';
|
|
@ -0,0 +1,4 @@
|
||||||
|
export * from './_';
|
||||||
|
export { default as PersistenceRegistry } from './PersistenceRegistry';
|
||||||
|
|
||||||
|
import './middleware';
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { toState } from './functions';
|
import { MiddlewareRegistry, toState } from '../redux';
|
||||||
import MiddlewareRegistry from './MiddlewareRegistry';
|
|
||||||
import PersistencyRegistry from './PersistencyRegistry';
|
import PersistenceRegistry from './PersistenceRegistry';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The delay that passes between the last state change and the persisting of
|
* The delay in milliseconds that passes between the last state change and the
|
||||||
* that state in the storage.
|
* persisting of that state in the storage.
|
||||||
*/
|
*/
|
||||||
const PERSIST_STATE_DELAY = 2000;
|
const PERSIST_STATE_DELAY = 2000;
|
||||||
|
|
||||||
|
@ -17,21 +17,23 @@ const PERSIST_STATE_DELAY = 2000;
|
||||||
*/
|
*/
|
||||||
const throttledPersistState
|
const throttledPersistState
|
||||||
= _.throttle(
|
= _.throttle(
|
||||||
state => PersistencyRegistry.persistState(state),
|
state => PersistenceRegistry.persistState(state),
|
||||||
PERSIST_STATE_DELAY);
|
PERSIST_STATE_DELAY);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A master MiddleWare to selectively persist state. Please use the
|
* 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.
|
* be persisted.
|
||||||
*
|
*
|
||||||
* @param {Store} store - The redux store.
|
* @param {Store} store - The redux store.
|
||||||
* @returns {Function}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
MiddlewareRegistry.register(store => next => action => {
|
MiddlewareRegistry.register(store => next => action => {
|
||||||
|
const oldState = toState(store);
|
||||||
const result = next(action);
|
const result = next(action);
|
||||||
|
const newState = toState(store);
|
||||||
|
|
||||||
throttledPersistState(toState(store));
|
oldState === newState || throttledPersistState(newState);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
|
@ -0,0 +1 @@
|
||||||
|
import './polyfills-browser';
|
|
@ -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
|
|
@ -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.
|
* @param {Object} state - The redux state.
|
||||||
* @returns {{
|
* @returns {{
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import { PersistencyRegistry, ReducerRegistry } from '../base/redux';
|
import { ReducerRegistry } from '../base/redux';
|
||||||
|
import { PersistenceRegistry } from '../base/storage';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
STORE_CURRENT_CONFERENCE,
|
STORE_CURRENT_CONFERENCE,
|
||||||
|
@ -30,14 +31,14 @@ export const MAX_LIST_SIZE = 30;
|
||||||
const STORE_NAME = 'features/recent-list';
|
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
|
list: true
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reduces the redux actions of the feature features/recent-list.
|
* Reduces the redux actions of the feature recent-list.
|
||||||
*/
|
*/
|
||||||
ReducerRegistry.register(
|
ReducerRegistry.register(
|
||||||
STORE_NAME,
|
STORE_NAME,
|
||||||
|
|
Loading…
Reference in New Issue