import React, { useState, useRef, useCallback, useEffect } from "react"; import { motion, AnimatePresence } from "framer-motion"; import { useTranslation } from "react-i18next"; import { fetchPostPreview, uploadPostMedia, createNewsPost } from "../../api/client"; import { listSubscriptions, createPost as createAppPost } from "../../api/extAppsApi"; // Content modes const CONTENT_MODES = [ { id: "text", icon: "fa-pen", label: "Text", color: "from-blue-500 to-cyan-500" }, { id: "photo", icon: "fa-image", label: "Photo", color: "from-emerald-500 to-green-500" }, { id: "link", icon: "fa-link", label: "Link", color: "from-purple-500 to-violet-500" }, { id: "live", icon: "fa-broadcast-tower", label: "Live", color: "from-red-500 to-rose-500" }, { id: "news", icon: "fa-newspaper", label: "News", color: "from-amber-500 to-orange-500" }, ]; const CATEGORIES = [ { id: "News", icon: "fa-newspaper", color: "from-blue-500 to-cyan-500" }, { id: "Friends", icon: "fa-user-group", color: "from-emerald-500 to-green-500" }, { id: "Events", icon: "fa-calendar", color: "from-purple-500 to-violet-500" }, { id: "Market", icon: "fa-store", color: "from-amber-500 to-orange-500" }, { id: "Apps", icon: "fa-puzzle-piece", color: "from-pink-500 to-rose-500" }, ]; const SUB_CATEGORIES = { News: ["World", "Local", "Politics", "Tech", "Finance", "Sports"], Friends: ["Nearby", "Stories", "Hangout", "Help"], Events: ["Today", "Weekend", "Music", "Sports", "Food"], Market: ["Deals", "Services", "Jobs", "Housing"], Apps: [], // Dynamic - filled from subscribed apps }; export default function PostCreateModal({ isOpen, onClose, onSubmit, onGoLive, coords, initialCategory = "News", isSaving = false, saveError = "", }) { const { t } = useTranslation(); const [mode, setMode] = useState("text"); const [category, setCategory] = useState(initialCategory); const [subCategory, setSubCategory] = useState(SUB_CATEGORIES[initialCategory]?.[0] || ""); const [title, setTitle] = useState(""); const [body, setBody] = useState(""); const [linkUrl, setLinkUrl] = useState(""); const [linkPreview, setLinkPreview] = useState(null); const [linkLoading, setLinkLoading] = useState(false); const [images, setImages] = useState([]); const [uploading, setUploading] = useState(false); const [uploadError, setUploadError] = useState(""); const fileInputRef = useRef(null); const cameraInputRef = useRef(null); const textareaRef = useRef(null); const linkTimeoutRef = useRef(null); // Apps state const [subscribedApps, setSubscribedApps] = useState([]); const [selectedApp, setSelectedApp] = useState(null); const [appsLoading, setAppsLoading] = useState(false); // Reset on open useEffect(() => { if (isOpen) { setMode("text"); setTitle(""); setBody(""); setLinkUrl(""); setLinkPreview(null); setImages([]); setCategory(initialCategory); setSubCategory(SUB_CATEGORIES[initialCategory]?.[0] || ""); setSelectedApp(null); setUploadError(""); } }, [isOpen, initialCategory]); // Load apps when Apps category is selected useEffect(() => { if (category === "Apps" && subscribedApps.length === 0) { loadApps(); } }, [category]); const loadApps = async () => { try { setAppsLoading(true); const result = await listSubscriptions(); const apps = (result.subscriptions || []).filter( (sub) => sub.capabilities?.includes("posts") ); setSubscribedApps(apps); } catch (err) { console.error("Failed to load apps:", err); } finally { setAppsLoading(false); } }; // Auto-resize textarea useEffect(() => { if (textareaRef.current) { textareaRef.current.style.height = "auto"; textareaRef.current.style.height = Math.min(textareaRef.current.scrollHeight, 200) + "px"; } }, [body]); // Link preview fetch (for link and news modes) useEffect(() => { if ((mode !== "link" && mode !== "news") || !linkUrl.trim()) { setLinkPreview(null); return; } clearTimeout(linkTimeoutRef.current); linkTimeoutRef.current = setTimeout(async () => { try { setLinkLoading(true); const preview = await fetchPostPreview(linkUrl.trim()); if (preview) { setLinkPreview(preview); if (!title && preview.title) setTitle(preview.title); } } catch {} setLinkLoading(false); }, 500); return () => clearTimeout(linkTimeoutRef.current); }, [linkUrl, mode, title]); const handleFileChange = useCallback(async (e) => { const files = Array.from(e.target.files || []); if (files.length === 0) return; setUploading(true); setUploadError(""); try { for (const file of files) { const result = await uploadPostMedia(file); if (result?.url) { setImages((prev) => [...prev, result.url].slice(0, 5)); } else if (result?.error) { setUploadError(result.error); } } } catch (err) { console.error("Upload failed:", err); setUploadError(err.message || "Upload failed"); } setUploading(false); e.target.value = ""; }, [category]); const removeImage = (index) => { setImages((prev) => prev.filter((_, i) => i !== index)); }; const handleSubmit = async () => { if (mode === "live") { onGoLive?.({ category, subCategory, title, body, coords }); return; } // Handle News mode - post to core-api /api/news/post (text + optional image) if (mode === "news") { if (!title.trim() && !body.trim()) return; try { setUploading(true); const postData = { title: title.trim() || body.trim().slice(0, 100), description: body.trim(), url: linkUrl.trim() || null, images: images, location: coords ? { lat: coords[1], lon: coords[0] } : null, }; await createNewsPost(postData); onClose?.(); } catch (err) { setUploadError(err.message || "Failed to create news post"); } finally { setUploading(false); } return; } // Handle Apps category - post to external app if (category === "Apps" && selectedApp) { try { setUploading(true); const postData = { title: title.trim(), description: body.trim(), images, location: coords ? { lat: coords[1], lon: coords[0] } : null, content: {} }; await createAppPost(selectedApp.app_id, postData); onClose?.(); } catch (err) { setUploadError(err.message || "Failed to create post"); } finally { setUploading(false); } return; } // Regular post if (!title.trim() && !body.trim() && images.length === 0 && !linkUrl.trim()) return; onSubmit?.({ mode, title: title.trim(), body: body.trim(), category, subCategory, images, linkUrl: linkUrl.trim(), linkPreview, coords, }); }; const canSubmit = mode === "live" || (mode === "news" && (title.trim() || body.trim()) && !isSaving && !uploading) || (category === "Apps" && selectedApp && title.trim()) || ((title.trim() || body.trim() || images.length > 0 || linkUrl.trim()) && !isSaving && !uploading); if (!isOpen) return null; return ( {/* Backdrop */} {/* Modal */} {/* Glowing top border */}
{/* Header */}

Create Post

{isSaving || uploading ? ( ) : mode === "live" ? ( <> Go Live ) : ( "Post" )}
{/* Content modes (hidden for Apps category) */} {category !== "Apps" && (
{CONTENT_MODES.map((m) => ( setMode(m.id)} > {m.label} ))}
)} {/* Scrollable content */}
{/* Category row (hidden for news mode) */} {mode !== "news" && (
{CATEGORIES.map((cat) => ( { setCategory(cat.id); setSubCategory(SUB_CATEGORIES[cat.id]?.[0] || ""); setSelectedApp(null); }} > {cat.id} ))}
)} {/* Apps category - show subscribed apps */} {category === "Apps" ? (
{appsLoading ? (
) : subscribedApps.length === 0 ? (

No apps available

Subscribe to apps in the App Store to post

) : ( <> {/* App selector */}
{subscribedApps.map((app) => ( setSelectedApp(app)} >

{app.app_name}

))}
{/* App post form */} {selectedApp && (
setTitle(e.target.value)} className="w-full px-4 py-3 rounded-xl bg-surface-700/50 border border-white/10 text-white text-base font-medium placeholder:text-surface-500 focus:outline-none focus:border-pink-500/50" />