mirror of
https://github.com/Luchexx/wacrm.git
synced 2026-11-02 17:29:45 +00:00
Merge pull request #70 from ArnasDon/fix/inbox-messages-stale-after-deeplink
fix(inbox): don't drop messages when ?c= URL param updates
This commit is contained in:
commit
7182abe7d8
@ -163,6 +163,16 @@ export default function InboxPage() {
|
|||||||
loaded.length > 0
|
loaded.length > 0
|
||||||
) {
|
) {
|
||||||
autoSelectedForDeepLinkRef.current = deepLinkConvId;
|
autoSelectedForDeepLinkRef.current = deepLinkConvId;
|
||||||
|
// If the deep-linked conversation is already the active one
|
||||||
|
// (e.g. because the user clicked it in the list and we
|
||||||
|
// router.replace()'d the URL, which made the ConversationList
|
||||||
|
// refetch and land us back here), do NOT re-apply it. Doing so
|
||||||
|
// would setMessages([]) on a thread whose messages have
|
||||||
|
// already been loaded by MessageThread — and because
|
||||||
|
// conversationId didn't change, MessageThread wouldn't
|
||||||
|
// refetch. The thread would read "No messages yet" until a
|
||||||
|
// full page reload rehydrated state from scratch.
|
||||||
|
if (activeConversation?.id === deepLinkConvId) return;
|
||||||
const match = loaded.find((c) => c.id === deepLinkConvId);
|
const match = loaded.find((c) => c.id === deepLinkConvId);
|
||||||
if (match) {
|
if (match) {
|
||||||
setActiveConversation(match);
|
setActiveConversation(match);
|
||||||
@ -171,7 +181,7 @@ export default function InboxPage() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[deepLinkConvId]
|
[deepLinkConvId, activeConversation?.id]
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleSelectConversation = useCallback(
|
const handleSelectConversation = useCallback(
|
||||||
@ -184,6 +194,14 @@ export default function InboxPage() {
|
|||||||
setActiveConversation(conv);
|
setActiveConversation(conv);
|
||||||
setActiveContact(conv.contact ?? null);
|
setActiveContact(conv.contact ?? null);
|
||||||
setMessages([]);
|
setMessages([]);
|
||||||
|
// Record the selection on the deep-link ref BEFORE we change the
|
||||||
|
// URL. The router.replace below flips `deepLinkConvId`, which can
|
||||||
|
// in turn cause ConversationList to refetch and eventually call
|
||||||
|
// handleConversationsLoaded again. Without this line, the ref
|
||||||
|
// still points at the previous value, the auto-select block
|
||||||
|
// sees `ref !== deepLinkConvId`, fires a second time, and
|
||||||
|
// clobbers the messages MessageThread just fetched.
|
||||||
|
autoSelectedForDeepLinkRef.current = conv.id;
|
||||||
// Reflect the selection in the URL so a refresh lands the user
|
// Reflect the selection in the URL so a refresh lands the user
|
||||||
// back in the same thread, and so copy-paste links work. Use
|
// back in the same thread, and so copy-paste links work. Use
|
||||||
// replace() to avoid polluting browser history with every click.
|
// replace() to avoid polluting browser history with every click.
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState, useEffect, useCallback, useMemo } from "react";
|
import { useState, useEffect, useCallback, useMemo, useRef } from "react";
|
||||||
import { createClient } from "@/lib/supabase/client";
|
import { createClient } from "@/lib/supabase/client";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import type { Conversation, ConversationStatus } from "@/types";
|
import type { Conversation, ConversationStatus } from "@/types";
|
||||||
@ -46,32 +46,49 @@ export function ConversationList({
|
|||||||
const [filter, setFilter] = useState<ConversationStatus | "all">("all");
|
const [filter, setFilter] = useState<ConversationStatus | "all">("all");
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
const fetchConversations = useCallback(async () => {
|
// Keep the latest callback in a ref so the fetch effect below can
|
||||||
const supabase = createClient();
|
// have a stable, empty-dep identity. Previously the fetch useCallback
|
||||||
const { data, error } = await supabase
|
// depended on `onConversationsLoaded`, which depends on the parent's
|
||||||
.from("conversations")
|
// `deepLinkConvId` — so every URL change (including one the parent
|
||||||
.select("*, contact:contacts(*)")
|
// triggered via router.replace after a click) caused a fresh
|
||||||
.order("last_message_at", { ascending: false });
|
// conversations fetch. That extra refetch was the trigger for the
|
||||||
|
// deep-link auto-select running a second time and wiping the active
|
||||||
if (error) {
|
// thread's messages.
|
||||||
// Supabase errors have non-enumerable properties — log fields explicitly
|
const onConversationsLoadedRef = useRef(onConversationsLoaded);
|
||||||
console.error("Failed to fetch conversations:", {
|
onConversationsLoadedRef.current = onConversationsLoaded;
|
||||||
message: error.message,
|
|
||||||
details: error.details,
|
|
||||||
hint: error.hint,
|
|
||||||
code: error.code,
|
|
||||||
});
|
|
||||||
setLoading(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
onConversationsLoaded(data ?? []);
|
|
||||||
setLoading(false);
|
|
||||||
}, [onConversationsLoaded]);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchConversations();
|
const supabase = createClient();
|
||||||
}, [fetchConversations]);
|
let cancelled = false;
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const { data, error } = await supabase
|
||||||
|
.from("conversations")
|
||||||
|
.select("*, contact:contacts(*)")
|
||||||
|
.order("last_message_at", { ascending: false });
|
||||||
|
|
||||||
|
if (cancelled) return;
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
// Supabase errors have non-enumerable properties — log fields explicitly
|
||||||
|
console.error("Failed to fetch conversations:", {
|
||||||
|
message: error.message,
|
||||||
|
details: error.details,
|
||||||
|
hint: error.hint,
|
||||||
|
code: error.code,
|
||||||
|
});
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
onConversationsLoadedRef.current(data ?? []);
|
||||||
|
setLoading(false);
|
||||||
|
})();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
cancelled = true;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const filtered = useMemo(() => {
|
const filtered = useMemo(() => {
|
||||||
let result = conversations;
|
let result = conversations;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user