2018-01-17 11:19:10 +00:00
|
|
|
// @flow
|
2018-01-29 22:20:38 +00:00
|
|
|
|
2018-01-17 11:19:10 +00:00
|
|
|
import Logger from 'jitsi-meet-logger';
|
|
|
|
import md5 from 'js-md5';
|
|
|
|
|
|
|
|
const logger = Logger.getLogger(__filename);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The name of the localStorage store where the app persists its values to.
|
|
|
|
*/
|
|
|
|
const PERSISTED_STATE_NAME = 'jitsi-state';
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The type of the name-config pairs stored in this reducer.
|
|
|
|
*/
|
|
|
|
declare type PersistencyConfigMap = { [name: string]: Object };
|
|
|
|
|
|
|
|
/**
|
2018-01-29 22:20:38 +00:00
|
|
|
* A registry to allow features to register their redux store subtree to be
|
|
|
|
* persisted and also handles the persistency calls too.
|
2018-01-17 11:19:10 +00:00
|
|
|
*/
|
2018-02-02 19:35:49 +00:00
|
|
|
class PersistenceRegistry {
|
2018-01-17 11:19:10 +00:00
|
|
|
_checksum: string;
|
2018-01-29 22:20:38 +00:00
|
|
|
|
2018-01-17 11:19:10 +00:00
|
|
|
_elements: PersistencyConfigMap;
|
|
|
|
|
|
|
|
/**
|
2018-02-02 19:35:49 +00:00
|
|
|
* Initializes a new {@ code PersistenceRegistry} instance.
|
2018-01-17 11:19:10 +00:00
|
|
|
*/
|
|
|
|
constructor() {
|
|
|
|
this._elements = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-01-29 22:20:38 +00:00
|
|
|
* Returns the persisted redux state. This function takes the
|
|
|
|
* {@link #_elements} into account as we may have persisted something in the
|
|
|
|
* past that we don't want to retreive anymore. The next
|
|
|
|
* {@link #persistState} will remove those values.
|
2018-01-17 11:19:10 +00:00
|
|
|
*
|
|
|
|
* @returns {Object}
|
|
|
|
*/
|
|
|
|
getPersistedState() {
|
|
|
|
let filteredPersistedState = {};
|
|
|
|
let persistedState = window.localStorage.getItem(PERSISTED_STATE_NAME);
|
|
|
|
|
|
|
|
if (persistedState) {
|
|
|
|
try {
|
|
|
|
persistedState = JSON.parse(persistedState);
|
|
|
|
} catch (error) {
|
|
|
|
logger.error(
|
2018-01-29 22:20:38 +00:00
|
|
|
'Error parsing persisted state',
|
|
|
|
persistedState,
|
|
|
|
error);
|
2018-01-17 11:19:10 +00:00
|
|
|
persistedState = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
filteredPersistedState
|
|
|
|
= this._getFilteredState(persistedState);
|
|
|
|
}
|
|
|
|
|
|
|
|
this._checksum = this._calculateChecksum(filteredPersistedState);
|
2018-01-29 22:20:38 +00:00
|
|
|
logger.info('redux state rehydrated as', filteredPersistedState);
|
2018-01-17 11:19:10 +00:00
|
|
|
|
|
|
|
return filteredPersistedState;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-01-29 22:20:38 +00:00
|
|
|
* Initiates a persist operation, but its execution will depend on the
|
|
|
|
* current checksums (checks changes).
|
2018-01-17 11:19:10 +00:00
|
|
|
*
|
|
|
|
* @param {Object} state - The redux state.
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
persistState(state: Object) {
|
|
|
|
const filteredState = this._getFilteredState(state);
|
|
|
|
const newCheckSum = this._calculateChecksum(filteredState);
|
|
|
|
|
|
|
|
if (newCheckSum !== this._checksum) {
|
|
|
|
try {
|
|
|
|
window.localStorage.setItem(
|
|
|
|
PERSISTED_STATE_NAME,
|
2018-01-29 22:20:38 +00:00
|
|
|
JSON.stringify(filteredState));
|
2018-01-17 11:19:10 +00:00
|
|
|
logger.info(
|
2018-01-29 22:20:38 +00:00
|
|
|
`redux state persisted. ${this._checksum} -> ${
|
|
|
|
newCheckSum}`);
|
2018-01-17 11:19:10 +00:00
|
|
|
this._checksum = newCheckSum;
|
|
|
|
} catch (error) {
|
2018-01-29 22:20:38 +00:00
|
|
|
logger.error('Error persisting redux state', error);
|
2018-01-17 11:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Registers a new subtree config to be used for the persistency.
|
|
|
|
*
|
|
|
|
* @param {string} name - The name of the subtree the config belongs to.
|
|
|
|
* @param {Object} config - The config object.
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
register(name: string, config: Object) {
|
|
|
|
this._elements[name] = config;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Calculates the checksum of the current or the new values of the state.
|
|
|
|
*
|
|
|
|
* @private
|
2018-01-29 22:20:38 +00:00
|
|
|
* @param {Object} filteredState - The filtered/persisted redux state.
|
2018-01-17 11:19:10 +00:00
|
|
|
* @returns {string}
|
|
|
|
*/
|
|
|
|
_calculateChecksum(filteredState: Object) {
|
|
|
|
try {
|
|
|
|
return md5.hex(JSON.stringify(filteredState) || '');
|
|
|
|
} catch (error) {
|
|
|
|
logger.error(
|
2018-01-29 22:20:38 +00:00
|
|
|
'Error calculating checksum for state',
|
|
|
|
filteredState,
|
|
|
|
error);
|
2018-01-17 11:19:10 +00:00
|
|
|
|
|
|
|
return '';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-01-29 22:20:38 +00:00
|
|
|
* Prepares a filtered state from the actual or the persisted redux state,
|
|
|
|
* based on this registry.
|
2018-01-17 11:19:10 +00:00
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @param {Object} state - The actual or persisted redux state.
|
|
|
|
* @returns {Object}
|
|
|
|
*/
|
|
|
|
_getFilteredState(state: Object) {
|
|
|
|
const filteredState = {};
|
|
|
|
|
|
|
|
for (const name of Object.keys(this._elements)) {
|
|
|
|
if (state[name]) {
|
2018-01-29 22:20:38 +00:00
|
|
|
filteredState[name]
|
|
|
|
= this._getFilteredSubtree(
|
|
|
|
state[name],
|
|
|
|
this._elements[name]);
|
2018-01-17 11:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return filteredState;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-01-29 22:20:38 +00:00
|
|
|
* Prepares a filtered subtree based on the config for persisting or for
|
|
|
|
* retrieval.
|
2018-01-17 11:19:10 +00:00
|
|
|
*
|
|
|
|
* @private
|
|
|
|
* @param {Object} subtree - The redux state subtree.
|
|
|
|
* @param {Object} subtreeConfig - The related config.
|
|
|
|
* @returns {Object}
|
|
|
|
*/
|
|
|
|
_getFilteredSubtree(subtree, subtreeConfig) {
|
|
|
|
const filteredSubtree = {};
|
|
|
|
|
|
|
|
for (const persistedKey of Object.keys(subtree)) {
|
|
|
|
if (subtreeConfig[persistedKey]) {
|
2018-01-29 22:20:38 +00:00
|
|
|
filteredSubtree[persistedKey] = subtree[persistedKey];
|
2018-01-17 11:19:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return filteredSubtree;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-02-02 19:35:49 +00:00
|
|
|
export default new PersistenceRegistry();
|