diff options
Diffstat (limited to 'ui/lib/state/local')
| -rw-r--r-- | ui/lib/state/local/channels.svelte.js | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/ui/lib/state/local/channels.svelte.js b/ui/lib/state/local/channels.svelte.js new file mode 100644 index 0000000..9040685 --- /dev/null +++ b/ui/lib/state/local/channels.svelte.js @@ -0,0 +1,118 @@ +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(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; + }, {}); +} |
