SequenceDiagram/web/scripts/storage/MultiLocalStorage.mjs

113 lines
3.1 KiB
JavaScript

export default class MultiLocalStorage {
constructor(slotManager, slotStorage) {
this.slotManager = slotManager;
this.slotStorage = slotStorage;
this.slot = this.slotManager.getSlot();
this.value = this.get();
this.originalValue = this.value;
this.loadTime = Date.now();
this.internalStorageListener = this.internalStorageListener.bind(this);
window.addEventListener('storage', this.internalStorageListener);
this.checkSlot();
}
getCurrentValue() {
// If the page just loaded, clone the original document
// (works around glitches with CodeMirror when duplicating tabs)
if(Date.now() < this.loadTime + 500) {
return this.originalValue;
}
return this.value;
}
key() {
return this.slotStorage.getSlotKey(this.slot);
}
checkSlot() {
const key = this.key();
window.localStorage.removeItem(`chk-${key}`);
window.localStorage.removeItem(`res-${key}`);
window.localStorage.removeItem(`ack-${key}`);
// Check if any other tabs are viewing the same document
window.localStorage.setItem(`chk-${key}`, '1');
}
cloneSlot() {
const slotLimit = this.slotManager.maxSlots();
const newSlot = this.slotStorage.nextAvailableSlot(slotLimit);
if(!newSlot) {
return;
}
const value = this.getCurrentValue();
this.slotStorage.set(newSlot, value);
this.slot = newSlot;
this.slotManager.setSlot(newSlot);
// Force editor to load corrected content if needed
if(value !== this.value) {
document.location.reload();
}
}
// eslint-disable-next-line complexity
internalStorageListener({ storageArea, key, newValue }) {
if(storageArea !== window.localStorage) {
return;
}
const ownKey = this.key();
if(key === ownKey && newValue !== this.value) {
if(newValue === null) {
// Somebody deleted our document; put it back
// (a nicer explanation for the deleter may be nice, but later)
window.localStorage.setItem(ownKey, this.value);
}
// Another tab unexpectedly changed a value we own
// Remind them that we own the document
window.localStorage.removeItem(`res-${ownKey}`);
window.localStorage.setItem(`res-${ownKey}`, '1');
}
if(key === `chk-${ownKey}` && newValue) {
// Another tab is checking if our slot is in use; reply yes
window.localStorage.setItem(`res-${ownKey}`, '1');
}
if(key === `res-${ownKey}` && newValue) {
// Another tab owns our slot; clone the document
window.localStorage.removeItem(`chk-${ownKey}`);
window.localStorage.removeItem(`res-${ownKey}`);
window.localStorage.setItem(`ack-${ownKey}`, '1');
this.cloneSlot();
}
if(key === `ack-${ownKey}` && newValue) {
// Another tab has acknowledged us as the owner of the document
// Restore 'correct' value in case it was clobbered accidentally
window.localStorage.removeItem(`ack-${ownKey}`, '1');
window.localStorage.setItem(ownKey, this.value);
}
}
set(value) {
this.value = value;
this.slotStorage.set(this.slot, value);
}
get() {
return this.slotStorage.get(this.slot);
}
remove() {
this.slotStorage.remove(this.slot);
}
close() {
window.removeEventListener('storage', this.internalStorageListener);
}
}