aboutsummaryrefslogtreecommitdiffstats
path: root/src/routes
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-03-07 10:39:57 -0800
committerAdam Soltys <[email protected]>2026-03-07 10:39:57 -0800
commit63249aed9dd62e59aa302ddcf7c3a419effc43b3 (patch)
tree48e89bcc0c58a92753a75d5d8d9e9dea4291dc87 /src/routes
parentad0b4009b2b2ae9173fffb41f0cfa7b35edcb303 (diff)
downloadcoinos-ui-63249aed9dd62e59aa302ddcf7c3a419effc43b3.tar.gz
coinos-ui-63249aed9dd62e59aa302ddcf7c3a419effc43b3.zip
improve message delivery with websocket reconnect and push data transport
- Enable auto-reconnect on relay connections so dropped WebSockets recover - Connect relays in parallel with 5s timeout for faster startup - Add visibility change handler to catch up missed messages on tab return - Forward kind 445 events via push notifications as a data channel - Show "reconnecting..." status in chat UI when connections are down Co-Authored-By: Claude Opus 4.6 <[email protected]>
Diffstat (limited to 'src/routes')
-rw-r--r--src/routes/(app)/messages/+page.svelte28
-rw-r--r--src/routes/(app)/messages/[id]/+page.svelte23
2 files changed, 49 insertions, 2 deletions
diff --git a/src/routes/(app)/messages/+page.svelte b/src/routes/(app)/messages/+page.svelte
index 1981b5f1..215f07b0 100644
--- a/src/routes/(app)/messages/+page.svelte
+++ b/src/routes/(app)/messages/+page.svelte
@@ -4,6 +4,7 @@
import {
getGroups, subscribeToMessages, fetchGroupHistory,
searchMlsUsers, preloadMlsUsers, resolveUser, displayName,
+ isConnected, onConnectionChange,
} from "$lib/messaging";
import type { GroupInfo } from "$lib/messaging";
import { t } from "$lib/translations";
@@ -22,6 +23,7 @@
let groups: GroupInfo[] = $state([]);
let loaded = $state(false);
+ let connected = $state(isConnected());
let memberInfos = $state(new Map<string, any>());
// Search state
@@ -68,8 +70,10 @@
};
let closeSub: (() => void) | undefined;
+ let closeConnectionListener: (() => void) | undefined;
onMount(() => {
+ closeConnectionListener = onConnectionChange((c) => (connected = c));
// Show cached groups immediately, then fetch history in background
refreshGroups().then(() => { loaded = true; });
fetchGroupHistory(user).then(refreshGroups);
@@ -77,9 +81,16 @@
subscribeToMessages(user, refreshGroups).then((close) => (closeSub = close));
});
- onDestroy(() => closeSub?.());
+ onDestroy(() => {
+ closeSub?.();
+ closeConnectionListener?.();
+ });
</script>
+{#if !connected}
+ <div class="reconnecting-banner">Reconnecting...</div>
+{/if}
+
<div class="container mx-auto max-w-xl px-4 space-y-1">
<input
use:focus
@@ -134,3 +145,18 @@
{/each}
{/if}
</div>
+
+<style>
+ .reconnecting-banner {
+ text-align: center;
+ font-size: 0.75rem;
+ color: #f59e0b;
+ padding: 0.25rem;
+ animation: pulse-text 1.5s ease-in-out infinite;
+ }
+
+ @keyframes pulse-text {
+ 0%, 100% { opacity: 0.6; }
+ 50% { opacity: 1; }
+ }
+</style>
diff --git a/src/routes/(app)/messages/[id]/+page.svelte b/src/routes/(app)/messages/[id]/+page.svelte
index a5768df7..4af0def5 100644
--- a/src/routes/(app)/messages/[id]/+page.svelte
+++ b/src/routes/(app)/messages/[id]/+page.svelte
@@ -12,6 +12,7 @@
findOrCreateDmGroup,
resolveUser, displayName,
decryptMediaUrl,
+ isConnected, onConnectionChange,
} from "$lib/messaging";
import type { GroupInfo } from "$lib/messaging";
import { t } from "$lib/translations";
@@ -48,6 +49,7 @@
let dates: number[] = $state([]);
let sendError = $state("");
let uploading = $state(false);
+ let connected = $state(isConnected());
let senderInfos = $state(new Map<string, any>());
const groupDisplayName = (g: GroupInfo | undefined): string => {
@@ -243,8 +245,10 @@
};
let closeSub: (() => void) | undefined;
+ let closeConnectionListener: (() => void) | undefined;
onMount(() => {
+ closeConnectionListener = onConnectionChange((c) => (connected = c));
loadGroup();
const vv = window.visualViewport;
@@ -272,7 +276,10 @@
}).then((close) => (closeSub = close));
});
- onDestroy(() => closeSub?.());
+ onDestroy(() => {
+ closeSub?.();
+ closeConnectionListener?.();
+ });
</script>
<div class="chat-container">
@@ -281,6 +288,9 @@
{#if groupInfo && groupInfo.members.length > 2}
<span class="text-secondary text-sm">{groupInfo.members.length} members</span>
{/if}
+ {#if !connected}
+ <span class="connection-status">reconnecting...</span>
+ {/if}
</div>
<div id="messages">
@@ -394,6 +404,17 @@
flex-shrink: 0;
}
+ .connection-status {
+ font-size: 0.75rem;
+ color: #f59e0b;
+ animation: pulse-text 1.5s ease-in-out infinite;
+ }
+
+ @keyframes pulse-text {
+ 0%, 100% { opacity: 0.6; }
+ 50% { opacity: 1; }
+ }
+
.warning {
color: #ff7f00;
}