diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2024-10-05 23:00:58 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2024-10-05 23:00:58 -0400 |
| commit | 05de3c7b211727039b3912311aa4bab6787a7457 (patch) | |
| tree | 08a3860b68391514390f42872ccc1cb4c6e6afd2 /hi-ui/src | |
| parent | bc514e0ea5f0a553f15ab8275961907877181520 (diff) | |
| parent | 6a10fcaf64938da52b326ea80013d9f30ed62a6c (diff) | |
Merge branch 'wip/boot'
Diffstat (limited to 'hi-ui/src')
| -rw-r--r-- | hi-ui/src/apiServer.js | 48 | ||||
| -rw-r--r-- | hi-ui/src/lib/ActiveChannel.svelte | 12 | ||||
| -rw-r--r-- | hi-ui/src/lib/Channel.svelte | 4 | ||||
| -rw-r--r-- | hi-ui/src/lib/ChannelList.svelte | 22 | ||||
| -rw-r--r-- | hi-ui/src/lib/CreateChannelForm.svelte | 3 | ||||
| -rw-r--r-- | hi-ui/src/lib/MessageInput.svelte | 4 | ||||
| -rw-r--r-- | hi-ui/src/routes/+page.svelte | 25 | ||||
| -rw-r--r-- | hi-ui/src/store.js | 8 | ||||
| -rw-r--r-- | hi-ui/src/store/channels.js | 71 | ||||
| -rw-r--r-- | hi-ui/src/store/messages.js | 41 |
10 files changed, 158 insertions, 80 deletions
diff --git a/hi-ui/src/apiServer.js b/hi-ui/src/apiServer.js index 5e521de..f4a89a4 100644 --- a/hi-ui/src/apiServer.js +++ b/hi-ui/src/apiServer.js @@ -1,5 +1,5 @@ import axios from 'axios'; -import { activeChannel, channelsList, events } from './store'; +import { activeChannel, channelsList, messages } from './store'; export const apiServer = axios.create({ baseURL: '/api/', @@ -21,10 +21,6 @@ export async function logOut() { return apiServer.post('/auth/logout', {}); } -export async function listChannels() { - return apiServer.get('/channels'); -} - export async function createChannel(name) { return apiServer.post('/channels', { name }); } @@ -37,9 +33,10 @@ export async function deleteMessage(messageId) { // TODO } -export function subscribeToEvents() { - const evtSource = new EventSource("/api/events"); - events.update(() => []); +export function subscribeToEvents(resume_point) { + const eventsUrl = new URL('/api/events', window.location); + eventsUrl.searchParams.append('resume_point', resume_point); + const evtSource = new EventSource(eventsUrl.toString()); // TODO: this should process all incoming events and store them. // TODO: eventually we'll need to handle expiring old info, so as not to use // infinite browser memory. @@ -59,41 +56,18 @@ export function subscribeToEvents() { switch (data.type) { case 'created': + channelsList.update((value) => value.addChannel(data.channel)) break; case 'message': - events.update((value) => { - const eventList = [...value, data]; - eventList.sort((a, b) => a.at - b.at); - return eventList; - }); + messages.update((value) => value.addMessage(data)); break; case 'message_deleted': - events.update((value) => { - const eventList = value.map((el) => { - if (el.message?.id === data.message) { - el.message.body = '«…»'; - return el - } else { - return el; - } - }); - return eventList; - }); + messages.update((value) => value.deleteMessage(data.channel.id, data.message)); break; case 'deleted': - activeChannel.update((value) => { - if (value?.id === data.channel) { - return null; - } - return value; - }); - channelsList.update((value) => { - const channelIndex = value.map((e) => e.id).indexOf(data.channel); - if (channelIndex !== -1) { - value.splice(channelIndex, 1); - } - return value; - }); + activeChannel.update((value) => value.deleteChannel(data.channel)); + channelsList.update((value) => value.deleteChannel(data.channel)); + messages.update((value) => value.deleteChannel(data.channel)); break; default: break; diff --git a/hi-ui/src/lib/ActiveChannel.svelte b/hi-ui/src/lib/ActiveChannel.svelte index 84f9119..d2d92fb 100644 --- a/hi-ui/src/lib/ActiveChannel.svelte +++ b/hi-ui/src/lib/ActiveChannel.svelte @@ -1,15 +1,9 @@ <script> - import { activeChannel, events } from '../store'; + import { activeChannel, messages } from '../store'; import Message from './Message.svelte'; let container; - $: messages = $events.filter( - (ev) => ( - ev.type === 'message' - && $activeChannel !== null - && ev.channel.id === $activeChannel.id - ) - ); + $: messageList = $activeChannel.isSet() ? $messages.inChannel($activeChannel.get()) : []; // TODO: eventually, store scroll height/last unread in channel? scroll there? @@ -19,7 +13,7 @@ </script> <div class="container" bind:this={container}> - {#each messages as message} + {#each messageList as message} <div use:scroll> <Message {...message} /> </div> diff --git a/hi-ui/src/lib/Channel.svelte b/hi-ui/src/lib/Channel.svelte index 7826c46..ad07594 100644 --- a/hi-ui/src/lib/Channel.svelte +++ b/hi-ui/src/lib/Channel.svelte @@ -6,11 +6,11 @@ let active = false; activeChannel.subscribe((value) => { - active = value ? value.id == id : false; + active = value.is(id); }); function activate() { - activeChannel.update(() => ({ id, name })); + activeChannel.update((value) => value.set(id)); } </script> diff --git a/hi-ui/src/lib/ChannelList.svelte b/hi-ui/src/lib/ChannelList.svelte index 9f88e24..ba48e5d 100644 --- a/hi-ui/src/lib/ChannelList.svelte +++ b/hi-ui/src/lib/ChannelList.svelte @@ -1,32 +1,18 @@ <script> - import { onMount } from 'svelte'; - - import { listChannels } from '../apiServer'; import { channelsList } from '../store'; import Channel from './Channel.svelte'; let channels; - let loading = true; channelsList.subscribe((value) => { - channels = value; - }); - - onMount(async () => { - let channels = await listChannels(); - channelsList.update(() => channels.data); - loading = false; + channels = value.channels; }); </script> <ul class="select-none"> - {#if loading} - <li><em>loading channels…</em></li> - {:else} - {#each channels as channel} - <Channel {...channel} /> - {/each} - {/if} + {#each channels as channel} + <Channel {...channel} /> + {/each} </ul> <style> diff --git a/hi-ui/src/lib/CreateChannelForm.svelte b/hi-ui/src/lib/CreateChannelForm.svelte index 70dc13d..c08430b 100644 --- a/hi-ui/src/lib/CreateChannelForm.svelte +++ b/hi-ui/src/lib/CreateChannelForm.svelte @@ -1,8 +1,6 @@ <script> import { createChannel } from '../apiServer'; - import { channelsList } from '../store'; - let name = ''; let disabled = false; @@ -10,7 +8,6 @@ disabled = true; const response = await createChannel(name); if (200 <= response.status && response.status < 300) { - channelsList.update((value) => [...value, response.data]); name = ''; } disabled = false; diff --git a/hi-ui/src/lib/MessageInput.svelte b/hi-ui/src/lib/MessageInput.svelte index 9a8475c..b899221 100644 --- a/hi-ui/src/lib/MessageInput.svelte +++ b/hi-ui/src/lib/MessageInput.svelte @@ -8,12 +8,12 @@ let self; let input; - $: disabled = $activeChannel == null; + $: disabled = !$activeChannel.isSet(); async function handleSubmit(event) { disabled = true; // TODO try/catch: - await postToChannel($activeChannel?.id, input); + await postToChannel($activeChannel.get(), input); input = ''; disabled = false; await tick(); diff --git a/hi-ui/src/routes/+page.svelte b/hi-ui/src/routes/+page.svelte index 66b4f8d..1a61b3e 100644 --- a/hi-ui/src/routes/+page.svelte +++ b/hi-ui/src/routes/+page.svelte @@ -2,7 +2,7 @@ import { onMount } from 'svelte'; import { boot, subscribeToEvents } from '../apiServer'; - import { currentUser } from '../store'; + import { currentUser, channelsList, messages } from '../store'; import ActiveChannel from '../lib/ActiveChannel.svelte'; import ChannelList from '../lib/ChannelList.svelte'; @@ -18,16 +18,29 @@ user = value; }); + function onBooted(boot) { + currentUser.update(() => ({ + id: boot.login.id, + username: boot.login.name, + })); + let channels = boot.channels.map((channel) => ({ + id: channel.id, + name: channel.name, + })); + channelsList.update((value) => value.setChannels(channels)); + let bootMessages = boot.channels.map((channel) => [channel.id, channel.messages]); + for (let [channel, channelMessages] of bootMessages) { + messages.update((value) => value.addMessages(channel, channelMessages)); + } + } + onMount(async () => { try { let response = await boot(); switch (response.status) { case 200: - currentUser.update(() => ({ - username: response.data.login.name, - id: response.data.login.id, - })); - subscribeToEvents(); + onBooted(response.data); + subscribeToEvents(response.data.resume_point); break; case 401: currentUser.update(() => null); diff --git a/hi-ui/src/store.js b/hi-ui/src/store.js index a9d9421..4e6b4f1 100644 --- a/hi-ui/src/store.js +++ b/hi-ui/src/store.js @@ -1,6 +1,8 @@ import { writable } from 'svelte/store'; +import { ActiveChannel, Channels } from './store/channels'; +import { Messages } from './store/messages'; export const currentUser = writable(null); -export const activeChannel = writable(null); -export const channelsList = writable([]); -export const events = writable([]); +export const activeChannel = writable(new ActiveChannel()); +export const channelsList = writable(new Channels()); +export const messages = writable(new Messages()); diff --git a/hi-ui/src/store/channels.js b/hi-ui/src/store/channels.js new file mode 100644 index 0000000..20702cc --- /dev/null +++ b/hi-ui/src/store/channels.js @@ -0,0 +1,71 @@ +export class Channels { + constructor() { + this.channels = []; + } + + setChannels(channels) { + this.channels = [...channels]; + this.sort(); + return this; + } + + addChannel(channel) { + this.channels = [...this.channels, channel]; + this.sort(); + return this; + } + + deleteChannel(id) { + const channelIndex = this.channels.map((e) => e.id).indexOf(id); + if (channelIndex !== -1) { + this.channels.splice(channelIndex, 1); + } + return this; + } + + sort() { + this.channels.sort((a, b) => { + if (a.name < b.name) { + return -1; + } else if (a.name > b.name) { + return 1; + } + return 0; + }); + } +} + +export class ActiveChannel { + constructor() { + this.channel = null; + } + + isSet() { + return this.channel !== null; + } + + get() { + return this.channel; + } + + is(id) { + return this.channel === id; + } + + set(id) { + this.channel = id; + return this; + } + + deleteChannel(id) { + if (this.is(id)) { + return this.clear(); + } + return this; + } + + clear() { + this.channel = null; + return this; + } +} diff --git a/hi-ui/src/store/messages.js b/hi-ui/src/store/messages.js new file mode 100644 index 0000000..560b9e1 --- /dev/null +++ b/hi-ui/src/store/messages.js @@ -0,0 +1,41 @@ +export class Messages { + constructor() { + this.channels = {}; + } + + inChannel(channel) { + return this.channels[channel] || []; + } + + addMessage(message) { + let { + channel, + ...payload + } = message; + let channel_id = channel.id; + this.updateChannel(channel_id, (messages) => [...messages, payload]); + return this; + } + + addMessages(channel, payloads) { + this.updateChannel(channel, (messages) => [...messages, ...payloads]); + return this; + } + + + deleteMessage(channel, message) { + let messages = this.messages(channel).filter((msg) => msg.message.id != message); + this.channels[channel] = messages; + } + + 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; + } +} |
