"Fossies" - the Fresh Open Source Software Archive 
Member "jitsi-meet-7319/react/features/base/redux/PersistenceRegistry.ts" (6 Jun 2023, 8060 Bytes) of package /linux/misc/jitsi-meet-7319.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) TypeScript source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 // @ts-expect-error
2 import Bourne from '@hapi/bourne';
3 // eslint-disable-next-line lines-around-comment
4 // @ts-expect-error
5 import { jitsiLocalStorage } from '@jitsi/js-utils';
6 import md5 from 'js-md5';
7
8 import logger from './logger';
9
10 declare let __DEV__: any;
11
12 /**
13 * Mixed type of the element (subtree) config. If it's a {@code boolean} (and is
14 * {@code true}), we persist the entire subtree. If it's an {@code Object}, we
15 * perist a filtered subtree based on the properties of the config object.
16 */
17 declare type ElementConfig = boolean | Object;
18
19 /**
20 * The type of the name-config pairs stored in {@code PersistenceRegistry}.
21 */
22 declare type PersistencyConfigMap = { [name: string]: ElementConfig; };
23
24 /**
25 * A registry to allow features to register their redux store subtree to be
26 * persisted and also handles the persistency calls too.
27 */
28 class PersistenceRegistry {
29 _checksum = '';
30 _defaultStates: { [name: string ]: Object | undefined; } = {};
31 _elements: PersistencyConfigMap = {};
32
33 /**
34 * Returns the persisted redux state. Takes the {@link #_elements} into
35 * account as we may have persisted something in the past that we don't want
36 * to retrieve anymore. The next {@link #persistState} will remove such
37 * values.
38 *
39 * @returns {Object}
40 */
41 getPersistedState() {
42 const filteredPersistedState: any = {};
43
44 // localStorage key per feature
45 for (const subtreeName of Object.keys(this._elements)) {
46 // Assumes that the persisted value is stored under the same key as
47 // the feature's redux state name.
48 const persistedSubtree
49 = this._getPersistedSubtree(
50 subtreeName,
51 this._elements[subtreeName],
52 this._defaultStates[subtreeName]);
53
54 if (persistedSubtree !== undefined) {
55 filteredPersistedState[subtreeName] = persistedSubtree;
56 }
57 }
58
59 // Initialize the checksum.
60 this._checksum = this._calculateChecksum(filteredPersistedState);
61
62 if (typeof __DEV__ !== 'undefined' && __DEV__) {
63 logger.info('redux state rehydrated as', filteredPersistedState);
64 }
65
66 return filteredPersistedState;
67 }
68
69 /**
70 * Initiates a persist operation, but its execution will depend on the
71 * current checksums (checks changes).
72 *
73 * @param {Object} state - The redux state.
74 * @returns {void}
75 */
76 persistState(state: Object) {
77 const filteredState = this._getFilteredState(state);
78 const checksum = this._calculateChecksum(filteredState);
79
80 if (checksum !== this._checksum) {
81 for (const subtreeName of Object.keys(filteredState)) {
82 try {
83 jitsiLocalStorage.setItem(subtreeName, JSON.stringify(filteredState[subtreeName]));
84 } catch (error) {
85 logger.error('Error persisting redux subtree', subtreeName, error);
86 }
87 }
88 logger.info(`redux state persisted. ${this._checksum} -> ${checksum}`);
89 this._checksum = checksum;
90 }
91 }
92
93 /**
94 * Registers a new subtree config to be used for the persistency.
95 *
96 * @param {string} name - The name of the subtree the config belongs to.
97 * @param {ElementConfig} config - The config {@code Object}, or
98 * {@code boolean} if the entire subtree needs to be persisted.
99 * @param {Object} defaultState - The default state of the component. If
100 * it's provided, the rehydrated state will be merged with it before it gets
101 * pushed into Redux.
102 * @returns {void}
103 */
104 register(
105 name: string,
106 config: ElementConfig = true,
107 defaultState?: Object) {
108 this._elements[name] = config;
109 this._defaultStates[name] = defaultState;
110 }
111
112 /**
113 * Calculates the checksum of a specific state.
114 *
115 * @param {Object} state - The redux state to calculate the checksum of.
116 * @private
117 * @returns {string} The checksum of the specified {@code state}.
118 */
119 _calculateChecksum(state: Object) {
120 try {
121 return md5.hex(JSON.stringify(state) || '');
122 } catch (error) {
123 logger.error('Error calculating checksum for state', error);
124
125 return '';
126 }
127 }
128
129 /**
130 * Prepares a filtered state from the actual or the persisted redux state,
131 * based on this registry.
132 *
133 * @param {Object} state - The actual or persisted redux state.
134 * @private
135 * @returns {Object}
136 */
137 _getFilteredState(state: any): any {
138 const filteredState: any = {};
139
140 for (const name of Object.keys(this._elements)) {
141 if (state[name]) {
142 filteredState[name]
143 = this._getFilteredSubtree(
144 state[name],
145 this._elements[name]);
146 }
147 }
148
149 return filteredState;
150 }
151
152 /**
153 * Prepares a filtered subtree based on the config for persisting or for
154 * retrieval.
155 *
156 * @param {Object} subtree - The redux state subtree.
157 * @param {ElementConfig} subtreeConfig - The related config.
158 * @private
159 * @returns {Object}
160 */
161 _getFilteredSubtree(subtree: any, subtreeConfig: any) {
162 let filteredSubtree: any;
163
164 if (typeof subtreeConfig === 'object') {
165 // Only a filtered subtree gets persisted as specified by
166 // subtreeConfig.
167 filteredSubtree = {};
168 for (const persistedKey of Object.keys(subtree)) {
169 if (subtreeConfig[persistedKey]) {
170 filteredSubtree[persistedKey] = subtree[persistedKey];
171 }
172 }
173 } else if (subtreeConfig) {
174 // Persist the entire subtree.
175 filteredSubtree = subtree;
176 }
177
178 return filteredSubtree;
179 }
180
181 /**
182 * Retrieves a persisted subtree from the storage.
183 *
184 * @param {string} subtreeName - The name of the subtree.
185 * @param {Object} subtreeConfig - The config of the subtree from
186 * {@link #_elements}.
187 * @param {Object} subtreeDefaults - The defaults of the persisted subtree.
188 * @private
189 * @returns {Object}
190 */
191 _getPersistedSubtree(subtreeName: string, subtreeConfig: Object, subtreeDefaults?: Object) {
192 let persistedSubtree = jitsiLocalStorage.getItem(subtreeName);
193
194 if (persistedSubtree) {
195 try {
196 persistedSubtree = Bourne.parse(persistedSubtree);
197
198 const filteredSubtree
199 = this._getFilteredSubtree(persistedSubtree, subtreeConfig);
200
201 if (filteredSubtree !== undefined) {
202 return this._mergeDefaults(
203 filteredSubtree, subtreeDefaults);
204 }
205 } catch (error) {
206 logger.error(
207 'Error parsing persisted subtree',
208 subtreeName,
209 persistedSubtree,
210 error);
211 }
212 }
213
214 return undefined;
215 }
216
217 /**
218 * Merges the persisted subtree with its defaults before rehydrating the
219 * values.
220 *
221 * @private
222 * @param {Object} subtree - The Redux subtree.
223 * @param {?Object} defaults - The defaults, if any.
224 * @returns {Object}
225 */
226 _mergeDefaults(subtree: Object, defaults?: Object) {
227 if (!defaults) {
228 return subtree;
229 }
230
231 // If the subtree is an array, we don't need to merge it with the
232 // defaults, because if it has a value, it will overwrite it, and if
233 // it's undefined, it won't be even returned, and Redux will natively
234 // use the default values instead.
235 if (!Array.isArray(subtree)) {
236 return {
237 ...defaults,
238 ...subtree
239 };
240 }
241 }
242 }
243
244 export default new PersistenceRegistry();