diff --git a/src/components/Map/MapView.jsx b/src/components/Map/MapView.jsx index eb3dab2..9351551 100644 --- a/src/components/Map/MapView.jsx +++ b/src/components/Map/MapView.jsx @@ -298,6 +298,7 @@ export default function MapView({ const [draftImageLarge, setDraftImageLarge] = useState(""); const [draftImageFile, setDraftImageFile] = useState(null); const [draftImagePreview, setDraftImagePreview] = useState(""); + const [draftMediaUrls, setDraftMediaUrls] = useState([]); const [useCustomImage, setUseCustomImage] = useState(false); const [postVisibility, setPostVisibility] = useState("public"); const [postGroupId, setPostGroupId] = useState(""); @@ -1037,6 +1038,7 @@ export default function MapView({ setDraftImage(""); setDraftImageFile(null); setDraftImagePreview(""); + setDraftMediaUrls([]); setUseCustomImage(false); setPostVisibility("public"); setPostGroupId(""); @@ -1067,84 +1069,135 @@ export default function MapView({ setSearchQuery(""); }; + const updateImageVariantsFromGallery = (urls) => { + const cleaned = (urls || []).filter(Boolean); + const small = cleaned[0] || ""; + const medium = cleaned[1] || small; + const large = cleaned[2] || medium || small; + setDraftImageSmall(small); + setDraftImageMedium(medium); + setDraftImageLarge(large); + setDraftImage(small); + setDraftImagePreview(small); + }; + + const addMediaUrlToGallery = (rawUrl) => { + const trimmed = (rawUrl || "").trim(); + if (!trimmed) return false; + setDraftMediaUrls((prev) => { + if (prev.includes(trimmed)) { + updateImageVariantsFromGallery(prev); + return prev; + } + const next = [...prev, trimmed]; + updateImageVariantsFromGallery(next); + return next; + }); + setUseCustomImage(true); + return true; + }; + + const removeMediaUrlAtIndex = (index) => { + setDraftMediaUrls((prev) => { + if (index < 0 || index >= prev.length) return prev; + const next = prev.filter((_, idx) => idx !== index); + updateImageVariantsFromGallery(next); + if (next.length === 0) { + setDraftImage(""); + setDraftImagePreview(""); + } else { + setDraftImage(next[0]); + setDraftImagePreview(next[0]); + } + return next; + }); + }; + + const handleAddCustomImageUrl = () => { + const raw = (draftImage || "").trim(); + if (!raw) { + setSaveError("Paste an image URL first."); + return; + } + if (!/^https?:\/\//i.test(raw)) { + setSaveError("Image URL must start with http:// or https://"); + return; + } + const added = addMediaUrlToGallery(raw); + if (added) { + setDraftImage(""); + setSaveError(""); + } + }; + const handleImageFileChange = async (e) => { - const file = e.target.files?.[0]; - if (!file) return; + const files = Array.from(e.target.files || []); + if (!files.length) return; setImageTouched(true); - - // Vérifier le type de fichier - if (!file.type.startsWith('image/')) { - setSaveError("Please select an image file"); - return; - } - - // Vérifier la taille (max 5MB) - if (file.size > 5 * 1024 * 1024) { - setSaveError("Image must be less than 5MB"); - return; - } - - setDraftImageFile(file); - - // Créer preview - const reader = new FileReader(); - reader.onloadend = () => { - setDraftImagePreview(reader.result); - }; - reader.readAsDataURL(file); - - // Upload immédiatement - setUploadingImage(true); setSaveError(""); - try { - const formData = new FormData(); - formData.append('file', file); - formData.append('visibility', imageVisibility); - if (imageGroupId.trim()) formData.append('group_id', imageGroupId.trim()); - if (imageUsers.trim()) formData.append('users', imageUsers.trim()); - - const res = await fetch('/api/assets/upload', { - method: 'POST', - body: formData, - credentials: 'include', - headers: token ? { 'Authorization': `Bearer ${token}` } : {}, - }); - - if (!res.ok) { - throw new Error('Upload failed'); + setUploadingImage(true); + for (const file of files) { + if (!file.type.startsWith("image/")) { + setSaveError("Please select an image file"); + continue; + } + if (file.size > 5 * 1024 * 1024) { + setSaveError("Image must be less than 5MB"); + continue; } - const data = await res.json(); - const variants = data && data.variants ? data.variants : null; - const pickRef = (v) => { - if (!v) return ""; - if (v.asset_id) return `asset://${v.asset_id}`; - return v.url || ""; + setDraftImageFile(file); + const reader = new FileReader(); + reader.onloadend = () => { + setDraftImagePreview(reader.result); }; - const smallRef = variants ? pickRef(variants.small) : ""; - const mediumRef = variants ? pickRef(variants.medium) : ""; - let largeRef = variants ? pickRef(variants.large) : ""; - if (!largeRef) { - if (data && data.url) largeRef = data.url; - else if (data && data.asset_id) largeRef = `asset://${data.asset_id}`; + reader.readAsDataURL(file); + + try { + const formData = new FormData(); + formData.append("file", file); + formData.append("visibility", imageVisibility); + if (imageGroupId.trim()) formData.append("group_id", imageGroupId.trim()); + if (imageUsers.trim()) formData.append("users", imageUsers.trim()); + + const res = await fetch("/api/assets/upload", { + method: "POST", + body: formData, + credentials: "include", + headers: token ? { Authorization: `Bearer ${token}` } : {}, + }); + + if (!res.ok) { + throw new Error("Upload failed"); + } + + const data = await res.json(); + const variants = data && data.variants ? data.variants : null; + const pickRef = (v) => { + if (!v) return ""; + if (v.asset_id) return `asset://${v.asset_id}`; + return v.url || ""; + }; + const smallRef = variants ? pickRef(variants.small) : ""; + const mediumRef = variants ? pickRef(variants.medium) : ""; + let largeRef = variants ? pickRef(variants.large) : ""; + if (!largeRef) { + if (data && data.url) largeRef = data.url; + else if (data && data.asset_id) largeRef = `asset://${data.asset_id}`; + } + const fallbackRef = largeRef || mediumRef || smallRef; + const selected = fallbackRef || mediumRef || smallRef; + if (selected) { + addMediaUrlToGallery(selected); + } + } catch (err) { + console.error("Image upload error:", err); + setSaveError("Failed to upload image"); } - const fallbackRef = largeRef || mediumRef || smallRef; - setDraftImageSmall(smallRef || fallbackRef); - setDraftImageMedium(mediumRef || fallbackRef); - setDraftImageLarge(largeRef || fallbackRef); - if (fallbackRef) setDraftImage(fallbackRef); - setUploadingImage(false); - } catch (err) { - console.error('Image upload error:', err); - setSaveError('Failed to upload image'); - setUploadingImage(false); - setDraftImageFile(null); - setDraftImagePreview(""); - setDraftImageSmall(""); - setDraftImageMedium(""); - setDraftImageLarge(""); } + setUploadingImage(false); + e.target.value = ""; }; const handleUrlPreview = async (rawOverride) => { @@ -1173,10 +1226,7 @@ export default function MapView({ if (!imageTouched && image) { setUseCustomImage(true); if (!draftImageFile && !draftImagePreview) { - setDraftImage(image); - setDraftImageSmall(image); - setDraftImageMedium(image); - setDraftImageLarge(image); + addMediaUrlToGallery(image); } } } catch (err) { @@ -1280,16 +1330,18 @@ export default function MapView({ payload.published_at = draftPublishedAt.trim(); } - if ( - draftImageSmall || - draftImageMedium || - draftImageLarge || - (useCustomImage && draftImage.trim()) - ) { + const mediaCandidates = draftMediaUrls.filter(Boolean); + if (mediaCandidates.length > 0) { + payload.image_small = mediaCandidates[0]; + payload.image_medium = mediaCandidates[1] || mediaCandidates[0]; + payload.image_large = + mediaCandidates[2] || mediaCandidates[1] || mediaCandidates[0]; + payload.media_urls = mediaCandidates; + } else if (useCustomImage && draftImage.trim()) { const fallback = draftImage.trim(); - payload.image_small = (draftImageSmall || fallback).trim(); - payload.image_medium = (draftImageMedium || fallback).trim(); - payload.image_large = (draftImageLarge || fallback).trim(); + payload.image_small = fallback; + payload.image_medium = fallback; + payload.image_large = fallback; } const newPost = await createPost(payload, token); @@ -1336,6 +1388,13 @@ export default function MapView({ } }; + const galleryPreviewImage = + draftMediaUrls[0] || + draftImageLarge || + draftImageMedium || + draftImageSmall || + draftImage; + return (
@@ -1587,22 +1646,9 @@ export default function MapView({ )} {createStep === 2 && ( <> - {(draftImagePreview || - draftImageLarge || - draftImageMedium || - draftImageSmall || - draftImage) && ( + {galleryPreviewImage && (
- Link preview + Link preview
{draftSource ? {draftSource} : null} {draftPublishedAt ? ( @@ -1611,6 +1657,29 @@ export default function MapView({
)} + {draftMediaUrls.length > 0 && ( +
+
+ {draftMediaUrls.length} image + {draftMediaUrls.length > 1 ? "s" : ""} +
+
+ {draftMediaUrls.map((url, idx) => ( +
+ {`Selected + +
+ ))} +
+
+ )}