2025-04-01 22:57:28 +01:00

261 lines
7.5 KiB
JavaScript

import { useState, useEffect } from "react";
import { Layout, Space } from "antd";
import { useLocation, useNavigate } from "react-router-dom";
import { AnimatePresence, motion } from "framer-motion";
import ParticlesBackground from "./components/ParticlesBackground";
import { useMediaQuery } from "react-responsive";
import { useKeycloak } from "./utils/KeycloakProvider";
import MainView from "./views/MainView";
import NotFoundView from "./views/NotFoundView";
import CVView from "./views/CVView";
import ContactView from "./views/ContactView";
import SocialsView from "./views/SocialsView";
import ExperienceView from "./views/ExperienceView";
import BlogsView from "./views/BlogsView";
import BlogView from "./views/BlogView";
import CacheReloadView from "./utils/CacheReloadView";
import { LoadingOutlined } from "@ant-design/icons";
const { Content } = Layout;
const { Footer } = Layout;
const App = () => {
const location = useLocation(); // This gives you access to location object
const navigate = useNavigate(); // To navigate programmatically
const isMobile = useMediaQuery({ maxWidth: 600 });
const { isAuthenticated, loading, keycloak } = useKeycloak();
// Get the query parameter from the URL
const getQueryParam = (param) => {
const searchParams = new URLSearchParams(location.search); // Use location.search directly
return searchParams.get(param) || "index"; // Default to "index" if param is not found
};
// Initialize state with query parameters
const [currentView, setCurrentView] = useState(getQueryParam("to"));
const referrer = getQueryParam("r");
useEffect(() => {
const queryParams = new URLSearchParams();
// Add the "to" parameter to the query
queryParams.set("to", currentView);
// If the referrer exists, add it to the query as well
if (referrer) {
queryParams.set("r", referrer);
}
// Navigate with the updated query string
navigate(`?${queryParams.toString()}`);
// Initial body setup to prevent scrollbars
document.body.style.overflow = "hidden";
document.body.style.margin = "0";
document.body.style.padding = "0";
}, [currentView, referrer, navigate]);
const fadeVariants = {
initial: {
clipPath: "polygon(0 0, 0 0, 0 100%, 0% 100%)",
opacity: 0,
},
animate: {
clipPath: "polygon(0 0, 100% 0, 100% 100%, 0 100%)",
opacity: 1,
transition: {
duration: 0.7,
ease: "easeInOut",
},
},
exit: {
clipPath: "polygon(100% 0, 100% 0, 100% 100%, 100% 100%)",
opacity: 0,
transition: {
duration: 0.7,
ease: "easeInOut",
},
},
};
// Render different views based on currentView state
const renderView = () => {
if (currentView.startsWith("blog-")) {
const blogSlug = currentView.replace("blog-", "");
return <BlogView setCurrentView={setCurrentView} slug={blogSlug} />;
}
switch (currentView) {
case "index":
return <MainView setCurrentView={setCurrentView} />;
case "cv":
return <CVView setCurrentView={setCurrentView} />;
case "experience":
return <ExperienceView setCurrentView={setCurrentView} />;
case "blogs":
return <BlogsView setCurrentView={setCurrentView} />;
case "contact":
return <ContactView setCurrentView={setCurrentView} />;
case "socials":
return (
<SocialsView setCurrentView={setCurrentView} referrer={referrer} />
);
case "cacheReload":
return <CacheReloadView setCurrentView={setCurrentView} />;
default:
return <NotFoundView setCurrentView={setCurrentView} />;
}
};
return (
<div
style={{
position: "fixed",
top: 0,
left: 0,
width: "100%",
height: "100%",
overflow: "hidden",
}}
>
<ParticlesBackground />
<div
style={{
background: "rgba(255, 255, 255, 0.0)",
backdropFilter: "blur(50px)",
width: "100%",
height: "100%",
top: 0,
left: 0,
position: "absolute",
display: "inline",
}}
></div>
<Layout
style={{
height: "100%",
display: "flex",
flexDirection: "column",
background: "transparent",
}}
>
{/* Content */}
<Content
style={{
position: "relative",
flex: 1,
overflow: "hidden",
display: "flex",
flexDirection: "column",
}}
>
<AnimatePresence mode="wait">
<motion.div
key={currentView}
variants={fadeVariants}
initial="initial"
animate="animate"
exit="exit"
style={{
width: "100%",
height: "100%",
overflow: "auto",
position: "absolute",
top: 0,
left: 0,
}}
>
{renderView()}
</motion.div>
</AnimatePresence>
</Content>
{/* Footer */}
<motion.div
initial={{ paddingLeft: "20px" }}
animate={{
paddingLeft:
(currentView === "index" || currentView === "socials") && isMobile
? "50px"
: "20px",
}}
transition={{ duration: 0.5 }}
style={{
position: "fixed",
bottom: "0",
left: isMobile ? "0" : "50%",
transform: isMobile ? "unset" : "translateX(-50%)",
}}
>
<Footer
style={{
zIndex: 1,
textAlign: isMobile ? "left" : "center",
backgroundRepeat: "no-repeat",
backgroundSize: "cover",
background: "transparent",
color: "#FFFFFF",
padding: "20px 25px",
paddingLeft: "0px",
paddingTop: "0px",
fontSize: "16px",
}}
>
<Space>
<a
href="#"
onClick={(e) => {
e.preventDefault();
setCurrentView("index");
}}
>
© {new Date().getFullYear()}{" "}
</a>
<a
href="#"
onClick={(e) => {
e.preventDefault();
setCurrentView("socials");
}}
>
<img
src="https://cdn.tombutcher.work/icons/web/w-link.svg"
alt="Social Media Links"
height={16}
style={{ marginBottom: "2px" }}
/>
</a>
{loading ? (
<LoadingOutlined spinning />
) : (
<>
{!isAuthenticated && (
<a
href="#"
onClick={(e) => {
e.preventDefault();
keycloak.login();
}}
>
<img
src="https://cdn.tombutcher.work/icons/web/w-auth.svg"
alt="Login to tombutcher.work"
height={16}
style={{ marginBottom: "2px" }}
/>
</a>
)}
</>
)}
</Space>
</Footer>
</motion.div>
</Layout>
</div>
);
};
export default App;