diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2025-02-15 15:17:03 -0500 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2025-02-21 17:49:38 -0500 |
| commit | fc0f1654a56d2247728a766f43e72ff169704888 (patch) | |
| tree | 945f44c9a90bf51de20c61a5a8c5ed82c2c05009 /ui/lib/components | |
| parent | 36cadfe00cacc6a6523f9862d3f7a08a9d0ce611 (diff) | |
Hoist global state access out of individual components.
Access to "global" (maybe "external?") state is now handled at the top level of the component hierarchy, in `+page.svelte`, `+layout.svelte`, and their associated scripts. State is otherwise passed down through props, and changes are passed up through callbacks.
This is - hopefully - groundwork for refactoring state management a bit. I wanted to move access to state out to a smaller number of places, so that I have fewer places to update to implement reconnect logic. My broader goal is to make it easier to refactor these kinds of external side effects, as well, though no such changes are in this branch.
This change also makes testing a mile easier, since tests can interact with props and callbacks instead of emulating the whole HTTP request stack and the Pilcrow API. This change removes do-very-little tests.
Diffstat (limited to 'ui/lib/components')
| -rw-r--r-- | ui/lib/components/ActiveChannel.svelte | 6 | ||||
| -rw-r--r-- | ui/lib/components/ChangePassword.svelte | 24 | ||||
| -rw-r--r-- | ui/lib/components/CreateChannelForm.svelte | 15 | ||||
| -rw-r--r-- | ui/lib/components/Invites.svelte | 8 | ||||
| -rw-r--r-- | ui/lib/components/LogIn.svelte | 27 | ||||
| -rw-r--r-- | ui/lib/components/LogOut.svelte | 10 | ||||
| -rw-r--r-- | ui/lib/components/Message.svelte | 7 | ||||
| -rw-r--r-- | ui/lib/components/MessageInput.svelte | 25 | ||||
| -rw-r--r-- | ui/lib/components/MessageRun.svelte | 10 |
9 files changed, 63 insertions, 69 deletions
diff --git a/ui/lib/components/ActiveChannel.svelte b/ui/lib/components/ActiveChannel.svelte index 9c181e4..f7837aa 100644 --- a/ui/lib/components/ActiveChannel.svelte +++ b/ui/lib/components/ActiveChannel.svelte @@ -1,9 +1,9 @@ <script> import MessageRun from './MessageRun.svelte'; - let { messageRuns } = $props(); + let { messageRuns, deleteMessage = async (id) => {} } = $props(); </script> -{#each messageRuns as { sender, messages }} - <MessageRun {sender} {messages} /> +{#each messageRuns as { sender, ownMessage, messages }} + <MessageRun {sender} {ownMessage} {messages} {deleteMessage} /> {/each} diff --git a/ui/lib/components/ChangePassword.svelte b/ui/lib/components/ChangePassword.svelte index bf94ea7..742d4f1 100644 --- a/ui/lib/components/ChangePassword.svelte +++ b/ui/lib/components/ChangePassword.svelte @@ -1,28 +1,26 @@ <script> - import { changePassword } from '$lib/apiServer.js'; + let { changePassword = async (currentPassword, newPassword) => {} } = $props(); - let currentPassword = $state(''), - newPassword = $state(''), - confirmPassword = $state(''), - pending = $state(false), - form; + let currentPassword = $state(''); + let newPassword = $state(''); + let confirmPassword = $state(''); + let pending = $state(false); let valid = $derived(newPassword === confirmPassword && newPassword !== currentPassword); let disabled = $derived(pending || !valid); async function onsubmit(event) { event.preventDefault(); pending = true; - let response = await changePassword(currentPassword, newPassword); - switch (response.status) { - case 200: - form.reset(); - break; + try { + await changePassword(currentPassword, newPassword); + event.target.reset(); + } finally { + pending = false; } - pending = false; } </script> -<form class="form" {onsubmit} bind:this={form}> +<form class="form" {onsubmit}> <label >current password <input diff --git a/ui/lib/components/CreateChannelForm.svelte b/ui/lib/components/CreateChannelForm.svelte index 85c85bb..471c2b7 100644 --- a/ui/lib/components/CreateChannelForm.svelte +++ b/ui/lib/components/CreateChannelForm.svelte @@ -1,21 +1,22 @@ <script> - import { createChannel } from '$lib/apiServer'; + let { createChannel = async (name) => {} } = $props(); let name = $state(''); let disabled = $state(false); - async function handleSubmit(event) { + async function onsubmit(event) { event.preventDefault(); disabled = true; - const response = await createChannel(name); - if (200 <= response.status && response.status < 300) { - name = ''; + try { + await createChannel(name); + event.target.reset(); + } finally { + disabled = false; } - disabled = false; } </script> -<form onsubmit={handleSubmit}> +<form {onsubmit}> <input type="text" placeholder="create channel" bind:value={name} {disabled} /> <button type="submit">➕</button> </form> diff --git a/ui/lib/components/Invites.svelte b/ui/lib/components/Invites.svelte index 27d3754..226ccce 100644 --- a/ui/lib/components/Invites.svelte +++ b/ui/lib/components/Invites.svelte @@ -1,15 +1,11 @@ <script> - import { createInvite } from '$lib/apiServer'; import Invite from '$lib/components/Invite.svelte'; - let invites = $state([]); + let { invites, createInvite = async () => {} } = $props(); async function onsubmit(event) { event.preventDefault(); - let response = await createInvite(); - if (response.status == 200) { - invites.push(response.data); - } + await createInvite(); } </script> diff --git a/ui/lib/components/LogIn.svelte b/ui/lib/components/LogIn.svelte index 5bfdae2..c49ea3b 100644 --- a/ui/lib/components/LogIn.svelte +++ b/ui/lib/components/LogIn.svelte @@ -1,20 +1,29 @@ <script> - let { - username = $bindable(), - password = $bindable(), - legend = 'sign in', - disabled, - onsubmit - } = $props(); + let { legend = 'sign in', logIn = async (username, password) => {} } = $props(); + + let username = $state(); + let password = $state(); + let disabled = $state(false); + + async function onsubmit(event) { + event.preventDefault(); + disabled = true; + try { + await logIn(username, password); + event.target.reset(); + } finally { + disabled = false; + } + } </script> <div> <form class="form" {onsubmit}> - <label for="username"> + <label> username <input name="username" type="text" placeholder="username" bind:value={username} {disabled} /> </label> - <label for="password"> + <label> password <input name="password" diff --git a/ui/lib/components/LogOut.svelte b/ui/lib/components/LogOut.svelte index 1cb8fb5..bb24681 100644 --- a/ui/lib/components/LogOut.svelte +++ b/ui/lib/components/LogOut.svelte @@ -1,15 +1,9 @@ <script> - import { goto } from '$app/navigation'; - import { logOut } from '$lib/apiServer.js'; - import { currentUser } from '$lib/store'; + let { logOut = async () => {} } = $props(); async function onsubmit(event) { event.preventDefault(); - const response = await logOut(); - if (200 <= response.status && response.status < 300) { - currentUser.set(null); - await goto('/login'); - } + await logOut(); } </script> diff --git a/ui/lib/components/Message.svelte b/ui/lib/components/Message.svelte index 1b1598b..dacd900 100644 --- a/ui/lib/components/Message.svelte +++ b/ui/lib/components/Message.svelte @@ -1,16 +1,15 @@ <script> import { DateTime } from 'luxon'; - import { deleteMessage } from '$lib/apiServer'; function scroll(message) { message.scrollIntoView(); } - let { id, at, body, renderedBody, editable = false } = $props(); + let { id, at, body, renderedBody, editable = false, deleteMessage = async (id) => {} } = $props(); let deleteArmed = $state(false); let atFormatted = $derived(at.toLocaleString(DateTime.DATETIME_SHORT)); - function onDelete(event) { + function ondelete(event) { event.preventDefault(); if (deleteArmed) { deleteArmed = false; @@ -29,7 +28,7 @@ <div class="handle"> {atFormatted} {#if editable} - <button onclick={onDelete}>🗑️</button> + <button onclick={ondelete}>🗑️</button> {/if} </div> <section use:scroll class="message-body"> diff --git a/ui/lib/components/MessageInput.svelte b/ui/lib/components/MessageInput.svelte index 1eb1d7b..69a8298 100644 --- a/ui/lib/components/MessageInput.svelte +++ b/ui/lib/components/MessageInput.svelte @@ -1,29 +1,30 @@ <script> - import { postToChannel } from '$lib/apiServer'; + let { sendMessage = async (message) => {} } = $props(); - let { channel } = $props(); - - let form; let value = $state(''); let disabled = $state(false); - async function onSubmit(event) { + async function onsubmit(event) { event.preventDefault(); disabled = true; - await postToChannel(channel, value); - form.reset(); - disabled = false; + try { + await sendMessage(value); + event.target.reset(); + } finally { + disabled = false; + } } - function onKeyDown(event) { + function onkeydown(event) { let modifier = event.shiftKey || event.altKey || event.ctrlKey || event.metaKey; if (!modifier && event.key === 'Enter') { - onSubmit(event); + event.preventDefault(); + event.target.form.requestSubmit(); } } </script> -<form bind:this={form} onsubmit={onSubmit}> - <textarea onkeydown={onKeyDown} bind:value {disabled} placeholder="Say something..."></textarea> +<form {onsubmit}> + <textarea {onkeydown} bind:value {disabled} placeholder="Say something..."></textarea> <button type="submit">»</button> </form> diff --git a/ui/lib/components/MessageRun.svelte b/ui/lib/components/MessageRun.svelte index bee64e8..f1facd3 100644 --- a/ui/lib/components/MessageRun.svelte +++ b/ui/lib/components/MessageRun.svelte @@ -1,18 +1,14 @@ <script> - import { logins, currentUser } from '$lib/store'; import Message from '$lib/components/Message.svelte'; - let { sender, messages } = $props(); - - let name = $derived($logins.get(sender)); - let ownMessage = $derived($currentUser !== null && $currentUser.id == sender); + let { sender, messages, ownMessage, deleteMessage = async (id) => {} } = $props(); </script> <div class="message-run" class:own-message={ownMessage} class:other-message={!ownMessage}> <span class="username"> - @{name}: + @{sender}: </span> {#each messages as message} - <Message {...message} editable={ownMessage} /> + <Message {...message} editable={ownMessage} {deleteMessage} /> {/each} </div> |
