From 733296dd596c9f1e9cbde114c9689a994751f84d Mon Sep 17 00:00:00 2001 From: Your Name Date: Sat, 24 Jan 2026 07:34:45 +0000 Subject: [PATCH] feat: Add planets API and translations (safe incremental) - Add planet API functions to client.js (fetchPlanets, createPlanet, etc.) - Add updateIsland function for island customization - Add sociowire:create-post event listener in App.jsx - Add planets translations (EN, FR, ES) Co-Authored-By: Claude Opus 4.5 --- src/App.jsx | 12 ++ src/api/client.js | 189 ++++++++++++++++++++++++++++++++ src/locales/en/translation.json | 7 +- src/locales/es/translation.json | 7 +- src/locales/fr/translation.json | 7 +- 5 files changed, 219 insertions(+), 3 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 12484d9..d04fa3d 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -147,6 +147,18 @@ export default function App() { return () => window.removeEventListener("sw:openPostDetail", handleOpenPostDetail); }, []); + // Listen for island post creation event + useEffect(() => { + const handleIslandPost = (e) => { + const { island } = e.detail || {}; + if (island) { + window.location.href = `/island/${island}?create=true`; + } + }; + window.addEventListener('sociowire:create-post', handleIslandPost); + return () => window.removeEventListener('sociowire:create-post', handleIslandPost); + }, []); + // Island Universe handlers const handleOpenUniverse = () => { setShowUniverse(true); diff --git a/src/api/client.js b/src/api/client.js index 1c15a5e..51b81b7 100644 --- a/src/api/client.js +++ b/src/api/client.js @@ -1250,3 +1250,192 @@ export function getCurrentLocation() { ); }); } + +// ============================================ +// ISLAND CUSTOMIZATION API +// ============================================ + +/** + * Update island customization (terrain, colors, size) + */ +export async function updateIsland(islandId, updates) { + if (!islandId) return { ok: false, error: "Missing island ID" }; + try { + const res = await fetch(`${apiBase()}/islands/${encodeURIComponent(islandId)}`, { + method: "PATCH", + credentials: "include", + headers: { "Content-Type": "application/json", ...authHeaders() }, + body: JSON.stringify(updates), + }); + const data = await res.json().catch(() => ({})); + return { ok: res.ok, ...data }; + } catch (err) { + console.warn("updateIsland error:", err); + return { ok: false, error: err?.message || "Update failed" }; + } +} + +// ============================================ +// PLANETS API (Group spaces) +// ============================================ + +/** + * Fetch list of planets (group spaces) + */ +export async function fetchPlanets(params = {}) { + const qs = new URLSearchParams(); + if (typeof params.limit === "number") qs.set("limit", String(params.limit)); + if (typeof params.offset === "number") qs.set("offset", String(params.offset)); + if (params.search) qs.set("search", params.search); + + const url = `${apiBase()}/planets?${qs.toString()}`; + + try { + const res = await fetch(url, { credentials: "include" }); + if (!res.ok) { + console.warn("fetchPlanets failed", res.status); + return []; + } + const data = await res.json(); + return Array.isArray(data) ? data : data.planets || []; + } catch (err) { + console.warn("fetchPlanets error:", err); + return []; + } +} + +/** + * Fetch single planet by ID + */ +export async function fetchPlanet(planetId) { + if (!planetId) return null; + const url = `${apiBase()}/planets/${encodeURIComponent(planetId)}`; + + try { + const res = await fetch(url, { credentials: "include" }); + if (!res.ok) { + console.warn("fetchPlanet failed", res.status); + return null; + } + return await res.json(); + } catch (err) { + console.warn("fetchPlanet error:", err); + return null; + } +} + +/** + * Create a new planet + */ +export async function createPlanet(payload) { + try { + const res = await fetch(`${apiBase()}/planets`, { + method: "POST", + credentials: "include", + headers: { "Content-Type": "application/json", ...authHeaders() }, + body: JSON.stringify(payload), + }); + const data = await res.json().catch(() => ({})); + return { ok: res.ok, ...data }; + } catch (err) { + console.warn("createPlanet error:", err); + return { ok: false, error: err?.message || "Create failed" }; + } +} + +/** + * Update planet settings + */ +export async function updatePlanet(planetId, updates) { + if (!planetId) return { ok: false, error: "Missing planet ID" }; + try { + const res = await fetch(`${apiBase()}/planets/${encodeURIComponent(planetId)}`, { + method: "PATCH", + credentials: "include", + headers: { "Content-Type": "application/json", ...authHeaders() }, + body: JSON.stringify(updates), + }); + const data = await res.json().catch(() => ({})); + return { ok: res.ok, ...data }; + } catch (err) { + console.warn("updatePlanet error:", err); + return { ok: false, error: err?.message || "Update failed" }; + } +} + +/** + * Join a planet + */ +export async function joinPlanet(planetId) { + if (!planetId) return { ok: false }; + try { + const res = await fetch(`${apiBase()}/planets/${encodeURIComponent(planetId)}/join`, { + method: "POST", + credentials: "include", + headers: { ...authHeaders() }, + }); + const data = await res.json().catch(() => ({})); + return { ok: res.ok, ...data }; + } catch (err) { + console.warn("joinPlanet error:", err); + return { ok: false }; + } +} + +/** + * Leave a planet + */ +export async function leavePlanet(planetId) { + if (!planetId) return { ok: false }; + try { + const res = await fetch(`${apiBase()}/planets/${encodeURIComponent(planetId)}/leave`, { + method: "POST", + credentials: "include", + headers: { ...authHeaders() }, + }); + const data = await res.json().catch(() => ({})); + return { ok: res.ok, ...data }; + } catch (err) { + console.warn("leavePlanet error:", err); + return { ok: false }; + } +} + +/** + * Fetch planet members + */ +export async function fetchPlanetMembers(planetId, params = {}) { + if (!planetId) return []; + const qs = new URLSearchParams(); + if (typeof params.limit === "number") qs.set("limit", String(params.limit)); + + try { + const res = await fetch(`${apiBase()}/planets/${encodeURIComponent(planetId)}/members?${qs.toString()}`, { + credentials: "include", + }); + if (!res.ok) return []; + const data = await res.json(); + return Array.isArray(data) ? data : data.members || []; + } catch (err) { + console.warn("fetchPlanetMembers error:", err); + return []; + } +} + +/** + * Fetch user's planets (planets they've joined) + */ +export async function fetchUserPlanets() { + try { + const res = await fetch(`${apiBase()}/planets/my`, { + credentials: "include", + headers: { ...authHeaders() }, + }); + if (!res.ok) return []; + const data = await res.json(); + return Array.isArray(data) ? data : data.planets || []; + } catch (err) { + console.warn("fetchUserPlanets error:", err); + return []; + } +} diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json index 582d41b..d9836b5 100644 --- a/src/locales/en/translation.json +++ b/src/locales/en/translation.json @@ -270,7 +270,12 @@ "islandsCount": "{{count}} islands floating in virtual space", "clickToVisit": "Click to visit", "posts": "posts", - "help": "Hover over islands to see details - Click to visit" + "help": "Hover over islands to see details - Click to visit", + "planets": "planets", + "all": "All", + "islandsTab": "Islands", + "planetsTab": "Planets", + "members": "members" }, "controls": { "mySpot": "My spot", diff --git a/src/locales/es/translation.json b/src/locales/es/translation.json index 24cc5a7..aa74744 100644 --- a/src/locales/es/translation.json +++ b/src/locales/es/translation.json @@ -270,7 +270,12 @@ "islandsCount": "{{count}} islas flotando en el espacio virtual", "clickToVisit": "Clic para visitar", "posts": "posts", - "help": "Pasa el ratón sobre las islas para ver detalles - Clic para visitar" + "help": "Pasa el ratón sobre las islas para ver detalles - Clic para visitar", + "planets": "planetas", + "all": "Todo", + "islandsTab": "Islas", + "planetsTab": "Planetas", + "members": "miembros" }, "controls": { "mySpot": "Mi ubicación", diff --git a/src/locales/fr/translation.json b/src/locales/fr/translation.json index a729675..d371bfe 100644 --- a/src/locales/fr/translation.json +++ b/src/locales/fr/translation.json @@ -273,7 +273,12 @@ "islandsCount": "{{count}} îles flottant dans l'espace virtuel", "clickToVisit": "Cliquez pour visiter", "posts": "posts", - "help": "Survolez les îles pour voir les détails - Cliquez pour visiter" + "help": "Survolez les îles pour voir les détails - Cliquez pour visiter", + "planets": "planètes", + "all": "Tout", + "islandsTab": "Îles", + "planetsTab": "Planètes", + "members": "membres" }, "controls": { "mySpot": "Ma position",