feat: Integrate IdleMainPagesSlideshow component into App.js and update links in MainPageLayout for improved navigation to articles
This commit is contained in:
104
src/components/IdleMainPagesSlideshow.js
Normal file
104
src/components/IdleMainPagesSlideshow.js
Normal 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;
|
||||
}
|
||||
@@ -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" },
|
||||
|
||||
Reference in New Issue
Block a user