"use client"; import { useCallback, useEffect, useState } from "react"; import { api } from "./StartGame"; import { PanoramaPreview } from "./PanoramaPreview"; import { ContributionInstructions } from "./ContributionInstructions"; type Contribution = { id: string; name: string; owner_id: string; contributorName: string; state: string; contribution_status: string; panorama_count: number; contribution_count: number; rejection_reason: string | null; }; export function OwnerContributions({ id, allowed, serverAddress, bounds, locked, onChange, count, }: { id: string; allowed: boolean; serverAddress: string | null; bounds: { minX: number; minZ: number; maxX: number; maxZ: number }; locked: boolean; onChange: () => void; count: number; }) { const [items, setItems] = useState([]), [address, setAddress] = useState(serverAddress ?? ""), [saved, setSaved] = useState(""), [error, setError] = useState(""), [busy, setBusy] = useState(false), [preview, setPreview] = useState(null), [reasons, setReasons] = useState>({}); useEffect(() => { setAddress(serverAddress ?? ""); }, [serverAddress]); async function saveAddress(value: string) { setBusy(true); setError(""); setSaved(""); try { await api( `datasets/${id}/settings`, { contributionServerAddress: value.trim() || null }, "PATCH", ); setAddress(value); setSaved( value.trim() ? "Server address saved." : "Server check turned off. Submissions still need your approval.", ); onChange(); } catch (e) { setError((e as Error).message); } finally { setBusy(false); } } const load = useCallback(async () => { try { const result = await api<{ contributions: Contribution[] }>( `contributions/${id}/inbox`, ); setItems(result.contributions); } catch (e) { setError((e as Error).message); } }, [id]); useEffect(() => { load(); const timer = setInterval(load, 10000); return () => clearInterval(timer); }, [load]); async function toggle(value: boolean) { setBusy(true); setError(""); try { await api( `datasets/${id}/settings`, { allowContributions: value }, "PATCH", ); onChange(); } catch (e) { setError((e as Error).message); } finally { setBusy(false); } } async function decide(child: string, decision: string) { setBusy(true); setError(""); try { await api(`contributions/${id}/inbox/${child}`, { decision, reason: reasons[child], }); await load(); onChange(); } catch (e) { setError((e as Error).message); } finally { setBusy(false); } } return (

Add panoramas · {count}/24

Owners can fill all 24 slots, even with player contributions turned off. Export a fresh capture, then use the command below to add it to this map.

Five random locations are picked for each game. Add up to 24 locations to make repeat games more varied. You can use the map ID below to add your own panoramas too.

Use the Minecraft hostname or IP players connect to. This address is shown publicly when player contributions are enabled. The mod records the connected server; plugin admins set server-address before exporting. You can override the captured address here, or clear it to allow unchecked submissions.

{saved &&

{saved}

}

Player submissions

{locked && (

Wait for the current moderator review before accepting another submission.

)} {!items.length &&

No contributions yet.

} {items.map((c) => (

{c.name}

{c.state === "failed" ? "failed" : c.contribution_status}

{c.panorama_count || c.contribution_count} panoramas · {c.state}

Contributor: {c.contributorName}

{c.rejection_reason &&

{c.rejection_reason}

} {c.contribution_status === "submitted" && ( <>