From fa90d7426b31ae0ff800c59ce64af871a8603e21 Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 24 Jan 2026 23:15:56 +0000 Subject: [PATCH] v115: Use existing FAB for island posts with island_username context - FAB (+) button now context-aware - includes island_username when on island - PostComposer accepts islandUsername prop and includes in payload - Removed custom post buttons from island terrain view - Global __activeIslandContext set when viewing an island - App.jsx handles sw:openPostCreator event for island posts Co-Authored-By: Claude Opus 4.5 --- package.json | 2 +- src/App.jsx | 46 +++++++++++++++++++++-- src/components/Posts/PostComposer.jsx | 21 +++++++++-- src/components/World/IslandsWorld.jsx | 54 ++++++++++----------------- 4 files changed, 80 insertions(+), 43 deletions(-) diff --git a/package.json b/package.json index 312657f..7e364f4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "sociowire-frontend", "private": true, - "version": "0.0.114", + "version": "0.0.115", "type": "module", "scripts": { "dev": "vite --host 0.0.0.0", diff --git a/src/App.jsx b/src/App.jsx index ae0b653..9de4b84 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -7,7 +7,7 @@ import ContentFilters from "./components/Filters/ContentFilters"; import IslandViewer from "./components/Island/IslandViewer"; import ChatContainer from "./components/Chat/ChatContainer"; import PostDetailModal from "./components/Posts/PostDetailModal"; -import { CreatePostFAB } from "./components/Posts/PostComposer"; +import PostComposer, { CreatePostFAB } from "./components/Posts/PostComposer"; import { AnimatePresence, motion } from "framer-motion"; import { fetchIslandByUsername } from "./api/client"; @@ -66,6 +66,10 @@ export default function App() { // Island Viewer state (for viewing individual islands) const [selectedIsland, setSelectedIsland] = useState(null); + // Post Composer state (for creating posts with island context) + const [showPostComposer, setShowPostComposer] = useState(false); + const [postComposerIsland, setPostComposerIsland] = useState(null); + // Post detail modal state (triggered from map markers) const [detailPost, setDetailPost] = useState(null); @@ -152,16 +156,30 @@ export default function App() { return () => window.removeEventListener("sw:openPostDetail", handleOpenPostDetail); }, []); - // Listen for island post creation event + // Listen for post creator events useEffect(() => { + // Handle opening post composer with island context + const handleOpenPostCreator = (e) => { + const { island_username } = e.detail || {}; + setPostComposerIsland(island_username || null); + setShowPostComposer(true); + }; + + // Legacy island post event const handleIslandPost = (e) => { const { island } = e.detail || {}; if (island) { - window.location.href = `/island/${island}?create=true`; + setPostComposerIsland(island); + setShowPostComposer(true); } }; + + window.addEventListener('sw:openPostCreator', handleOpenPostCreator); window.addEventListener('sociowire:create-post', handleIslandPost); - return () => window.removeEventListener('sociowire:create-post', handleIslandPost); + return () => { + window.removeEventListener('sw:openPostCreator', handleOpenPostCreator); + window.removeEventListener('sociowire:create-post', handleIslandPost); + }; }, []); // World navigation handlers @@ -362,6 +380,26 @@ export default function App() { )} + {/* Post Composer Modal (for island posts) */} + + {showPostComposer && ( + { + setShowPostComposer(false); + setPostComposerIsland(null); + }} + onPostCreated={(newPost) => { + // Refresh wall posts if on map view + if (currentView === 'map') { + setWallPosts((prev) => [newPost, ...prev]); + } + }} + /> + )} + + {/* Floating Action Button for creating posts */} diff --git a/src/components/Posts/PostComposer.jsx b/src/components/Posts/PostComposer.jsx index 26684bd..406498e 100644 --- a/src/components/Posts/PostComposer.jsx +++ b/src/components/Posts/PostComposer.jsx @@ -15,7 +15,7 @@ const CATEGORIES = [ { id: "weather", icon: "fa-cloud-sun", label: "Weather", color: "from-sky-500 to-blue-500" }, ]; -export default function PostComposer({ onClose, onPostCreated }) { +export default function PostComposer({ onClose, onPostCreated, islandUsername }) { const { t } = useTranslation(); const { token, username, isAuthenticated } = useAuth(); const fileInputRef = useRef(null); @@ -159,6 +159,11 @@ export default function PostComposer({ onClose, onPostCreated }) { payload.location_name = locationName || ""; } + // Add island context if posting to an island + if (islandUsername) { + payload.island_username = islandUsername; + } + // Add media URLs if (uploadedMedia.length > 0) { const firstMedia = uploadedMedia[0]; @@ -493,10 +498,20 @@ export default function PostComposer({ onClose, onPostCreated }) { } // Floating Action Button for creating posts - triggers the map's create post flow +// Context-aware: if viewing an island, includes island info export function CreatePostFAB() { const handleClick = () => { - // Dispatch event to open the map's post creation modal - window.dispatchEvent(new CustomEvent("sw:openMapPostCreator")); + // Check if we're viewing an island + const islandContext = window.__activeIslandContext; + if (islandContext && islandContext.isOwner) { + // Open post creator with island context + window.dispatchEvent(new CustomEvent("sw:openPostCreator", { + detail: { island_username: islandContext.username } + })); + } else { + // Default: open map's post creation modal + window.dispatchEvent(new CustomEvent("sw:openMapPostCreator")); + } }; return ( diff --git a/src/components/World/IslandsWorld.jsx b/src/components/World/IslandsWorld.jsx index da2182b..7ec9832 100644 --- a/src/components/World/IslandsWorld.jsx +++ b/src/components/World/IslandsWorld.jsx @@ -30,6 +30,21 @@ export default function IslandsWorld({ theme, onIslandClick, onOpenMap, onOpenPl const [islandPosts, setIslandPosts] = useState([]); const [loadingPosts, setLoadingPosts] = useState(false); + // Set global island context when zoomed (for FAB to use) + useEffect(() => { + if (zoomedIsland) { + window.__activeIslandContext = { + username: zoomedIsland.username, + isOwner: username && username.toLowerCase() === (zoomedIsland.username || '').toLowerCase() + }; + } else { + window.__activeIslandContext = null; + } + return () => { + window.__activeIslandContext = null; + }; + }, [zoomedIsland, username]); + // Fetch islands useEffect(() => { let mounted = true; @@ -289,19 +304,9 @@ export default function IslandsWorld({ theme, onIslandClick, onOpenMap, onOpenPl {username && username.toLowerCase() === (zoomedIsland.username || '').toLowerCase() && ( - <> - - - + )} @@ -318,14 +323,7 @@ export default function IslandsWorld({ theme, onIslandClick, onOpenMap, onOpenPl

{t('worlds.emptyIsland')}

{username && username.toLowerCase() === (zoomedIsland.username || '').toLowerCase() && ( - +

{t('worlds.useButtonToPost')}

)} )} @@ -334,20 +332,6 @@ export default function IslandsWorld({ theme, onIslandClick, onOpenMap, onOpenPl ))} - {/* Floating create post button for island owner */} - {username && username.toLowerCase() === (zoomedIsland.username || '').toLowerCase() && ( - - )} )}