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.

PersistencyRegistry.js 4.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  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
  15. * subtree to be persisted and also handles the persistency calls too.
  16. */
  17. class PersistencyRegistry {
  18. _checksum: string;
  19. _elements: PersistencyConfigMap;
  20. /**
  21. * Initiates the PersistencyRegistry.
  22. */
  23. constructor() {
  24. this._elements = {};
  25. }
  26. /**
  27. * Returns the persisted redux state. This function takes
  28. * the PersistencyRegistry._elements into account as we may have
  29. * persisted something in the past that we don't want to retreive anymore.
  30. * The next {@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', persistedState, error
  43. );
  44. persistedState = {};
  45. }
  46. filteredPersistedState
  47. = this._getFilteredState(persistedState);
  48. }
  49. this._checksum = this._calculateChecksum(filteredPersistedState);
  50. logger.info('Redux state rehydrated as', filteredPersistedState);
  51. return filteredPersistedState;
  52. }
  53. /**
  54. * Initiates a persist operation, but its execution will depend on
  55. * the current checksums (checks changes).
  56. *
  57. * @param {Object} state - The redux state.
  58. * @returns {void}
  59. */
  60. persistState(state: Object) {
  61. const filteredState = this._getFilteredState(state);
  62. const newCheckSum = this._calculateChecksum(filteredState);
  63. if (newCheckSum !== this._checksum) {
  64. try {
  65. window.localStorage.setItem(
  66. PERSISTED_STATE_NAME,
  67. JSON.stringify(filteredState)
  68. );
  69. logger.info(
  70. `Redux state persisted. ${this._checksum} -> ${newCheckSum}`
  71. );
  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', filteredState, error
  101. );
  102. return '';
  103. }
  104. }
  105. /**
  106. * Prepares a filtered state from the actual or the
  107. * persisted Redux state, based on this registry.
  108. *
  109. * @private
  110. * @param {Object} state - The actual or persisted redux state.
  111. * @returns {Object}
  112. */
  113. _getFilteredState(state: Object) {
  114. const filteredState = {};
  115. for (const name of Object.keys(this._elements)) {
  116. if (state[name]) {
  117. filteredState[name] = this._getFilteredSubtree(
  118. state[name],
  119. this._elements[name]
  120. );
  121. }
  122. }
  123. return filteredState;
  124. }
  125. /**
  126. * Prepares a filtered subtree based on the config for
  127. * persisting or for retreival.
  128. *
  129. * @private
  130. * @param {Object} subtree - The redux state subtree.
  131. * @param {Object} subtreeConfig - The related config.
  132. * @returns {Object}
  133. */
  134. _getFilteredSubtree(subtree, subtreeConfig) {
  135. const filteredSubtree = {};
  136. for (const persistedKey of Object.keys(subtree)) {
  137. if (subtreeConfig[persistedKey]) {
  138. filteredSubtree[persistedKey]
  139. = subtree[persistedKey];
  140. }
  141. }
  142. return filteredSubtree;
  143. }
  144. }
  145. export default new PersistencyRegistry();