diff options
| author | Kit La Touche <kit@transneptune.net> | 2025-02-11 23:16:16 -0500 |
|---|---|---|
| committer | Kit La Touche <kit@transneptune.net> | 2025-02-20 21:53:25 -0500 |
| commit | 43af74832f9a2fa7f40dc71985eec9b0ada087dd (patch) | |
| tree | dab580484cc169825ad48ca66d0afb18b7aa8f6c /ui | |
| parent | 8a2b499fc7e00e841c56d23ac41f4d587b728a50 (diff) | |
Merge local channel data and remote to maintain state
When we hit the boot endpoint, we get the server's view of things. If we
just setChannels with that, we overwrite all our locally-stored info in
on things like lastReadAt. So we need to merge data.
Eventually, this might militate for a `meta` key containing an object of
locally stored data, rather than having to handle each key specially.
Diffstat (limited to 'ui')
| -rw-r--r-- | ui/lib/constants.js | 1 | ||||
| -rw-r--r-- | ui/lib/store.js | 6 | ||||
| -rw-r--r-- | ui/lib/store/channels.svelte.js | 60 | ||||
| -rw-r--r-- | ui/routes/(app)/ch/[channel]/+page.svelte | 12 |
4 files changed, 67 insertions, 12 deletions
diff --git a/ui/lib/constants.js b/ui/lib/constants.js new file mode 100644 index 0000000..a707c4b --- /dev/null +++ b/ui/lib/constants.js @@ -0,0 +1 @@ +export const STORE_KEY_CHANNELS_DATA = 'pilcrow:channelsData'; diff --git a/ui/lib/store.js b/ui/lib/store.js index 47ebbc2..f408c0c 100644 --- a/ui/lib/store.js +++ b/ui/lib/store.js @@ -1,9 +1,13 @@ import { writable } from 'svelte/store'; +import { browser } from '$app/environment'; import { Channels } from '$lib/store/channels.svelte.js'; import { Messages } from '$lib/store/messages.svelte.js'; import { Logins } from '$lib/store/logins'; +// Get channelsList content from the local storage +const channelsData = (browser && JSON.parse(localStorage.getItem('pilcrow:channelsData'))) || {}; + export const currentUser = writable(null); export const logins = writable(new Logins()); -export const channelsList = writable(new Channels()); +export const channelsList = writable(new Channels({ channelsData })); export const messages = writable(new Messages()); diff --git a/ui/lib/store/channels.svelte.js b/ui/lib/store/channels.svelte.js index c82f9aa..49cc31c 100644 --- a/ui/lib/store/channels.svelte.js +++ b/ui/lib/store/channels.svelte.js @@ -1,4 +1,5 @@ import { DateTime } from 'luxon'; +import { STORE_KEY_CHANNELS_DATA } from '$lib/constants'; const EPOCH_STRING = '1970-01-01T00:00:00Z'; // For reasons unclear to me, a straight up class definition with a constructor @@ -10,24 +11,79 @@ const EPOCH_STRING = '1970-01-01T00:00:00Z'; // Emphasis on "simple object". // --Kit function makeChannelObject({ id, name, draft = '', lastReadAt = null, scrollPosition = null }) { + let lastReadAtParsed; + if (Boolean(lastReadAt)) { + if (typeof lastReadAt === "string") { + lastReadAtParsed = DateTime.fromISO(lastReadAt); + } else { + lastReadAtParsed = lastReadAt; + } + } else { + lastReadAtParsed = DateTime.fromISO(EPOCH_STRING); + } return { id, name, - lastReadAt: lastReadAt || DateTime.fromISO(EPOCH_STRING), + lastReadAt: lastReadAtParsed, draft, scrollPosition }; } +function mergeLocalData(remoteData, currentData) { + let currentDataObj = currentData.reduce( + (acc, cur) => { + acc[cur.id] = cur; + return acc; + }, + {} + ); + const ret = remoteData.map( + (ch) => { + const newCh = makeChannelObject(ch); + if (Boolean(currentDataObj[ch.id])) { + newCh.lastReadAt = currentDataObj[ch.id].lastReadAt; + } + return newCh; + } + ); + return ret; +} + export class Channels { channels = $state([]); + constructor({ channelsData }) { + this.channels = channelsData.map(makeChannelObject); + // On channel edits (inc 'last read' ones), write out to localstorage? + } + + writeOutToLocalStorage() { + localStorage.setItem( + STORE_KEY_CHANNELS_DATA, + JSON.stringify(this.channels) + ); + } + + updateLastReadAt(channelId, at) { + const channelObject = this.getChannel(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 (at > channelObject.lastReadAt) { + channelObject.lastReadAt = at; + this.writeOutToLocalStorage(); + } + } + getChannel(channelId) { return this.channels.filter((ch) => ch.id === channelId)[0] || null; } setChannels(channels) { - this.channels = channels.map(makeChannelObject); + // This gets called, among other times, when the page is first loaded, with + // server-sent data from the `boot` endpoint. That needs to be merged with + // locally stored data! + this.channels = mergeLocalData(channels, this.channels); this.sort(); return this; } diff --git a/ui/routes/(app)/ch/[channel]/+page.svelte b/ui/routes/(app)/ch/[channel]/+page.svelte index dbdb507..d64a8c9 100644 --- a/ui/routes/(app)/ch/[channel]/+page.svelte +++ b/ui/routes/(app)/ch/[channel]/+page.svelte @@ -33,16 +33,10 @@ } function setLastRead() { - const channelObject = $channelsList.getChannel(channel); const lastInView = getLastVisibleMessage(); - if (!channelObject || !lastInView) { - return; - } - const at = DateTime.fromISO(lastInView.dataset.at); - // Do it this way, rather than with Math.max tricks, to avoid assignment - // when we don't need it, to minimize reactive changes: - if (at > channelObject.lastReadAt) { - channelObject.lastReadAt = at; + if (lastInView) { + const at = DateTime.fromISO(lastInView.dataset.at); + $channelsList.updateLastReadAt(channel, at); } } |
