@@ -165,34 +165,47 @@ const Editor = () => {
165
165
return updatedContent ;
166
166
} ;
167
167
168
+
169
+ const dataURLtoFile = ( dataUrl ) => {
170
+ const arr = dataUrl . split ( "," ) ;
171
+ const mime = arr [ 0 ] . match ( / : ( .* ?) ; / ) [ 1 ] ;
172
+ const bstr = atob ( arr [ 1 ] ) ;
173
+ let n = bstr . length ;
174
+ const u8arr = new Uint8Array ( n ) ;
175
+ while ( n -- ) {
176
+ u8arr [ n ] = bstr . charCodeAt ( n ) ;
177
+ }
178
+ return new File ( [ u8arr ] , `upload-${ Date . now ( ) } ` , { type : mime } ) ;
179
+ } ;
180
+
181
+
168
182
169
183
const processMediaUploads = async ( content ) => {
170
184
const base64Images = extractBase64Images ( content ) ;
171
185
const base64Map = { } ;
172
186
173
- // Upload each base64 image using your new endpoint
187
+ // For each base64 image, convert to a File and upload it via handleImageUpload
174
188
for ( let base64 of base64Images ) {
175
189
try {
176
- const res = await fetch ( "/api/uploadMedia" , {
177
- method : "POST" ,
178
- headers : { "Content-Type" : "application/json" } ,
179
- body : JSON . stringify ( { base64, folder : "blog_media" } ) ,
180
- } ) ;
181
- const data = await res . json ( ) ;
182
- if ( ! res . ok ) throw new Error ( data . error ) ;
183
- const url = data . url ; // URL returned from the endpoint
184
- base64Map [ base64 ] = url ; // Store the mapping for replacement
185
-
186
- // Optionally update your local media array (if you need to track it)
187
- setMedia ( ( prev ) => [ ...prev , url ] ) ;
190
+ // Convert the base64 string to a File object
191
+ const file = dataURLtoFile ( base64 ) ;
192
+ // Upload the file using the refactored handleImageUpload function
193
+ const { url, publicId } = await handleImageUpload ( file , "blog_media" ) ;
194
+ // Store the mapping from base64 to Cloudinary URL so you can replace it in the content
195
+ base64Map [ base64 ] = url ;
196
+
197
+ // Optionally update your local media state array
198
+ setMedia ( ( prev ) => [ ...prev , { url, publicId } ] ) ;
188
199
} catch ( error ) {
189
200
console . error ( "Upload failed:" , error ) ;
190
201
}
191
202
}
192
203
204
+ // Replace each base64 string in your content with its uploaded URL
193
205
return replaceBase64WithUrls ( content , base64Map ) ;
194
206
} ;
195
207
208
+
196
209
197
210
198
211
const autoSaveDraft = async ( ) => {
@@ -253,6 +266,8 @@ const Editor = () => {
253
266
draft,
254
267
keywords : Array . isArray ( keywords ) ? keywords : keywords . split ( "," ) ,
255
268
description,
269
+ media, // ✅ Store media in local storage
270
+
256
271
} ;
257
272
258
273
setPreviewData ( blogData ) ;
@@ -321,66 +336,6 @@ const Editor = () => {
321
336
322
337
323
338
324
- const saveBlog = async ( ) => {
325
- const isUnique = await fetchSlugUniqueness ( slug ) ; // ✅ Ensure uniqueness before saving
326
-
327
- if ( ! isUnique ) {
328
- alert ( `Title: ${ title } is not unique. Choose another one.` ) ;
329
- return ;
330
- }
331
-
332
- // ✅ Process sanitization & YouTube fix only once at save time
333
- let cleanContent = DOMPurify . sanitize ( blogContent , {
334
- FORBID_TAGS : [ "script" ] ,
335
- ADD_TAGS : [ "iframe" , "style" ] ,
336
- ADD_ATTR : [ "allow" , "allowfullscreen" , "frameborder" , "src" , "width" , "height" , "title" ] ,
337
- } ) ;
338
-
339
- // ✅ Fix YouTube Shorts without clearing iframes
340
- const tempDiv = document . createElement ( "div" ) ;
341
- tempDiv . innerHTML = cleanContent ;
342
-
343
- tempDiv . querySelectorAll ( "iframe" ) . forEach ( ( iframe ) => {
344
- let src = iframe . src ;
345
- if ( src . includes ( "youtube.com/shorts/" ) ) {
346
- src = src . replace ( "youtube.com/shorts/" , "youtube.com/embed/" ) ;
347
- }
348
- iframe . src = src ; // ✅ Apply fixed URL
349
- } ) ;
350
-
351
- const processedContent = tempDiv . innerHTML ; // ✅ Final sanitized + fixed content
352
-
353
- console . log ( "AFTER CONTENT: " , processedContent ) ;
354
-
355
- // ✅ Update state before saving so iframes don't disappear
356
- setBlogContent ( processedContent ) ;
357
-
358
- const blogData = {
359
- image,
360
- title,
361
- slug,
362
- category,
363
- blogContent : processedContent , // ✅ Use sanitized content
364
- readingTime,
365
- date,
366
- draft,
367
- keywords : typeof keywords === "string"
368
- ? keywords . split ( "," ) . map ( ( keyword ) => keyword . trim ( ) )
369
- : keywords ,
370
- description,
371
- } ;
372
-
373
- setPreviewData ( blogData ) ;
374
- setSaved ( true ) ;
375
-
376
- setTimeout ( ( ) => {
377
- setSaved ( false ) ;
378
- } , 5000 ) ;
379
- localStorage . removeItem ( "previewData" ) ;
380
- } ;
381
-
382
-
383
-
384
339
385
340
useEffect ( ( ) => {
386
341
const savedData = previewData ; // Automatically handled by the useLocalStorage hook
@@ -456,18 +411,15 @@ const Editor = () => {
456
411
// const [localStorageAutoSaveDuration, setLocalStorageAutoSaveDuration] = useLocalStorage("autoSaveDuration", "");
457
412
458
413
459
- useEffect ( ( ) => {
460
- console . log ( "AutoSave status:" , autoSave ?'active' :'deactivated' ) ;
461
- console . log ( "AutoSave Duration:" , autoSaveDuration ) ;
462
- } , [ autoSave ] ) ;
414
+
463
415
464
416
useEffect ( ( ) => {
465
417
let interval ;
466
418
setLocalStorageAutoSave ( autoSave ) ;
467
419
if ( autoSave ) {
468
- console . log ( "Starting Auto Save..." ) ;
420
+ // console.log("Starting Auto Save...");
469
421
interval = setInterval ( async ( ) => {
470
- console . log ( "Calling Auto save" ) ;
422
+ // console.log("Calling Auto save");
471
423
await autoSaveDraft ( ) ;
472
424
} , autoSaveDuration ) ;
473
425
}
@@ -519,7 +471,7 @@ const Editor = () => {
519
471
/>
520
472
</ div >
521
473
< div className = { styles . imgContainer } >
522
- < ImageUploader image = { image } setImage = { setImage } />
474
+ < ImageUploader image = { image } setImage = { setImage } media = { media } setMedia = { setMedia } />
523
475
{ image && (
524
476
< div
525
477
className = { styles . close }
0 commit comments