gamja-old/store.js
2023-06-08 12:09:24 +02:00

166 lines
3.2 KiB
JavaScript

import { ReceiptType, Unread } from "./state.js";
const PREFIX = "gamja_";
class Item {
constructor(k) {
this.k = PREFIX + k;
}
load() {
let v = localStorage.getItem(this.k);
if (!v) {
return null;
}
return JSON.parse(v);
}
put(v) {
if (v) {
localStorage.setItem(this.k, JSON.stringify(v));
} else {
localStorage.removeItem(this.k);
}
}
}
export const autoconnect = new Item("autoconnect");
export const naggedProtocolHandler = new Item("naggedProtocolHandler");
export const settings = new Item("settings");
function debounce(f, delay) {
let timeout = null;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => {
timeout = null;
f(...args);
}, delay);
};
}
export class Buffer {
raw = new Item("buffers");
m = null;
constructor() {
let obj = this.raw.load();
this.m = new Map(Object.entries(obj || {}));
let saveImmediately = this.save.bind(this);
this.save = debounce(saveImmediately, 500);
document.addEventListener("visibilitychange", () => {
if (document.visibilityState === "hidden") {
saveImmediately();
}
});
}
key(buf) {
// TODO: use case-mapping here somehow
return JSON.stringify({
name: buf.name.toLowerCase(),
server: {
bouncerNetwork: buf.server.bouncerNetwork,
},
});
}
save() {
if (this.m.size > 0) {
this.raw.put(Object.fromEntries(this.m));
} else {
this.raw.put(null);
}
}
get(buf) {
return this.m.get(this.key(buf));
}
put(buf) {
let key = this.key(buf);
let updated = !this.m.has(key);
let prev = this.m.get(key) || {};
let unread = prev.unread || Unread.NONE;
if (buf.unread !== undefined && buf.unread !== prev.unread) {
unread = buf.unread;
updated = true;
}
let receipts = { ...prev.receipts };
if (buf.receipts) {
Object.keys(buf.receipts).forEach((k) => {
// Use a not-equals comparison here so that no-op receipt
// changes are correctly handled
if (!receipts[k] || receipts[k].time < buf.receipts[k].time) {
receipts[k] = buf.receipts[k];
updated = true;
}
});
if (receipts[ReceiptType.DELIVERED] < receipts[ReceiptType.READ]) {
receipts[ReceiptType.DELIVERED] = receipts[ReceiptType.READ];
updated = true;
}
}
let closed = prev.closed || false;
if (buf.closed !== undefined && buf.closed !== prev.closed) {
closed = buf.closed;
updated = true;
}
if (!updated) {
return false;
}
this.m.set(this.key(buf), {
name: buf.name,
unread,
receipts,
closed,
server: {
bouncerNetwork: buf.server.bouncerNetwork,
},
});
this.save();
return true;
}
delete(buf) {
this.m.delete(this.key(buf));
this.save();
}
list(server) {
// Some gamja versions would store the same buffer multiple times
let names = new Set();
let buffers = [];
for (const buf of this.m.values()) {
if (buf.server.bouncerNetwork !== server.bouncerNetwork) {
continue;
}
if (names.has(buf.name)) {
continue;
}
buffers.push(buf);
names.add(buf.name);
}
return buffers;
}
clear(server) {
if (server) {
for (const buf of this.list(server)) {
this.m.delete(this.key(buf));
}
} else {
this.m = new Map();
}
this.save();
}
}