Improve MineGeo interface and functionality

This commit is contained in:
2026-10-06 19:24:41 +05:00
parent 7404f1dc1c
commit 233e4d57f0
67 changed files with 3053 additions and 643 deletions

View File

@@ -1,4 +1,7 @@
"use client";
import { statusMessage } from "@/shared/i18n";
import { useI18n } from "@/components/I18nProvider";
import { useCallback, useEffect, useState } from "react";
import { api } from "./StartGame";
import { PanoramaPreview } from "./PanoramaPreview";
@@ -31,6 +34,7 @@ export function OwnerContributions({
onChange: () => void;
count: number;
}) {
const { t } = useI18n();
const [items, setItems] = useState<Contribution[]>([]),
[address, setAddress] = useState(serverAddress ?? ""),
[saved, setSaved] = useState(""),
@@ -113,11 +117,13 @@ export function OwnerContributions({
}
return (
<section className="token-box">
<h2>Add panoramas · {count}/24</h2>
<h2>
{t("Add panoramas ·")} {count}/24
</h2>
<p className="muted">
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.
{t(
"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.",
)}
</p>
<label>
<input
@@ -126,15 +132,15 @@ export function OwnerContributions({
disabled={busy}
onChange={(e) => toggle(e.target.checked)}
/>{" "}
Allow players to add panoramas with you
{t("Allow players to add panoramas with you")}
</label>
<p className="muted">
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.
{t(
"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.",
)}
</p>
<label className="field">
Contribution server address
{t("Contribution server address")}
<input
value={address}
placeholder="play.example.com:25565"
@@ -146,11 +152,9 @@ export function OwnerContributions({
/>
</label>
<p className="muted">
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.
{t(
"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.",
)}
</p>
<div className="inline-actions">
<button
@@ -158,36 +162,40 @@ export function OwnerContributions({
disabled={busy}
onClick={() => saveAddress(address)}
>
Save server address
{t("Save server address")}
</button>
<button
className="button"
disabled={busy || !serverAddress}
onClick={() => saveAddress("")}
>
Clear address and turn off check
{t("Clear address and turn off check")}
</button>
</div>
{saved && <p role="status">{saved}</p>}
{saved && <p role="status">{t(saved)}</p>}
<ContributionInstructions
id={id}
bounds={bounds}
serverAddress={serverAddress}
isOwner
/>
<h3>Player submissions</h3>
<h3>{t("Player submissions")}</h3>
<p className="muted">
Submissions older than five days are hidden from this list. Accepted
panoramas stay in your map.
{t(
"Submissions older than five days are hidden from this list. Accepted panoramas stay in your map.",
)}
</p>
{locked && (
<p className="muted">
Wait for the current moderator review before accepting another
submission.
{t(
"Wait for the current moderator review before accepting another submission.",
)}
</p>
)}
{!items.length && (
<p className="muted">No contributions from the last five days.</p>
<p className="muted">
{t("No contributions from the last five days.")}
</p>
)}
{items.map((c) => (
<article className="tool-card" key={c.id}>
@@ -196,17 +204,20 @@ export function OwnerContributions({
{c.state === "failed" ? "failed" : c.contribution_status}
</span>
<p>
{c.panorama_count || c.contribution_count} panoramas · {c.state}
{c.panorama_count || c.contribution_count} {t("panoramas ·")}{" "}
{t(statusMessage(c.state))}
</p>
<p className="muted">
{t("Contributor:")} {c.contributorName}
</p>
<p className="muted">Contributor: {c.contributorName}</p>
{c.rejection_reason && <p>{c.rejection_reason}</p>}
{c.contribution_status === "submitted" && (
<>
<button className="button" onClick={() => setPreview(c.id)}>
Inspect submitted panoramas
{t("Inspect submitted panoramas")}
</button>
<label className="field">
Feedback
{t("Feedback")}
<textarea
value={reasons[c.id] ?? ""}
onChange={(e) =>
@@ -220,14 +231,14 @@ export function OwnerContributions({
disabled={busy || locked}
onClick={() => decide(c.id, "accept")}
>
Accept into map draft
{t("Accept into map draft")}
</button>
<button
className="button"
disabled={busy || locked || !reasons[c.id]?.trim()}
onClick={() => decide(c.id, "reject")}
>
Reject with feedback
{t("Reject with feedback")}
</button>
</div>
</>
@@ -242,7 +253,7 @@ export function OwnerContributions({
)}
{error && (
<p className="error-message" role="alert">
{error}
{t(error)}
</p>
)}
</section>