107 lines
3.3 KiB
TypeScript
107 lines
3.3 KiB
TypeScript
let isLocalStorageEnabled = true;
|
|
|
|
interface MemoryStore {
|
|
[key: string]: string;
|
|
}
|
|
|
|
const memoryStorage: MemoryStore = {};
|
|
|
|
const subscriptions = new EventTarget();
|
|
|
|
export interface LocalStorageEvent extends CustomEvent {
|
|
key: string;
|
|
newValue: string;
|
|
}
|
|
|
|
export const getLocalStorage = (key: string): string | null => {
|
|
if (!isLocalStorageEnabled) {
|
|
return Object.prototype.hasOwnProperty.call(memoryStorage, key) ? memoryStorage[key] : null;
|
|
}
|
|
|
|
try {
|
|
const value = window.localStorage.getItem(key);
|
|
if (value && memoryStorage[key] !== value) {
|
|
memoryStorage[key] = value;
|
|
}
|
|
return value;
|
|
} catch (e) {
|
|
// eslint-disable-next-line no-console
|
|
console.warn('localStorage is diasabled using memory storage instead.', e);
|
|
isLocalStorageEnabled = false;
|
|
return getLocalStorage(key);
|
|
}
|
|
};
|
|
|
|
export const setLocalStorage = (key: string, newValue: string): void => {
|
|
const event = new CustomEvent('local-storage-change', { detail: { key, newValue } });
|
|
|
|
if (!isLocalStorageEnabled) {
|
|
memoryStorage[key] = newValue;
|
|
subscriptions.dispatchEvent(event);
|
|
return;
|
|
}
|
|
try {
|
|
window.localStorage.setItem(key, newValue);
|
|
memoryStorage[key] = newValue;
|
|
subscriptions.dispatchEvent(event);
|
|
} catch (e) {
|
|
// eslint-disable-next-line no-console
|
|
console.warn('localStorage is diasabled using memory storage instead.', e);
|
|
isLocalStorageEnabled = false;
|
|
return setLocalStorage(key, newValue);
|
|
}
|
|
};
|
|
|
|
export const isKeySet = (key: string): boolean => {
|
|
if (isLocalStorageEnabled) {
|
|
try {
|
|
for (let i = 0; i < window.localStorage.length; i++) {
|
|
if (window.localStorage.key(i) === key) {
|
|
return true;
|
|
}
|
|
}
|
|
} catch {
|
|
// ignore the error
|
|
}
|
|
}
|
|
return Object.keys(memoryStorage).includes(key);
|
|
};
|
|
|
|
// This function allows you to subscribe to localStorage changes. By default
|
|
// this includes changes that are made in other tabs/windows of the same
|
|
// browser. The 'callback' will be called whenever a change is made to
|
|
// localStorage at the given 'key'. An unsubscibe function is returned, which
|
|
// you can call when you are ready to unsubscribe.
|
|
export const subscribeToLocalStorage = (
|
|
key: string,
|
|
callback: (value: string) => void,
|
|
options?: { excludeExternal?: boolean }
|
|
): (() => void) => {
|
|
const { excludeExternal } = options || {};
|
|
const updateHandler = (e: LocalStorageEvent | { detail: { key: string; newValue: string } }) => {
|
|
const { key: eventKey, newValue } = 'detail' in e ? e.detail : e;
|
|
if (eventKey === key) {
|
|
callback(newValue);
|
|
}
|
|
};
|
|
// subscribe to events for this instance of the page only
|
|
subscriptions.addEventListener(
|
|
'local-storage-change',
|
|
updateHandler as EventListenerOrEventListenerObject
|
|
);
|
|
|
|
// if not excluded, subscribe to events for all instances of the page
|
|
!excludeExternal &&
|
|
window.addEventListener('storage', updateHandler as EventListenerOrEventListenerObject);
|
|
|
|
// return a function that will be used to unsubscribe to these events.
|
|
return () => {
|
|
subscriptions.removeEventListener(
|
|
'local-storage-change',
|
|
updateHandler as EventListenerOrEventListenerObject
|
|
);
|
|
!excludeExternal &&
|
|
window.removeEventListener('storage', updateHandler as EventListenerOrEventListenerObject);
|
|
};
|
|
};
|