202 lines
6.1 KiB
JavaScript
202 lines
6.1 KiB
JavaScript
import React, { Suspense, useEffect, useState, useCallback } from "react";
|
|
import TopBar from "./components/Layout/TopBar";
|
|
import PostList from "./components/Posts/PostList";
|
|
import IslandUniverse from "./components/Island/IslandUniverse";
|
|
import IslandViewer from "./components/Island/IslandViewer";
|
|
|
|
const MapView = React.lazy(() => import("./components/Map/MapView"));
|
|
|
|
const THEME_KEY = "sociowire:theme";
|
|
const ACTIVE_CHATS_KEY = "sociowire:activeChats";
|
|
|
|
export default function App() {
|
|
const [theme, setTheme] = useState("blue");
|
|
|
|
// Wall state (lifted from MapView)
|
|
const [wallPosts, setWallPosts] = useState([]);
|
|
const [wallLoading, setWallLoading] = useState(false);
|
|
const [wallError, setWallError] = useState("");
|
|
const [selectedPost, setSelectedPost] = useState(null);
|
|
|
|
// Island Universe & Island Viewer state
|
|
const [showUniverse, setShowUniverse] = useState(false);
|
|
const [selectedIsland, setSelectedIsland] = useState(null);
|
|
|
|
const handlePostsState = useCallback((next) => {
|
|
if (!next) return;
|
|
if (Array.isArray(next.posts)) setWallPosts(next.posts);
|
|
setWallLoading(!!next.loading);
|
|
setWallError(next.error || "");
|
|
}, []);
|
|
|
|
// charge le thème au démarrage
|
|
useEffect(() => {
|
|
if (typeof window === "undefined") return;
|
|
try {
|
|
const saved = localStorage.getItem(THEME_KEY);
|
|
if (saved === "dark" || saved === "blue" || saved === "light") {
|
|
setTheme(saved);
|
|
document.body.setAttribute("data-theme", saved);
|
|
} else {
|
|
setTheme("blue");
|
|
document.body.setAttribute("data-theme", "blue");
|
|
}
|
|
} catch (e) {
|
|
setTheme("blue");
|
|
document.body.setAttribute("data-theme", "blue");
|
|
}
|
|
}, []);
|
|
|
|
// applique / sauvegarde le thème
|
|
useEffect(() => {
|
|
if (typeof window === "undefined") return;
|
|
try {
|
|
document.body.setAttribute("data-theme", theme);
|
|
localStorage.setItem(THEME_KEY, theme);
|
|
} catch (e) {
|
|
// ignore
|
|
}
|
|
}, [theme]);
|
|
|
|
// Chats actifs (s'ajoutent quand on clique sur un contact)
|
|
const [activeChats, setActiveChats] = useState([]);
|
|
const [showContactsList, setShowContactsList] = useState(false);
|
|
|
|
useEffect(() => {
|
|
const onScroll = () => {
|
|
const threshold = window.innerHeight * 0.55;
|
|
const expanded = window.scrollY > threshold;
|
|
document.body.classList.toggle("sw-wall-expanded", expanded);
|
|
};
|
|
window.addEventListener("scroll", onScroll, { passive: true });
|
|
onScroll();
|
|
return () => window.removeEventListener("scroll", onScroll);
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
try {
|
|
const saved = localStorage.getItem(ACTIVE_CHATS_KEY);
|
|
if (saved) setActiveChats(JSON.parse(saved));
|
|
} catch {}
|
|
}, []);
|
|
|
|
useEffect(() => {
|
|
try {
|
|
localStorage.setItem(ACTIVE_CHATS_KEY, JSON.stringify(activeChats));
|
|
} catch {}
|
|
}, [activeChats]);
|
|
|
|
// Ouvrir un chat
|
|
const openChat = (name) => {
|
|
if (!activeChats.includes(name)) {
|
|
setActiveChats(prev => [...prev, name]);
|
|
}
|
|
setShowContactsList(false);
|
|
};
|
|
|
|
const contactsList = ["Amely", "Nancy", "Stéphanie"];
|
|
|
|
// Island Universe handlers
|
|
const handleOpenUniverse = () => {
|
|
setShowUniverse(true);
|
|
};
|
|
|
|
const handleCloseUniverse = () => {
|
|
setShowUniverse(false);
|
|
};
|
|
|
|
const handleIslandClick = (island) => {
|
|
setShowUniverse(false); // Close universe
|
|
setSelectedIsland(island); // Open island viewer
|
|
};
|
|
|
|
return (
|
|
<div className="app-root">
|
|
<TopBar
|
|
theme={theme}
|
|
onChangeTheme={setTheme}
|
|
onIslandsClick={handleOpenUniverse}
|
|
/>
|
|
|
|
<div className="main-shell">
|
|
<div className="map-shell">
|
|
<Suspense fallback={<div style={{ padding: 16 }}>Loading map…</div>}>
|
|
<MapView
|
|
theme={theme}
|
|
onPostsState={handlePostsState}
|
|
selectedPost={selectedPost}
|
|
onSelectPost={setSelectedPost}
|
|
selectedIsland={selectedIsland}
|
|
onSelectIsland={setSelectedIsland}
|
|
/>
|
|
</Suspense>
|
|
</div>
|
|
|
|
{/* BELOW THE MAP (page scroll like Facebook) */}
|
|
<div className="below-stage">
|
|
<div className="below-row">
|
|
<div className="sociowall-panel">
|
|
<div className="sociowall-header">SOCIOWALL</div>
|
|
<PostList
|
|
posts={wallPosts}
|
|
loading={wallLoading}
|
|
error={wallError}
|
|
selectedPost={selectedPost}
|
|
onSelectPost={setSelectedPost}
|
|
/>
|
|
</div>
|
|
{/* CHAT PANEL SUPPRIMÉ */}
|
|
</div>
|
|
</div>
|
|
|
|
<div style={{ height: 18 }} />
|
|
|
|
{/* Bouton Contacts flottant en bas à droite */}
|
|
<button className="contacts-float-btn" onClick={() => setShowContactsList(!showContactsList)}>
|
|
<span className="contact-icon">👥</span>
|
|
Contacts
|
|
</button>
|
|
|
|
{/* Liste des contacts (dropdown) */}
|
|
{showContactsList && (
|
|
<div className="contacts-dropdown">
|
|
{contactsList.map((name, idx) => (
|
|
<div key={idx} className="contact-item" onClick={() => openChat(name)}>
|
|
<div className="contact-avatar">👤</div>
|
|
<span>{name}</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* Bulles de chats actifs flottantes à côté du bouton */}
|
|
<div className="active-chats-bubbles">
|
|
{activeChats.map((name, idx) => (
|
|
<div key={idx} className="chat-bubble">
|
|
<div className="chat-avatar">👤</div>
|
|
<span className="chat-name">{name}</span>
|
|
<button className="bubble-remove" onClick={() => setActiveChats(prev => prev.filter(n => n !== name))}>✕</button>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Island Universe Modal */}
|
|
{showUniverse && (
|
|
<IslandUniverse
|
|
onClose={handleCloseUniverse}
|
|
onIslandClick={handleIslandClick}
|
|
/>
|
|
)}
|
|
|
|
{/* Island Viewer Modal */}
|
|
{selectedIsland && (
|
|
<IslandViewer
|
|
island={selectedIsland}
|
|
onClose={() => setSelectedIsland(null)}
|
|
/>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|