From a72125aab32e863801786cb73aacfb0e4ee99aaa Mon Sep 17 00:00:00 2001 From: dolphin Date: Wed, 29 Jul 2026 17:31:26 +0800 Subject: [PATCH] fix(chat): render an image from the link the client already has MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A message the user just sent isn't in the database yet, so asking the backend to re-issue a link came back empty — workflow chat showed "no longer available", task mode sat in its skeleton — and both only came right after a refresh. The upload already handed the client a working link, so that renders immediately and the re-issued one takes over when it arrives, which is what an older conversation needs. Stored links carry the internal storage host, so they get the same rewrite the download card does before being handed to the browser. --- .../src/components/Chat/AiMessageBubble.tsx | 1 + .../Chat/Messages/Content/MessageImage.tsx | 28 ++++++++++++++----- .../pages/appChat/components/MessageUser.tsx | 1 + 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/frontend/client/src/components/Chat/AiMessageBubble.tsx b/src/frontend/client/src/components/Chat/AiMessageBubble.tsx index 185fe6de6..222e8f4ae 100644 --- a/src/frontend/client/src/components/Chat/AiMessageBubble.tsx +++ b/src/frontend/client/src/components/Chat/AiMessageBubble.tsx @@ -118,6 +118,7 @@ function UploadedFileList({ files, conversationId }: { files: any[]; conversatio conversationId={conversationId} fileId={file.file_id} altText={file.name || file.file_name} + initialUrl={file.filepath || file.file_path || file.file_url} /> ))} diff --git a/src/frontend/client/src/components/Chat/Messages/Content/MessageImage.tsx b/src/frontend/client/src/components/Chat/Messages/Content/MessageImage.tsx index d670a4f1a..61bbf042d 100644 --- a/src/frontend/client/src/components/Chat/Messages/Content/MessageImage.tsx +++ b/src/frontend/client/src/components/Chat/Messages/Content/MessageImage.tsx @@ -17,37 +17,51 @@ export function MessageImage({ conversationId, fileId, altText, + initialUrl, }: { conversationId?: string; fileId?: string; altText?: string; + /** Link the client already holds from the upload, if any. */ + initialUrl?: string; }) { - const [url, setUrl] = useState(null); + // Stored links carry the internal storage host, which the browser can't + // reach — same swap the download card does. + const toReachable = (u?: string | null) => + u ? u.replace(/https?:\/\/[^/]+/, __APP_ENV__.BASE_URL) : null; + + const [url, setUrl] = useState(toReachable(initialUrl)); const [failed, setFailed] = useState(false); useEffect(() => { let cancelled = false; - // A just-sent message reaches here before the backend has assigned the - // conversation its id. That's "not yet", not "gone" — keep waiting, or the - // placeholder flashes up on every image the user sends. + // Nothing to ask for, and nothing to show either. if (!conversationId || !fileId) { + if (!initialUrl) { + return; + } + setUrl(toReachable(initialUrl)); return; } setFailed(false); + // The upload link renders straight away — a message the user just sent + // isn't in the database yet, so asking the backend for a link would come + // back empty and read as a dead image. The re-issued link replaces it when + // it arrives, which is what makes an old conversation work. getAttachmentUrl(conversationId, fileId).then((fresh) => { if (cancelled) { return; } if (fresh) { - setUrl(fresh); - } else { + setUrl(toReachable(fresh)); + } else if (!initialUrl) { setFailed(true); } }); return () => { cancelled = true; }; - }, [conversationId, fileId]); + }, [conversationId, fileId, initialUrl]); // Debug aid: the resolved link and the ids it was resolved from are on the // wrapper, so a broken picture can be traced from devtools without digging diff --git a/src/frontend/client/src/pages/appChat/components/MessageUser.tsx b/src/frontend/client/src/pages/appChat/components/MessageUser.tsx index b94573b66..fd82b02e7 100644 --- a/src/frontend/client/src/pages/appChat/components/MessageUser.tsx +++ b/src/frontend/client/src/pages/appChat/components/MessageUser.tsx @@ -100,6 +100,7 @@ export default function MessageUser({ useName, data, showButton, disabledSearch conversationId={data.chat_id || chatId} fileId={file.file_id} altText={file.file_name || file.name} + initialUrl={file.file_url || file.filepath} /> ))}