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 <noreply@anthropic.com>
This commit is contained in:
parent
c50c61ff36
commit
733296dd59
12
src/App.jsx
12
src/App.jsx
|
|
@ -147,6 +147,18 @@ export default function App() {
|
||||||
return () => window.removeEventListener("sw:openPostDetail", handleOpenPostDetail);
|
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
|
// Island Universe handlers
|
||||||
const handleOpenUniverse = () => {
|
const handleOpenUniverse = () => {
|
||||||
setShowUniverse(true);
|
setShowUniverse(true);
|
||||||
|
|
|
||||||
|
|
@ -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 [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -270,7 +270,12 @@
|
||||||
"islandsCount": "{{count}} islands floating in virtual space",
|
"islandsCount": "{{count}} islands floating in virtual space",
|
||||||
"clickToVisit": "Click to visit",
|
"clickToVisit": "Click to visit",
|
||||||
"posts": "posts",
|
"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": {
|
"controls": {
|
||||||
"mySpot": "My spot",
|
"mySpot": "My spot",
|
||||||
|
|
|
||||||
|
|
@ -270,7 +270,12 @@
|
||||||
"islandsCount": "{{count}} islas flotando en el espacio virtual",
|
"islandsCount": "{{count}} islas flotando en el espacio virtual",
|
||||||
"clickToVisit": "Clic para visitar",
|
"clickToVisit": "Clic para visitar",
|
||||||
"posts": "posts",
|
"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": {
|
"controls": {
|
||||||
"mySpot": "Mi ubicación",
|
"mySpot": "Mi ubicación",
|
||||||
|
|
|
||||||
|
|
@ -273,7 +273,12 @@
|
||||||
"islandsCount": "{{count}} îles flottant dans l'espace virtuel",
|
"islandsCount": "{{count}} îles flottant dans l'espace virtuel",
|
||||||
"clickToVisit": "Cliquez pour visiter",
|
"clickToVisit": "Cliquez pour visiter",
|
||||||
"posts": "posts",
|
"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": {
|
"controls": {
|
||||||
"mySpot": "Ma position",
|
"mySpot": "Ma position",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue