diff options
| author | Kit La Touche <kit@transneptune.net> | 2024-10-03 23:30:10 -0400 |
|---|---|---|
| committer | Kit La Touche <kit@transneptune.net> | 2024-10-03 23:30:10 -0400 |
| commit | 30c13478d61065a512f5bc8824fecbf2ee6afc81 (patch) | |
| tree | 5b9f0fe65458f6d19f7f0b3fed9c8d3e4676a175 /hi-ui | |
| parent | 01d995c731c296292cd3f1f9a4702eb96a0bf628 (diff) | |
Handle basics of interface scrolling
Diffstat (limited to 'hi-ui')
| -rw-r--r-- | hi-ui/src/apiServer.js | 61 | ||||
| -rw-r--r-- | hi-ui/src/lib/ActiveChannel.svelte | 31 | ||||
| -rw-r--r-- | hi-ui/src/lib/CreateChannelForm.svelte | 3 | ||||
| -rw-r--r-- | hi-ui/src/lib/LogIn.svelte | 3 | ||||
| -rw-r--r-- | hi-ui/src/lib/LogOut.svelte | 3 | ||||
| -rw-r--r-- | hi-ui/src/lib/Message.svelte | 38 | ||||
| -rw-r--r-- | hi-ui/src/lib/MessageInput.svelte | 18 | ||||
| -rw-r--r-- | hi-ui/src/routes/+page.svelte | 5 |
8 files changed, 130 insertions, 32 deletions
diff --git a/hi-ui/src/apiServer.js b/hi-ui/src/apiServer.js index 7365a36..5e521de 100644 --- a/hi-ui/src/apiServer.js +++ b/hi-ui/src/apiServer.js @@ -1,5 +1,5 @@ import axios from 'axios'; -import { events } from './store'; +import { activeChannel, channelsList, events } from './store'; export const apiServer = axios.create({ baseURL: '/api/', @@ -33,13 +33,70 @@ export async function postToChannel(channelId, message) { return apiServer.post(`/channels/${channelId}`, { message }); } +export async function deleteMessage(messageId) { + // TODO +} + export function subscribeToEvents() { const evtSource = new EventSource("/api/events"); + events.update(() => []); // 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. + /* + * Known message types as of now: + * - created: a channel is created. + * - action: ignore. + * - message: a message is created. + * - action: display message in channel. + * - message_deleted: a message is deleted. + * - action: replace message with <...>. + * - deleted: a channel is deleted. + * - action: remove channel from sidebar. + */ evtSource.onmessage = (evt) => { const data = JSON.parse(evt.data); - events.update((value) => [...value, data]); + + switch (data.type) { + case 'created': + break; + case 'message': + events.update((value) => { + const eventList = [...value, data]; + eventList.sort((a, b) => a.at - b.at); + return eventList; + }); + 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; + }); + 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; + }); + break; + default: + break; + } } } diff --git a/hi-ui/src/lib/ActiveChannel.svelte b/hi-ui/src/lib/ActiveChannel.svelte index 680a785..84f9119 100644 --- a/hi-ui/src/lib/ActiveChannel.svelte +++ b/hi-ui/src/lib/ActiveChannel.svelte @@ -1,28 +1,33 @@ <script> import { activeChannel, events } from '../store'; + import Message from './Message.svelte'; - let channel; - let allMessages = []; - $: messages = allMessages.filter( - (ev) => ev.type === 'message' && channel !== null && ev.channel.id === channel.id + let container; + $: messages = $events.filter( + (ev) => ( + ev.type === 'message' + && $activeChannel !== null + && ev.channel.id === $activeChannel.id + ) ); - activeChannel.subscribe((value) => { - channel = value; - }); + // TODO: eventually, store scroll height/last unread in channel? scroll there? - events.subscribe((value) => { - allMessages = value; - }); + let scroll = (message) => { + message.scrollIntoView(); + } </script> -<div> +<div class="container" bind:this={container}> {#each messages as message} - <div> - <pre><tt>{message.at} @{message.sender.name}: {message.message.body}</tt></pre> + <div use:scroll> + <Message {...message} /> </div> {/each} </div> <style> + .container { + overflow: scroll; + } </style> diff --git a/hi-ui/src/lib/CreateChannelForm.svelte b/hi-ui/src/lib/CreateChannelForm.svelte index 584fa61..70dc13d 100644 --- a/hi-ui/src/lib/CreateChannelForm.svelte +++ b/hi-ui/src/lib/CreateChannelForm.svelte @@ -7,7 +7,6 @@ let disabled = false; async function handleSubmit(event) { - event.preventDefault(); disabled = true; const response = await createChannel(name); if (200 <= response.status && response.status < 300) { @@ -18,7 +17,7 @@ } </script> -<form on:submit={handleSubmit}> +<form on:submit|preventDefault={handleSubmit}> <input type="text" placeholder="channel name" bind:value={name} disabled={disabled} /> <button type="submit">create</button> </form> diff --git a/hi-ui/src/lib/LogIn.svelte b/hi-ui/src/lib/LogIn.svelte index df734ee..1ec6772 100644 --- a/hi-ui/src/lib/LogIn.svelte +++ b/hi-ui/src/lib/LogIn.svelte @@ -7,7 +7,6 @@ let password = ''; async function handleLogin(event) { - event.preventDefault(); disabled = true; const response = await logIn(username, password); if (200 <= response.status && response.status < 300) { @@ -19,7 +18,7 @@ } </script> -<form class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4" on:submit={handleLogin}> +<form class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4" on:submit|preventDefault={handleLogin}> <div class="mb-4"> <label class="block text-gray-700 text-sm font-bold mb-2" for="username"> username diff --git a/hi-ui/src/lib/LogOut.svelte b/hi-ui/src/lib/LogOut.svelte index 64d26c9..738be24 100644 --- a/hi-ui/src/lib/LogOut.svelte +++ b/hi-ui/src/lib/LogOut.svelte @@ -9,7 +9,6 @@ }); async function handleLogout(event) { - event.preventDefault(); const response = await logOut(); if (200 <= response.status && response.status < 300) { currentUser.update(() => null); @@ -17,7 +16,7 @@ } </script> -<form on:submit={handleLogout}> +<form on:submit|preventDefault={handleLogout}> @{user.username} <button class="border-slate-500 border-solid border-2 font-bold p-1 rounded" diff --git a/hi-ui/src/lib/Message.svelte b/hi-ui/src/lib/Message.svelte new file mode 100644 index 0000000..d3ecbd8 --- /dev/null +++ b/hi-ui/src/lib/Message.svelte @@ -0,0 +1,38 @@ +<script> + import { currentUser } from '../store'; + import { deleteMessage } from '../apiServer'; + + export let at; + export let sender; + export let message; + + let timestamp = new Date(at).toTimeString(); + + function handleDeleteMessage() { + deleteMessage(message.id); + } +</script> + +<div class="hover:bg-zinc-300 flex flex-row"> + <div class="sender basis-20 text-right mr-1"> + @{sender.name}: + </div> + <div class="body grow"> + {message.body} + </div> + <div class="timestamp basis-6"> + <!-- TODO: this is too long and looks awful. --> + <!-- {timestamp} --> + </div> + {#if sender.id === $currentUser?.id} + <div class="controls basis-1 hidden relative -top-3 rounded-md border-2 border-slate-600 px-1 bg-slate-300"> + <button on:click|preventDefault={handleDeleteMessage}>🗑</button> + </div> + {/if} +</div> + +<style> + div:hover .controls { + display: block; + } +</style> diff --git a/hi-ui/src/lib/MessageInput.svelte b/hi-ui/src/lib/MessageInput.svelte index 96e9577..938e467 100644 --- a/hi-ui/src/lib/MessageInput.svelte +++ b/hi-ui/src/lib/MessageInput.svelte @@ -1,27 +1,25 @@ <script> + import { tick } from 'svelte'; import { postToChannel } from '../apiServer'; import { activeChannel } from '../store'; + let self; let input; - let disabled = false; - let activeChannelId; - - activeChannel.subscribe((value) => { - activeChannelId = value ? value.id : null; - }); + $: disabled = $activeChannel == null; async function handleSubmit(event) { - event.preventDefault(); disabled = true; // TODO try/catch: - await postToChannel(activeChannelId, input); + await postToChannel($activeChannel?.id, input); input = ''; disabled = false; + await tick(); + self.focus(); } </script> -<form on:submit={handleSubmit}> - <input type="text" class="border rounded px-3" bind:value={input} disabled={disabled}> +<form on:submit|preventDefault={handleSubmit}> + <input type="text" class="border rounded px-3" bind:this={self} bind:value={input} disabled={disabled}> <button type="submit">➤</button> </form> diff --git a/hi-ui/src/routes/+page.svelte b/hi-ui/src/routes/+page.svelte index 646665e..66b4f8d 100644 --- a/hi-ui/src/routes/+page.svelte +++ b/hi-ui/src/routes/+page.svelte @@ -25,6 +25,7 @@ case 200: currentUser.update(() => ({ username: response.data.login.name, + id: response.data.login.id, })); subscribeToEvents(); break; @@ -66,7 +67,7 @@ <style> #interface { - height: 90%; + height: 89vh; margin: 1rem; display: grid; grid-template-columns: 18rem auto; @@ -74,6 +75,8 @@ grid-gap: 0.25rem; } #interface div { + max-height: 100%; + overflow: scroll; border: 1px solid grey; } </style> |
