import { DateTime } from 'luxon'; import { SvelteMap } from 'svelte/reactivity'; import * as iter from '$lib/iterator.js'; export const STORE_KEY_CHANNELS_DATA = 'pilcrow:channelsData'; class Channel { draft = $state(); lastReadAt = $state(null); scrollPosition = $state(null); static fromStored({ draft, lastReadAt, scrollPosition }) { return new Channel({ draft, lastReadAt: lastReadAt == null ? null : DateTime.fromISO(lastReadAt), scrollPosition }); } constructor({ draft = '', lastReadAt = null, scrollPosition = null } = {}) { this.draft = draft; this.lastReadAt = lastReadAt; this.scrollPosition = scrollPosition; } toStored() { const { draft, lastReadAt, scrollPosition } = this; return { draft, lastReadAt: lastReadAt?.toISO(), scrollPosition }; } } export class Channels { // Store channelId -> { draft = '', lastReadAt = null, scrollPosition = null } all = $state(); static fromLocalStorage() { const stored = localStorage.getItem(STORE_KEY_CHANNELS_DATA); if (stored !== null) { return Channels.fromStored(JSON.parse(stored)); } return Channels.empty(); } static fromStored(stored) { const loaded = Object.keys(stored).map((channelId) => [ channelId, Channel.fromStored(stored[channelId]) ]); const all = new SvelteMap(loaded); return new Channels({ all }); } static empty() { return new Channels({ all: new SvelteMap() }); } constructor({ all }) { this.all = all; } channel(channelId) { let channel = this.all.get(channelId); if (channel === undefined) { channel = new Channel(); this.all.set(channelId, channel); } return channel; } updateLastReadAt(channelId, at) { const channel = this.channel(channelId); // Do it this way, rather than with Math.max tricks, to avoid assignment // when we don't need it, to minimize reactive changes: if (channel.lastReadAt === null || at > channel.lastReadAt) { channel.lastReadAt = at; this.save(); } } retainChannels(channelIds) { const retain = new Set(channelIds); for (const channelId of Array.from(this.all.keys())) { if (!retain.has(channelId)) { this.all.delete(channelId); } } this.save(); } toStored() { return iter.reduce( this.all.entries(), (stored, [channelId, channel]) => ({ ...stored, [channelId]: channel.toStored() }), {} ); } save() { let stored = this.toStored(); console.log(this, stored); localStorage.setItem(STORE_KEY_CHANNELS_DATA, JSON.stringify(stored)); } } function objectMap(object, mapFn) { return Object.keys(object).reduce((result, key) => { result[key] = mapFn(object[key]); return result; }, {}); }