From db1367b8feee0ee290509910c12696e0733e3cdf Mon Sep 17 00:00:00 2001 From: Your Name Date: Mon, 19 Oct 2026 18:06:00 +0800 Subject: [PATCH] fix: render inline support images --- web/src/mobile/im.test.ts | 38 ++++++++++++++++++++++++++++++++++++++ web/src/mobile/im.ts | 6 +++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/web/src/mobile/im.test.ts b/web/src/mobile/im.test.ts index 274d95d..af3aa0b 100644 --- a/web/src/mobile/im.test.ts +++ b/web/src/mobile/im.test.ts @@ -265,4 +265,42 @@ describe('customer service media upload', () => { 'https://cdn.example.com/public/image.png', ); }); + + it('renders inline Drafty images sent by the IM web client', () => { + const onMessages = vi.fn(); + new CustomerServiceChat(session(), onMessages, { origin: 'https://www.yuexuan1.shop' }); + const tinode = mocks.instances[0]; + tinode.topic.messages.mockImplementation((callback: (message: unknown) => void) => { + callback({ + seq: 4, + from: 'usr_support', + ts: '2026-10-19T10:02:32.090Z', + content: { + ent: [{ + tp: 'IM', + data: { + val: 'aGVsbG8=', + mime: 'image/png', + name: 'proof.png', + width: 70, + height: 70, + }, + }], + fmt: [{ len: 1 }], + txt: ' ', + }, + }); + }); + + tinode.topic.onData?.(); + + expect(onMessages).toHaveBeenCalledWith([ + expect.objectContaining({ + seq: 4, + type: 'image', + media_url: 'data:image/png;base64,aGVsbG8=', + file_name: 'proof.png', + }), + ]); + }); }); diff --git a/web/src/mobile/im.ts b/web/src/mobile/im.ts index def8cb6..efc78b3 100644 --- a/web/src/mobile/im.ts +++ b/web/src/mobile/im.ts @@ -349,10 +349,14 @@ function messageContent(content: unknown, mediaURL: (ref?: string) => string) { const media = drafty.ent?.find((entity) => entity?.tp === 'IM' || entity?.tp === 'VD'); if (media?.data) { const type = media.tp === 'VD' ? 'video' : 'image'; + const inline = typeof media.data.val === 'string' ? media.data.val : ''; + const mime = typeof media.data.mime === 'string' && media.data.mime.startsWith(`${type}/`) + ? media.data.mime + : type === 'video' ? 'video/webm' : 'image/jpeg'; return { text: '', type, - media_url: mediaURL(String(media.data.ref || '')), + media_url: inline ? `data:${mime};base64,${inline}` : mediaURL(String(media.data.ref || '')), preview_url: mediaURL(String(media.data.preref || '')), file_name: String(media.data.name || ''), } as const;