You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

PersistenceRegistry.js 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. // @flow
  2. import Logger from 'jitsi-meet-logger';
  3. import md5 from 'js-md5';
  4. const logger = Logger.getLogger(__filename);
  5. /**
  6. * The name of the localStorage store where the app persists its values to.
  7. */
  8. const PERSISTED_STATE_NAME = 'jitsi-state';
  9. /**
  10. * The type of the name-config pairs stored in this reducer.
  11. */
  12. declare type PersistencyConfigMap = { [name: string]: Object };
  13. /**
  14. * A registry to allow features to register their redux store subtree to be
  15. * persisted and also handles the persistency calls too.
  16. */
  17. class PersistenceRegistry {
  18. _checksum: string;
  19. _elements: PersistencyConfigMap;
  20. /**
  21. * Initializes a new {@ code PersistenceRegistry} instance.
  22. */
  23. constructor() {
  24. this._elements = {};
  25. }
  26. /**
  27. * Returns the persisted redux state. This function takes the
  28. * {@link #_elements} into account as we may have persisted something in the
  29. * past that we don't want to retreive anymore. The next
  30. * {@link #persistState} will remove those values.
  31. *
  32. * @returns {Object}
  33. */
  34. getPersistedState() {
  35. let filteredPersistedState = {};
  36. let persistedState = window.localStorage.getItem(PERSISTED_STATE_NAME);
  37. if (persistedState) {
  38. try {
  39. persistedState = JSON.parse(persistedState);
  40. } catch (error) {
  41. logger.error(
  42. 'Error parsing persisted state',
  43. persistedState,
  44. error);
  45. persistedState = {};
  46. }
  47. filteredPersistedState
  48. = this._getFilteredState(persistedState);
  49. }
  50. this._checksum = this._calculateChecksum(filteredPersistedState);
  51. logger.info('redux state rehydrated as', filteredPersistedState);
  52. return filteredPersistedState;
  53. }
  54. /**
  55. * Initiates a persist operation, but its execution will depend on the
  56. * current checksums (checks changes).
  57. *
  58. * @param {Object} state - The redux state.
  59. * @returns {void}
  60. */
  61. persistState(state: Object) {
  62. const filteredState = this._getFilteredState(state);
  63. const newCheckSum = this._calculateChecksum(filteredState);
  64. if (newCheckSum !== this._checksum) {
  65. try {
  66. window.localStorage.setItem(
  67. PERSISTED_STATE_NAME,
  68. JSON.stringify(filteredState));
  69. logger.info(
  70. `redux state persisted. ${this._checksum} -> ${
  71. newCheckSum}`);
  72. this._checksum = newCheckSum;
  73. } catch (error) {
  74. logger.error('Error persisting redux state', error);
  75. }
  76. }
  77. }
  78. /**
  79. * Registers a new subtree config to be used for the persistency.
  80. *
  81. * @param {string} name - The name of the subtree the config belongs to.
  82. * @param {Object} config - The config object.
  83. * @returns {void}
  84. */
  85. register(name: string, config: Object) {
  86. this._elements[name] = config;
  87. }
  88. /**
  89. * Calculates the checksum of the current or the new values of the state.
  90. *
  91. * @private
  92. * @param {Object} filteredState - The filtered/persisted redux state.
  93. * @returns {string}
  94. */
  95. _calculateChecksum(filteredState: Object) {
  96. try {
  97. return md5.hex(JSON.stringify(filteredState) || '');
  98. } catch (error) {
  99. logger.error(
  100. 'Error calculating checksum for state',
  101. filteredState,
  102. error);
  103. return '';
  104. }
  105. }
  106. /**
  107. * Prepares a filtered state from the actual or the persisted redux state,
  108. * based on this registry.
  109. *
  110. * @private
  111. * @param {Object} state - The actual or persisted redux state.
  112. * @returns {Object}
  113. */
  114. _getFilteredState(state: Object) {
  115. const filteredState = {};
  116. for (const name of Object.keys(this._elements)) {
  117. if (state[name]) {
  118. filteredState[name]
  119. = this._getFilteredSubtree(
  120. state[name],
  121. this._elements[name]);
  122. }
  123. }
  124. return filteredState;
  125. }
  126. /**
  127. * Prepares a filtered subtree based on the config for persisting or for
  128. * retrieval.
  129. *
  130. * @private
  131. * @param {Object} subtree - The redux state subtree.
  132. * @param {Object} subtreeConfig - The related config.
  133. * @returns {Object}
  134. */
  135. _getFilteredSubtree(subtree, subtreeConfig) {
  136. const filteredSubtree = {};
  137. for (const persistedKey of Object.keys(subtree)) {
  138. if (subtreeConfig[persistedKey]) {
  139. filteredSubtree[persistedKey] = subtree[persistedKey];
  140. }
  141. }
  142. return filteredSubtree;
  143. }
  144. }
  145. export default new PersistenceRegistry();