summaryrefslogtreecommitdiff
path: root/ui
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2024-11-02 20:02:24 -0400
committerOwen Jacobson <owen@grimoire.ca>2024-11-02 20:02:24 -0400
commit22ce0549e20ee397cf5953bd6b7aafc752deaa28 (patch)
tree59e116cd0a198de04a2dbd3bbb632ec3dee6fed5 /ui
parent0e14a3b7e365c05992848cfbc4b8d7d9681d6d04 (diff)
Run prettier, make lint part of pre-commit
Diffstat (limited to 'ui')
-rw-r--r--ui/lib/apiServer.js148
-rw-r--r--ui/lib/components/ActiveChannel.svelte62
-rw-r--r--ui/lib/components/Channel.svelte27
-rw-r--r--ui/lib/components/ChannelList.svelte18
-rw-r--r--ui/lib/components/CreateChannelForm.svelte34
-rw-r--r--ui/lib/components/CurrentUser.svelte33
-rw-r--r--ui/lib/components/Invite.svelte5
-rw-r--r--ui/lib/components/Invites.svelte12
-rw-r--r--ui/lib/components/LogIn.svelte48
-rw-r--r--ui/lib/components/Message.svelte44
-rw-r--r--ui/lib/components/MessageInput.svelte50
-rw-r--r--ui/lib/components/MessageRun.svelte33
-rw-r--r--ui/lib/store/messages.js5
-rw-r--r--ui/routes/(app)/+layout.svelte238
-rw-r--r--ui/routes/(app)/ch/[channel]/+page.svelte8
-rw-r--r--ui/routes/(app)/me/+page.svelte46
-rw-r--r--ui/routes/(login)/invite/[invite]/+page.js2
-rw-r--r--ui/routes/(login)/invite/[invite]/+page.svelte49
-rw-r--r--ui/routes/(login)/login/+page.svelte33
-rw-r--r--ui/routes/(login)/setup/+page.svelte33
-rw-r--r--ui/routes/+layout.svelte58
21 files changed, 518 insertions, 468 deletions
diff --git a/ui/lib/apiServer.js b/ui/lib/apiServer.js
index 5c6e5ef..3714b63 100644
--- a/ui/lib/apiServer.js
+++ b/ui/lib/apiServer.js
@@ -2,122 +2,124 @@ import axios from 'axios';
import { channelsList, logins, messages } from '$lib/store';
export const apiServer = axios.create({
- baseURL: '/api/',
- validateStatus: () => true,
+ baseURL: '/api/',
+ validateStatus: () => true
});
export async function boot() {
- return apiServer.get('/boot');
+ return apiServer.get('/boot');
}
export async function setup(name, password) {
- return apiServer.post('/setup', { name, password });
+ return apiServer.post('/setup', { name, password });
}
export async function logIn(name, password) {
- return apiServer.post('/auth/login', { name, password });
+ return apiServer.post('/auth/login', { name, password });
}
export async function logOut() {
- return apiServer.post('/auth/logout', {});
+ return apiServer.post('/auth/logout', {});
}
export async function changePassword(password, to) {
- return apiServer.post('/password', { password, to });
+ return apiServer.post('/password', { password, to });
}
export async function createChannel(name) {
- return apiServer.post('/channels', { name });
+ return apiServer.post('/channels', { name });
}
export async function postToChannel(channelId, body) {
- return apiServer.post(`/channels/${channelId}`, { body });
+ return apiServer.post(`/channels/${channelId}`, { body });
}
export async function deleteMessage(messageId) {
- // TODO
+ // TODO
}
export async function createInvite() {
- return apiServer.post(`/invite`, {});
+ return apiServer.post(`/invite`, {});
}
export async function getInvite(inviteId) {
- return apiServer.get(`/invite/${inviteId}`);
+ return apiServer.get(`/invite/${inviteId}`);
}
export async function acceptInvite(inviteId, username, password) {
- const data = {
- name: username,
- password,
- };
- return apiServer.post(`/invite/${inviteId}`, data);
+ const data = {
+ name: username,
+ password
+ };
+ return apiServer.post(`/invite/${inviteId}`, data);
}
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.
- /*
- * 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);
-
- switch (data.type) {
- case 'login':
- onLoginEvent(data);
- break;
- case 'channel':
- onChannelEvent(data);
- break;
- case 'message':
- onMessageEvent(data);
- break;
- }
- }
-
- return evtSource;
+ 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.
+ /*
+ * 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);
+
+ switch (data.type) {
+ case 'login':
+ onLoginEvent(data);
+ break;
+ case 'channel':
+ onChannelEvent(data);
+ break;
+ case 'message':
+ onMessageEvent(data);
+ break;
+ }
+ };
+
+ return evtSource;
}
function onLoginEvent(data) {
- switch (data.event) {
- case 'created':
- logins.update((value) => value.addLogin(data.id, data.name))
- break;
- }
+ switch (data.event) {
+ case 'created':
+ logins.update((value) => value.addLogin(data.id, data.name));
+ break;
+ }
}
function onChannelEvent(data) {
- switch (data.event) {
- case 'created':
- channelsList.update((value) => value.addChannel(data.id, data.name))
- break;
- case 'deleted':
- channelsList.update((value) => value.deleteChannel(data.id));
- messages.update((value) => value.deleteChannel(data.id));
- break;
- }
+ switch (data.event) {
+ case 'created':
+ channelsList.update((value) => value.addChannel(data.id, data.name));
+ break;
+ case 'deleted':
+ channelsList.update((value) => value.deleteChannel(data.id));
+ messages.update((value) => value.deleteChannel(data.id));
+ break;
+ }
}
function onMessageEvent(data) {
- switch (data.event) {
- case 'sent':
- messages.update((value) => value.addMessage(data.channel, data.id, data.at, data.sender, data.body));
- break;
- case 'deleted':
- messages.update((value) => value.deleteMessage(data.id));
- break;
- }
+ switch (data.event) {
+ case 'sent':
+ messages.update((value) =>
+ value.addMessage(data.channel, data.id, data.at, data.sender, data.body)
+ );
+ break;
+ case 'deleted':
+ messages.update((value) => value.deleteMessage(data.id));
+ break;
+ }
}
diff --git a/ui/lib/components/ActiveChannel.svelte b/ui/lib/components/ActiveChannel.svelte
index 76bf13a..212048a 100644
--- a/ui/lib/components/ActiveChannel.svelte
+++ b/ui/lib/components/ActiveChannel.svelte
@@ -1,42 +1,42 @@
<script>
- import { messages } from '$lib/store';
- import MessageRun from './MessageRun.svelte';
+ import { messages } from '$lib/store';
+ import MessageRun from './MessageRun.svelte';
- export let channel = null;
- $: messageList = channel !== null ? $messages.inChannel(channel) : [];
+ export let channel = null;
+ $: messageList = channel !== null ? $messages.inChannel(channel) : [];
- function *chunkBy(xs, fn) {
- let chunk;
- let key;
- for (let x of xs) {
- let newKey = fn(x);
- if (key !== newKey) {
- if (chunk !== undefined) {
- yield [key, chunk];
- }
+ function* chunkBy(xs, fn) {
+ let chunk;
+ let key;
+ for (let x of xs) {
+ let newKey = fn(x);
+ if (key !== newKey) {
+ if (chunk !== undefined) {
+ yield [key, chunk];
+ }
- chunk = [x];
- key = newKey;
- } else {
- chunk.push(x);
- }
- }
- if (chunk !== undefined) {
- yield [key, chunk];
- }
- }
+ chunk = [x];
+ key = newKey;
+ } else {
+ chunk.push(x);
+ }
+ }
+ if (chunk !== undefined) {
+ yield [key, chunk];
+ }
+ }
</script>
<div class="container">
- {#each chunkBy(messageList, msg => msg.sender) as [sender, messages]}
- <div>
- <MessageRun {sender} {messages} />
- </div>
- {/each}
+ {#each chunkBy(messageList, (msg) => msg.sender) as [sender, messages]}
+ <div>
+ <MessageRun {sender} {messages} />
+ </div>
+ {/each}
</div>
<style>
- .container {
- overflow: auto;
- }
+ .container {
+ overflow: auto;
+ }
</style>
diff --git a/ui/lib/components/Channel.svelte b/ui/lib/components/Channel.svelte
index bbe9ff7..60c9092 100644
--- a/ui/lib/components/Channel.svelte
+++ b/ui/lib/components/Channel.svelte
@@ -1,21 +1,18 @@
<script>
- import { showMenu } from '$lib/store';
+ import { showMenu } from '$lib/store';
- export let id;
- export let name;
- export let active = false;
+ export let id;
+ export let name;
+ export let active = false;
- function hideMenu() {
- showMenu.update(() => false);
- }
+ function hideMenu() {
+ showMenu.update(() => false);
+ }
</script>
-<li
- class="rounded-full"
- class:bg-slate-400={active}
->
-<a href="/ch/{id}" on:click={hideMenu}>
- <span class="badge bg-primary-500">#</span>
- <span class="flex-auto">{name}</span>
-</a>
+<li class="rounded-full" class:bg-slate-400={active}>
+ <a href="/ch/{id}" on:click={hideMenu}>
+ <span class="badge bg-primary-500">#</span>
+ <span class="flex-auto">{name}</span>
+ </a>
</li>
diff --git a/ui/lib/components/ChannelList.svelte b/ui/lib/components/ChannelList.svelte
index 316e404..f7376c1 100644
--- a/ui/lib/components/ChannelList.svelte
+++ b/ui/lib/components/ChannelList.svelte
@@ -1,16 +1,16 @@
<script>
- import { channelsList } from '$lib/store';
- import Channel from './Channel.svelte';
+ import { channelsList } from '$lib/store';
+ import Channel from './Channel.svelte';
- export let active = null;
+ export let active = null;
- $: channels = $channelsList.channels;
+ $: channels = $channelsList.channels;
</script>
<nav class="list-nav">
- <ul>
- {#each channels as channel}
- <Channel {...channel} active={active === channel.id} />
- {/each}
- </ul>
+ <ul>
+ {#each channels as channel}
+ <Channel {...channel} active={active === channel.id} />
+ {/each}
+ </ul>
</nav>
diff --git a/ui/lib/components/CreateChannelForm.svelte b/ui/lib/components/CreateChannelForm.svelte
index b716736..6b50fb1 100644
--- a/ui/lib/components/CreateChannelForm.svelte
+++ b/ui/lib/components/CreateChannelForm.svelte
@@ -1,23 +1,29 @@
<script>
- import { createChannel } from '$lib/apiServer';
+ import { createChannel } from '$lib/apiServer';
- let name = "";
- let pending = false;
- $: disabled = pending;
+ let name = '';
+ let pending = false;
+ $: disabled = pending;
- async function handleSubmit(event) {
- pending = true;
- const response = await createChannel(name);
- if (200 <= response.status && response.status < 300) {
- name = '';
- }
- pending = false;
- }
+ async function handleSubmit(event) {
+ pending = true;
+ const response = await createChannel(name);
+ if (200 <= response.status && response.status < 300) {
+ name = '';
+ }
+ pending = false;
+ }
</script>
<form on:submit|preventDefault={handleSubmit} class="form form-row flex-nowrap">
- <input type="text" placeholder="create channel" bind:value={name} disabled={disabled} class="input flex-auto h-6 w-9/12" />
- <button type="submit" class="flex-none w-6 h-6">&#x2795;</button>
+ <input
+ type="text"
+ placeholder="create channel"
+ bind:value={name}
+ {disabled}
+ class="input flex-auto h-6 w-9/12"
+ />
+ <button type="submit" class="flex-none w-6 h-6">&#x2795;</button>
</form>
<style>
diff --git a/ui/lib/components/CurrentUser.svelte b/ui/lib/components/CurrentUser.svelte
index 4b1b974..97ff980 100644
--- a/ui/lib/components/CurrentUser.svelte
+++ b/ui/lib/components/CurrentUser.svelte
@@ -1,25 +1,24 @@
<script>
- import { goto } from '$app/navigation';
- import { logOut} from '$lib/apiServer';
- import { currentUser } from '$lib/store';
+ import { goto } from '$app/navigation';
+ import { logOut } from '$lib/apiServer';
+ import { currentUser } from '$lib/store';
- async function handleLogout() {
- const response = await logOut();
- if (200 <= response.status && response.status < 300) {
- currentUser.update(() => null);
- goto('/login');
- }
- }
+ async function handleLogout() {
+ const response = await logOut();
+ if (200 <= response.status && response.status < 300) {
+ currentUser.update(() => null);
+ goto('/login');
+ }
+ }
</script>
<form on:submit|preventDefault={handleLogout}>
- {#if $currentUser}
- <a href="/me">@{$currentUser.username}</a>
- {/if}
- <button
- class="border-slate-500 border-solid border-2 font-bold p-1 rounded"
- type="submit"
- >log out</button>
+ {#if $currentUser}
+ <a href="/me">@{$currentUser.username}</a>
+ {/if}
+ <button class="border-slate-500 border-solid border-2 font-bold p-1 rounded" type="submit"
+ >log out</button
+ >
</form>
<style>
diff --git a/ui/lib/components/Invite.svelte b/ui/lib/components/Invite.svelte
index 7fdc753..ea8dd1d 100644
--- a/ui/lib/components/Invite.svelte
+++ b/ui/lib/components/Invite.svelte
@@ -7,7 +7,6 @@
<button
class="border-slate-500 border-solid border-2 font-bold p-1 rounded"
- use:clipboard={inviteUrl}>Copy</button>
+ use:clipboard={inviteUrl}>Copy</button
+>
<span data-clipboard="inviteUrl">{inviteUrl}</span>
-
-
diff --git a/ui/lib/components/Invites.svelte b/ui/lib/components/Invites.svelte
index df51afb..bfaf2b6 100644
--- a/ui/lib/components/Invites.svelte
+++ b/ui/lib/components/Invites.svelte
@@ -4,25 +4,21 @@
import Invite from '$lib/components/Invite.svelte';
let invites = writable([]);
- $: $invites, console.log("invites", $invites);
+ $: $invites, console.log('invites', $invites);
async function onSubmit() {
let response = await createInvite();
if (response.status == 200) {
- invites.update(val => [...val, response.data]);
+ invites.update((val) => [...val, response.data]);
}
}
</script>
<ul>
{#each $invites as invite}
- <li><Invite id={invite.id} /></li>
+ <li><Invite id={invite.id} /></li>
{/each}
</ul>
<form on:submit|preventDefault={onSubmit}>
- <button
- class="btn variant-filled"
- type="submit">
- Create Invitation
- </button>
+ <button class="btn variant-filled" type="submit"> Create Invitation </button>
</form>
diff --git a/ui/lib/components/LogIn.svelte b/ui/lib/components/LogIn.svelte
index bb80ccd..4346c47 100644
--- a/ui/lib/components/LogIn.svelte
+++ b/ui/lib/components/LogIn.svelte
@@ -1,22 +1,36 @@
<script>
- export let disabled = false;
- export let username = '';
- export let password = '';
- export let legend = 'sign in';
+ export let disabled = false;
+ export let username = '';
+ export let password = '';
+ export let legend = 'sign in';
</script>
<div class="card m-4 p-4">
- <form on:submit|preventDefault>
- <label class="label" for="username">
- username
- <input class="input" name="username" type="text" placeholder="username" bind:value={username} disabled={disabled}>
- </label>
- <label class="label" for="password">
- password
- <input class="input" name="password" type="password" placeholder="password" bind:value={password} disabled={disabled}>
- </label>
- <button class="btn variant-filled" type="submit">
- {legend}
- </button>
- </form>
+ <form on:submit|preventDefault>
+ <label class="label" for="username">
+ username
+ <input
+ class="input"
+ name="username"
+ type="text"
+ placeholder="username"
+ bind:value={username}
+ {disabled}
+ />
+ </label>
+ <label class="label" for="password">
+ password
+ <input
+ class="input"
+ name="password"
+ type="password"
+ placeholder="password"
+ bind:value={password}
+ {disabled}
+ />
+ </label>
+ <button class="btn variant-filled" type="submit">
+ {legend}
+ </button>
+ </form>
</div>
diff --git a/ui/lib/components/Message.svelte b/ui/lib/components/Message.svelte
index b71cf3c..9376cbe 100644
--- a/ui/lib/components/Message.svelte
+++ b/ui/lib/components/Message.svelte
@@ -1,33 +1,33 @@
<script>
- import { marked } from 'marked';
- import DOMPurify from 'dompurify';
+ import { marked } from 'marked';
+ import DOMPurify from 'dompurify';
- export let at;
- export let body;
+ export let at;
+ export let body;
- $: renderedBody = DOMPurify.sanitize(marked.parse(body));
+ $: renderedBody = DOMPurify.sanitize(marked.parse(body));
- let scroll = (message) => {
- message.scrollIntoView();
- }
+ let scroll = (message) => {
+ message.scrollIntoView();
+ };
</script>
<div class="message relative">
- <span class="timestamp chip variant-soft absolute top-0 right-0">{at}</span>
- <section use:scroll class="py-1 message-body">
- {@html renderedBody}
- </section>
+ <span class="timestamp chip variant-soft absolute top-0 right-0">{at}</span>
+ <section use:scroll class="py-1 message-body">
+ {@html renderedBody}
+ </section>
</div>
<style>
- .message .timestamp {
- display: none;
- }
- .message:hover .timestamp {
- display: flex;
- }
- .message-body:empty:after {
- content: ".";
- visibility: hidden;
- }
+ .message .timestamp {
+ display: none;
+ }
+ .message:hover .timestamp {
+ display: flex;
+ }
+ .message-body:empty:after {
+ content: '.';
+ visibility: hidden;
+ }
</style>
diff --git a/ui/lib/components/MessageInput.svelte b/ui/lib/components/MessageInput.svelte
index 03ac7fa..7aac442 100644
--- a/ui/lib/components/MessageInput.svelte
+++ b/ui/lib/components/MessageInput.svelte
@@ -1,28 +1,38 @@
<script>
- import { tick } from 'svelte';
- import { postToChannel } from '$lib/apiServer';
+ import { tick } from 'svelte';
+ import { postToChannel } from '$lib/apiServer';
- export let channel = null;
- let input;
- let value = '';
- let pending = false;
+ export let channel = null;
+ let input;
+ let value = '';
+ let pending = false;
- $: disabled = pending || (channel === null);
+ $: disabled = pending || channel === null;
- async function handleSubmit() {
- if (channel !== null) {
- pending = true;
- // TODO try/catch:
- await postToChannel(channel, value);
- pending = false;
- value = '';
- await tick();
- input.focus();
- }
- }
+ async function handleSubmit() {
+ if (channel !== null) {
+ pending = true;
+ // TODO try/catch:
+ await postToChannel(channel, value);
+ pending = false;
+ value = '';
+ await tick();
+ input.focus();
+ }
+ }
</script>
<form on:submit|preventDefault={handleSubmit} class="flex flex-row flex-nowrap">
- <input bind:this={input} bind:value={value} disabled={disabled} type="search" class="flex-auto h-6 input rounded-r-none" />
- <button color="primary variant-filled-secondary" type="submit" class="flex-none w-6 h-6 btn-icon variant-filled rounded-l-none">&raquo;</button>
+ <input
+ bind:this={input}
+ bind:value
+ {disabled}
+ type="search"
+ class="flex-auto h-6 input rounded-r-none"
+ />
+ <button
+ color="primary variant-filled-secondary"
+ type="submit"
+ class="flex-none w-6 h-6 btn-icon variant-filled rounded-l-none">&raquo;</button
+ >
</form>
diff --git a/ui/lib/components/MessageRun.svelte b/ui/lib/components/MessageRun.svelte
index af699ab..4894885 100644
--- a/ui/lib/components/MessageRun.svelte
+++ b/ui/lib/components/MessageRun.svelte
@@ -1,23 +1,24 @@
<script>
- import { logins, currentUser } from '$lib/store';
- import Message from '$lib/components/Message.svelte';
+ import { logins, currentUser } from '$lib/store';
+ import Message from '$lib/components/Message.svelte';
- export let sender;
- export let messages;
+ export let sender;
+ export let messages;
- let name;
- $: name = $logins.get(sender);
- $: ownMessage = $currentUser.id == sender;
+ let name;
+ $: name = $logins.get(sender);
+ $: ownMessage = $currentUser.id == sender;
</script>
<div
- class="card card-hover m-4 px-4 py-1 relative"
- class:own-message={ownMessage}
- class:other-message={!ownMessage}>
- <span class="chip variant-soft sticky top-o left-0">
- @{name}:
- </span>
- {#each messages as { at, body }}
- <Message {at} {body} />
- {/each}
+ class="card card-hover m-4 px-4 py-1 relative"
+ class:own-message={ownMessage}
+ class:other-message={!ownMessage}
+>
+ <span class="chip variant-soft sticky top-o left-0">
+ @{name}:
+ </span>
+ {#each messages as { at, body }}
+ <Message {at} {body} />
+ {/each}
</div>
diff --git a/ui/lib/store/messages.js b/ui/lib/store/messages.js
index 7d1fbe1..884b296 100644
--- a/ui/lib/store/messages.js
+++ b/ui/lib/store/messages.js
@@ -4,7 +4,7 @@ export class Messages {
}
inChannel(channel) {
- return this.channels[channel] = (this.channels[channel] || []);
+ return (this.channels[channel] = this.channels[channel] || []);
}
addMessage(channel, id, at, sender, body) {
@@ -15,12 +15,11 @@ export class Messages {
setMessages(messages) {
this.channels = {};
for (let { channel, id, at, sender, body } of messages) {
- this.inChannel(channel).push({ id, at, sender, body, });
+ 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));
diff --git a/ui/routes/(app)/+layout.svelte b/ui/routes/(app)/+layout.svelte
index ae80324..9843979 100644
--- a/ui/routes/(app)/+layout.svelte
+++ b/ui/routes/(app)/+layout.svelte
@@ -1,145 +1,147 @@
<script>
- import { page } from '$app/stores';
- import { goto } from '$app/navigation';
+ import { page } from '$app/stores';
+ import { goto } from '$app/navigation';
import { onMount, onDestroy } from 'svelte';
import { boot, subscribeToEvents } from '$lib/apiServer';
import { showMenu, currentUser, logins, channelsList, messages } from '$lib/store';
- import ChannelList from '$lib/components/ChannelList.svelte';
- import CreateChannelForm from '$lib/components/CreateChannelForm.svelte';
- import MessageInput from '$lib/components/MessageInput.svelte';
-
- let loading = true;
- let events = null;
- let showMenuValue;
- showMenu.subscribe((value) => {
- showMenuValue = value;
- });
+ import ChannelList from '$lib/components/ChannelList.svelte';
+ import CreateChannelForm from '$lib/components/CreateChannelForm.svelte';
+ import MessageInput from '$lib/components/MessageInput.svelte';
+ let loading = true;
+ let events = null;
+ let showMenuValue;
+ showMenu.subscribe((value) => {
+ showMenuValue = value;
+ });
- function toggleMenu() {
- showMenu.update((value) => !value);
- }
+ function toggleMenu() {
+ showMenu.update((value) => !value);
+ }
- $: channel = $page?.params?.channel;
+ $: channel = $page?.params?.channel;
- function onBooted(boot) {
- currentUser.update(() => ({
- id: boot.login.id,
- username: boot.login.name,
- }));
- logins.update((value) => value.setLogins(boot.logins));
- channelsList.update((value) => value.setChannels(boot.channels));
- messages.update((value) => value.setMessages(boot.messages));
- }
+ function onBooted(boot) {
+ currentUser.update(() => ({
+ id: boot.login.id,
+ username: boot.login.name
+ }));
+ logins.update((value) => value.setLogins(boot.logins));
+ channelsList.update((value) => value.setChannels(boot.channels));
+ messages.update((value) => value.setMessages(boot.messages));
+ }
onMount(async () => {
- let response = await boot();
- switch (response.status) {
- case 200:
- onBooted(response.data);
- events = subscribeToEvents(response.data.resume_point);
- break;
- case 401:
- currentUser.update(() => null);
- goto('/login');
- break;
- case 503:
- currentUser.update(() => null);
- goto('/setup');
- break;
- default:
- // TODO: display error.
- break;
- }
- loading = false;
+ let response = await boot();
+ switch (response.status) {
+ case 200:
+ onBooted(response.data);
+ events = subscribeToEvents(response.data.resume_point);
+ break;
+ case 401:
+ currentUser.update(() => null);
+ goto('/login');
+ break;
+ case 503:
+ currentUser.update(() => null);
+ goto('/setup');
+ break;
+ default:
+ // TODO: display error.
+ break;
+ }
+ loading = false;
});
- onDestroy(async () => {
- if (events !== null) {
- events.close();
- }
- });
+ onDestroy(async () => {
+ if (events !== null) {
+ events.close();
+ }
+ });
</script>
<svelte:head>
- <title>understory</title>
+ <title>understory</title>
</svelte:head>
{#if loading}
- <h2>Loading&hellip;</h2>
+ <h2>Loading&hellip;</h2>
{:else}
- <div id="interface" class="p-2">
- <nav id="sidebar" data-expanded={showMenuValue}>
- <div class="channel-list">
- <ChannelList active={channel} />
- </div>
- <div class="create-channel">
- <CreateChannelForm />
- </div>
- </nav>
- <main>
- <div class="active-channel">
- <slot />
- </div>
- <div class="create-message max-h-full">
- <MessageInput {channel} />
- </div>
- </main>
- </div>
+ <div id="interface" class="p-2">
+ <nav id="sidebar" data-expanded={showMenuValue}>
+ <div class="channel-list">
+ <ChannelList active={channel} />
+ </div>
+ <div class="create-channel">
+ <CreateChannelForm />
+ </div>
+ </nav>
+ <main>
+ <div class="active-channel">
+ <slot />
+ </div>
+ <div class="create-message max-h-full">
+ <MessageInput {channel} />
+ </div>
+ </main>
+ </div>
{/if}
<style>
-:root {
- --app-bar-height: 68px;
- --input-row-height: 2rem;
- --interface-padding: 16px;
-}
+ :root {
+ --app-bar-height: 68px;
+ --input-row-height: 2rem;
+ --interface-padding: 16px;
+ }
-#interface {
- margin: unset;
- display: grid;
- grid-template:
- 'side main' 1fr
- / auto 1fr
- ;
- height: calc(100vh - var(--app-bar-height));
+ #interface {
+ margin: unset;
+ display: grid;
+ grid-template:
+ 'side main' 1fr
+ / auto 1fr;
+ height: calc(100vh - var(--app-bar-height));
- @media (width > 640px) {
- --overlay: static;
- --translate: 0;
- }
-}
-nav {
- grid-area: side;
- background-color: rgb(var(--color-surface-800));
- inset: auto auto 0 0;
- padding: 0.25rem;
- position: var(--overlay, absolute);
- transition: translate 300ms ease-out;
- width: 21rem;
- height: calc(100vh - var(--app-bar-height) - var(--interface-padding));
- z-index: 10;
-}
-nav button {
- position: absolute;
- top: 0;
- right: 0;
-}
-main {
- grid-area: main;
- height: calc(100vh - var(--app-bar-height) - var(--interface-padding));
-}
-.active-channel {
- height: calc(100vh - var(--app-bar-height) - var(--interface-padding) - var(--input-row-height));
- overflow: auto;
-}
-.channel-list {
- height: calc(100vh - var(--app-bar-height) - var(--interface-padding) - var(--input-row-height));
- overflow: auto;
-}
-nav[data-expanded=false] {
- translate: var(--translate, -100% 0);
-}
+ @media (width > 640px) {
+ --overlay: static;
+ --translate: 0;
+ }
+ }
+ nav {
+ grid-area: side;
+ background-color: rgb(var(--color-surface-800));
+ inset: auto auto 0 0;
+ padding: 0.25rem;
+ position: var(--overlay, absolute);
+ transition: translate 300ms ease-out;
+ width: 21rem;
+ height: calc(100vh - var(--app-bar-height) - var(--interface-padding));
+ z-index: 10;
+ }
+ nav button {
+ position: absolute;
+ top: 0;
+ right: 0;
+ }
+ main {
+ grid-area: main;
+ height: calc(100vh - var(--app-bar-height) - var(--interface-padding));
+ }
+ .active-channel {
+ height: calc(
+ 100vh - var(--app-bar-height) - var(--interface-padding) - var(--input-row-height)
+ );
+ overflow: auto;
+ }
+ .channel-list {
+ height: calc(
+ 100vh - var(--app-bar-height) - var(--interface-padding) - var(--input-row-height)
+ );
+ overflow: auto;
+ }
+ nav[data-expanded='false'] {
+ translate: var(--translate, -100% 0);
+ }
</style>
diff --git a/ui/routes/(app)/ch/[channel]/+page.svelte b/ui/routes/(app)/ch/[channel]/+page.svelte
index 7bd28d9..74ad28b 100644
--- a/ui/routes/(app)/ch/[channel]/+page.svelte
+++ b/ui/routes/(app)/ch/[channel]/+page.svelte
@@ -1,10 +1,10 @@
<script>
- import { page } from '$app/stores';
- import ActiveChannel from '$lib/components/ActiveChannel.svelte';
+ import { page } from '$app/stores';
+ import ActiveChannel from '$lib/components/ActiveChannel.svelte';
- $: channel = $page?.params?.channel;
+ $: channel = $page?.params?.channel;
</script>
<div class="active-channel">
- <ActiveChannel {channel} />
+ <ActiveChannel {channel} />
</div>
diff --git a/ui/routes/(app)/me/+page.svelte b/ui/routes/(app)/me/+page.svelte
index 82af3c7..26537ad 100644
--- a/ui/routes/(app)/me/+page.svelte
+++ b/ui/routes/(app)/me/+page.svelte
@@ -3,9 +3,12 @@
import Invites from '$lib/components/Invites.svelte';
- let currentPassword = "", newPassword = "", confirmPassword = "", passwordForm;
+ let currentPassword = '',
+ newPassword = '',
+ confirmPassword = '',
+ passwordForm;
let pending = false;
- $: valid = (newPassword === confirmPassword) && (newPassword !== currentPassword);
+ $: valid = newPassword === confirmPassword && newPassword !== currentPassword;
$: disabled = pending || !valid;
async function onPasswordChange() {
@@ -20,22 +23,41 @@
}
</script>
-<form on:submit|preventDefault={onPasswordChange} bind:this={passwordForm} >
- <label>current password
- <input class="input" name="currentPassword" type="password" placeholder="password" bind:value={currentPassword}>
+<form on:submit|preventDefault={onPasswordChange} bind:this={passwordForm}>
+ <label
+ >current password
+ <input
+ class="input"
+ name="currentPassword"
+ type="password"
+ placeholder="password"
+ bind:value={currentPassword}
+ />
</label>
- <label>new password
- <input class="input" name="newPassword" type="password" placeholder="password" bind:value={newPassword}>
+ <label
+ >new password
+ <input
+ class="input"
+ name="newPassword"
+ type="password"
+ placeholder="password"
+ bind:value={newPassword}
+ />
</label>
- <label>confirm new password
- <input class="input" name="confirmPassword" type="password" placeholder="password" bind:value={confirmPassword}>
+ <label
+ >confirm new password
+ <input
+ class="input"
+ name="confirmPassword"
+ type="password"
+ placeholder="password"
+ bind:value={confirmPassword}
+ />
</label>
- <button class="btn variant-filled" type="submit" disabled={disabled}>
- change password
- </button>
+ <button class="btn variant-filled" type="submit" {disabled}> change password </button>
</form>
<Invites />
diff --git a/ui/routes/(login)/invite/[invite]/+page.js b/ui/routes/(login)/invite/[invite]/+page.js
index e6664d2..ced109a 100644
--- a/ui/routes/(login)/invite/[invite]/+page.js
+++ b/ui/routes/(login)/invite/[invite]/+page.js
@@ -5,7 +5,7 @@ export async function load({ params }) {
let response = await getInvite(invite);
switch (response.status) {
case 200:
- let invite = response.data
+ let invite = response.data;
return { invite };
break;
case 404:
diff --git a/ui/routes/(login)/invite/[invite]/+page.svelte b/ui/routes/(login)/invite/[invite]/+page.svelte
index 7ae388a..8f4d1a4 100644
--- a/ui/routes/(login)/invite/[invite]/+page.svelte
+++ b/ui/routes/(login)/invite/[invite]/+page.svelte
@@ -1,34 +1,35 @@
<script>
- import { goto } from '$app/navigation';
- import { acceptInvite } from '$lib/apiServer';
+ import { goto } from '$app/navigation';
+ import { acceptInvite } from '$lib/apiServer';
- import LogIn from '$lib/components/LogIn.svelte';
+ import LogIn from '$lib/components/LogIn.svelte';
- export let data;
+ export let data;
- let username = "", password = "";
- let pending = false;
- $: disabled = pending;
+ let username = '',
+ password = '';
+ let pending = false;
+ $: disabled = pending;
- async function onSubmit() {
- pending = true;
- const response = await acceptInvite(data.invite.id, username, password);
- if (200 <= response.status && response.status < 300) {
- username = '';
- password = '';
- goto('/');
- }
- pending = false;
- }
+ async function onSubmit() {
+ pending = true;
+ const response = await acceptInvite(data.invite.id, username, password);
+ if (200 <= response.status && response.status < 300) {
+ username = '';
+ password = '';
+ goto('/');
+ }
+ pending = false;
+ }
</script>
{#await data}
-<div class="card m-4 p-4">
- <p>Loading invitation…</p>
-</div>
+ <div class="card m-4 p-4">
+ <p>Loading invitation…</p>
+ </div>
{:then { invite }}
-<div class="card m-4 p-4">
- <p>Hi there! {invite.issuer} invites you to the conversation.</p>
-</div>
-<LogIn bind:disabled bind:username bind:password on:submit={onSubmit} />
+ <div class="card m-4 p-4">
+ <p>Hi there! {invite.issuer} invites you to the conversation.</p>
+ </div>
+ <LogIn bind:disabled bind:username bind:password on:submit={onSubmit} />
{/await}
diff --git a/ui/routes/(login)/login/+page.svelte b/ui/routes/(login)/login/+page.svelte
index 0387e4a..dba7f5a 100644
--- a/ui/routes/(login)/login/+page.svelte
+++ b/ui/routes/(login)/login/+page.svelte
@@ -1,23 +1,24 @@
<script>
- import { goto } from '$app/navigation';
- import { logIn } from '$lib/apiServer';
+ import { goto } from '$app/navigation';
+ import { logIn } from '$lib/apiServer';
- import LogIn from '$lib/components/LogIn.svelte';
+ import LogIn from '$lib/components/LogIn.svelte';
- let username = "", password = "";
- let pending = false;
- $: disabled = pending;
+ let username = '',
+ password = '';
+ let pending = false;
+ $: disabled = pending;
- async function onSubmit() {
- pending = true;
- const response = await logIn(username, password);
- if (200 <= response.status && response.status < 300) {
- username = '';
- password = '';
- goto('/');
- }
- pending = false;
- }
+ async function onSubmit() {
+ pending = true;
+ const response = await logIn(username, password);
+ if (200 <= response.status && response.status < 300) {
+ username = '';
+ password = '';
+ goto('/');
+ }
+ pending = false;
+ }
</script>
<LogIn bind:disabled bind:username bind:password on:submit={onSubmit} />
diff --git a/ui/routes/(login)/setup/+page.svelte b/ui/routes/(login)/setup/+page.svelte
index a1974b8..2f098ef 100644
--- a/ui/routes/(login)/setup/+page.svelte
+++ b/ui/routes/(login)/setup/+page.svelte
@@ -1,23 +1,24 @@
<script>
- import { goto } from '$app/navigation';
- import { setup } from '$lib/apiServer';
+ import { goto } from '$app/navigation';
+ import { setup } from '$lib/apiServer';
- import LogIn from '$lib/components/LogIn.svelte';
+ import LogIn from '$lib/components/LogIn.svelte';
- let username = "", password = "";
- let pending = false;
- $: disabled = pending;
+ let username = '',
+ password = '';
+ let pending = false;
+ $: disabled = pending;
- async function onSubmit() {
- pending = true;
- const response = await setup(username, password);
- if (200 <= response.status && response.status < 300) {
- username = '';
- password = '';
- goto('/');
- }
- pending = false;
- }
+ async function onSubmit() {
+ pending = true;
+ const response = await setup(username, password);
+ if (200 <= response.status && response.status < 300) {
+ username = '';
+ password = '';
+ goto('/');
+ }
+ pending = false;
+ }
</script>
<LogIn bind:disabled bind:username bind:password legend="set up" on:submit={onSubmit} />
diff --git a/ui/routes/+layout.svelte b/ui/routes/+layout.svelte
index a86a7e8..22377bb 100644
--- a/ui/routes/+layout.svelte
+++ b/ui/routes/+layout.svelte
@@ -1,41 +1,41 @@
<script>
- import "../app.css";
- import logo from '$lib/assets/logo.png';
+ import '../app.css';
+ import logo from '$lib/assets/logo.png';
- import { AppBar } from '@skeletonlabs/skeleton';
- import { showMenu, currentUser } from '$lib/store';
+ import { AppBar } from '@skeletonlabs/skeleton';
+ import { showMenu, currentUser } from '$lib/store';
- import CurrentUser from '$lib/components/CurrentUser.svelte';
+ import CurrentUser from '$lib/components/CurrentUser.svelte';
- function toggleMenu() {
- showMenu.update((value) => !value);
- }
+ function toggleMenu() {
+ showMenu.update((value) => !value);
+ }
</script>
<div id="app" class="m-0 p-0 h-vh w-full">
- <div class="w-full">
- <AppBar class="app-bar">
- <svelte:fragment slot="lead">
- <a on:click|preventDefault={toggleMenu} class="cursor-pointer">
- <img class="w-8 h-8" alt="logo" src={logo} />
- </a>
- </svelte:fragment>
- <a href="/">understory</a>
- <svelte:fragment slot="trail">
- {#if $currentUser}
- <CurrentUser />
- {/if}
- </svelte:fragment>
- </AppBar>
- </div>
+ <div class="w-full">
+ <AppBar class="app-bar">
+ <svelte:fragment slot="lead">
+ <a on:click|preventDefault={toggleMenu} class="cursor-pointer">
+ <img class="w-8 h-8" alt="logo" src={logo} />
+ </a>
+ </svelte:fragment>
+ <a href="/">understory</a>
+ <svelte:fragment slot="trail">
+ {#if $currentUser}
+ <CurrentUser />
+ {/if}
+ </svelte:fragment>
+ </AppBar>
+ </div>
- <slot />
+ <slot />
</div>
<style>
- #app {
- margin: 0;
- height: 100vh;
- width: 100%;
- }
+ #app {
+ margin: 0;
+ height: 100vh;
+ width: 100%;
+ }
</style>