feat(artifact): refresh button

This commit is contained in:
2026-04-09 07:59:37 +07:00 Unverified
parent 10efbb4a9f
commit 37b31c782c
3 changed files with 61 additions and 25 deletions
+49 -25
View File
@@ -5,6 +5,7 @@ import {
CopyCheck,
ImageIcon,
OctagonAlert,
RefreshCwIcon,
ScanSearch,
SquircleDashed,
} from "lucide-react";
@@ -13,7 +14,8 @@ import { useEffect, useState } from "react";
import Enka from "#/enka_logo.png";
import { SimpleTooltip } from "@/components/tooltip";
import { Button } from "@/components/ui/button";
import { getCardStatus } from "@/lib/api";
import { Spinner } from "@/components/ui/spinner";
import { getCardStatus, revalidateCard } from "@/lib/api";
import { cn } from "@/lib/utils";
export function EnkaBrowser({ sub, uid }: { sub: string; uid: string }) {
@@ -21,6 +23,14 @@ export function EnkaBrowser({ sub, uid }: { sub: string; uid: string }) {
const [error, setError] = useState<string | null>(null);
const [useWeb, setUseWeb] = useState(false);
const [isCached, setIsCached] = useState(true);
const [t, setT] = useState(0);
async function refresh() {
setReady(false);
setIsCached(false);
if (!useWeb) await revalidateCard(sub);
setT(Date.now());
}
// biome-ignore lint/correctness/useExhaustiveDependencies: for update
useEffect(() => {
@@ -31,7 +41,7 @@ export function EnkaBrowser({ sub, uid }: { sub: string; uid: string }) {
setIsCached(s.cached);
if (!s.cached) setError(s.error);
});
}, [sub, uid]);
}, [sub, uid, t]);
return (
<div className="h-full w-full relative">
{!ready && !error && (isCached || useWeb) && (
@@ -68,30 +78,44 @@ export function EnkaBrowser({ sub, uid }: { sub: string; uid: string }) {
</div>
)}
<SimpleTooltip text={useWeb ? "สลับเป็นรูปภาพ" : "สลับเป็น Enka"}>
<Button
variant="outline"
className={cn(
"absolute left-1 z-45 bg-[#222a]! backdrop-blur-sm opacity-70 md:opacity-50 hover:opacity-100",
useWeb ? "-bottom-19" : "bottom-1",
)}
size="icon"
onClick={() => {
setReady(false);
setError(null);
setUseWeb((x) => !x);
}}
>
{useWeb ? (
<ImageIcon />
) : (
<Image src={Enka} alt="Enka" className="size-5" />
)}
</Button>
</SimpleTooltip>
<div
className={cn(
"absolute left-1 z-45 opacity-70 md:opacity-50 hover:opacity-100 flex gap-1",
useWeb ? "-bottom-19" : "bottom-1",
)}
>
<SimpleTooltip text={useWeb ? "สลับเป็นรูปภาพ" : "สลับเป็น Enka"}>
<Button
variant="outline"
className="bg-[#222a]! backdrop-blur-sm"
size="icon"
onClick={() => {
setReady(false);
setError(null);
setUseWeb((x) => !x);
}}
>
{useWeb ? (
<ImageIcon />
) : (
<Image src={Enka} alt="Enka" className="size-5" />
)}
</Button>
</SimpleTooltip>
<SimpleTooltip text="รีเฟรช">
<Button
variant="ghost"
size="icon"
onClick={refresh}
disabled={!error && !ready}
>
{!error && !ready ? <Spinner /> : <RefreshCwIcon />}
</Button>
</SimpleTooltip>
</div>
{useWeb ? (
<iframe
src={`https://enka.network/u/${uid}`}
src={`https://enka.network/u/${uid}?t=${t}`}
className={cn(
"w-full border-0 bg-card -mt-20 h-[calc(100%+5rem)]",
!ready && "grayscale blur-md brightness-50 pointer-events-none",
@@ -108,7 +132,7 @@ export function EnkaBrowser({ sub, uid }: { sub: string; uid: string }) {
)}
>
<Image
src={`/api/card/${sub}`}
src={`/api/card/${sub}?t=${t}`}
alt={`Enka card for ${sub}`}
fill
objectFit="contain"
+6
View File
@@ -229,6 +229,12 @@ export async function random() {
else throw "ไม่พบผู้ลงทะเบียนที่ยังไม่ตรวจสอบ";
}
export async function revalidateCard(sub: string) {
if (!(await adminCheck())) throw "Unauthorized";
await db.delete(cards).where(eq(cards.submission, sub));
revalidatePath(`/api/card/${sub}`);
}
export async function tlState(
data: Partial<typeof tierlistStates.$inferInsert>,
) {
+6
View File
@@ -27,6 +27,12 @@ const nextConfig: NextConfig = withBundleAnalyzer({
hostname: "cards.enka.network",
},
],
localPatterns: [
{ pathname: "/api/card/*" },
{
pathname: "/cdn/*",
},
],
minimumCacheTTL: 86400,
},
experimental: {