summaryrefslogtreecommitdiff
path: root/ui/src/lib
diff options
context:
space:
mode:
authorOwen Jacobson <owen@grimoire.ca>2024-10-10 21:51:10 -0400
committerOwen Jacobson <owen@grimoire.ca>2024-10-10 21:52:26 -0400
commit215b0c5cb2ff0ef0b2c7b5549704e23d651a4df9 (patch)
tree356484ce699539f2937b768d1a1c9b83f0c7a402 /ui/src/lib
parent4401dce2b5545ce8117818812d8e3c8919f5f7fd (diff)
Hoist the UI one step up further
Diffstat (limited to 'ui/src/lib')
-rw-r--r--ui/src/lib/apiServer.js101
-rw-r--r--ui/src/lib/components/ActiveChannel.svelte27
-rw-r--r--ui/src/lib/components/Channel.svelte21
-rw-r--r--ui/src/lib/components/ChannelList.svelte18
-rw-r--r--ui/src/lib/components/CreateChannelForm.svelte23
-rw-r--r--ui/src/lib/components/LogIn.svelte35
-rw-r--r--ui/src/lib/components/LogOut.svelte22
-rw-r--r--ui/src/lib/components/Message.svelte33
-rw-r--r--ui/src/lib/components/MessageInput.svelte30
-rw-r--r--ui/src/lib/index.js1
-rw-r--r--ui/src/lib/store.js10
-rw-r--r--ui/src/lib/store/channels.js71
-rw-r--r--ui/src/lib/store/logins.js22
-rw-r--r--ui/src/lib/store/messages.js44
14 files changed, 0 insertions, 458 deletions
diff --git a/ui/src/lib/apiServer.js b/ui/src/lib/apiServer.js
deleted file mode 100644
index f6d6148..0000000
--- a/ui/src/lib/apiServer.js
+++ /dev/null
@@ -1,101 +0,0 @@
-import axios from 'axios';
-import { activeChannel, channelsList, logins, messages } from '$lib/store';
-
-export const apiServer = axios.create({
- baseURL: '/api/',
-});
-
-export async function boot() {
- return apiServer.get('/boot');
-}
-
-export async function logIn(username, password) {
- const data = {
- name: username,
- password,
- };
- return apiServer.post('/auth/login', data);
-}
-
-export async function logOut() {
- return apiServer.post('/auth/logout', {});
-}
-
-export async function createChannel(name) {
- return apiServer.post('/channels', { name });
-}
-
-export async function postToChannel(channelId, body) {
- return apiServer.post(`/channels/${channelId}`, { body });
-}
-
-export async function deleteMessage(messageId) {
- // TODO
-}
-
-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;
- }
- }
-}
-
-function onLoginEvent(data) {
- 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':
- activeChannel.update((value) => value.deleteChannel(data.id));
- 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;
- }
-}
diff --git a/ui/src/lib/components/ActiveChannel.svelte b/ui/src/lib/components/ActiveChannel.svelte
deleted file mode 100644
index 978e952..0000000
--- a/ui/src/lib/components/ActiveChannel.svelte
+++ /dev/null
@@ -1,27 +0,0 @@
-<script>
- import { activeChannel, messages } from '$lib/store';
- import Message from './Message.svelte';
-
- let container;
- $: messageList = $activeChannel.isSet() ? $messages.inChannel($activeChannel.get()) : [];
-
- // TODO: eventually, store scroll height/last unread in channel? scroll there?
-
- let scroll = (message) => {
- message.scrollIntoView();
- }
-</script>
-
-<div class="container" bind:this={container}>
- {#each messageList as message}
- <div use:scroll>
- <Message {...message} />
- </div>
- {/each}
-</div>
-
-<style>
- .container {
- overflow: scroll;
- }
-</style>
diff --git a/ui/src/lib/components/Channel.svelte b/ui/src/lib/components/Channel.svelte
deleted file mode 100644
index 97fea1f..0000000
--- a/ui/src/lib/components/Channel.svelte
+++ /dev/null
@@ -1,21 +0,0 @@
-<script>
- import { activeChannel } from '$lib/store';
-
- export let id;
- export let name;
- let active = false;
-
- activeChannel.subscribe((value) => {
- active = value.is(id);
- });
-</script>
-
-<li
- class="rounded-full"
- class:bg-slate-400={active}
->
-<a href="/ch/{id}">
- <span class="badge bg-primary-500">#</span>
- <span class="flex-auto">{name}</span>
-</a>
-</li>
diff --git a/ui/src/lib/components/ChannelList.svelte b/ui/src/lib/components/ChannelList.svelte
deleted file mode 100644
index e0e5f06..0000000
--- a/ui/src/lib/components/ChannelList.svelte
+++ /dev/null
@@ -1,18 +0,0 @@
-<script>
- import { channelsList } from '$lib/store';
- import Channel from './Channel.svelte';
-
- let channels;
-
- channelsList.subscribe((value) => {
- channels = value.channels;
- });
-</script>
-
-<nav class="list-nav">
- <ul>
- {#each channels as channel}
- <Channel {...channel} />
- {/each}
- </ul>
-</nav>
diff --git a/ui/src/lib/components/CreateChannelForm.svelte b/ui/src/lib/components/CreateChannelForm.svelte
deleted file mode 100644
index ddcf486..0000000
--- a/ui/src/lib/components/CreateChannelForm.svelte
+++ /dev/null
@@ -1,23 +0,0 @@
-<script>
- import { createChannel } from '$lib/apiServer';
-
- let name = '';
- let disabled = false;
-
- async function handleSubmit(event) {
- disabled = true;
- const response = await createChannel(name);
- if (200 <= response.status && response.status < 300) {
- name = '';
- }
- disabled = 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>
-</form>
-
-<style>
-</style>
diff --git a/ui/src/lib/components/LogIn.svelte b/ui/src/lib/components/LogIn.svelte
deleted file mode 100644
index 2836e6d..0000000
--- a/ui/src/lib/components/LogIn.svelte
+++ /dev/null
@@ -1,35 +0,0 @@
-<script>
- import { logIn } from '$lib/apiServer';
- import { currentUser } from '$lib/store';
-
- let disabled = false;
- let username = '';
- let password = '';
-
- async function handleLogin(event) {
- disabled = true;
- const response = await logIn(username, password);
- if (200 <= response.status && response.status < 300) {
- currentUser.update(() => ({ username }));
- username = '';
- password = '';
- }
- disabled = false;
- }
-</script>
-
-<div class="card m-4 p-4">
- <form on:submit|preventDefault={handleLogin}>
- <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">
- sign in or up
- </button>
- </form>
-</div>
diff --git a/ui/src/lib/components/LogOut.svelte b/ui/src/lib/components/LogOut.svelte
deleted file mode 100644
index 01bef1b..0000000
--- a/ui/src/lib/components/LogOut.svelte
+++ /dev/null
@@ -1,22 +0,0 @@
-<script>
- import { logOut} from '$lib/apiServer';
- import { currentUser } from '$lib/store';
-
- async function handleLogout(event) {
- const response = await logOut();
- if (200 <= response.status && response.status < 300) {
- currentUser.update(() => null);
- }
- }
-</script>
-
-<form on:submit|preventDefault={handleLogout}>
- @{$currentUser.username}
- <button
- class="border-slate-500 border-solid border-2 font-bold p-1 rounded"
- type="submit"
- >log out</button>
-</form>
-
-<style>
-</style>
diff --git a/ui/src/lib/components/Message.svelte b/ui/src/lib/components/Message.svelte
deleted file mode 100644
index d040433..0000000
--- a/ui/src/lib/components/Message.svelte
+++ /dev/null
@@ -1,33 +0,0 @@
-<script>
- import SvelteMarkdown from 'svelte-markdown';
- import { currentUser, logins } from '$lib/store';
- import { deleteMessage } from '$lib/apiServer';
-
- export let at; // XXX: Omitted for now.
- export let sender;
- export let body;
-
- let timestamp = new Date(at).toTimeString();
- let name;
- $: name = $logins.get(sender);
-</script>
-
-<div class="card card-hover m-4 relative">
- <span class="chip variant-soft sticky top-o left-0">
- <!-- TODO: should this show up for only the first of a run? -->
- @{name}:
- </span>
- <span class="timestamp chip variant-soft absolute top-0 right-0">{at}</span>
- <section class="p-4">
- <SvelteMarkdown source={body} />
- </section>
-</div>
-
-<style>
- .card .timestamp {
- display: none;
- }
- .card:hover .timestamp {
- display: flex;
- }
-</style>
diff --git a/ui/src/lib/components/MessageInput.svelte b/ui/src/lib/components/MessageInput.svelte
deleted file mode 100644
index b33574b..0000000
--- a/ui/src/lib/components/MessageInput.svelte
+++ /dev/null
@@ -1,30 +0,0 @@
-<script>
- import { tick } from 'svelte';
- import { postToChannel } from '$lib/apiServer';
- import { activeChannel } from '$lib/store';
-
- let input;
- let value;
- let disabled;
- activeChannel.subscribe((value) => {
- disabled = !value.isSet();
- if (input && !disabled) {
- input.focus();
- }
- });
-
- async function handleSubmit(event) {
- disabled = true;
- // TODO try/catch:
- await postToChannel($activeChannel.get(), value);
- value = '';
- disabled = false;
- 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>
-</form>
diff --git a/ui/src/lib/index.js b/ui/src/lib/index.js
deleted file mode 100644
index 856f2b6..0000000
--- a/ui/src/lib/index.js
+++ /dev/null
@@ -1 +0,0 @@
-// place files you want to import through the `$lib` alias in this folder.
diff --git a/ui/src/lib/store.js b/ui/src/lib/store.js
deleted file mode 100644
index b964b4b..0000000
--- a/ui/src/lib/store.js
+++ /dev/null
@@ -1,10 +0,0 @@
-import { writable } from 'svelte/store';
-import { ActiveChannel, Channels } from '$lib/store/channels';
-import { Messages } from '$lib/store/messages';
-import { Logins } from '$lib/store/logins';
-
-export const currentUser = writable(null);
-export const activeChannel = writable(new ActiveChannel());
-export const logins = writable(new Logins());
-export const channelsList = writable(new Channels());
-export const messages = writable(new Messages());
diff --git a/ui/src/lib/store/channels.js b/ui/src/lib/store/channels.js
deleted file mode 100644
index bb6c86c..0000000
--- a/ui/src/lib/store/channels.js
+++ /dev/null
@@ -1,71 +0,0 @@
-export class Channels {
- constructor() {
- this.channels = [];
- }
-
- setChannels(channels) {
- this.channels = [...channels];
- this.sort();
- return this;
- }
-
- addChannel(id, name) {
- this.channels = [...this.channels, { id, name }];
- 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/ui/src/lib/store/logins.js b/ui/src/lib/store/logins.js
deleted file mode 100644
index 5b45206..0000000
--- a/ui/src/lib/store/logins.js
+++ /dev/null
@@ -1,22 +0,0 @@
-export class Logins {
- constructor() {
- this.logins = {};
- }
-
- addLogin(id, name) {
- this.logins[id] = name;
- return this;
- }
-
- setLogins(logins) {
- this.logins = {};
- for (let { id, name } of logins) {
- this.addLogin(id, name);
- }
- return this;
- }
-
- get(id) {
- return this.logins[id];
- }
-}
diff --git a/ui/src/lib/store/messages.js b/ui/src/lib/store/messages.js
deleted file mode 100644
index 931b8fb..0000000
--- a/ui/src/lib/store/messages.js
+++ /dev/null
@@ -1,44 +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, });
- }
- for (let channel in this.channels) {
- this.channels[channel].sort((a, b) => a.at - b.at);
- }
- 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;
- }
-}