diff --git a/src/components/chat/messages/MessageFile.tsx b/src/components/chat/messages/MessageFile.tsx index c6ab612a..e6327752 100644 --- a/src/components/chat/messages/MessageFile.tsx +++ b/src/components/chat/messages/MessageFile.tsx @@ -13,16 +13,35 @@ interface MessageFileProps { const MessageFile: React.FC = ({ attachments }) => { const { t } = useLanguage('chat'); - const downloadFile = (attachment: Attachment) => { - const result = openAttachmentInNewTab({ - url: attachment.data_url, - filename: attachment.fallback_title || t('messages.messageFile.fileFallback'), - }); + const downloadFile = async (attachment: Attachment) => { + const url = attachment.data_url?.trim(); + if (!url) return; - if (result === 'download-fallback') { - toast.info(t('messages.messageImage.downloadStarted'), { - description: attachment.fallback_title || t('messages.messageFile.fileFallbackTitle'), + const filename = attachment.fallback_title || t('messages.messageFile.fileFallback'); + + try { + const response = await fetch(url, { mode: 'cors' }); + if (!response.ok) { + console.warn('[MessageFile] non-ok response:', response.status, url); + toast.warning(t('messages.messageFile.downloadFallbackOpenedInNewTab'), { + description: t('messages.messageFile.downloadFallbackReason.serverError', { status: response.status }), + }); + openAttachmentInNewTab({ url, filename }); + return; + } + const blob = await response.blob(); + const blobUrl = URL.createObjectURL(blob); + const link = document.createElement('a'); + link.href = blobUrl; + link.download = filename; + link.click(); + setTimeout(() => URL.revokeObjectURL(blobUrl), 0); + } catch (err) { + console.warn('[MessageFile] download fallback after fetch error:', err); + toast.warning(t('messages.messageFile.downloadFallbackOpenedInNewTab'), { + description: t('messages.messageFile.downloadFallbackReason.network'), }); + openAttachmentInNewTab({ url, filename }); } }; diff --git a/src/i18n/locales/en/chat.json b/src/i18n/locales/en/chat.json index e71510a4..3c7b3f28 100644 --- a/src/i18n/locales/en/chat.json +++ b/src/i18n/locales/en/chat.json @@ -61,7 +61,12 @@ "messageFile": { "fileFallback": "file", "unknownSize": "Unknown size", - "fileFallbackTitle": "File" + "fileFallbackTitle": "File", + "downloadFallbackOpenedInNewTab": "File opened in new tab", + "downloadFallbackReason": { + "serverError": "Server returned {{status}} — could not download directly.", + "network": "Network error or CORS block — could not download directly." + } }, "messageImage": { "downloadStarted": "Download started", diff --git a/src/i18n/locales/es/chat.json b/src/i18n/locales/es/chat.json index 665b0f4b..f15a1e76 100644 --- a/src/i18n/locales/es/chat.json +++ b/src/i18n/locales/es/chat.json @@ -60,7 +60,12 @@ "messageFile": { "fileFallback": "archivo", "unknownSize": "Tamaño desconocido", - "fileFallbackTitle": "Archivo" + "fileFallbackTitle": "Archivo", + "downloadFallbackOpenedInNewTab": "Archivo abierto en nueva pestaña", + "downloadFallbackReason": { + "serverError": "El servidor devolvió {{status}} — no se pudo descargar directamente.", + "network": "Error de red o bloqueo de CORS — no se pudo descargar directamente." + } }, "messageImage": { "downloadStarted": "Descarga iniciada", diff --git a/src/i18n/locales/fr/chat.json b/src/i18n/locales/fr/chat.json index 4a282fb6..0718634f 100644 --- a/src/i18n/locales/fr/chat.json +++ b/src/i18n/locales/fr/chat.json @@ -60,7 +60,12 @@ "messageFile": { "fileFallback": "fichier", "unknownSize": "Taille inconnue", - "fileFallbackTitle": "Fichier" + "fileFallbackTitle": "Fichier", + "downloadFallbackOpenedInNewTab": "Fichier ouvert dans un nouvel onglet", + "downloadFallbackReason": { + "serverError": "Le serveur a renvoyé {{status}} — impossible de télécharger directement.", + "network": "Erreur réseau ou blocage CORS — impossible de télécharger directement." + } }, "messageImage": { "downloadStarted": "Téléchargement démarré", diff --git a/src/i18n/locales/it/chat.json b/src/i18n/locales/it/chat.json index 061be92b..33094163 100644 --- a/src/i18n/locales/it/chat.json +++ b/src/i18n/locales/it/chat.json @@ -60,7 +60,12 @@ "messageFile": { "fileFallback": "file", "unknownSize": "Dimensione sconosciuta", - "fileFallbackTitle": "File" + "fileFallbackTitle": "File", + "downloadFallbackOpenedInNewTab": "File aperto in una nuova scheda", + "downloadFallbackReason": { + "serverError": "Il server ha restituito {{status}} — impossibile scaricare direttamente.", + "network": "Errore di rete o blocco CORS — impossibile scaricare direttamente." + } }, "messageImage": { "downloadStarted": "Download avviato", diff --git a/src/i18n/locales/pt-BR/chat.json b/src/i18n/locales/pt-BR/chat.json index 9f7d2dce..860393fa 100644 --- a/src/i18n/locales/pt-BR/chat.json +++ b/src/i18n/locales/pt-BR/chat.json @@ -61,7 +61,12 @@ "messageFile": { "fileFallback": "arquivo", "unknownSize": "Tamanho desconhecido", - "fileFallbackTitle": "Arquivo" + "fileFallbackTitle": "Arquivo", + "downloadFallbackOpenedInNewTab": "Arquivo aberto em nova aba", + "downloadFallbackReason": { + "serverError": "Servidor retornou {{status}} — não foi possível baixar diretamente.", + "network": "Falha de rede ou bloqueio de CORS — não foi possível baixar diretamente." + } }, "messageImage": { "downloadStarted": "Download iniciado", diff --git a/src/i18n/locales/pt/chat.json b/src/i18n/locales/pt/chat.json index ebd9b141..b5a570a9 100644 --- a/src/i18n/locales/pt/chat.json +++ b/src/i18n/locales/pt/chat.json @@ -60,7 +60,12 @@ "messageFile": { "fileFallback": "arquivo", "unknownSize": "Tamanho desconhecido", - "fileFallbackTitle": "Arquivo" + "fileFallbackTitle": "Arquivo", + "downloadFallbackOpenedInNewTab": "Ficheiro aberto em nova aba", + "downloadFallbackReason": { + "serverError": "O servidor retornou {{status}} — não foi possível descarregar diretamente.", + "network": "Falha de rede ou bloqueio de CORS — não foi possível descarregar diretamente." + } }, "messageImage": { "downloadStarted": "Download iniciado",