Add auto-redirect countdown to realm landing pages

- Show beautiful landing pages for 5 seconds before redirecting
- Add countdown timer in sticky banner (5, 4, 3, 2, 1...)
- 'Go Now' button to skip countdown and redirect immediately
- Pulsing icon animation during countdown
- GameForge → aethex.foundation/gameforge
- Labs → aethex.studio
- Foundation → aethex.foundation
- Best of both worlds: showcase pages + auto-navigation
This commit is contained in:
MrPiglr 2026-01-10 20:25:56 +00:00 committed by GitHub
parent b6b3cb6804
commit 7aedd591b7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 57 additions and 21 deletions

View file

@ -364,9 +364,8 @@ const App = () => (
/> />
<Route path="/research" element={<ResearchLabs />} /> <Route path="/research" element={<ResearchLabs />} />
{/* Labs redirects to aethex.studio (Skunkworks R&D) */} {/* Labs page with auto-redirect to aethex.studio (Skunkworks R&D) */}
<Route path="/labs" element={<ExternalRedirect to="https://aethex.studio" />} /> <Route path="/labs" element={<Labs />} />
<Route path="/labs/*" element={<ExternalRedirect to="https://aethex.studio" />} />
{/* GameForge Management routes stay local on aethex.dev (Axiom Model - Write/Control) */} {/* GameForge Management routes stay local on aethex.dev (Axiom Model - Write/Control) */}
<Route <Route
@ -386,13 +385,11 @@ const App = () => (
} }
/> />
{/* GameForge public routes redirect to aethex.foundation/gameforge (Axiom Model - Read-Only Showcase) */} {/* GameForge public route with auto-redirect to aethex.foundation/gameforge (Axiom Model - Read-Only Showcase) */}
<Route path="/gameforge" element={<ExternalRedirect to="https://aethex.foundation/gameforge" />} /> <Route path="/gameforge" element={<GameForge />} />
<Route path="/gameforge/*" element={<ExternalRedirect to="https://aethex.foundation/gameforge" />} />
{/* Foundation redirects to aethex.foundation (Non-Profit Guardian - Axiom Model) */} {/* Foundation page with auto-redirect to aethex.foundation (Non-Profit Guardian - Axiom Model) */}
<Route path="/foundation" element={<ExternalRedirect to="https://aethex.foundation" />} /> <Route path="/foundation" element={<Foundation />} />
<Route path="/foundation/*" element={<ExternalRedirect to="https://aethex.foundation" />} />
<Route path="/corp" element={<Corp />} /> <Route path="/corp" element={<Corp />} />
<Route <Route

View file

