Removed floating overlays. Integrated ActivityFeed and Chat directly into Bento grid cells. Refined layout for maximum clarity and 'Dennis' feel.
111 lines
3.4 KiB
TypeScript
111 lines
3.4 KiB
TypeScript
"use client";
|
|
|
|
import React, { useEffect, useState } from "react";
|
|
import { usePathname } from "next/navigation";
|
|
import dynamic from "next/dynamic";
|
|
import { ToastProvider } from "@/components/Toast";
|
|
import ErrorBoundary from "@/components/ErrorBoundary";
|
|
import { AnalyticsProvider } from "@/components/AnalyticsProvider";
|
|
import { ConsentProvider, useConsent } from "./ConsentProvider";
|
|
import { ThemeProvider } from "./ThemeProvider";
|
|
|
|
// Dynamic import with SSR disabled to avoid framer-motion issues
|
|
const BackgroundBlobs = dynamic(() => import("@/components/BackgroundBlobs").catch(() => ({ default: () => null })), {
|
|
ssr: false,
|
|
loading: () => null,
|
|
});
|
|
|
|
export default function ClientProviders({
|
|
children,
|
|
}: {
|
|
children: React.ReactNode;
|
|
}) {
|
|
const [mounted, setMounted] = useState(false);
|
|
const [is404Page, setIs404Page] = useState(false);
|
|
const pathname = usePathname();
|
|
|
|
useEffect(() => {
|
|
setMounted(true);
|
|
// Check if we're on a 404 page by looking for the data attribute or pathname
|
|
const check404 = () => {
|
|
try {
|
|
if (typeof window !== "undefined" && typeof document !== "undefined") {
|
|
const has404Component = document.querySelector('[data-404-page]');
|
|
const is404Path = pathname === '/404' || (window.location && (window.location.pathname === '/404' || window.location.pathname.includes('404')));
|
|
setIs404Page(!!has404Component || is404Path);
|
|
}
|
|
} catch (error) {
|
|
// Silently fail - 404 detection is not critical
|
|
if (process.env.NODE_ENV === 'development') {
|
|
console.warn('Error checking 404 status:', error);
|
|
}
|
|
}
|
|
};
|
|
// Check immediately and after a short delay
|
|
try {
|
|
check404();
|
|
const timeout = setTimeout(check404, 100);
|
|
const interval = setInterval(check404, 500);
|
|
return () => {
|
|
try {
|
|
clearTimeout(timeout);
|
|
clearInterval(interval);
|
|
} catch {
|
|
// Silently fail during cleanup
|
|
}
|
|
};
|
|
} catch (error) {
|
|
// If setup fails, just return empty cleanup
|
|
if (process.env.NODE_ENV === 'development') {
|
|
console.warn('Error setting up 404 check:', error);
|
|
}
|
|
return () => {};
|
|
}
|
|
}, [pathname]);
|
|
|
|
// Wrap in multiple error boundaries to isolate failures
|
|
return (
|
|
<ErrorBoundary>
|
|
<ErrorBoundary>
|
|
<ConsentProvider>
|
|
<ThemeProvider attribute="class" defaultTheme="light" enableSystem={false}>
|
|
<GatedProviders mounted={mounted} is404Page={is404Page}>
|
|
{children}
|
|
</GatedProviders>
|
|
</ThemeProvider>
|
|
</ConsentProvider>
|
|
</ErrorBoundary>
|
|
</ErrorBoundary>
|
|
);
|
|
}
|
|
|
|
function GatedProviders({
|
|
children,
|
|
mounted,
|
|
is404Page,
|
|
}: {
|
|
children: React.ReactNode;
|
|
mounted: boolean;
|
|
is404Page: boolean;
|
|
}) {
|
|
const { consent } = useConsent();
|
|
const pathname = usePathname();
|
|
|
|
const isAdminRoute = pathname.startsWith("/manage") || pathname.startsWith("/editor");
|
|
|
|
// If consent is not decided yet, treat optional features as off
|
|
const analyticsEnabled = !!consent?.analytics;
|
|
const chatEnabled = !!consent?.chat;
|
|
|
|
const content = (
|
|
<ErrorBoundary>
|
|
<ToastProvider>
|
|
{mounted && <BackgroundBlobs />}
|
|
<div className="relative z-10">{children}</div>
|
|
</ToastProvider>
|
|
</ErrorBoundary>
|
|
);
|
|
|
|
return analyticsEnabled ? <AnalyticsProvider>{content}</AnalyticsProvider> : content;
|
|
}
|