123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192 |
- import AsyncStorage from '@react-native-community/async-storage';
-
- /**
- * A Web Sorage API implementation used for polyfilling
- * {@code window.localStorage} and/or {@code window.sessionStorage}.
- * <p>
- * The Web Storage API is synchronous whereas React Native's builtin generic
- * storage API {@code AsyncStorage} is asynchronous so the implementation with
- * persistence is optimistic: it will first store the value locally in memory so
- * that results can be served synchronously and then persist the value
- * asynchronously. If an asynchronous operation produces an error, it's ignored.
- */
- export default class Storage {
- /**
- * Initializes a new {@code Storage} instance. Loads all previously
- * persisted data items from React Native's {@code AsyncStorage} if
- * necessary.
- *
- * @param {string|undefined} keyPrefix - The prefix of the
- * {@code AsyncStorage} keys to be persisted by this storage.
- */
- constructor(keyPrefix) {
- /**
- * The prefix of the {@code AsyncStorage} keys persisted by this
- * storage. If {@code undefined}, then the data items stored in this
- * storage will not be persisted.
- *
- * @private
- * @type {string}
- */
- this._keyPrefix = keyPrefix;
-
- // Perform optional asynchronous initialization.
- const initializing = this._initializeAsync();
-
- if (initializing) {
- // Indicate that asynchronous initialization is under way.
- this._initializing = initializing;
-
- // When the asynchronous initialization completes, indicate its
- // completion.
- initializing.finally(() => {
- if (this._initializing === initializing) {
- this._initializing = undefined;
- }
- });
- }
- }
-
- /**
- * Removes all keys from this storage.
- *
- * @returns {void}
- */
- clear() {
- for (const key of Object.keys(this)) {
- this.removeItem(key);
- }
- }
-
- /**
- * Returns the value associated with a specific key in this storage.
- *
- * @param {string} key - The name of the key to retrieve the value of.
- * @returns {string|null} The value associated with {@code key} or
- * {@code null}.
- */
- getItem(key) {
- return this.hasOwnProperty(key) ? this[key] : null;
- }
-
- /**
- * Returns the value associated with a specific key in this {@code Storage}
- * in an async manner. The method is required for the cases where we need
- * the stored data but we're not sure yet whether this {@code Storage} is
- * already initialized (e.g. On app start).
- *
- * @param {string} key - The name of the key to retrieve the value of.
- * @returns {Promise}
- */
- _getItemAsync(key) {
- return (
- (this._initializing || Promise.resolve())
- .catch(() => { /* _getItemAsync should always resolve! */ })
- .then(() => this.getItem(key)));
- }
-
- /**
- * Performs asynchronous initialization of this {@code Storage} instance
- * such as loading all keys from {@link AsyncStorage}.
- *
- * @private
- * @returns {Promise}
- */
- _initializeAsync() {
- if (typeof this._keyPrefix !== 'undefined') {
- // Load all previously persisted data items from React Native's
- // AsyncStorage.
-
- return new Promise(resolve => {
- AsyncStorage.getAllKeys().then((...getAllKeysCallbackArgs) => {
- // XXX The keys argument of getAllKeys' callback may or may
- // not be preceded by an error argument.
- const keys
- = getAllKeysCallbackArgs[
- getAllKeysCallbackArgs.length - 1
- ].filter(key => key.startsWith(this._keyPrefix));
-
- AsyncStorage.multiGet(keys)
- .then((...multiGetCallbackArgs) => {
- // XXX The result argument of multiGet may or may not be
- // preceded by an errors argument.
- const result
- = multiGetCallbackArgs[
- multiGetCallbackArgs.length - 1
- ];
- const keyPrefixLength
- = this._keyPrefix && this._keyPrefix.length;
-
- // eslint-disable-next-line prefer-const
- for (let [ key, value ] of result) {
- key = key.substring(keyPrefixLength);
-
- // XXX The loading of the previously persisted data
- // items from AsyncStorage is asynchronous which
- // means that it is technically possible to invoke
- // setItem with a key before the key is loaded from
- // AsyncStorage.
- if (!this.hasOwnProperty(key)) {
- this[key] = value;
- }
- }
-
- resolve();
- });
- });
- });
- }
-
- return undefined;
- }
-
- /**
- * Returns the name of the nth key in this storage.
- *
- * @param {number} n - The zero-based integer index of the key to get the
- * name of.
- * @returns {string} The name of the nth key in this storage.
- */
- key(n) {
- const keys = Object.keys(this);
-
- return n < keys.length ? keys[n] : null;
- }
-
- /**
- * Returns an integer representing the number of data items stored in this
- * storage.
- *
- * @returns {number}
- */
- get length() {
- return Object.keys(this).length;
- }
-
- /**
- * Removes a specific key from this storage.
- *
- * @param {string} key - The name of the key to remove.
- * @returns {void}
- */
- removeItem(key) {
- delete this[key];
- typeof this._keyPrefix === 'undefined'
- || AsyncStorage.removeItem(`${String(this._keyPrefix)}${key}`);
- }
-
- /**
- * Adds a specific key to this storage and associates it with a specific
- * value. If the key exists already, updates its value.
- *
- * @param {string} key - The name of the key to add/update.
- * @param {string} value - The value to associate with {@code key}.
- * @returns {void}
- */
- setItem(key, value) {
- value = String(value); // eslint-disable-line no-param-reassign
- this[key] = value;
- typeof this._keyPrefix === 'undefined'
- || AsyncStorage.setItem(`${String(this._keyPrefix)}${key}`, value);
- }
- }
|