mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 09:19:51 +00:00
Merge pull request #11 from ArnasDon/fix/inbox-refresh-loop-and-banner
fix: inbox refresh loop, wrong connection status, overlapping banner
This commit is contained in:
commit
2446056842
@ -8,7 +8,7 @@ import { ConversationList } from "@/components/inbox/conversation-list";
|
|||||||
import { MessageThread } from "@/components/inbox/message-thread";
|
import { MessageThread } from "@/components/inbox/message-thread";
|
||||||
import { ContactSidebar } from "@/components/inbox/contact-sidebar";
|
import { ContactSidebar } from "@/components/inbox/contact-sidebar";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { Wifi, WifiOff } from "lucide-react";
|
import { WifiOff } from "lucide-react";
|
||||||
|
|
||||||
export default function InboxPage() {
|
export default function InboxPage() {
|
||||||
const [conversations, setConversations] = useState<Conversation[]>([]);
|
const [conversations, setConversations] = useState<Conversation[]>([]);
|
||||||
@ -31,11 +31,13 @@ export default function InboxPage() {
|
|||||||
|
|
||||||
if (!user) return;
|
if (!user) return;
|
||||||
|
|
||||||
|
// Table is `whatsapp_config` (singular) — the previous "whatsapp_configs"
|
||||||
|
// query always returned no rows, so the banner always showed "not connected".
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from("whatsapp_configs")
|
.from("whatsapp_config")
|
||||||
.select("status")
|
.select("status")
|
||||||
.eq("user_id", user.id)
|
.eq("user_id", user.id)
|
||||||
.single();
|
.maybeSingle();
|
||||||
|
|
||||||
setWhatsappConnected(data?.status === "connected");
|
setWhatsappConnected(data?.status === "connected");
|
||||||
};
|
};
|
||||||
@ -167,10 +169,11 @@ export default function InboxPage() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="-m-6 flex h-[calc(100vh-3.5rem)] overflow-hidden">
|
<div className="-m-6 flex h-[calc(100vh-3.5rem)] flex-col overflow-hidden">
|
||||||
{/* WhatsApp connection banner */}
|
{/* WhatsApp connection banner — in the flex column, not absolute,
|
||||||
|
so it pushes the panels down instead of overlapping them. */}
|
||||||
{whatsappConnected === false && (
|
{whatsappConnected === false && (
|
||||||
<div className="absolute left-60 right-0 top-14 z-10 flex items-center justify-center gap-2 bg-amber-500/10 px-4 py-2">
|
<div className="flex shrink-0 items-center justify-center gap-2 border-b border-amber-500/20 bg-amber-500/10 px-4 py-2">
|
||||||
<WifiOff className="h-4 w-4 text-amber-400" />
|
<WifiOff className="h-4 w-4 text-amber-400" />
|
||||||
<p className="text-xs text-amber-400">
|
<p className="text-xs text-amber-400">
|
||||||
WhatsApp is not connected. Go to Settings to connect your account.
|
WhatsApp is not connected. Go to Settings to connect your account.
|
||||||
@ -178,6 +181,7 @@ export default function InboxPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<div className="flex flex-1 overflow-hidden">
|
||||||
{/* Left panel: Conversation list */}
|
{/* Left panel: Conversation list */}
|
||||||
<ConversationList
|
<ConversationList
|
||||||
activeConversationId={activeConversation?.id ?? null}
|
activeConversationId={activeConversation?.id ?? null}
|
||||||
@ -201,5 +205,6 @@ export default function InboxPage() {
|
|||||||
<ContactSidebar contact={activeContact} />
|
<ContactSidebar contact={activeContact} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -101,36 +101,61 @@ export function MessageThread({
|
|||||||
return { expired, remaining };
|
return { expired, remaining };
|
||||||
}, [messages]);
|
}, [messages]);
|
||||||
|
|
||||||
const fetchMessages = useCallback(async () => {
|
// Store latest callback in a ref so fetchMessages doesn't need to
|
||||||
if (!conversation) return;
|
// depend on `onMessagesLoaded` — otherwise parent re-renders cause
|
||||||
setLoading(true);
|
// fetchMessages to change → useEffect re-fires → refetch → realtime
|
||||||
|
// UPDATE on conversations.unread_count → parent re-renders → LOOP.
|
||||||
|
const onMessagesLoadedRef = useRef(onMessagesLoaded);
|
||||||
|
onMessagesLoadedRef.current = onMessagesLoaded;
|
||||||
|
|
||||||
|
const conversationId = conversation?.id;
|
||||||
|
const hasUnread = (conversation?.unread_count ?? 0) > 0;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!conversationId) return;
|
||||||
|
|
||||||
const supabase = createClient();
|
const supabase = createClient();
|
||||||
|
let cancelled = false;
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
setLoading(true);
|
||||||
|
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
.from("messages")
|
.from("messages")
|
||||||
.select("*")
|
.select("*")
|
||||||
.eq("conversation_id", conversation.id)
|
.eq("conversation_id", conversationId)
|
||||||
.order("created_at", { ascending: true });
|
.order("created_at", { ascending: true });
|
||||||
|
|
||||||
|
if (cancelled) return;
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error("Failed to fetch messages:", error);
|
console.error("Failed to fetch messages:", error);
|
||||||
} else {
|
} else {
|
||||||
onMessagesLoaded(data ?? []);
|
onMessagesLoadedRef.current(data ?? []);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset unread count
|
// Only issue the unread_count reset when there's actually something
|
||||||
|
// to reset. Unconditional updates fire a realtime UPDATE event every
|
||||||
|
// time, which — combined with the parent's conversation-event handler
|
||||||
|
// — used to retrigger this effect in a loop.
|
||||||
|
if (hasUnread) {
|
||||||
await supabase
|
await supabase
|
||||||
.from("conversations")
|
.from("conversations")
|
||||||
.update({ unread_count: 0 })
|
.update({ unread_count: 0 })
|
||||||
.eq("id", conversation.id);
|
.eq("id", conversationId);
|
||||||
|
}
|
||||||
|
|
||||||
setLoading(false);
|
if (!cancelled) setLoading(false);
|
||||||
}, [conversation, onMessagesLoaded]);
|
})();
|
||||||
|
|
||||||
useEffect(() => {
|
return () => {
|
||||||
fetchMessages();
|
cancelled = true;
|
||||||
}, [fetchMessages]);
|
};
|
||||||
|
// Re-fetch only when the selected conversation changes. `hasUnread`
|
||||||
|
// is used inside but is a boolean derived from conversation; the
|
||||||
|
// conversationId dep is the real trigger.
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [conversationId]);
|
||||||
|
|
||||||
// Auto-scroll to bottom on new messages
|
// Auto-scroll to bottom on new messages
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user