@ -27,6 +27,7 @@ export default function Foundation() {
const { theme } = useArmTheme(); const { theme } = useArmTheme();
const armToast = useArmToast(); const armToast = useArmToast();
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [countdown, setCountdown] = useState(5);
const toastShownRef = useRef(false); const toastShownRef = useRef(false);
useEffect(() => { useEffect(() => {
@ -41,6 +42,18 @@ export default function Foundation() {
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, [armToast]); }, [armToast]);
// Auto-redirect countdown
useEffect(() => {
if (isLoading) return;
if (countdown > 0) {
const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
return () => clearTimeout(timer);
} else {
window.location.href = 'https://aethex.foundation';
}
}, [countdown, isLoading]);
if (isLoading) { if (isLoading) {
return ( return (
<LoadingScreen <LoadingScreen
@ -56,14 +69,14 @@ export default function Foundation() {
return ( return (
<Layout> <Layout>
<div className="min-h-screen bg-gradient-to-b from-black via-red-950/20 to-black"> <div className="min-h-screen bg-gradient-to-b from-black via-red-950/20 to-black">
{/* Informational Banner */} {/* Informational Banner with Countdown */}
<div className="bg-red-500/10 border-b border-red-400/30 py-3 sticky top-0 z-50 backdrop-blur-sm"> <div className="bg-red-500/10 border-b border-red-400/30 py-3 sticky top-0 z-50 backdrop-blur-sm">
<div className="container mx-auto max-w-7xl px-4"> <div className="container mx-auto max-w-7xl px-4">
<div className="flex items-center justify-between gap-4 flex-wrap"> <div className="flex items-center justify-between gap-4 flex-wrap">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<ExternalLink className="h-5 w-5 text-red-400" /> <ExternalLink className="h-5 w-5 text-red-400 animate-pulse" />
<p className="text-sm text-red-200"> <p className="text-sm text-red-200">
<strong>External Platform:</strong> Foundation is hosted at{" "} <strong>Redirecting in {countdown}s...</strong> Foundation is hosted at{" "}
<a href="https://aethex.foundation" className="underline font-semibold hover:text-red-300"> <a href="https://aethex.foundation" className="underline font-semibold hover:text-red-300">
aethex.foundation aethex.foundation
</a> </a>
@ -75,7 +88,7 @@ export default function Foundation() {
onClick={() => window.location.href = 'https://aethex.foundation'} onClick={() => window.location.href = 'https://aethex.foundation'}
> >
<ExternalLink className="h-4 w-4 mr-2" /> <ExternalLink className="h-4 w-4 mr-2" />
Visit Platform Go Now
</Button> </Button>
</div> </div>
</div> </div>

View file

@ -27,6 +27,7 @@ export default function GameForge() {
const { theme } = useArmTheme(); const { theme } = useArmTheme();
const armToast = useArmToast(); const armToast = useArmToast();
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [countdown, setCountdown] = useState(5);
const toastShownRef = useRef(false); const toastShownRef = useRef(false);
useEffect(() => { useEffect(() => {
@ -41,6 +42,18 @@ export default function GameForge() {
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, [armToast]); }, [armToast]);
// Auto-redirect countdown
useEffect(() => {
if (isLoading) return;
if (countdown > 0) {
const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
return () => clearTimeout(timer);
} else {
window.location.href = 'https://aethex.foundation/gameforge';
}
}, [countdown, isLoading]);
if (isLoading) { if (isLoading) {
return ( return (
<LoadingScreen <LoadingScreen
@ -90,14 +103,14 @@ export default function GameForge() {
return ( return (
<Layout> <Layout>
<div className="relative min-h-screen bg-black text-white overflow-hidden"> <div className="relative min-h-screen bg-black text-white overflow-hidden">
{/* Informational Banner */} {/* Informational Banner with Countdown */}
<div className="bg-green-500/10 border-b border-green-400/30 py-3 sticky top-0 z-50 backdrop-blur-sm"> <div className="bg-green-500/10 border-b border-green-400/30 py-3 sticky top-0 z-50 backdrop-blur-sm">
<div className="container mx-auto max-w-7xl px-4"> <div className="container mx-auto max-w-7xl px-4">
<div className="flex items-center justify-between gap-4 flex-wrap"> <div className="flex items-center justify-between gap-4 flex-wrap">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<ExternalLink className="h-5 w-5 text-green-400" /> <ExternalLink className="h-5 w-5 text-green-400 animate-pulse" />
<p className="text-sm text-green-200"> <p className="text-sm text-green-200">
<strong>External Platform:</strong> GameForge is hosted at{" "} <strong>Redirecting in {countdown}s...</strong> GameForge is hosted at{" "}
<a href="https://aethex.foundation/gameforge" className="underline font-semibold hover:text-green-300"> <a href="https://aethex.foundation/gameforge" className="underline font-semibold hover:text-green-300">
aethex.foundation/gameforge aethex.foundation/gameforge
</a> </a>
@ -109,7 +122,7 @@ export default function GameForge() {
onClick={() => window.location.href = 'https://aethex.foundation/gameforge'} onClick={() => window.location.href = 'https://aethex.foundation/gameforge'}
> >
<ExternalLink className="h-4 w-4 mr-2" /> <ExternalLink className="h-4 w-4 mr-2" />
Visit Platform Go Now
</Button> </Button>
</div> </div>
</div> </div>

View file

@ -23,6 +23,7 @@ export default function Labs() {
const { theme } = useArmTheme(); const { theme } = useArmTheme();
const armToast = useArmToast(); const armToast = useArmToast();
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
const [countdown, setCountdown] = useState(5);
const toastShownRef = useRef(false); const toastShownRef = useRef(false);
useEffect(() => { useEffect(() => {
@ -37,6 +38,18 @@ export default function Labs() {
return () => clearTimeout(timer); return () => clearTimeout(timer);
}, [armToast]); }, [armToast]);
// Auto-redirect countdown
useEffect(() => {
if (isLoading) return;
if (countdown > 0) {
const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
return () => clearTimeout(timer);
} else {
window.location.href = 'https://aethex.studio';
}
}, [countdown, isLoading]);
if (isLoading) { if (isLoading) {
return ( return (
<LoadingScreen <LoadingScreen
@ -112,14 +125,14 @@ export default function Labs() {
return ( return (
<Layout> <Layout>
<div className="relative min-h-screen bg-black text-white overflow-hidden"> <div className="relative min-h-screen bg-black text-white overflow-hidden">
{/* Informational Banner */} {/* Informational Banner with Countdown */}
<div className="bg-yellow-500/10 border-b border-yellow-400/30 py-3 sticky top-0 z-50 backdrop-blur-sm"> <div className="bg-yellow-500/10 border-b border-yellow-400/30 py-3 sticky top-0 z-50 backdrop-blur-sm">
<div className="container mx-auto max-w-7xl px-4"> <div className="container mx-auto max-w-7xl px-4">
<div className="flex items-center justify-between gap-4 flex-wrap"> <div className="flex items-center justify-between gap-4 flex-wrap">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<ExternalLink className="h-5 w-5 text-yellow-400" /> <ExternalLink className="h-5 w-5 text-yellow-400 animate-pulse" />
<p className="text-sm text-yellow-200"> <p className="text-sm text-yellow-200">
<strong>External Platform:</strong> Labs is hosted at{" "} <strong>Redirecting in {countdown}s...</strong> Labs is hosted at{" "}
<a href="https://aethex.studio" className="underline font-semibold hover:text-yellow-300"> <a href="https://aethex.studio" className="underline font-semibold hover:text-yellow-300">
aethex.studio aethex.studio
</a> </a>
@ -131,7 +144,7 @@ export default function Labs() {
onClick={() => window.location.href = 'https://aethex.studio'} onClick={() => window.location.href = 'https://aethex.studio'}
> >
<ExternalLink className="h-4 w-4 mr-2" /> <ExternalLink className="h-4 w-4 mr-2" />
Visit Studio Go Now
</Button> </Button>
</div> </div>
</div> </div>