Improve MineGeo interface and functionality
This commit is contained in:
@@ -1,4 +1,7 @@
|
||||
"use client";
|
||||
import { statusMessage } from "@/shared/i18n";
|
||||
import { useI18n } from "@/components/I18nProvider";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import Image from "next/image";
|
||||
import { api } from "./StartGame";
|
||||
@@ -31,6 +34,7 @@ type Draft = {
|
||||
ban_reason: string | null;
|
||||
};
|
||||
export function DraftEditor({ id }: { id: string }) {
|
||||
const { t } = useI18n();
|
||||
const dirty = useRef(false);
|
||||
const [draft, setDraft] = useState<Draft | null>(null),
|
||||
[name, setName] = useState(""),
|
||||
@@ -123,7 +127,9 @@ export function DraftEditor({ id }: { id: string }) {
|
||||
}
|
||||
if (!draft)
|
||||
return (
|
||||
<p role={error ? "alert" : undefined}>{error || "Loading your draft…"}</p>
|
||||
<p role={error ? "alert" : undefined}>
|
||||
{error ? t(error) : t("Loading your draft…")}
|
||||
</p>
|
||||
);
|
||||
const editable =
|
||||
(["draft", "rejected"].includes(draft.state) ||
|
||||
@@ -139,11 +145,11 @@ export function DraftEditor({ id }: { id: string }) {
|
||||
return (
|
||||
<>
|
||||
<div className="editor-navigation">
|
||||
<Link href="/dashboard">← Your maps</Link>
|
||||
<a href="#locations">Choose locations</a>
|
||||
{!draft.parent_dataset_id && <a href="#map-tools">Map tools</a>}
|
||||
<Link href="/dashboard">{t("← Your maps")}</Link>
|
||||
<a href="#locations">{t("Choose locations")}</a>
|
||||
{!draft.parent_dataset_id && <a href="#map-tools">{t("Map tools")}</a>}
|
||||
</div>
|
||||
<span className="badge">{draft.state}</span>
|
||||
<span className="badge">{t(statusMessage(draft.state))}</span>
|
||||
{!draft.parent_dataset_id &&
|
||||
(draft.report_strikes > 0 || draft.banned_at) && (
|
||||
<MapAppeal
|
||||
@@ -155,47 +161,63 @@ export function DraftEditor({ id }: { id: string }) {
|
||||
/>
|
||||
)}
|
||||
{draft.update_state && (
|
||||
<span className="badge">Update: {draft.update_state}</span>
|
||||
<span className="badge">
|
||||
{t("Update:")} {t(statusMessage(draft.update_state))}
|
||||
</span>
|
||||
)}
|
||||
{draft.contribution_status && (
|
||||
<p role="status">
|
||||
Contribution: {draft.contribution_status}.{" "}
|
||||
{t("Contribution:")} {t(statusMessage(draft.contribution_status))}.{" "}
|
||||
{draft.state === "failed"
|
||||
? "This contribution no longer reserves panorama slots. Upload a fresh capture to try again."
|
||||
? t(
|
||||
"This contribution no longer reserves panorama slots. Upload a fresh capture to try again.",
|
||||
)
|
||||
: draft.contribution_status === "accepted"
|
||||
? "The owner included your panoramas in the map draft. Map updates become playable after moderator approval."
|
||||
? t(
|
||||
"The owner included your panoramas in the map draft. Map updates become playable after moderator approval.",
|
||||
)
|
||||
: draft.contribution_status === "submitted"
|
||||
? "Waiting for the map owner's approval."
|
||||
? t("Waiting for the map owner's approval.")
|
||||
: draft.contribution_status === "rejected"
|
||||
? "Upload a fresh capture if you want to try again."
|
||||
: "Choose your locations, then submit them to the map owner."}
|
||||
? t("Upload a fresh capture if you want to try again.")
|
||||
: t(
|
||||
"Choose your locations, then submit them to the map owner.",
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
{draft.state === "published" && (
|
||||
<p className="muted">
|
||||
The approved version stays playable while you edit and moderators
|
||||
review your update.
|
||||
{t(
|
||||
"The approved version stays playable while you edit and moderators review your update.",
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
<ProcessingProgress job={draft.upload_jobs} />
|
||||
{draft.upload_jobs?.kind === "map" &&
|
||||
draft.upload_jobs.state === "failed" && (
|
||||
<p role="alert" className="error-message">
|
||||
Map refresh failed: {draft.upload_jobs.error}. Your previous map is
|
||||
still available. Try refreshing again below.
|
||||
{t("Map refresh failed:")} {draft.upload_jobs.error}
|
||||
{t(
|
||||
". Your previous map is still available. Try refreshing again below.",
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
{refreshing && (
|
||||
<p role="status" className="success-message">
|
||||
Refreshing your guessing map. This page updates automatically; the
|
||||
published version stays playable.
|
||||
{t(
|
||||
"Refreshing your guessing map. This page updates automatically; the published version stays playable.",
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
{["uploading", "processing"].includes(draft.state) && (
|
||||
<p role="status" className="success-message">
|
||||
{draft.state === "uploading"
|
||||
? "Waiting for the capture tool to finish uploading. Retry upload in the tool if the connection was interrupted."
|
||||
: "Your map and panoramas are being rendered. This page updates automatically."}
|
||||
? t(
|
||||
"Waiting for the capture tool to finish uploading. Retry upload in the tool if the connection was interrupted.",
|
||||
)
|
||||
: t(
|
||||
"Your map and panoramas are being rendered. This page updates automatically.",
|
||||
)}
|
||||
</p>
|
||||
)}
|
||||
{draft.rejection_reason && (
|
||||
@@ -205,8 +227,12 @@ export function DraftEditor({ id }: { id: string }) {
|
||||
)}
|
||||
<p className="muted">
|
||||
{draft.source === "plugin"
|
||||
? "Server-generated views use vanilla terrain and simplified lighting. Entities, shaders, custom packs and dynamic sign text are omitted."
|
||||
: "Player captures show the terrain received by the client. Unexplored map coverage remains blank."}
|
||||
? t(
|
||||
"Server-generated views use vanilla terrain and simplified lighting. Entities, shaders, custom packs and dynamic sign text are omitted.",
|
||||
)
|
||||
: t(
|
||||
"Player captures show the terrain received by the client. Unexplored map coverage remains blank.",
|
||||
)}
|
||||
</p>
|
||||
{draft.map && (
|
||||
<div className="detail-map" style={{ maxWidth: 600 }}>
|
||||
@@ -215,15 +241,15 @@ export function DraftEditor({ id }: { id: string }) {
|
||||
width={1024}
|
||||
height={1024}
|
||||
loading="eager"
|
||||
alt="Draft surface map"
|
||||
alt={t("Draft surface map")}
|
||||
unoptimized
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<section className="editor-panel">
|
||||
<h2>Map details</h2>
|
||||
<h2>{t("Map details")}</h2>
|
||||
<label className="field">
|
||||
Map name
|
||||
{t("Map name")}
|
||||
<input
|
||||
value={name}
|
||||
onChange={(e) => {
|
||||
@@ -234,7 +260,7 @@ export function DraftEditor({ id }: { id: string }) {
|
||||
/>
|
||||
</label>
|
||||
<label className="field">
|
||||
Description
|
||||
{t("Description")}
|
||||
<textarea
|
||||
value={description}
|
||||
onChange={(e) => {
|
||||
@@ -245,10 +271,12 @@ export function DraftEditor({ id }: { id: string }) {
|
||||
/>
|
||||
</label>
|
||||
</section>
|
||||
<h2 id="locations">Choose your locations.</h2>
|
||||
<h2 id="locations">{t("Choose your locations.")}</h2>
|
||||
<p className="muted">
|
||||
Keep at least {minimum} clear, useful panorama{minimum === 1 ? "" : "s"}
|
||||
. Selected: {selected.length} / 24
|
||||
{t("Minimum panoramas: {minimum}. Selected: {selected} / 24", {
|
||||
minimum,
|
||||
selected: selected.length,
|
||||
})}
|
||||
</p>
|
||||
<div className="preview-grid">
|
||||
{draft.previews.map((p) => (
|
||||
@@ -258,7 +286,7 @@ export function DraftEditor({ id }: { id: string }) {
|
||||
type="checkbox"
|
||||
checked={selected.includes(p.id)}
|
||||
disabled={!editable || busy}
|
||||
aria-label={`Keep ${p.label}`}
|
||||
aria-label={t("Keep {location}", { location: p.label })}
|
||||
onChange={(e) => {
|
||||
dirty.current = true;
|
||||
setSelected((s) =>
|
||||
@@ -281,7 +309,7 @@ export function DraftEditor({ id }: { id: string }) {
|
||||
</label>
|
||||
{p.image && (
|
||||
<button className="button full" onClick={() => setPreview(p.id)}>
|
||||
Look around
|
||||
{t("Look around")}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
@@ -301,7 +329,7 @@ export function DraftEditor({ id }: { id: string }) {
|
||||
disabled={busy || refreshing || selected.length < minimum}
|
||||
onClick={() => save()}
|
||||
>
|
||||
Save draft
|
||||
{t("Save draft")}
|
||||
</button>
|
||||
<button
|
||||
className="button primary"
|
||||
@@ -309,8 +337,8 @@ export function DraftEditor({ id }: { id: string }) {
|
||||
onClick={() => save(true)}
|
||||
>
|
||||
{draft.parent_dataset_id
|
||||
? "Submit to map owner →"
|
||||
: "Submit for review →"}
|
||||
? t("Submit to map owner →")
|
||||
: t("Submit for review →")}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
@@ -346,18 +374,18 @@ export function DraftEditor({ id }: { id: string }) {
|
||||
))) && (
|
||||
<button className="button" disabled={busy} onClick={cancel}>
|
||||
{draft.parent_dataset_id
|
||||
? "Withdraw contribution"
|
||||
: "Cancel processing"}
|
||||
? t("Withdraw contribution")
|
||||
: t("Cancel processing")}
|
||||
</button>
|
||||
)}
|
||||
{error && (
|
||||
<p role="alert" className="error-message">
|
||||
{error}
|
||||
{t(error)}
|
||||
</p>
|
||||
)}
|
||||
{message && (
|
||||
<p role="status" className="success-message">
|
||||
{message}
|
||||
{t(message)}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user