From c0e8723642613b1b2873c2c561080d8a4571ada0 Mon Sep 17 00:00:00 2001 From: Nounspace Ryan Date: Mon, 15 Jul 2024 08:54:36 -0500 Subject: [PATCH] feat: preserve sidebar state across route changes for global music player --- .../components/organisms/Navigation.tsx | 19 ++--- src/common/components/organisms/Sidebar.tsx | 72 ++++++++++++++----- src/common/components/pages/SpacePage.tsx | 31 ++------ src/common/providers/index.tsx | 5 +- src/pages/_app.tsx | 21 +++++- src/pages/homebase/index.tsx | 6 -- src/pages/s/[handle].tsx | 6 -- 7 files changed, 93 insertions(+), 67 deletions(-) diff --git a/src/common/components/organisms/Navigation.tsx b/src/common/components/organisms/Navigation.tsx index 93e31de4..85dfdbc6 100644 --- a/src/common/components/organisms/Navigation.tsx +++ b/src/common/components/organisms/Navigation.tsx @@ -1,4 +1,5 @@ import React, { useCallback, useEffect, useMemo, useState } from "react"; +import { useRouter } from "next/router"; import { mergeClasses } from "@/common/lib/utils/mergeClasses"; import BrandHeader from "../molecules/BrandHeader"; import Player from "@/common/components/organisms/Player"; @@ -15,6 +16,7 @@ import { Button } from "../atoms/button"; import { FaPaintbrush, FaDiscord } from "react-icons/fa6"; import { NOUNISH_LOWFI_URL } from "@/constants/nounishLowfi"; import { UserTheme } from "@/common/lib/theme"; +import { useUserTheme } from "@/common/lib/theme/UserThemeProvider"; import { AnalyticsEvent, analytics, @@ -33,14 +35,9 @@ type NavItemProps = { type NavProps = { isEditable: boolean; enterEditMode: () => void; - theme?: UserTheme; }; -const Navigation: React.FC = ({ - isEditable, - enterEditMode, - theme: userTheme, -}) => { +const Navigation: React.FC = ({ isEditable, enterEditMode }) => { const { setModalOpen, getIsLoggedIn, getIsInitializing } = useAppStore( (state) => ({ setModalOpen: state.setup.setModalOpen, @@ -48,6 +45,7 @@ const Navigation: React.FC = ({ getIsInitializing: state.getIsInitializing, }), ); + const userTheme: UserTheme = useUserTheme(); const logout = useLogout(); function turnOnEditMode() { @@ -79,12 +77,7 @@ const Navigation: React.FC = ({ [user], ); - const [currentUrl, setCurrentUrl] = useState(""); - - useEffect(() => { - // Get the current URL - setCurrentUrl(window.location.pathname); - }, []); + const router = useRouter(); const NavItem: React.FC = ({ label, @@ -100,7 +93,7 @@ const Navigation: React.FC = ({ href={disable ? "#" : href} className={mergeClasses( "flex items-center p-2 text-gray-900 rounded-lg dark:text-white hover:bg-gray-100 dark:hover:bg-gray-700 group w-full", - href === currentUrl ? "bg-gray-100" : "", + href === router.asPath ? "bg-gray-100" : "", )} onClick={onClick} rel={openInNewTab ? "noopener noreferrer" : undefined} diff --git a/src/common/components/organisms/Sidebar.tsx b/src/common/components/organisms/Sidebar.tsx index 5388341f..c1cc02f2 100644 --- a/src/common/components/organisms/Sidebar.tsx +++ b/src/common/components/organisms/Sidebar.tsx @@ -1,30 +1,70 @@ -import React from "react"; +import React, { + createContext, + useContext, + useState, + useRef, + useMemo, +} from "react"; import Navigation from "./Navigation"; -import { UserTheme } from "@/common/lib/theme"; -export interface SidebarProps { +export interface SidebarProps {} + +export type SidebarContextProviderProps = { children: React.ReactNode }; + +export type SidebarContextValue = { editMode: boolean; - enterEditMode: () => void; - theme?: UserTheme; - isEditable: boolean; + setEditMode: (value: boolean) => void; + sidebarEditable: boolean; + setSidebarEditable: (value: boolean) => void; portalRef: React.RefObject; -} - -export const Sidebar: React.FC = ({ - editMode, - enterEditMode, - isEditable, - portalRef, - theme, +}; + +export const SidebarContext = createContext( + {} as SidebarContextValue, +); + +export const SidebarContextProvider: React.FC = ({ + children, }) => { + const [editMode, setEditMode] = useState(false); + const [sidebarEditable, setSidebarEditable] = useState(false); + const portalRef = useRef(null); + + const value = useMemo( + () => ({ + editMode, + setEditMode, + sidebarEditable, + setSidebarEditable, + portalRef, + }), + [editMode, sidebarEditable, portalRef], + ); + + return ( + {children} + ); +}; + +export const useSidebarContext = (): SidebarContextValue => { + return useContext(SidebarContext); +}; + +export const Sidebar: React.FC = () => { + const { editMode, setEditMode, sidebarEditable, portalRef } = + useSidebarContext(); + + function enterEditMode() { + setEditMode(true); + } + return ( <>
diff --git a/src/common/components/pages/SpacePage.tsx b/src/common/components/pages/SpacePage.tsx index 27e1f4cd..5448d7cf 100644 --- a/src/common/components/pages/SpacePage.tsx +++ b/src/common/components/pages/SpacePage.tsx @@ -1,8 +1,8 @@ -import React, { ReactNode, useRef, useState } from "react"; -import Sidebar from "../organisms/Sidebar"; +import React, { ReactNode } from "react"; import Space, { SpaceConfig, SpaceConfigSaveDetails } from "../templates/Space"; import { isUndefined } from "lodash"; -import SpaceLoading from "../templates/SpaceLoading"; +import SpaceLoading from "@/common/components/templates/SpaceLoading"; +import { useSidebarContext } from "@/common/components/organisms/Sidebar"; type SpacePageArgs = { config?: SpaceConfig; @@ -21,28 +21,11 @@ export default function SpacePage({ profile, loading, }: SpacePageArgs) { - const [editMode, setEditMode] = useState(false); - const [sidebarEditable, setSidebarEditable] = useState(false); - const portalRef = useRef(null); - - function enterEditMode() { - setEditMode(true); - } + const { editMode, setEditMode, setSidebarEditable, portalRef } = + useSidebarContext(); return ( -
-
- -
+ <> {isUndefined(config) || isUndefined(saveConfig) || isUndefined(commitConfig) || @@ -62,6 +45,6 @@ export default function SpacePage({ portalRef={portalRef} /> )} -
+ ); } diff --git a/src/common/providers/index.tsx b/src/common/providers/index.tsx index ee580399..005a628f 100644 --- a/src/common/providers/index.tsx +++ b/src/common/providers/index.tsx @@ -10,6 +10,7 @@ import UserThemeProvider from "@/common/lib/theme/UserThemeProvider"; import LoggedInStateProvider from "./LoggedInStateProvider"; import AnalyticsProvider from "./AnalyticsProvider"; import VersionCheckProivder from "./VersionCheckProvider"; +import { SidebarContextProvider } from "@/common/components/organisms/Sidebar"; export default function Providers({ children }: { children: React.ReactNode }) { return ( @@ -22,7 +23,9 @@ export default function Providers({ children }: { children: React.ReactNode }) { - {children} + + {children} + diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index f51dc84a..2e6cd974 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -5,6 +5,7 @@ import type { AppProps } from "next/app"; import type { NextPage } from "next"; import Head from "next/head"; import Providers from "@/common/providers"; +import Sidebar from "@/common/components/organisms/Sidebar"; export type NextPageWithLayout

= NextPage & { getLayout?: (page: React.ReactElement) => React.ReactNode; @@ -14,9 +15,27 @@ type AppPropsWithLayout = AppProps & { Component: NextPageWithLayout; }; +const sidebarLayout = (page: React.ReactElement) => { + return ( + <> +

+
+
+ +
+ {page} +
+
+ + ); +}; + export default function MyApp({ Component, pageProps }: AppPropsWithLayout) { // Use the layout defined at the page level, if available - const getLayout = Component.getLayout ?? ((page) => page); + const getLayout = Component.getLayout ?? sidebarLayout; return ( <> diff --git a/src/pages/homebase/index.tsx b/src/pages/homebase/index.tsx index c05ec960..5781a319 100644 --- a/src/pages/homebase/index.tsx +++ b/src/pages/homebase/index.tsx @@ -57,10 +57,4 @@ const Homebase: NextPageWithLayout = () => { return ; }; -Homebase.getLayout = function getLayout(page: React.ReactElement) { - return ( -
{page}
- ); -}; - export default Homebase; diff --git a/src/pages/s/[handle].tsx b/src/pages/s/[handle].tsx index ec2c53ca..9335b5d4 100644 --- a/src/pages/s/[handle].tsx +++ b/src/pages/s/[handle].tsx @@ -94,10 +94,4 @@ const UserPrimarySpace: NextPageWithLayout = ({ return ; }; -UserPrimarySpace.getLayout = (page: React.ReactElement) => { - return ( -
{page}
- ); -}; - export default UserPrimarySpace;