tremor-tracker/src/components/BottomFooter.tsx

204 lines
7.1 KiB
TypeScript
Raw Normal View History

2025-05-31 17:54:56 +01:00
import React, { useCallback, useRef, useState } from "react";
2025-05-25 19:59:53 +01:00
import Link from "next/link";
2025-05-31 17:54:56 +01:00
export default function BottomFooter() {
// Instagram Lava Flood Easter Egg
const [lavaActive, setLavaActive] = useState(false);
2025-05-31 17:54:56 +01:00
const lavaTimeout = useRef<any>(null);
// Instagram sound
const earthquakeAudio = useRef<HTMLAudioElement | null>(null);
// LinkedIn Shake Easter Egg
2025-05-31 17:54:56 +01:00
const [shaking, setShaking] = useState(false);
const shakeTimeout = useRef<any>(null);
// X Logo Full-Page Crack & Spin Easter Egg
2025-05-31 17:54:56 +01:00
const [showCracks, setShowCracks] = useState(false);
const [crackOverlayActive, setCrackOverlayActive] = useState(false);
2025-05-31 17:54:56 +01:00
// Lava flood & sound handler
2025-05-31 17:54:56 +01:00
const handleInstagramClick = useCallback((e: React.MouseEvent) => {
e.preventDefault();
setLavaActive(true);
// Play earthquake sound
if (earthquakeAudio.current) {
earthquakeAudio.current.currentTime = 0;
earthquakeAudio.current.play();
}
2025-05-31 17:54:56 +01:00
clearTimeout(lavaTimeout.current);
lavaTimeout.current = setTimeout(() => setLavaActive(false), 2000);
}, []);
// LinkedIn shake handler
const handleLinkedInClick = useCallback(
(e: React.MouseEvent) => {
e.preventDefault();
if (shaking) return; // prevent stacking
setShaking(true);
const body = document.body;
2025-05-31 17:54:56 +01:00
body.classList.remove("shake-screen");
void body.offsetWidth;
body.classList.add("shake-screen");
shakeTimeout.current = setTimeout(() => {
setShaking(false);
body.classList.remove("shake-screen");
}, 1000);
},
[shaking]
);
2025-05-31 17:54:56 +01:00
// X click = crack, spin page, then reset after 2s
const handleXClick = useCallback(
(e: React.MouseEvent) => {
e.preventDefault();
if (crackOverlayActive) return;
setShowCracks(true);
setCrackOverlayActive(true);
document.body.classList.remove("body-spin-back");
document.body.classList.add("body-cracked");
2025-05-31 17:54:56 +01:00
setTimeout(() => {
document.body.classList.remove("body-cracked");
document.body.classList.add("body-spin-back");
setTimeout(() => {
setShowCracks(false);
setCrackOverlayActive(false);
document.body.classList.remove("body-spin-back");
}, 200);
}, 2000);
},
[crackOverlayActive]
);
2025-05-31 17:54:56 +01:00
// Clean up classes and timeouts on unmount
2025-05-31 17:54:56 +01:00
React.useEffect(() => {
return () => {
clearTimeout(lavaTimeout.current);
clearTimeout(shakeTimeout.current);
document.body.classList.remove("shake-screen");
document.body.classList.remove("body-cracked");
document.body.classList.remove("body-spin-back");
2025-05-31 17:54:56 +01:00
};
}, []);
return (
<>
{/* Hidden audio element */}
<audio ref={earthquakeAudio} src="/earthquake.mp3" preload="auto" />
2025-05-31 17:54:56 +01:00
{/* Lava Flood Overlay */}
{lavaActive && (
<div className="lava-flood-overlay lava-active">
<img src="/lava.jpg" alt="Lava flood" draggable={false} />
</div>
)}
{/* Crack overlay for the spinning effect */}
{showCracks && (
<div className="crack-overlay" style={{ pointerEvents: "none" }}>
2025-05-31 17:54:56 +01:00
<img className="crack crack1" src="/crack1.png" alt="" />
<img className="crack crack2" src="/crack2.png" alt="" />
</div>
)}
{/* Footer */}
<footer className="bg-[#16424b] text-white pt-12 pb-4 px-6 mt-12 z-0">
<div className="max-w-6xl mx-auto flex flex-col md:flex-row justify-between gap-8">
{/* Useful Links */}
<div className="min-w-[200px] mb-8 md:mb-0 flex-1">
<h3 className="font-bold underline text-lg mb-3">Useful links</h3>
<ul className="space-y-2">
<li>
<Link
href="https://www.gov.uk/guidance/extreme-weather-and-natural-hazards"
className="hover:underline"
target="_blank"
rel="noopener noreferrer"
>
Gov.UK guidance
</Link>
</li>
<li>
<Link
href="https://www.dysoninstitute.ac.uk/about-us/governance/privacy-notices/"
className="hover:underline"
>
Privacy policy
</Link>
</li>
<li>
<Link
href="https://privacy.dyson.com/en/globalcookiepolicy.aspx"
className="hover:underline"
>
Cookies policy
</Link>
</li>
</ul>
</div>
{/* Donate Section */}
<div className="min-w-[220px] mb-8 md:mb-0 flex-1">
<h3 className="font-bold underline text-lg mb-3">Donate</h3>
<p className="mb-4">
We are a nonprofit entirely funded by your donations, every penny helps provide life saving information.
</p>
<Link
href="https://shelterbox.org/"
className="bg-gray-200 hover:bg-blue-600 hover:text-white text-black font-bold rounded-full px-8 py-2 shadow transition-colors duration-200 inline-block text-center"
>
Donate Now
</Link>
</div>
</div>
{/* Bottom bar */}
<div className="max-w-6xl mx-auto mt-8 pt-6 flex flex-col md:flex-row items-center justify-between border-t border-gray-200/30">
<div className="flex flex-row items-center w-full md:w-auto">
<img
src="/logo.png"
alt="TremorTracker logo"
className="h-16 w-auto mr-4 object-contain"
style={{ maxHeight: 75 }}
/>
</div>
2025-05-31 17:54:56 +01:00
<span className="text-sm flex items-center">
<span className="mr-2">&#169;</span> TremorTracker 2025
</span>
<div className="flex flex-col items-end">
<span className="text-sm mb-2">Follow us on</span>
<div className="flex space-x-3">
<a
href="#"
onClick={handleInstagramClick}
style={{ cursor: "pointer" }}
aria-label="Instagram Lava Easter egg"
>
<img src="instagram.png" alt="instagram" className="h-7 w-7 rounded-full shadow" />
</a>
<a
href="#"
onClick={handleLinkedInClick}
style={{ cursor: "pointer" }}
aria-label="LinkedIn Shake Easter egg"
>
<img src="linkedin.png" alt="linkedin" className="h-7 w-7 rounded-full shadow" />
</a>
<a
href="#"
onClick={handleXClick}
style={{ cursor: "pointer" }}
aria-label="X Crack Easter egg"
>
<span className="footer-x-logo-wrap">
<img
src="x_logo.jpg"
alt="X"
className="h-7 w-7 rounded-full shadow"
style={{ display: "block"}}
/>
</span>
2025-05-31 17:54:56 +01:00
</a>
</div>
</div>
</div>
</footer>
</>
);
}