[RN] Add polyfill for localStorage
It's built on top of React Native's AsyncStorage. They have differing APIs, so we implement a synchronous API on top of an asynchronous one. This is done by being optimistic and hoping that operations will happen asynchronously. If one such operation fails, the error is ignored and life goes on, since operations are performed in the in-memory cache first. Note to reviewers: LocalStorage.js lacks Flow annotations because indexable class declarations are not yet supported: https://github.com/facebook/flow/issues/1323 and yours truly couldn't find a way to make the required syntax work without making it unnecessarily complex.
This commit is contained in:
parent
0e234bfd82
commit
bfeaf329e1
|
@ -0,0 +1,112 @@
|
|||
import { AsyncStorage } from 'react-native';
|
||||
|
||||
/**
|
||||
* Prefix used to mark keys stored by our app in the global React Native
|
||||
* storage.
|
||||
*/
|
||||
const JITSI_KEY_PREFIX = '@jitsi/';
|
||||
|
||||
/**
|
||||
* Web Sorage API compatible object used for polyfilling window.localStorage.
|
||||
* The Web Storage API is synchronous, whereas AsyncStorage, the builtin generic
|
||||
* storage API in React Native, is asynchronous, so this object is optimistic:
|
||||
* it will first store the value locally (in memory) so results can be served
|
||||
* synchronously, and then save the value asynchronously.
|
||||
*
|
||||
* If any of the asynchronous operations produces an error, it's ignored.
|
||||
*/
|
||||
export default class LocalStorage {
|
||||
/**
|
||||
* Loads all keys from React Native's AsyncStorage.
|
||||
*/
|
||||
constructor() {
|
||||
AsyncStorage.getAllKeys()
|
||||
.then(keys => {
|
||||
const jitsiKeys
|
||||
= keys.filter(key => key.startsWith(JITSI_KEY_PREFIX));
|
||||
|
||||
AsyncStorage.multiGet(jitsiKeys)
|
||||
.then(items => {
|
||||
for (const item of items) {
|
||||
const key = item[0].slice(JITSI_KEY_PREFIX.length);
|
||||
const value = item[1];
|
||||
|
||||
this[key] = value;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of items stored.
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get length() {
|
||||
return Object.keys(this).length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all keys from the storage.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
clear() {
|
||||
const keys = Object.keys(this);
|
||||
|
||||
for (const key of keys) {
|
||||
delete this[key];
|
||||
AsyncStorage.removeItem(`${JITSI_KEY_PREFIX}${key}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the element that was stored for the given `key`.
|
||||
*
|
||||
* @param {string} key - The requested `key`.
|
||||
* @returns {string|null}
|
||||
*/
|
||||
getItem(key) {
|
||||
if (this.hasOwnProperty(key)) {
|
||||
return this[key];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the nth element in the storage.
|
||||
*
|
||||
* @param {number} n - The element number that is requested.
|
||||
* @returns {string}
|
||||
*/
|
||||
key(n) {
|
||||
return Object.keys(this)[n || 0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given `key` from the storage.
|
||||
*
|
||||
* @param {string} key - The `key` which will be removed.
|
||||
* @returns {void}
|
||||
*/
|
||||
removeItem(key) {
|
||||
delete this[key];
|
||||
AsyncStorage.removeItem(`${JITSI_KEY_PREFIX}${key}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores the given `value` for the given `key`. If a value or ready exists
|
||||
* for that key, it's updated.
|
||||
*
|
||||
* @param {string} key - The key for the value which will be stored.
|
||||
* @param {string} value - The value which will be stored.
|
||||
* @returns {void}
|
||||
*/
|
||||
setItem(key, value) {
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
value = String(value);
|
||||
this[key] = value;
|
||||
AsyncStorage.setItem(`${JITSI_KEY_PREFIX}${key}`, value);
|
||||
}
|
||||
}
|
|
@ -2,6 +2,8 @@ import Iterator from 'es6-iterator';
|
|||
import BackgroundTimer from 'react-native-background-timer';
|
||||
import 'url-polyfill'; // Polyfill for URL constructor
|
||||
|
||||
import LocalStorage from './LocalStorage';
|
||||
|
||||
/**
|
||||
* Gets the first common prototype of two specified Objects (treating the
|
||||
* objects themselves as prototypes as well).
|
||||
|
@ -264,6 +266,9 @@ function _visitNode(node, callback) {
|
|||
global.document = document;
|
||||
}
|
||||
|
||||
// localStorage
|
||||
global.localStorage = new LocalStorage();
|
||||
|
||||
// location
|
||||
if (typeof global.location === 'undefined') {
|
||||
global.location = {
|
||||
|
|
Loading…
Reference in New Issue