From 2e2e3980ab78052be74f4007c343e69a583648fe Mon Sep 17 00:00:00 2001
From: Owen Jacobson <owen@grimoire.ca>
Date: Thu, 10 Oct 2024 22:30:18 -0400
Subject: Compute the active channel from the current routing state, not from a
 store.

---
 ui/lib/components/MessageInput.svelte | 30 ++++++++++++++----------------
 1 file changed, 14 insertions(+), 16 deletions(-)

(limited to 'ui/lib/components/MessageInput.svelte')

diff --git a/ui/lib/components/MessageInput.svelte b/ui/lib/components/MessageInput.svelte
index b33574b..0da78d4 100644
--- a/ui/lib/components/MessageInput.svelte
+++ b/ui/lib/components/MessageInput.svelte
@@ -1,30 +1,28 @@
 <script>
     import { tick } from 'svelte';
     import { postToChannel } from '$lib/apiServer';
-    import { activeChannel } from '$lib/store';
 
+    export let channel = null;
     let input;
     let value;
-    let disabled;
-    activeChannel.subscribe((value) => {
-        disabled = !value.isSet();
-        if (input && !disabled) {
+    let sending = false;
+
+    $: disabled = (channel === null);
+
+    async function handleSubmit() {
+        if (channel !== null) {
+            sending = true;
+            // TODO try/catch:
+            await postToChannel(channel, value);
+            sending = false;
+            value = '';
+            await tick();
             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" />
+    <input bind:this={input} bind:value={value} disabled={sending || 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>
-- 
cgit v1.2.3


From 72f3d8c5ab3e2a42cf1a76d0c08815dbe46e50a1 Mon Sep 17 00:00:00 2001
From: Owen Jacobson <owen@grimoire.ca>
Date: Fri, 11 Oct 2024 01:18:18 -0400
Subject: Move login to its own route.

This - in passing - fixes the problem where the client failed to subscribe after logging in, by causing the whole subscription process to be re-run when returning to the main interface.
---
 src/ui.rs                             | 46 +++++++++++++++++++++++++----------
 ui/lib/apiServer.js                   |  2 ++
 ui/lib/components/LogIn.svelte        |  4 ++-
 ui/lib/components/LogOut.svelte       |  8 ++++--
 ui/lib/components/MessageInput.svelte |  2 +-
 ui/routes/(app)/+layout.svelte        | 24 +++++++++---------
 ui/routes/(login)/login/+page.svelte  |  5 ++++
 7 files changed, 63 insertions(+), 28 deletions(-)
 create mode 100644 ui/routes/(login)/login/+page.svelte

(limited to 'ui/lib/components/MessageInput.svelte')

diff --git a/src/ui.rs b/src/ui.rs
index b296325..34eb6f1 100644
--- a/src/ui.rs
+++ b/src/ui.rs
@@ -14,25 +14,45 @@ use crate::{app::App, channel, error::Internal, login::Login};
 #[folder = "target/ui"]
 struct Assets;
 
+impl Assets {
+    fn load(path: impl AsRef<str>) -> Result<Asset, NotFound<String>> {
+        let path = path.as_ref();
+        let mime = mime_guess::from_path(path).first_or_octet_stream();
+
+        Self::get(path)
+            .map(|file| Asset(mime, file))
+            .ok_or(NotFound(format!("not found: {path}")))
+    }
+
+    fn index() -> Result<Asset, Internal> {
+        // "not found" in this case really is an internal error, as it should
+        // never happen. `index.html` is a known-valid path.
+        Ok(Self::load("index.html")?)
+    }
+}
+
 pub fn router() -> Router<App> {
     Router::new()
         .route("/*path", get(asset))
         .route("/", get(root))
+        .route("/login", get(login))
         .route("/ch/:channel", get(channel))
 }
 
 async fn asset(Path(path): Path<String>) -> Result<Asset, NotFound<String>> {
-    let mime = mime_guess::from_path(&path).first_or_octet_stream();
+    Assets::load(path)
+}
 
-    Assets::get(&path)
-        .map(|file| Asset(mime, file))
-        .ok_or(NotFound(format!("not found: {path}")))
+async fn root(login: Option<Login>) -> Result<impl IntoResponse, Internal> {
+    if login.is_none() {
+        Ok(Redirect::temporary("/login").into_response())
+    } else {
+        Ok(Assets::index()?.into_response())
+    }
 }
 
-async fn root() -> Result<Asset, Internal> {
-    // "not found" in this case really is an internal error, as it should
-    // never happen. `index.html` is a known-valid path.
-    Ok(asset(Path(String::from("index.html"))).await?)
+async fn login() -> Result<impl IntoResponse, Internal> {
+    Assets::index()
 }
 
 async fn channel(
@@ -40,13 +60,13 @@ async fn channel(
     login: Option<Login>,
     Path(channel): Path<channel::Id>,
 ) -> Result<impl IntoResponse, Internal> {
-    Ok(if login.is_none() {
-        Redirect::temporary("/").into_response()
+    if login.is_none() {
+        Ok(Redirect::temporary("/").into_response())
     } else if app.channels().get(&channel).await?.is_none() {
-        NotFound(root().await?).into_response()
+        Ok(NotFound(Assets::index()?).into_response())
     } else {
-        root().await?.into_response()
-    })
+        Ok(Assets::index()?.into_response())
+    }
 }
 
 struct Asset(Mime, EmbeddedFile);
diff --git a/ui/lib/apiServer.js b/ui/lib/apiServer.js
index 538fa85..ccd6e66 100644
--- a/ui/lib/apiServer.js
+++ b/ui/lib/apiServer.js
@@ -66,6 +66,8 @@ export function subscribeToEvents(resume_point) {
         break;
     }
   }
+
+  return evtSource;
 }
 
 function onLoginEvent(data) {
diff --git a/ui/lib/components/LogIn.svelte b/ui/lib/components/LogIn.svelte
index 2836e6d..e1cda8a 100644
--- a/ui/lib/components/LogIn.svelte
+++ b/ui/lib/components/LogIn.svelte
@@ -1,4 +1,5 @@
 <script>
+    import { goto } from '$app/navigation';
     import { logIn } from '$lib/apiServer';
     import { currentUser } from '$lib/store';
 
@@ -6,13 +7,14 @@
     let username = '';
     let password = '';
 
-    async function handleLogin(event) {
+    async function handleLogin() {
         disabled = true;
         const response = await logIn(username, password);
         if (200 <= response.status && response.status < 300) {
             currentUser.update(() => ({ username }));
             username = '';
             password = '';
+            goto('/');
         }
         disabled = false;
     }
diff --git a/ui/lib/components/LogOut.svelte b/ui/lib/components/LogOut.svelte
index 01bef1b..ba0861a 100644
--- a/ui/lib/components/LogOut.svelte
+++ b/ui/lib/components/LogOut.svelte
@@ -1,17 +1,21 @@
 <script>
+    import { goto } from '$app/navigation';
     import { logOut} from '$lib/apiServer';
     import { currentUser } from '$lib/store';
 
-    async function handleLogout(event) {
+    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}>
-    @{$currentUser.username}
+    {#if $currentUser}
+        @{$currentUser.username}
+    {/if}
     <button
         class="border-slate-500 border-solid border-2 font-bold p-1 rounded"
         type="submit"
diff --git a/ui/lib/components/MessageInput.svelte b/ui/lib/components/MessageInput.svelte
index 0da78d4..b2746e0 100644
--- a/ui/lib/components/MessageInput.svelte
+++ b/ui/lib/components/MessageInput.svelte
@@ -4,7 +4,7 @@
 
     export let channel = null;
     let input;
-    let value;
+    let value = '';
     let sending = false;
 
     $: disabled = (channel === null);
diff --git a/ui/routes/(app)/+layout.svelte b/ui/routes/(app)/+layout.svelte
index 1d561ae..0f8e83c 100644
--- a/ui/routes/(app)/+layout.svelte
+++ b/ui/routes/(app)/+layout.svelte
@@ -1,22 +1,19 @@
 <script>
-	import { onMount } from 'svelte';
     import { page } from '$app/stores';
+    import { goto } from '$app/navigation';
+	import { onMount, onDestroy } from 'svelte';
 
 	import { boot, subscribeToEvents } from '$lib/apiServer';
 	import { currentUser, logins, channelsList, messages } from '$lib/store';
 
     import ChannelList from '$lib/components/ChannelList.svelte';
     import CreateChannelForm from '$lib/components/CreateChannelForm.svelte';
-    import LogIn from '$lib/components/LogIn.svelte';
     import MessageInput from '$lib/components/MessageInput.svelte';
 
-    let user;
     let loading = true;
-    $: channel = $page?.params?.channel;
+    let events = null;
 
-    currentUser.subscribe((value) => {
-        user = value;
-    });
+    $: channel = $page?.params?.channel;
 
     function onBooted(boot) {
         currentUser.update(() => ({
@@ -34,10 +31,11 @@
             switch (response.status) {
                 case 200:
                     onBooted(response.data);
-                    subscribeToEvents(response.data.resume_point);
+                    events = subscribeToEvents(response.data.resume_point);
                     break;
                 case 401:
                     currentUser.update(() => null);
+                    goto('/login');
                     break;
                 default:
                     // TODO: display error.
@@ -48,11 +46,17 @@
         }
         loading = false;
 	});
+
+    onDestroy(async () => {
+        if (events !== null) {
+            events.close();
+        }
+    });
 </script>
 
 {#if loading}
     <h2>Loading&hellip;</h2>
-{:else if user != null}
+{:else}
     <div id="interface">
         <div class="channel-list">
             <ChannelList active={channel} />
@@ -67,8 +71,6 @@
             <MessageInput {channel} />
         </div>
     </div>
-{:else}
-    <LogIn />
 {/if}
 
 <style>
diff --git a/ui/routes/(login)/login/+page.svelte b/ui/routes/(login)/login/+page.svelte
new file mode 100644
index 0000000..c333fdd
--- /dev/null
+++ b/ui/routes/(login)/login/+page.svelte
@@ -0,0 +1,5 @@
+<script>
+    import LogIn from '$lib/components/LogIn.svelte';
+</script>
+
+<LogIn />
-- 
cgit v1.2.3

