Files
MineGeo/app/page.tsx
pavel444-byte 5898ef35a5
Some checks failed
Deploy MineGeo to Dokploy / deploy (push) Failing after 10m22s
Move the field guide into the docs section
2026-10-28 11:08:36 +05:00

312 lines
11 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { getI18n } from "@/lib/i18n";
import Link from "next/link";
import Image from "next/image";
import {
ArrowRight,
Compass,
MapPin,
Timer,
Blocks,
Upload,
Download,
} from "lucide-react";
import { Header, Footer } from "@/components/Shell";
import { MapCard } from "@/components/MapCard";
import { catalog } from "@/lib/catalog";
import { leaderboard } from "@/lib/leaderboard";
import { LeaderboardPreview } from "@/components/Leaderboard";
import desktopRelease from "@/shared/desktop-release.json";
import { FirstVisitIntro } from "@/components/FirstVisitIntro";
import { SiteTrailer } from "@/components/SiteTrailer";
import { Suspense } from "react";
export const dynamic = "force-dynamic";
export default async function Home() {
const { t, formatNumber } = await getI18n();
const rankings = leaderboard({ limit: 5 });
const maps = await catalog();
const servers = new Set(maps.map((map) => map.server_id)).size;
const locations = maps.reduce((total, map) => total + map.panorama_count, 0);
return (
<>
<FirstVisitIntro />
<Header />
<main className="home-page" id="main-content" tabIndex={-1}>
<section className="hero">
<div className="hero-copy">
<div className="status-label">{t("A Minecraft guessing game")}</div>
<h1>
{t("Guess where you are")}
<br />
{t("in a")} <span>{t("blocky world.")}</span>
</h1>
<p>
{t("Drop into a random corner of a Minecraft world.")}
<br className="desktop" />{" "}
{t("Look around, spot the landmarks, and place your pin")}
<br className="desktop" /> {t("before the clock runs out.")}
</p>
<div className="hero-actions">
<Link href="/servers" className="button primary">
{t("Start exploring")} <ArrowRight size={18} />
</Link>
<a href={desktopRelease.file} className="button" download>
<Download size={18} /> {t("Download for Windows")}
</a>
<Link href="/leaderboard" className="text-link">
{t("Meet the top explorers")} <ArrowRight size={14} />
</Link>
</div>
<div className="hero-notes">
<span className="round-blocks" aria-hidden="true">
<i />
<i />
<i />
<i />
<i />
</span>
<span>{t("Five rounds. No roads. Just your instincts.")}</span>
</div>
</div>
<div className="hero-art" aria-label={t("Minecraft surface map")}>
<div className="map-art-frame minecraft-paper">
<Image
src={maps[0]?.mapImage || "/images/example-map.webp"}
alt={t(
"A Minecraft surface map on the original Minecraft parchment texture",
)}
width={1024}
height={1024}
loading="eager"
fetchPriority="high"
unoptimized
/>
<span className="square-pin pin-green" />
<span className="square-pin pin-red" />
<span className="square-pin pin-gold" />
</div>
<span className="compass-art" aria-hidden="true">
<Compass size={43} />
<small>N</small>
</span>
<div className="floating-tag">
<MapPin size={14} />
<strong>X: ??? Z: ???</strong>
<span>{t("Overworld")}</span>
</div>
</div>
</section>
<div className="grass-divider" aria-hidden="true">
<span />
</div>
<section className="world-stats" aria-label={t("Community statistics")}>
<div>
<i className="stat-cube green-cube" />
<div>
<strong>{formatNumber(servers)}</strong>
<span>{t("COMMUNITY SERVERS")}</span>
</div>
</div>
<div>
<i className="stat-cube gold-cube" />
<div>
<strong>{formatNumber(maps.length)}</strong>
<span>{t("PUBLISHED MAPS")}</span>
</div>
</div>
<div>
<i className="stat-cube cyan-cube" />
<div>
<strong>{formatNumber(locations)}</strong>
<span>{t("PLACES TO DISCOVER")}</span>
</div>
</div>
<div>
<i className="stat-cube red-cube" />
<div>
<strong>{t("5 rounds")}</strong>
<span>{t("EVERY EXPEDITION")}</span>
</div>
</div>
</section>
<section className="section catalog-section">
<div className="section-heading">
<div>
<span className="eyebrow">{t("Community maps")}</span>
<h2>{t("Explore community worlds.")}</h2>
</div>
<Link className="text-link" href="/servers">
{t("All maps ({count})", { count: maps.length })}{" "}
<ArrowRight size={15} />
</Link>
</div>
{maps.length ? (
<div className="cards-grid">
{maps.slice(0, 4).map((map, index) => (
<MapCard
map={map}
index={index}
key={map.id}
minecraftPaper
loading="lazy"
/>
))}
</div>
) : (
<div className="first-map-panel">
<div className="empty-map-art minecraft-paper" aria-hidden="true">
<Image
src="/images/example-map.webp"
alt=""
width={180}
height={180}
unoptimized
/>
<span>{t("YOUR WORLD HERE")}</span>
</div>
<div>
<span className="eyebrow">
{t("THE FIRST EXPEDITION STARTS WITH YOU")}
</span>
<h3>{t("Put your server on the map.")}</h3>
<p>
{t(
"No community maps have been published yet. Export your world with the server plugin, or capture five locations with the Fabric mod. Preview your map and submit it for review.",
)}
</p>
<Link className="button primary" href="/downloads">
<Upload size={16} /> {t("Get the capture tools")}
</Link>
</div>
</div>
)}
</section>
<SiteTrailer />
<Suspense
fallback={
<section
className="section leaderboard-preview-placeholder"
aria-busy="true"
>
<h2>{t("THE EXPLORERS’ BOARD")}</h2>
<div className="loading-surface" />
</section>
}
>
<HomeLeaderboard data={rankings} />
</Suspense>
<section className="section home-panels">
<article className="home-panel">
<span className="eyebrow">{t("How to play")}</span>
<h2>{t("Know your blocks?")}</h2>
<ol className="play-steps">
<li>
<span>01</span>
<div>
<h3>{t("Pick your world")}</h3>
<p>{t("Choose a community server, map, and difficulty.")}</p>
</div>
</li>
<li>
<span>02</span>
<div>
<h3>{t("Look for the clues")}</h3>
<p>
{t(
"Rotate the panorama. Find builds, biomes, and landmarks.",
)}
</p>
</div>
</li>
<li>
<span>03</span>
<div>
<h3>{t("Make your guess")}</h3>
<p>
{t(
"Drop a pin on the map. Closer guesses earn more points.",
)}
</p>
</div>
</li>
</ol>
<Link className="text-link" href="/docs?page=guide">
{t("Read the field guide")} <ArrowRight size={14} />
</Link>
</article>
<article className="home-panel">
<span className="eyebrow">{t("Choose a difficulty")}</span>
<h2>{t("Pick a difficulty.")}</h2>
<div className="mode-list">
{[
{
name: t("Easy"),
time: t("180 seconds"),
detail: t("Take your time. Rotate and zoom."),
color: "green",
},
{
name: t("Normal"),
time: t("90 seconds"),
detail: t("Trust your eye. Rotate and zoom."),
color: "gold",
},
{
name: t("Hard"),
time: t("30 seconds"),
detail: t("Think fast. No zoom allowed."),
color: "red",
},
{
name: t("Void"),
time: t("10 seconds"),
detail: t("The ultimate test. Fixed view, strict scoring."),
color: "void",
},
].map((mode) => (
<Link href="/servers" className="mode-row" key={mode.name}>
<i className={`stat-cube ${mode.color}-cube`} />
<div>
<h3>
{t(mode.name)}
<span>
<Timer size={12} />
{t(mode.time)}
</span>
</h3>
<p>{t(mode.detail)}</p>
</div>
<ArrowRight size={15} />
</Link>
))}
</div>
<p className="mode-footnote">
{t("Up to 5,000 points per round. How close can you get?")}
</p>
</article>
</section>
<section className="section contribute-banner">
<Blocks size={38} />
<div>
<span className="eyebrow">{t("Made by MineGeo players")}</span>
<h2>{t("Put your world on the map.")}</h2>
<p>
{t("Share your builds and your favorite corners of Minecraft.")}
</p>
</div>
<Link href="/downloads" className="button">
{t("Create a map")} <ArrowRight size={16} />
</Link>
</section>
</main>
<Footer />
</>
);
}
async function HomeLeaderboard({
data,
}: {
data: ReturnType<typeof leaderboard>;
}) {
return <LeaderboardPreview data={await data} />;
}