您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

PersistenceRegistry.js 5.5KB

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