From b602a9cea56cb1cda668ddd473d74b04ea160d4c Mon Sep 17 00:00:00 2001 From: archipelago Date: Tue, 16 Jun 2026 07:39:52 -0400 Subject: [PATCH] feat(toast): message toast opens the related chat + has a close icon (#33) - Add a close (X) button to the message toast (closeToast, @click.stop) like the system notifications. - Carry the sender pubkey on the toast; clicking now deep-links to that conversation (/dashboard/mesh?peer=) instead of the generic mesh page. - Mesh.vue reads ?peer= on mount and opens the matching peer (by pubkey_hex/did), gracefully falling back to the mesh list when no match (B1/B2 identity). type-check clean; useMessageToast tests 11/11. Co-Authored-By: Claude Opus 4.8 (1M context) --- neode-ui/src/App.vue | 9 +++++++++ neode-ui/src/composables/useMessageToast.ts | 17 ++++++++++++++--- neode-ui/src/views/Mesh.vue | 11 +++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) diff --git a/neode-ui/src/App.vue b/neode-ui/src/App.vue index 11f59657..74ac0e37 100644 --- a/neode-ui/src/App.vue +++ b/neode-ui/src/App.vue @@ -59,6 +59,15 @@

{{ toastMessage.text }}

Click to view

+ diff --git a/neode-ui/src/composables/useMessageToast.ts b/neode-ui/src/composables/useMessageToast.ts index cc5de069..aafde68d 100644 --- a/neode-ui/src/composables/useMessageToast.ts +++ b/neode-ui/src/composables/useMessageToast.ts @@ -14,7 +14,7 @@ const MESSAGE_POLL_INTERVAL = 30000 // 30s const receivedMessages = ref([]) const lastMessageCount = ref(0) const loadingMessages = ref(false) -const toastMessage = ref<{ show: boolean; text: string }>({ show: false, text: '' }) +const toastMessage = ref<{ show: boolean; text: string; fromPubkey: string }>({ show: false, text: '', fromPubkey: '' }) let pollTimer: ReturnType | null = null export function useMessageToast() { @@ -37,6 +37,9 @@ export function useMessageToast() { toastMessage.value = { show: true, text: (newCount === 1 ? latest?.message : null) ?? `${newCount} new messages`, + // Only deep-link to a specific chat when it's a single new message + // from one sender; otherwise open the mesh list. + fromPubkey: newCount === 1 ? (latest?.from_pubkey ?? '') : '', } lastMessageCount.value = msgs.length } else { @@ -83,9 +86,16 @@ export function useMessageToast() { } function dismissToastAndOpenMessages() { - toastMessage.value = { show: false, text: '' } + const peer = toastMessage.value.fromPubkey + toastMessage.value = { show: false, text: '', fromPubkey: '' } markAsRead() - router.push('/dashboard/mesh') + // Open the specific conversation when we know the sender; else the mesh list. + router.push(peer ? { path: '/dashboard/mesh', query: { peer } } : '/dashboard/mesh') + } + + // Dismiss the toast without navigating (the close icon). + function closeToast() { + toastMessage.value = { show: false, text: '', fromPubkey: '' } } return { @@ -99,5 +109,6 @@ export function useMessageToast() { stopPolling, markAsRead, dismissToastAndOpenMessages, + closeToast, } } diff --git a/neode-ui/src/views/Mesh.vue b/neode-ui/src/views/Mesh.vue index e63256a8..acc7dfb0 100644 --- a/neode-ui/src/views/Mesh.vue +++ b/neode-ui/src/views/Mesh.vue @@ -1,5 +1,6 @@