Hey is a decentralized and permissionless social media app built with Lens Protocol 馃尶
1import { CheckCircleIcon, XCircleIcon } from "@heroicons/react/24/solid";
2import { MeVariables } from "@hey/data/constants";
3import { useMeQuery } from "@hey/indexer";
4import { useIsClient } from "@uidotdev/usehooks";
5import { memo, useCallback, useEffect } from "react";
6import { Outlet, useLocation } from "react-router";
7import { Toaster, type ToasterProps } from "sonner";
8import FullPageLoader from "@/components/Shared/FullPageLoader";
9import GlobalAlerts from "@/components/Shared/GlobalAlerts";
10import GlobalModals from "@/components/Shared/GlobalModals";
11import Navbar from "@/components/Shared/Navbar";
12import BottomNavigation from "@/components/Shared/Navbar/BottomNavigation";
13import { Spinner } from "@/components/Shared/UI";
14import reloadAllTabs from "@/helpers/reloadAllTabs";
15import { useTheme } from "@/hooks/useTheme";
16import { useAccountStore } from "@/store/persisted/useAccountStore";
17import { hydrateAuthTokens, signOut } from "@/store/persisted/useAuthStore";
18import { useBetaStore } from "@/store/persisted/useBetaStore";
19import { useProStore } from "@/store/persisted/useProStore";
20import ReloadTabsWatcher from "./ReloadTabsWatcher";
21
22const Layout = () => {
23 const { pathname } = useLocation();
24 const { theme } = useTheme();
25 const { currentAccount, setCurrentAccount } = useAccountStore();
26 const { setProBannerDismissed } = useProStore();
27 const { setBetaBannerDismissed } = useBetaStore();
28 const isMounted = useIsClient();
29 const { accessToken } = hydrateAuthTokens();
30
31 useEffect(() => {
32 window.scrollTo(0, 0);
33 }, [pathname]);
34
35 const onError = useCallback(() => {
36 signOut();
37 reloadAllTabs();
38 }, []);
39
40 const { loading } = useMeQuery({
41 onCompleted: ({ me, proBanner, betaBanner }) => {
42 setCurrentAccount(me.loggedInAs.account);
43 if (proBanner?.__typename === "Post") {
44 setProBannerDismissed(proBanner.operations?.dismissed ?? false);
45 }
46 if (betaBanner?.__typename === "Post") {
47 setBetaBannerDismissed(betaBanner.operations?.dismissed ?? false);
48 }
49 },
50 onError,
51 skip: !accessToken,
52 variables: MeVariables
53 });
54
55 const accountLoading = !currentAccount && loading;
56
57 if (accountLoading || !isMounted) {
58 return <FullPageLoader />;
59 }
60
61 return (
62 <>
63 <Toaster
64 icons={{
65 error: <XCircleIcon className="size-5" />,
66 loading: <Spinner size="xs" />,
67 success: <CheckCircleIcon className="size-5" />
68 }}
69 position="bottom-right"
70 theme={theme as ToasterProps["theme"]}
71 toastOptions={{
72 className: "font-sofia-pro",
73 style: { boxShadow: "none", fontSize: "16px" }
74 }}
75 />
76 <GlobalModals />
77 <GlobalAlerts />
78 <ReloadTabsWatcher />
79 <div className="mx-auto flex w-full max-w-6xl items-start gap-x-8 px-0 md:px-5">
80 <Navbar />
81 <Outlet />
82 <BottomNavigation />
83 </div>
84 </>
85 );
86};
87
88export default memo(Layout);