import { lazy, Suspense, useEffect, useState } from "react"; import { useLocation } from "react-router"; import { t } from "@lingui/core/macro"; import { useAuth } from "../../hooks/auth/useAuth.ts"; const DumpCreateModal = lazy(() => import("../dump/create/DumpCreateModal.tsx").then((m) => ({ default: m.DumpCreateModal, })) ); /** * Floating "new dump" button shown once the header has scrolled out of view. * Sits in the bottom-right corner and lifts above the global player (which is * full-width on mobile) so the two never overlap. Only rendered for signed-in * users — dump creation requires an account. * * Mounted once at the app root (not per-page) since some pages render their own * header instead of going through PageShell. Visibility is derived from a live * read of the current page's `.app-header`, so it survives client-side * navigation and lazily-mounted route headers. */ export function DumpFab() { const { user } = useAuth(); const location = useLocation(); const [headerHidden, setHeaderHidden] = useState(false); const [modalOpen, setModalOpen] = useState(false); // Reserve clearance at the end of the scroll so the floating button never // hides the last content when scrolled all the way down. useEffect(() => { if (!user) return; document.body.classList.add("has-fab"); return () => document.body.classList.remove("has-fab"); }, [user]); // Reveal the button once the header's bottom edge scrolls above the viewport. // Re-reading the header on every tick keeps this correct across navigation // and lazily-mounted pages, where the header element is replaced. useEffect(() => { if (!user) return; const update = () => { const header = document.querySelector(".app-header"); setHeaderHidden(!!header && header.getBoundingClientRect().bottom <= 0); }; update(); globalThis.addEventListener("scroll", update, { passive: true }); globalThis.addEventListener("resize", update); // Body height changes when a lazy route finishes mounting — re-check then, // since no scroll/resize event fires in that case. const ro = new ResizeObserver(update); ro.observe(document.body); return () => { globalThis.removeEventListener("scroll", update); globalThis.removeEventListener("resize", update); ro.disconnect(); }; }, [user, location.pathname]); if (!user) return null; return ( <> {modalOpen && ( setModalOpen(false)} /> )} ); }