From 4c266155785aad5050ebff7211e3d5f9e15c3238 Mon Sep 17 00:00:00 2001
From: Matthias Nott <mnott@mnsoft.org>
Date: Sun, 08 Mar 2026 07:37:45 +0100
Subject: [PATCH] feat: explicit session addressing + toast queue + solid toast styling

---
 contexts/ChatContext.tsx |   23 ++++++++++++++++-------
 1 files changed, 16 insertions(+), 7 deletions(-)

diff --git a/contexts/ChatContext.tsx b/contexts/ChatContext.tsx
index b109bb7..144f375 100644
--- a/contexts/ChatContext.tsx
+++ b/contexts/ChatContext.tsx
@@ -167,7 +167,8 @@
   // Per-session typing indicator (sessionId → boolean)
   const typingMapRef = useRef<Record<string, boolean>>({});
   const [isTyping, setIsTyping] = useState(false);
-  // Toast for other-session incoming messages
+  // Toast queue for other-session incoming messages (show one at a time)
+  const toastQueueRef = useRef<{ sessionId: string; sessionName: string; preview: string }[]>([]);
   const [incomingToast, setIncomingToast] = useState<{ sessionId: string; sessionName: string; preview: string } | null>(null);
   // PAI projects list
   const [projects, setProjects] = useState<PaiProject[]>([]);
@@ -265,13 +266,19 @@
         ...u,
         [sessionId]: (u[sessionId] ?? 0) + 1,
       }));
-      // Show toast for other-session messages (assistant only, skip system noise)
+      // Queue toast for other-session messages (assistant only, skip system noise)
       if (msg.role === "assistant") {
         setSessions((prev) => {
           const session = prev.find((s) => s.id === sessionId);
           const name = session?.name ?? sessionId.slice(0, 8);
           const preview = msg.type === "voice" ? "🎤 Voice note" : msg.type === "image" ? "📷 Image" : (msg.content ?? "").slice(0, 60);
-          setIncomingToast({ sessionId, sessionName: name, preview });
+          const toast = { sessionId, sessionName: name, preview };
+          // If no toast is showing, show immediately; otherwise queue
+          setIncomingToast((current) => {
+            if (current === null) return toast;
+            toastQueueRef.current.push(toast);
+            return current;
+          });
           return prev;
         });
       }
@@ -444,7 +451,7 @@
         status: "sending",
       };
       addMessageToActive(msg);
-      const sent = wsSend(text);
+      const sent = wsSend(text, activeSessionIdRef.current ?? undefined);
       updateMessageStatus(id, sent ? "sent" : "error");
     },
     [wsSend, addMessageToActive, updateMessageStatus]
@@ -466,7 +473,7 @@
       addMessageToActive(msg);
       try {
         const base64 = await encodeAudioToBase64(audioUri);
-        const sent = wsVoice(base64, "", id);
+        const sent = wsVoice(base64, "", id, activeSessionIdRef.current ?? undefined);
         updateMessageStatus(id, sent ? "sent" : "error");
       } catch (err) {
         console.error("Failed to encode audio:", err);
@@ -489,7 +496,7 @@
         status: "sending",
       };
       addMessageToActive(msg);
-      const sent = wsImageSend(imageBase64, caption, mimeType);
+      const sent = wsImageSend(imageBase64, caption, mimeType, activeSessionIdRef.current ?? undefined);
       updateMessageStatus(id, sent ? "sent" : "error");
     },
     [wsImageSend, addMessageToActive, updateMessageStatus]
@@ -560,7 +567,9 @@
   }, [sendCommand]);
 
   const dismissToast = useCallback(() => {
-    setIncomingToast(null);
+    // Show next queued toast, or clear
+    const next = toastQueueRef.current.shift();
+    setIncomingToast(next ?? null);
   }, []);
 
   const loadMoreMessages = useCallback(() => {

--
Gitblit v1.3.1