main
tsx 192 lines 6.14 KB
Raw
1 import { SsgoiTransition } from "@ssgoi/react";
2 import { useEffect } from "react";
3 import { useLocation, useNavigate } from "react-router-dom";
4 import { BadgeDollarSign } from "lucide-react";
5
6 interface ServerDetailState {
7 id: string;
8 name: string;
9 description: string;
10 tags: string[];
11 thumbnail: string;
12 owner: string;
13 online: boolean;
14 serverUrl: string;
15 paymentEnabled?: boolean;
16 paymentLabel?: string;
17 }
18
19 export function ServerDetail() {
20 const location = useLocation();
21 const navigate = useNavigate();
22 const server = location.state as ServerDetailState;
23
24 // Detect back navigation using pageshow event
25 useEffect(() => {
26 const handlePageShow = () => {
27 // Navigate to home if restored from bfcache or has push flag in localStorage
28 const hasPushFlag = localStorage.getItem("isPush") === "true";
29
30 if (hasPushFlag) {
31 localStorage.removeItem("isPush");
32 navigate("/", { replace: true });
33 }
34 };
35
36 window.addEventListener("pageshow", handlePageShow);
37
38 return () => {
39 window.removeEventListener("pageshow", handlePageShow);
40 };
41 }, [navigate]);
42
43 useEffect(() => {
44 if (typeof localStorage === "undefined") return;
45 if (!server) {
46 navigate("/");
47 return;
48 }
49
50 // Check localStorage immediately (before pageshow)
51 const hasPushFlag = localStorage.getItem("isPush") === "true";
52
53 if (hasPushFlag) {
54 // If flag already exists, go home (double-check with pageshow handler)
55 localStorage.removeItem("isPush");
56 navigate("/");
57 return;
58 }
59
60 // Redirect after animation
61 const timer = setTimeout(() => {
62 localStorage.setItem("isPush", "true");
63 window.location.href = server.serverUrl;
64 }, 500);
65
66 return () => {
67 clearTimeout(timer);
68 };
69 }, [server, navigate]);
70
71 // If no server data, show nothing (will redirect)
72 if (!server) {
73 return null;
74 }
75
76 const {
77 id,
78 thumbnail,
79 name,
80 online,
81 description,
82 tags,
83 owner,
84 paymentEnabled,
85 paymentLabel = "",
86 } = server;
87 const normalizedPaymentLabel = paymentLabel.trim();
88 const showPaymentBadge = paymentEnabled || normalizedPaymentLabel !== "";
89
90 // Base size multiplier (1 = default, 2 = 2x size)
91 const basicSize = 2.5;
92
93 return (
94 <SsgoiTransition id={`/server/${id}`}>
95 <div
96 data-hero-key={`server-bg-${id}`}
97 className="fixed inset-0 bg-center bg-no-repeat bg-cover w-screen h-screen"
98 style={{ ...(thumbnail && { backgroundImage: `url(${thumbnail})` }) }}
99 >
100 {/* Content overlay - Full screen */}
101 <div className="absolute inset-0 flex items-center justify-center p-6 md:p-12">
102 <div className="w-full h-full max-w-7xl bg-background/78 backdrop-blur-sm rounded-lg flex flex-col gap-6 p-8 md:p-12 items-start text-start">
103 <div className="w-full h-full flex flex-col justify-center gap-6 md:gap-8">
104 <div
105 className="flex flex-col"
106 style={{ gap: `${1 * basicSize}%` }}
107 >
108 <div
109 className="flex items-center"
110 style={{ gap: `${0.8 * basicSize}%` }}
111 >
112 <div
113 className={`rounded-full ${
114 online ? "bg-green-status" : "bg-red-500"
115 }`}
116 style={{
117 width: `${1 * basicSize}vw`,
118 height: `${1 * basicSize}vw`,
119 }}
120 />
121 <p
122 className={`font-medium leading-normal ${
123 online ? "text-green-status" : "text-red-500"
124 }`}
125 style={{ fontSize: `${1.8 * basicSize}vw` }}
126 >
127 {online ? "Online" : "Offline"}
128 </p>
129 </div>
130 {showPaymentBadge && (
131 <div className="inline-flex w-fit max-w-full items-center gap-2 rounded-md border border-amber-400/25 bg-amber-400/10 px-4 py-2 font-display text-amber-700 dark:text-amber-100">
132 <BadgeDollarSign className="h-5 w-5 shrink-0" />
133 <span className="truncate text-base font-bold uppercase tracking-wide">
134 {normalizedPaymentLabel || "Paid app"}
135 </span>
136 </div>
137 )}
138 <p
139 className="text-foreground font-bold leading-tight"
140 style={{ fontSize: `${6 * basicSize}vw` }}
141 >
142 {name}
143 </p>
144 {description && (
145 <p
146 className="text-text-muted font-normal leading-normal max-w-4xl"
147 style={{ fontSize: `${2.5 * basicSize}vw` }}
148 >
149 {description}
150 </p>
151 )}
152 {tags && tags.length > 0 && (
153 <div
154 className="flex flex-wrap"
155 style={{
156 gap: `${0.8 * basicSize}vw`,
157 marginTop: `${0.5 * basicSize}%`,
158 }}
159 >
160 {tags.map((tag, index) => (
161 <span
162 key={index}
163 className="bg-secondary text-primary font-medium rounded-lg"
164 style={{
165 padding: `${0.6 * basicSize}vw ${1.2 * basicSize}vw`,
166 fontSize: `${1.5 * basicSize}vw`,
167 }}
168 >
169 {tag}
170 </span>
171 ))}
172 </div>
173 )}
174 {owner && (
175 <p
176 className="text-text-muted font-normal leading-normal"
177 style={{
178 fontSize: `${1.8 * basicSize}vw`,
179 marginTop: `${1 * basicSize}%`,
180 }}
181 >
182 by {owner}
183 </p>
184 )}
185 </div>
186 </div>
187 </div>
188 </div>
189 </div>
190 </SsgoiTransition>
191 );
192 }