vuex-shared-mutations
Version:
Share vuex mutations across tabs via localStorage
107 lines (97 loc) • 2.9 kB
JavaScript
const DEFAULT_KEY = "vuex-shared-mutations";
const globalObj =
typeof window !== "undefined"
? window
: /* istanbul ignore next: node env */ global;
const MAX_MESSAGE_LENGTH = 4 * 1024;
let messageCounter = 1;
function splitMessage(message) {
const partsCount = Math.ceil(message.length / MAX_MESSAGE_LENGTH);
return Array.from({ length: partsCount }).map((_, idx) =>
message.substr(idx * MAX_MESSAGE_LENGTH, MAX_MESSAGE_LENGTH)
);
}
export default class LocalStorageStrategy {
static available(
{ window: windowImpl, localStorage: localStorageImpl } = {
window: globalObj.window,
localStorage: globalObj.localStorage
}
) {
if (!windowImpl || !localStorageImpl) {
return false;
}
try {
localStorageImpl.setItem("vuex-shared-mutations-test-key", Date.now());
localStorageImpl.removeItem("vuex-shared-mutations-test-key");
return true;
} catch (e) {
return false;
}
}
constructor(options = {}) {
const windowImpl = options.window || globalObj.window;
const localStorageImpl = options.localStorage || globalObj.localStorage;
if (
!this.constructor.available({
window: windowImpl,
localStorage: localStorageImpl
})
) {
throw new Error("Strategy unavailable");
}
this.uniqueId = `${Date.now()}-${Math.random()}`;
this.messageBuffer = [];
this.window = windowImpl;
this.storage = localStorageImpl;
this.options = {
key: DEFAULT_KEY,
...options
};
}
// eslint-disable-next-line class-methods-use-this
addEventListener(fn) {
return this.window.addEventListener("storage", event => {
if (!event.newValue) {
return false;
}
if (
event.key.indexOf("##") === -1 ||
event.key.split("##")[0] !== this.options.key
) {
return false;
}
const message = this.window.JSON.parse(event.newValue);
/* istanbul ignore next: IE does not follow storage event spec */
if (message.author === this.uniqueId) {
return false;
}
this.messageBuffer.push(message.messagePart);
if (this.messageBuffer.length === message.total) {
const mutation = this.window.JSON.parse(this.messageBuffer.join(""));
this.messageBuffer = [];
fn(mutation);
}
return true;
});
}
share(message) {
const rawMessage = this.window.JSON.stringify(message);
const messageParts = splitMessage(rawMessage);
messageParts.forEach((m, idx) => {
messageCounter += 1;
const key = `${this.options.key}##${idx}`;
this.storage.setItem(
key,
JSON.stringify({
author: this.uniqueId,
part: idx,
total: messageParts.length,
messagePart: m,
messageCounter
})
);
this.storage.removeItem(key);
});
}
}