Merge branch 'main' into cloudflare

This commit is contained in:
hamster1963 2024-11-21 10:21:13 +08:00
commit 69426de708
12 changed files with 112 additions and 38 deletions

View File

@ -1,13 +1,12 @@
import { countryCodeMapping, reverseCountryCodeMapping } from "@/lib/geo"; import { countryCodeMapping, reverseCountryCodeMapping } from "@/lib/geo";
import { countryCoordinates } from "@/lib/geo-limit"; import { countryCoordinates } from "@/lib/geo-limit";
import { GetNezhaData } from "@/lib/serverFetch"; import { GetNezhaData } from "@/lib/serverFetch";
import { ServerStackIcon } from "@heroicons/react/20/solid";
import * as turf from "@turf/turf"; import * as turf from "@turf/turf";
import DottedMap from "dotted-map/without-countries"; import DottedMap from "dotted-map/without-countries";
import Link from "next/link";
import { geoJsonString } from "../../../lib/geo-json-string"; import { geoJsonString } from "../../../lib/geo-json-string";
import { mapJsonString } from "../../../lib/map-string"; import { mapJsonString } from "../../../lib/map-string";
import GlobalInfo from "./GlobalInfo";
interface GlobalProps { interface GlobalProps {
countries?: string[]; countries?: string[];
@ -30,6 +29,7 @@ export default async function ServerGlobal() {
} }
export async function Global({ countries = [] }: GlobalProps) { export async function Global({ countries = [] }: GlobalProps) {
// const t = useTranslations("Global");
const map = new DottedMap({ map: JSON.parse(mapJsonString) }); const map = new DottedMap({ map: JSON.parse(mapJsonString) });
const countries_alpha3 = countries const countries_alpha3 = countries
@ -96,12 +96,7 @@ export async function Global({ countries = [] }: GlobalProps) {
return ( return (
<section className="flex flex-col gap-4 mt-[3.2px]"> <section className="flex flex-col gap-4 mt-[3.2px]">
<Link <GlobalInfo countries={countries} />
href={`/`}
className="rounded-[50px] w-fit bg-stone-100 p-[10px] transition-all hover:bg-stone-200 dark:hover:bg-stone-700 dark:bg-stone-800"
>
<ServerStackIcon className="size-4" />
</Link>
<img <img
src={`data:image/svg+xml;utf8,${encodeURIComponent(finalMap)}`} src={`data:image/svg+xml;utf8,${encodeURIComponent(finalMap)}`}
alt="World Map with Highlighted Countries" alt="World Map with Highlighted Countries"

View File

@ -0,0 +1,20 @@
"use client";
import GlobalBackButton from "@/components/GlobalBackButton";
import { useTranslations } from "next-intl";
type GlobalInfoProps = {
countries: string[];
};
export default function GlobalInfo({ countries }: GlobalInfoProps) {
const t = useTranslations("Global");
return (
<section className="flex items-center justify-between">
<GlobalBackButton />
<p className="text-sm font-medium opacity-40">
{t("Distributions")} {countries.length} {t("Regions")}
</p>
</section>
);
}

View File

@ -0,0 +1,18 @@
"use client";
import GlobalBackButton from "@/components/GlobalBackButton";
import { Loader } from "@/components/loading/Loader";
import { useTranslations } from "next-intl";
export default function GlobalLoading() {
const t = useTranslations("Global");
return (
<section className="flex flex-col gap-4 mt-[3.2px]">
<GlobalBackButton />
<div className="flex min-h-40 flex-col items-center justify-center font-medium text-sm">
{t("Loading")}
<Loader visible={true} />
</div>
</section>
);
}

View File

@ -23,13 +23,11 @@ export default function ServerListClient() {
const [tag, setTag] = useState<string>(defaultTag); const [tag, setTag] = useState<string>(defaultTag);
const [isMounted, setIsMounted] = useState(false);
useEffect(() => { useEffect(() => {
const savedTag = sessionStorage.getItem("selectedTag") || defaultTag; const savedTag = sessionStorage.getItem("selectedTag") || defaultTag;
setTag(savedTag); setTag(savedTag);
restoreScrollPosition(); restoreScrollPosition();
setIsMounted(true);
}, []); }, []);
const handleTagChange = (newTag: string) => { const handleTagChange = (newTag: string) => {
@ -71,7 +69,7 @@ export default function ServerListClient() {
</div> </div>
); );
if (!data?.result || !isMounted) return null; if (!data?.result) return null;
const { result } = data; const { result } = data;
const sortedServers = result.sort((a, b) => { const sortedServers = result.sort((a, b) => {
@ -121,8 +119,6 @@ export default function ServerListClient() {
<section className="flex items-center gap-2 w-full overflow-hidden"> <section className="flex items-center gap-2 w-full overflow-hidden">
<button <button
onClick={() => { onClick={() => {
setStatus("all");
setFilter(false);
router.push(`/?global=true`); router.push(`/?global=true`);
}} }}
className="rounded-[50px] bg-stone-100 p-[10px] transition-all hover:bg-stone-200 dark:hover:bg-stone-700 dark:bg-stone-800" className="rounded-[50px] bg-stone-100 p-[10px] transition-all hover:bg-stone-200 dark:hover:bg-stone-700 dark:bg-stone-800"

View File

@ -43,12 +43,14 @@ export default function ServerOverviewClient() {
<section className="grid grid-cols-2 gap-4 lg:grid-cols-4"> <section className="grid grid-cols-2 gap-4 lg:grid-cols-4">
<Card <Card
onClick={() => { onClick={() => {
setFilter(false);
if (!global) { if (!global) {
setFilter(false);
setStatus("all"); setStatus("all");
} }
}} }}
className="cursor-pointer hover:border-blue-500 transition-all" className={cn("cursor-pointer hover:border-blue-500 transition-all", {
"pointer-events-none": global,
})}
> >
<CardContent className="px-6 py-3"> <CardContent className="px-6 py-3">
<section className="flex flex-col gap-1"> <section className="flex flex-col gap-1">
@ -74,8 +76,8 @@ export default function ServerOverviewClient() {
</Card> </Card>
<Card <Card
onClick={() => { onClick={() => {
setFilter(false);
if (!global) { if (!global) {
setFilter(false);
setStatus("online"); setStatus("online");
} }
}} }}
@ -84,6 +86,9 @@ export default function ServerOverviewClient() {
{ {
"ring-green-500 ring-2 border-transparent": status === "online", "ring-green-500 ring-2 border-transparent": status === "online",
}, },
{
"pointer-events-none": global,
},
)} )}
> >
<CardContent className="px-6 py-3"> <CardContent className="px-6 py-3">
@ -111,8 +116,8 @@ export default function ServerOverviewClient() {
</Card> </Card>
<Card <Card
onClick={() => { onClick={() => {
setFilter(false);
if (!global) { if (!global) {
setFilter(false);
setStatus("offline"); setStatus("offline");
} }
}} }}
@ -121,6 +126,9 @@ export default function ServerOverviewClient() {
{ {
"ring-red-500 ring-2 border-transparent": status === "offline", "ring-red-500 ring-2 border-transparent": status === "offline",
}, },
{
"pointer-events-none": global,
},
)} )}
> >
<CardContent className="px-6 py-3"> <CardContent className="px-6 py-3">
@ -148,8 +156,8 @@ export default function ServerOverviewClient() {
</Card> </Card>
<Card <Card
onClick={() => { onClick={() => {
setStatus("all");
if (!global) { if (!global) {
setStatus("all");
setFilter(true); setFilter(true);
} }
}} }}
@ -158,6 +166,9 @@ export default function ServerOverviewClient() {
{ {
"ring-purple-500 ring-2 border-transparent": filter === true, "ring-purple-500 ring-2 border-transparent": filter === true,
}, },
{
"pointer-events-none": global,
},
)} )}
> >
<CardContent className="relative px-6 py-3"> <CardContent className="relative px-6 py-3">

View File

@ -3,12 +3,10 @@ import ServerOverview from "@/components/ServerOverview";
export const runtime = "edge"; export const runtime = "edge";
import { Loader } from "@/components/loading/Loader";
import { ServerStackIcon } from "@heroicons/react/20/solid";
import Link from "next/link";
import { Suspense } from "react"; import { Suspense } from "react";
import ServerGlobal from "./ClientComponents/Global"; import ServerGlobal from "./ClientComponents/Global";
import GlobalLoading from "./ClientComponents/GlobalLoading";
export default async function Home({ export default async function Home({
searchParams, searchParams,
@ -21,22 +19,7 @@ export default async function Home({
<ServerOverview /> <ServerOverview />
{!global && <ServerList />} {!global && <ServerList />}
{global && ( {global && (
<Suspense <Suspense fallback={<GlobalLoading />}>
fallback={
<section className="flex flex-col gap-4 mt-[3.2px]">
<Link
href={`/`}
className="rounded-[50px] w-fit bg-stone-100 p-[10px] transition-all hover:bg-stone-200 dark:hover:bg-stone-700 dark:bg-stone-800"
>
<ServerStackIcon className="size-4" />
</Link>
<div className="flex min-h-40 flex-col items-center justify-center font-medium text-sm">
Loading...
<Loader visible={true} />
</div>
</section>
}
>
<ServerGlobal /> <ServerGlobal />
</Suspense> </Suspense>
)} )}

View File

@ -0,0 +1,31 @@
"use client";
import { useFilter } from "@/lib/network-filter-context";
import { useStatus } from "@/lib/status-context";
import { ServerStackIcon } from "@heroicons/react/20/solid";
import { useRouter } from "next/navigation";
import { useEffect } from "react";
export default function GlobalBackButton() {
const router = useRouter();
const { setStatus } = useStatus();
const { setFilter } = useFilter();
useEffect(() => {
setStatus("all");
setFilter(false);
sessionStorage.removeItem("selectedTag");
router.prefetch(`/`);
}, []);
return (
<button
onClick={() => {
router.push(`/`);
}}
className="rounded-[50px] w-fit bg-stone-100 p-[10px] transition-all hover:bg-stone-200 dark:hover:bg-stone-700 dark:bg-stone-800"
>
<ServerStackIcon className="size-4" />
</button>
);
}

View File

@ -90,6 +90,11 @@
"p_2277-2331_Overview": "👋 Overview", "p_2277-2331_Overview": "👋 Overview",
"p_2390-2457_wherethetimeis": "where the time is" "p_2390-2457_wherethetimeis": "where the time is"
}, },
"Global": {
"Loading": "Loading...",
"Distributions": "Servers are distributed in",
"Regions": "Regions"
},
"NotFoundPage": { "NotFoundPage": {
"h1_490-590_404NotFound": "404 Not Found", "h1_490-590_404NotFound": "404 Not Found",
"p_601-665_TARDISERROR": "TARDIS ERROR!", "p_601-665_TARDISERROR": "TARDIS ERROR!",

View File

@ -90,6 +90,11 @@
"p_2277-2331_Overview": "👋 概要", "p_2277-2331_Overview": "👋 概要",
"p_2390-2457_wherethetimeis": "現在の時間" "p_2390-2457_wherethetimeis": "現在の時間"
}, },
"Global": {
"Loading": "Loading...",
"Distributions": "サーバーは",
"Regions": "つの地域に分散されています"
},
"NotFoundPage": { "NotFoundPage": {
"h1_490-590_404NotFound": "404 見つかりませんでした", "h1_490-590_404NotFound": "404 見つかりませんでした",
"p_601-665_TARDISERROR": "ターディスエラー!", "p_601-665_TARDISERROR": "ターディスエラー!",

View File

@ -90,6 +90,11 @@
"p_2277-2331_Overview": "👋 概覽", "p_2277-2331_Overview": "👋 概覽",
"p_2390-2457_wherethetimeis": "當前時間" "p_2390-2457_wherethetimeis": "當前時間"
}, },
"Global": {
"Loading": "載入中...",
"Distributions": "伺服器分佈在",
"Regions": "個地區"
},
"NotFoundPage": { "NotFoundPage": {
"h1_490-590_404NotFound": "404 未找到", "h1_490-590_404NotFound": "404 未找到",
"p_601-665_TARDISERROR": "TARDIS 錯誤!", "p_601-665_TARDISERROR": "TARDIS 錯誤!",

View File

@ -90,6 +90,11 @@
"p_2277-2331_Overview": "👋 概览", "p_2277-2331_Overview": "👋 概览",
"p_2390-2457_wherethetimeis": "当前时间" "p_2390-2457_wherethetimeis": "当前时间"
}, },
"Global": {
"Loading": "加载中...",
"Distributions": "服务器分布在",
"Regions": "个地区"
},
"NotFoundPage": { "NotFoundPage": {
"h1_490-590_404NotFound": "404 未找到", "h1_490-590_404NotFound": "404 未找到",
"p_601-665_TARDISERROR": "TARDIS 错误!", "p_601-665_TARDISERROR": "TARDIS 错误!",

View File

@ -1,6 +1,6 @@
{ {
"name": "nezha-dash", "name": "nezha-dash",
"version": "1.4.3-fix", "version": "1.4.4",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev -p 3040", "dev": "next dev -p 3040",