feat: Integrate IdleMainPagesSlideshow component into App.js and update links in MainPageLayout for improved navigation to articles

This commit is contained in:
sebseb7
2026-03-26 21:10:46 +01:00
parent de8e59f1bb
commit defe3c9521
3 changed files with 108 additions and 2 deletions

View File

@@ -33,6 +33,7 @@ import i18n from './i18n/index.js';
import Header from "./components/Header.js";
import Footer from "./components/Footer.js";
import MainPageLayout from "./components/MainPageLayout.js";
import IdleMainPagesSlideshow from "./components/IdleMainPagesSlideshow.js";
import Content from "./components/Content.js";
import ProductDetail from "./components/ProductDetail.js";
@@ -253,6 +254,7 @@ const AppContent = ({ currentTheme, dynamicTheme, onThemeChange }) => {
)
}>
<CarouselProvider>
<IdleMainPagesSlideshow />
<Routes>
{/* Main pages using unified component */}
<Route path="/" element={<MainPageLayout />} />

View File

@@ -0,0 +1,104 @@
import { useEffect, useRef, useCallback } from "react";
import { useLocation, useNavigate } from "react-router-dom";
/** Same order as the main landing tiles (home → Aktionen → Filiale). */
const MAIN_PAGE_PATHS = ["/", "/aktionen", "/filiale"];
/** No input for this long before the slideshow starts. */
const IDLE_MS = 90_000;
/** Time between automatic page changes once the slideshow is running. */
const SLIDESHOW_STEP_MS = 14_000;
/** Ignore duplicate events (mousemove etc.) within this window. */
const ACTIVITY_THROTTLE_MS = 400;
/**
* After idle on /, /aktionen, or /filiale, cycles those routes slowly.
* Lives outside MainPageLayout so it is not reset when the route changes.
*/
export default function IdleMainPagesSlideshow() {
const location = useLocation();
const navigate = useNavigate();
const idleTimerRef = useRef(null);
const slideTimerRef = useRef(null);
const pathRef = useRef(location.pathname);
const wasOnMainPageRef = useRef(false);
const lastActivityRef = useRef(0);
pathRef.current = location.pathname;
const clearTimers = useCallback(() => {
if (idleTimerRef.current != null) {
clearTimeout(idleTimerRef.current);
idleTimerRef.current = null;
}
if (slideTimerRef.current != null) {
clearInterval(slideTimerRef.current);
slideTimerRef.current = null;
}
}, []);
const startSlideshow = useCallback(() => {
let idx = MAIN_PAGE_PATHS.indexOf(pathRef.current);
if (idx < 0) idx = 0;
const advance = () => {
idx = (idx + 1) % MAIN_PAGE_PATHS.length;
navigate(MAIN_PAGE_PATHS[idx], { replace: true });
};
slideTimerRef.current = setInterval(advance, SLIDESHOW_STEP_MS);
}, [navigate]);
const resetIdle = useCallback(() => {
clearTimers();
if (!MAIN_PAGE_PATHS.includes(pathRef.current)) return;
idleTimerRef.current = setTimeout(() => {
idleTimerRef.current = null;
startSlideshow();
}, IDLE_MS);
}, [clearTimers, startSlideshow]);
useEffect(() => {
const nowMain = MAIN_PAGE_PATHS.includes(location.pathname);
if (!nowMain) {
clearTimers();
wasOnMainPageRef.current = false;
return;
}
if (!wasOnMainPageRef.current) {
resetIdle();
}
wasOnMainPageRef.current = true;
}, [location.pathname, clearTimers, resetIdle]);
useEffect(() => {
const onActivity = () => {
const t = Date.now();
if (t - lastActivityRef.current < ACTIVITY_THROTTLE_MS) return;
lastActivityRef.current = t;
resetIdle();
};
const events = [
"mousedown",
"keydown",
"touchstart",
"touchmove",
"wheel",
"click",
"scroll",
];
events.forEach((ev) =>
window.addEventListener(ev, onActivity, { passive: true })
);
window.addEventListener("mousemove", onActivity, { passive: true });
return () => {
events.forEach((ev) => window.removeEventListener(ev, onActivity));
window.removeEventListener("mousemove", onActivity);
clearTimers();
};
}, [resetIdle, clearTimers]);
return null;
}

View File

@@ -320,8 +320,8 @@ const MainPageLayout = () => {
{ title: t('sections.konfigurator'), image: "/assets/images/konfigurator.avif", bgcolor: "#e8f5d6", link: "/Konfigurator" }
],
aktionen: [
{ title: t('sections.oilPress'), image: "/assets/images/presse.jpg", bgcolor: "#e1f0d3", link: "/presseverleih" },
{ title: t('sections.thcTest'), image: "/assets/images/purpl.jpg", bgcolor: "#e8f5d6", link: "/thc-test" }
{ title: t('sections.oilPress'), image: "/assets/images/presse.jpg", bgcolor: "#e1f0d3", link: "/Artikel/Graveda-10t-presse-tagesmiete-inkl-prepress-vorpressform" },
{ title: t('sections.thcTest'), image: "/assets/images/purpl.jpg", bgcolor: "#e8f5d6", link: "/Artikel/1x-messung-purplpro-thc-cbd-restfeuchte-wasseraktivitaet" }
],
filiale: [
{ title: t('sections.address1'), image: "/assets/images/filiale1.jpg", bgcolor: "#e1f0d3", link: "/filiale" },