[RN] Prepare to polyfill sessionStorage
This commit is contained in:
parent
bfeaf329e1
commit
bf523711df
|
@ -1,112 +0,0 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
/* @flow */
|
||||
|
||||
import { AsyncStorage } from 'react-native';
|
||||
|
||||
/**
|
||||
* A Web Sorage API implementation used for polyfilling
|
||||
* <tt>window.localStorage</tt> and/or <tt>window.sessionStorage</tt>.
|
||||
* <p>
|
||||
* The Web Storage API is synchronous whereas React Native's builtin generic
|
||||
* storage API <tt>AsyncStorage</tt> 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 {
|
||||
_items: Map<string, string>;
|
||||
|
||||
_keyPrefix: ?string;
|
||||
|
||||
/**
|
||||
* Initializes a new <tt>Storage</tt> instance. Loads all previously
|
||||
* persisted data items from React Native's <tt>AsyncStorage</tt> if
|
||||
* necessary.
|
||||
*
|
||||
* @param {string|undefined} keyPrefix - The prefix of the
|
||||
* <tt>AsyncStorage</tt> keys to be persisted by this storage.
|
||||
*/
|
||||
constructor(keyPrefix: ?string) {
|
||||
/**
|
||||
* The data items stored in this storage.
|
||||
*
|
||||
* @private
|
||||
* @type {Map}
|
||||
*/
|
||||
this._items = new Map();
|
||||
|
||||
/**
|
||||
* The prefix of the <tt>AsyncStorage</tt> keys persisted by this
|
||||
* storage. If <tt>undefined</tt>, then the data items stored in this
|
||||
* storage will not be persisted.
|
||||
*
|
||||
* @private
|
||||
* @type {string}
|
||||
*/
|
||||
this._keyPrefix = keyPrefix;
|
||||
|
||||
if (typeof this._keyPrefix !== 'undefined') {
|
||||
// Load all previously persisted data items from React Native's
|
||||
// AsyncStorage.
|
||||
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._items.has(key)) {
|
||||
this._items.set(key, value);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all keys from this storage.
|
||||
*
|
||||
* @returns {void}
|
||||
*/
|
||||
clear() {
|
||||
for (const key of this._items.keys()) {
|
||||
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 <tt>key</tt> or
|
||||
* <tt>null</tt>.
|
||||
*/
|
||||
getItem(key: string) {
|
||||
return this._items.has(key) ? this._items.get(key) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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: number) {
|
||||
let i = 0;
|
||||
|
||||
for (const key in this._items.keys()) {
|
||||
if (i++ === n) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an integer representing the number of data items stored in this
|
||||
* storage.
|
||||
*
|
||||
* @returns {number}
|
||||
*/
|
||||
get length(): number {
|
||||
return this._items.size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a specific key from this storage.
|
||||
*
|
||||
* @param {string} key - The name of the key to remove.
|
||||
* @returns {void}
|
||||
*/
|
||||
removeItem(key: string) {
|
||||
this._items.delete(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 <tt>key</tt>.
|
||||
* @returns {void}
|
||||
*/
|
||||
setItem(key: string, value: string) {
|
||||
value = String(value); // eslint-disable-line no-param-reassign
|
||||
this._items.set(key, value);
|
||||
typeof this._keyPrefix === 'undefined'
|
||||
|| AsyncStorage.setItem(`${String(this._keyPrefix)}${key}`, value);
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ import Iterator from 'es6-iterator';
|
|||
import BackgroundTimer from 'react-native-background-timer';
|
||||
import 'url-polyfill'; // Polyfill for URL constructor
|
||||
|
||||
import LocalStorage from './LocalStorage';
|
||||
import Storage from './Storage';
|
||||
|
||||
/**
|
||||
* Gets the first common prototype of two specified Objects (treating the
|
||||
|
@ -267,7 +267,9 @@ function _visitNode(node, callback) {
|
|||
}
|
||||
|
||||
// localStorage
|
||||
global.localStorage = new LocalStorage();
|
||||
if (typeof global.localStorage === 'undefined') {
|
||||
global.localStorage = new Storage('@jitsi-meet/');
|
||||
}
|
||||
|
||||
// location
|
||||
if (typeof global.location === 'undefined') {
|
||||
|
|
Loading…
Reference in New Issue