@@ -19,19 +19,18 @@ import { useTranslation } from 'react-i18next';
1919import { css } from 'styled-components' ;
2020
2121import { Box , ButtonCloseModal , Text } from '@/components' ;
22+ import { useMediaUrl } from '@/core' ;
2223import { useEditorStore } from '@/docs/doc-editor' ;
2324import { Doc , useTrans } from '@/docs/doc-management' ;
25+ import { fallbackLng } from '@/i18n/config' ;
2426
25- import {
26- exportCorsResolveFileUrl ,
27- exportResolveFileUrl ,
28- } from '../api/exportResolveFileUrl' ;
27+ import { exportCorsResolveFileUrl } from '../api/exportResolveFileUrl' ;
2928import { TemplatesOrdering , useTemplates } from '../api/useTemplates' ;
3029import { docxDocsSchemaMappings } from '../mappingDocx' ;
3130import { odtDocsSchemaMappings } from '../mappingODT' ;
3231import { pdfDocsSchemaMappings } from '../mappingPDF' ;
3332import {
34- deriveMediaFilename ,
33+ addMediaFilesToZip ,
3534 downloadFile ,
3635 generateHtmlDocument ,
3736} from '../utils' ;
@@ -61,6 +60,7 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
6160 DocDownloadFormat . PDF ,
6261 ) ;
6362 const { untitledDocument } = useTrans ( ) ;
63+ const mediaUrl = useMediaUrl ( ) ;
6464
6565 const templateOptions = useMemo ( ( ) => {
6666 const templateOptions = ( templates ?. pages || [ ] )
@@ -159,59 +159,11 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
159159 const domParser = new DOMParser ( ) ;
160160 const parsedDocument = domParser . parseFromString ( fullHtml , 'text/html' ) ;
161161
162- const mediaFiles : { filename : string ; blob : Blob } [ ] = [ ] ;
163- const mediaElements = Array . from (
164- parsedDocument . querySelectorAll <
165- | HTMLImageElement
166- | HTMLVideoElement
167- | HTMLAudioElement
168- | HTMLSourceElement
169- > ( 'img, video, audio, source' ) ,
170- ) ;
171-
172- await Promise . all (
173- mediaElements . map ( async ( element , index ) => {
174- const src = element . getAttribute ( 'src' ) ;
175-
176- if ( ! src ) {
177- return ;
178- }
179-
180- // data: URLs are already embedded and work offline; no need to create separate files.
181- if ( src . startsWith ( 'data:' ) ) {
182- return ;
183- }
184-
185- // Only download same-origin resources (internal media like /media/...).
186- // External URLs keep their original src and are not included in the ZIP
187- let url : URL | null = null ;
188- try {
189- url = new URL ( src , window . location . origin ) ;
190- } catch {
191- url = null ;
192- }
193-
194- if ( ! url || url . origin !== window . location . origin ) {
195- return ;
196- }
162+ const zip = new JSZip ( ) ;
197163
198- const fetched = await exportResolveFileUrl ( url . href ) ;
164+ await addMediaFilesToZip ( parsedDocument , zip , mediaUrl ) ;
199165
200- if ( ! ( fetched instanceof Blob ) ) {
201- return ;
202- }
203-
204- const filename = deriveMediaFilename ( {
205- src : url . href ,
206- index,
207- blob : fetched ,
208- } ) ;
209- element . setAttribute ( 'src' , filename ) ;
210- mediaFiles . push ( { filename, blob : fetched } ) ;
211- } ) ,
212- ) ;
213-
214- const lang = i18next . language || 'fr' ;
166+ const lang = i18next . language || fallbackLng ;
215167 const editorHtmlWithLocalMedia = parsedDocument . body . innerHTML ;
216168
217169 const htmlContent = generateHtmlDocument (
@@ -220,13 +172,8 @@ export const ModalExport = ({ onClose, doc }: ModalExportProps) => {
220172 lang ,
221173 ) ;
222174
223- const zip = new JSZip ( ) ;
224175 zip . file ( 'index.html' , htmlContent ) ;
225176
226- mediaFiles . forEach ( ( { filename, blob } ) => {
227- zip . file ( filename , blob ) ;
228- } ) ;
229-
230177 blobExport = await zip . generateAsync ( { type : 'blob' } ) ;
231178 } else {
232179 toast ( t ( 'The export failed' ) , VariantType . ERROR ) ;
0 commit comments