import PropTypes from "prop-types";
import { useState, useEffect, useRef } from "react";
import { Alert } from "antd";
import { useMediaQuery } from "react-responsive";
import { useNavigate, useLocation } from "react-router-dom";
import { Element, scroller } from "react-scroll";
import axios from "axios";
import Page from "./components/Page";
import Images from "./components/ImageSidebar";
import { ImageProvider, useImageContext } from "./contexts/ImageContext";
import LoadingModal from "./components/LoadingModal";
import { ActionProvider } from "./contexts/ActionContext";
import SubPage from "./components/SubPage";
import PropertyPage from "./components/PropertyPage";
import { MenuProvider } from "./contexts/MenuContext";
import { ThemeProvider } from "./contexts/ThemeContext";
import {
SettingsProvider,
useSettingsContext,
} from "./contexts/SettingsContext";
const apiUrl = import.meta.env.VITE_API_URL;
import "./global.css";
// Component that handles image loading after API data is fetched
const AppContent = ({ pages, properties, images }) => {
const { loadImages } = useImageContext();
const [loadedOnce, setLoadedOnce] = useState(false);
const [currentPage, setCurrentPage] = useState({ pageType: "landingPage" });
const [currentTheme, setCurrentTheme] = useState();
const [currentProperty, setCurrentProperty] = useState(null);
const [currentPageIdx, setCurrentPageIdx] = useState(0);
const [contentVisible, setContentVisible] = useState(true);
const [subPageVisible, setSubPageVisible] = useState(false);
const locationRef = useRef();
const previousPageRef = useRef(null);
const currentPageRef = useRef(currentPage);
const landingPages = pages.filter((page) => page.pageType == "landingPage");
const navigate = useNavigate();
const location = useLocation();
const settings = useSettingsContext();
const isMobile = useMediaQuery({ maxWidth: 800 });
// Load images when they become available
useEffect(() => {
if (!loadedOnce && images && images.length > 0) {
loadImages(images);
setLoadedOnce(true);
}
}, [images, loadImages, loadedOnce]);
const setPageTitle = (name) => {
document.title = name ? `${name} - The Hideout` : "The Hideout";
};
// Handle direct URL navigation
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(() => {
locationRef.current = location.pathname;
if (Object.keys(settings).length == 0) {
return;
}
// Check if the URL matches /properties/:slug
if (location.pathname.startsWith("/properties/")) {
const propertySlug = location.pathname.split("/properties/")[1];
if (propertySlug != "") {
const property = properties.find((p) => p.slug === propertySlug);
if (property) {
// Set previousPage when navigating to a property
if (currentPageRef.current.pageType !== "subPage") {
previousPageRef.current = { ...currentPageRef.current };
}
setCurrentProperty(property);
setContentVisible(true);
if (currentProperty != null || currentPage.pageType == "subPage") {
setSubPageVisible(false);
setTimeout(() => {
setSubPageVisible(true);
}, 500);
} else {
setSubPageVisible(true);
}
setPageTitle(property.name);
} else {
navigate(settings.redirects["404"]);
setCurrentProperty(null); // property not found
}
return; // exit early since we're on a property page
}
}
setCurrentProperty(null);
if (pages.length > 0 && location.pathname !== "/") {
const slug = location.pathname.slice(1); // Remove leading slash
const page = pages.find((p) => p.slug === slug);
if (page) {
// Set previousPage when navigating to a subpage
if (
page.pageType === "subPage" &&
currentPageRef.current.pageType !== "subPage"
) {
previousPageRef.current = { ...currentPageRef.current };
}
setCurrentPage(page);
setCurrentPageIdx(pages.indexOf(page));
setPageTitle(page.name);
if (page.pageType === "landingPage") {
// Ensure DOM has rendered before attempting to scroll
setSubPageVisible(false);
requestAnimationFrame(() => {
scroller.scrollTo(slug, {
duration: 0,
delay: 0,
smooth: "easeInOutQuart",
containerId: "app-container",
});
});
} else {
setContentVisible(false);
if (currentPage.pageType == page.pageType) {
setSubPageVisible(false);
setTimeout(() => {
setSubPageVisible(true);
}, 500);
} else {
setSubPageVisible(true);
}
setTimeout(() => {
setContentVisible(true);
}, 600);
}
} else {
navigate(settings.redirects["404"]);
}
} else if (
pages.length > 0 &&
location.pathname === "/" &&
settings?.redirects?.index != null
) {
console.log("settings.redirects", settings.redirects);
const indexPage = pages.find((p) => p.slug === settings.redirects.index);
// Default to index page
setCurrentPage(indexPage);
setCurrentPageIdx(0);
setPageTitle(indexPage.name);
navigate(`/${indexPage.slug}`, { replace: true });
}
}, [
location.pathname,
pages,
settings?.redirects?.index,
navigate,
settings,
properties,
currentPage.pageType,
currentProperty,
]);
// Set up scroll spy to update URL when scrolling
useEffect(() => {
if (currentPage.pageType == "subPage") {
return;
}
if (pages.length > 0) {
let scrollTimeout;
const handleScrollSpy = () => {
const container = document.getElementById("app-container");
if (!container) return;
const scrollTop = container.scrollTop;
const containerHeight = container.clientHeight;
const viewportCenter = scrollTop + containerHeight / 2;
// Find which page is currently in the center of the viewport
let currentPageIndex = 0;
let minDistance = Infinity;
pages.forEach((page, index) => {
const element = document.querySelector(`[data-name="${page.slug}"]`);
if (element) {
const elementTop = element.offsetTop;
//const elementBottom = elementTop + element.offsetHeight;
const elementCenter = elementTop + element.offsetHeight / 2;
const distance = Math.abs(viewportCenter - elementCenter);
if (distance < minDistance) {
minDistance = distance;
currentPageIndex = index;
if (minDistance != 0) {
setContentVisible(false);
}
if (minDistance == 0) {
setContentVisible(true);
}
}
}
});
// Debounce both current page state and URL updates to reduce frequency
clearTimeout(scrollTimeout);
scrollTimeout = setTimeout(() => {
if (pages[currentPageIndex] && pages[currentPageIndex].slug) {
// Update current page state for Images component
if (currentPageIdx !== currentPageIndex) {
setCurrentPage(pages[currentPageIndex]);
setCurrentPageIdx(currentPageIndex);
setPageTitle(pages[currentPageIndex].name);
const newPath = `/${pages[currentPageIndex].slug}`;
if (locationRef.current !== newPath) {
navigate(newPath, { replace: true });
}
}
}
}, 100); // Debounce for 100ms
};
const container = document.getElementById("app-container");
if (container) {
container.addEventListener("scroll", handleScrollSpy, {
passive: true,
});
return () => {
container.removeEventListener("scroll", handleScrollSpy);
clearTimeout(scrollTimeout);
};
}
}
}, [pages, navigate, currentPageIdx, currentPage.pageType]);
// Update currentPageRef whenever currentPage changes
useEffect(() => {
currentPageRef.current = currentPage;
}, [currentPage]);
// Set body background color to match current page's theme
useEffect(() => {
const theme = settings.themes.find(
(theme) => theme.name === currentPage?.theme
);
setCurrentTheme(theme);
}, [currentPage, settings.themes]);
// Handle subpage close with smart navigation
const handleSubPageClose = () => {
// Check if there's a previous page stored in ref
if (previousPageRef.current && previousPageRef.current.slug) {
// Navigate to the previous page's slug
navigate(`/${previousPageRef.current.slug}`);
} else {
// No previous page, redirect to home
navigate("/");
}
};
return (