diff options
Diffstat (limited to 'ui/lib/store')
| -rw-r--r-- | ui/lib/store/messages.js | 40 | ||||
| -rw-r--r-- | ui/lib/store/messages.svelte.js | 74 |
2 files changed, 74 insertions, 40 deletions
diff --git a/ui/lib/store/messages.js b/ui/lib/store/messages.js deleted file mode 100644 index 62c567a..0000000 --- a/ui/lib/store/messages.js +++ /dev/null @@ -1,40 +0,0 @@ -export class Messages { - constructor() { - this.channels = {}; - } - - inChannel(channel) { - return (this.channels[channel] = this.channels[channel] || []); - } - - addMessage(channel, id, at, sender, body) { - this.updateChannel(channel, (messages) => [...messages, { id, at, sender, body }]); - return this; - } - - setMessages(messages) { - this.channels = {}; - for (let { channel, id, at, sender, body } of messages) { - this.inChannel(channel).push({ id, at, sender, body }); - } - return this; - } - - deleteMessage(message) { - for (let channel in this.channels) { - this.updateChannel(channel, (messages) => messages.filter((msg) => msg.id != message)); - } - return this; - } - - deleteChannel(id) { - delete this.channels[id]; - return this; - } - - updateChannel(channel, callback) { - let messages = callback(this.inChannel(channel)); - messages.sort((a, b) => a.at - b.at); - this.channels[channel] = messages; - } -} diff --git a/ui/lib/store/messages.svelte.js b/ui/lib/store/messages.svelte.js new file mode 100644 index 0000000..c0db71b --- /dev/null +++ b/ui/lib/store/messages.svelte.js @@ -0,0 +1,74 @@ +const RUN_COALESCE_MAX_INTERVAL = 10 /* min */ * 60 /* sec */ * 1000; /* ms */ + +export class Messages { + channels = $state({}); + + inChannel(channel) { + return this.channels[channel]; + } + + addMessage(channel, id, { at, sender, body }) { + let parsedAt = new Date(at); + const message = { id, at: parsedAt, body }; + + // You might be thinking, can't this be + // + // let runs = (this.channels[channel] ||= []); + // + // Let me tell you, I thought that too. Javascript's semantics allow it. It + // didn't work - the first message in each channel was getting lost as the + // update to `this.channels` wasn't actually happening. I suspect this is + // due to the implementation of Svelte's `$state` rune, but I don't know it + // for sure. + // + // In any case, splitting the read and write up like this has the same + // semantics, and _works_. (This time, for sure!) + let runs = this.channels[channel] || []; + + let currentRun = runs.slice(-1)[0]; + if (currentRun === undefined) { + currentRun = { sender, messages: [message] }; + runs.push(currentRun); + } else { + let lastMessage = currentRun.messages.slice(-1)[0]; + let newRun = + currentRun.sender !== sender || parsedAt - lastMessage.at > RUN_COALESCE_MAX_INTERVAL; + + if (newRun) { + currentRun = { sender, messages: [message] }; + runs.push(currentRun); + } else { + currentRun.messages.push(message); + } + } + + this.channels[channel] = runs; + + return this; + } + + setMessages(messages) { + this.channels = {}; + for (let { channel, id, at, sender, body } of messages) { + this.addMessage(channel, id, { at, sender, body }); + } + return this; + } + + deleteMessage(messageId) { + for (let channel in this.channels) { + this.channels[channel] = this.channels[channel] + .map(({ sender, messages }) => ({ + sender, + messages: messages.filter(({ id }) => id != messageId) + })) + .filter(({ messages }) => messages.length > 0); + } + return this; + } + + deleteChannel(id) { + delete this.channels[id]; + return this; + } +} |
