mirror of
https://github.com/wassname/talk.git
synced 2026-06-30 12:39:33 +08:00
222 lines
5.8 KiB
JavaScript
222 lines
5.8 KiB
JavaScript
import uuid from 'uuid/v4';
|
|
|
|
function testStorageAccess(storage) {
|
|
const key = '__storage_test__';
|
|
|
|
// Create a unique test value.
|
|
const expectedValue = String(Date.now());
|
|
|
|
// Try to set, get, and remove that item.
|
|
storage.setItem(key, expectedValue);
|
|
const canSetGet = expectedValue === storage.getItem(key);
|
|
storage.removeItem(key);
|
|
|
|
if (!canSetGet) {
|
|
// We can't access the desired storage!
|
|
throw new Error('Storage access test failed');
|
|
}
|
|
}
|
|
|
|
// InMemoryStorage is a dumb implementation of the Storage interface that will
|
|
// not persist the data at all. It implements the Storage interface found:
|
|
//
|
|
// https://developer.mozilla.org/en-US/docs/Web/API/Storage
|
|
//
|
|
class InMemoryStorage {
|
|
constructor() {
|
|
this.storage = {};
|
|
}
|
|
|
|
get length() {
|
|
return Object.keys(this.storage).length;
|
|
}
|
|
|
|
key(n) {
|
|
if (this.length <= n) {
|
|
return undefined;
|
|
}
|
|
|
|
return this.storage[Object.keys(this.storage)[n]];
|
|
}
|
|
|
|
getItem(key) {
|
|
return this.storage[key];
|
|
}
|
|
|
|
setItem(key, value) {
|
|
this.storage[key] = value;
|
|
|
|
try {
|
|
// Test sessionStorage. We could have been given access recently.
|
|
testStorageAccess(sessionStorage);
|
|
|
|
// Test passed! Set the item in sessionStorage.
|
|
sessionStorage.setItem(key, value);
|
|
console.log(
|
|
'Attempt to persist InMemoryStorage value to sessionStorage succeeded'
|
|
);
|
|
} catch (err) {
|
|
console.warn(
|
|
'Attempt to persist InMemoryStorage value to sessionStorage failed',
|
|
err
|
|
);
|
|
}
|
|
}
|
|
|
|
removeItem(key) {
|
|
delete this.storage[key];
|
|
|
|
try {
|
|
// Test sessionStorage. We could have been given access recently.
|
|
testStorageAccess(sessionStorage);
|
|
|
|
// Test passed! Remove the item from sessionStorage.
|
|
sessionStorage.removeItem(key);
|
|
console.log(
|
|
'Attempt to persist InMemoryStorage delete to sessionStorage succeeded'
|
|
);
|
|
} catch (err) {
|
|
console.warn(
|
|
'Attempt to persist InMemoryStorage delete to sessionStorage failed',
|
|
err
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
// getStorage will test to see if the requested storage type is available, if it
|
|
// is not, it will try sessionStorage, and if that is also not available, it
|
|
// will fallback to InMemoryStorage.
|
|
function getStorage(type) {
|
|
try {
|
|
// Get the desired storage from the window and test it out.
|
|
const storage = window[type];
|
|
testStorageAccess(storage);
|
|
|
|
// Storage test was successful! Return it.
|
|
return storage;
|
|
} catch (err) {
|
|
// When third party cookies are disabled, session storage is readable/
|
|
// writable, but localStorage is not. Try to get the sessionStorage to use.
|
|
if (type !== 'sessionStorage') {
|
|
console.warn('Could not access', type, 'trying sessionStorage', err);
|
|
return getStorage('sessionStorage');
|
|
}
|
|
|
|
console.warn(
|
|
'Could not access sessionStorage falling back to InMemoryStorage',
|
|
err
|
|
);
|
|
}
|
|
|
|
// No acceptable storage could be found, returning the InMemoryStorage.
|
|
return new InMemoryStorage();
|
|
}
|
|
|
|
/**
|
|
* createStorage returns a localStorage wrapper if available
|
|
* @return {Object} localStorage wrapper
|
|
*/
|
|
export function createStorage(type = 'localStorage') {
|
|
return getStorage(type);
|
|
}
|
|
|
|
/**
|
|
* Creates a storage that relay requests over to pym.
|
|
* This is the counterpart of `connectStorageToPym`.
|
|
* @param {string} pym pym
|
|
* @return {Object} storage
|
|
*/
|
|
export function createPymStorage(pym, type = 'localStorage') {
|
|
// A Map of requestID => {resolve, reject}
|
|
const requests = {};
|
|
|
|
// Requests method with parameters over pym.
|
|
const call = (method, parameters) => {
|
|
const id = uuid();
|
|
return new Promise((resolve, reject) => {
|
|
requests[id] = { resolve, reject };
|
|
pym.sendMessage(
|
|
`pymStorage.${type}.request`,
|
|
JSON.stringify({ id, method, parameters })
|
|
);
|
|
});
|
|
};
|
|
|
|
// Receive successful responses.
|
|
pym.onMessage(`pymStorage.${type}.response`, msg => {
|
|
const { id, result } = JSON.parse(msg);
|
|
requests[id].resolve(result);
|
|
delete requests[id];
|
|
});
|
|
|
|
// Receive error responses.
|
|
pym.onMessage(`pymStorage.${type}.error`, msg => {
|
|
const { id, error } = JSON.parse(msg);
|
|
requests[id].reject(error);
|
|
delete requests[id];
|
|
});
|
|
|
|
return {
|
|
setItem: (key, value) => call('setItem', { key, value }),
|
|
getItem: (key, value) => call('getItem', { key, value }),
|
|
removeItem: key => call('removeItem', { key }),
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Listens to `pym` and relay storage requests to `storage`.
|
|
* This is the counterpart of `createPymStorage`.
|
|
* @param {Object} storage storage to perform requests on
|
|
* @param {Object} pym pym to listen to storage requests
|
|
* @param {string} prefix namespace requests by prepending a prefix to the keys
|
|
*/
|
|
export function connectStorageToPym(
|
|
storage,
|
|
pym,
|
|
type = 'localStorage',
|
|
prefix = 'talkPymStorage:'
|
|
) {
|
|
pym.onMessage(`pymStorage.${type}.request`, msg => {
|
|
const { id, method, parameters } = JSON.parse(msg);
|
|
const { key, value } = parameters;
|
|
const prefixedKey = `${prefix}${key}`;
|
|
|
|
// Variable for the method return value.
|
|
let result;
|
|
|
|
const sendError = error => {
|
|
console.error(error);
|
|
pym.sendMessage(
|
|
`pymStorage.${type}.error`,
|
|
JSON.stringify({ id, error })
|
|
);
|
|
};
|
|
|
|
try {
|
|
switch (method) {
|
|
case 'setItem':
|
|
result = storage.setItem(prefixedKey, value);
|
|
break;
|
|
case 'getItem':
|
|
result = storage.getItem(prefixedKey);
|
|
break;
|
|
case 'removeItem':
|
|
result = storage.removeItem(prefixedKey);
|
|
break;
|
|
default:
|
|
sendError(`Unknown method ${method}`);
|
|
return;
|
|
}
|
|
} catch (err) {
|
|
sendError(err.toString());
|
|
return;
|
|
}
|
|
|
|
pym.sendMessage(
|
|
`pymStorage.${type}.response`,
|
|
JSON.stringify({ id, result })
|
|
);
|
|
});
|
|
}
|