From ef1c8fc79d600c212752a3d3ad0209a943d3a3cd Mon Sep 17 00:00:00 2001 From: Sam Becker Date: Sun, 21 Apr 2024 22:36:49 -0500 Subject: [PATCH] Move auth to client state --- src/admin/AdminPhotoMenuClient.tsx | 9 ++- src/app/admin/layout.tsx | 10 +--- src/app/grid/page.tsx | 13 ++-- src/app/layout.tsx | 12 ++-- src/auth/SignInForm.tsx | 14 ++++- src/auth/actions.ts | 10 ++-- src/components/CommandKClient.tsx | 86 ++++++++++++++++++++++++--- src/components/FieldSetWithStatus.tsx | 4 +- src/site/CommandK.tsx | 60 ------------------- src/site/Footer.tsx | 76 +++++++++++++++++++++-- src/site/FooterClient.tsx | 75 ----------------------- src/site/Nav.tsx | 80 +++++++++++++++++++++---- src/site/NavClient.tsx | 75 ----------------------- src/state/AppState.ts | 3 + src/state/AppStateProvider.tsx | 7 +++ 15 files changed, 266 insertions(+), 268 deletions(-) delete mode 100644 src/site/FooterClient.tsx delete mode 100644 src/site/NavClient.tsx diff --git a/src/admin/AdminPhotoMenuClient.tsx b/src/admin/AdminPhotoMenuClient.tsx index f31c5a9e..24f23c70 100644 --- a/src/admin/AdminPhotoMenuClient.tsx +++ b/src/admin/AdminPhotoMenuClient.tsx @@ -9,6 +9,7 @@ import { isPathFavs, isPhotoFav } from '@/tag'; import { usePathname } from 'next/navigation'; import { BiTrash } from 'react-icons/bi'; import MoreMenu from '@/components/MoreMenu'; +import { useAppState } from '@/state/AppState'; export default function AdminPhotoMenuClient({ photo, @@ -16,14 +17,16 @@ export default function AdminPhotoMenuClient({ }: Omit, 'items'> & { photo: Photo }) { + const { isUserSignedIn } = useAppState(); + const isFav = isPhotoFav(photo); const path = usePathname(); const shouldRedirectFav = isPathFavs(path) && isFav; const shouldRedirectDelete = pathForPhoto(photo.id) === path; return ( - <> - - + : null ); } diff --git a/src/app/admin/layout.tsx b/src/app/admin/layout.tsx index 0c7cee19..6baa955d 100644 --- a/src/app/admin/layout.tsx +++ b/src/app/admin/layout.tsx @@ -1,6 +1,4 @@ import AdminNav from '@/admin/AdminNav'; -import AdminNavClient from '@/admin/AdminNavClient'; -import { Suspense } from 'react'; export default async function AdminLayout({ children, @@ -9,13 +7,7 @@ export default async function AdminLayout({ }) { return (
- }> - - + {children}
); diff --git a/src/app/grid/page.tsx b/src/app/grid/page.tsx index 48c141f3..e5ee6817 100644 --- a/src/app/grid/page.tsx +++ b/src/app/grid/page.tsx @@ -11,7 +11,6 @@ import { Metadata } from 'next/types'; import PhotoGridSidebar from '@/photo/PhotoGridSidebar'; import { getPhotoSidebarDataCached } from '@/photo/data'; import { MorePhotosGrid } from '@/photo/MorePhotosGrid'; -import { Suspense } from 'react'; export const revalidate = 3600; @@ -37,13 +36,11 @@ export default async function GridPage() { ? - - - + } contentSide={
- +
); diff --git a/src/site/CommandK.tsx b/src/site/CommandK.tsx index b365dc91..e94a3f79 100644 --- a/src/site/CommandK.tsx +++ b/src/site/CommandK.tsx @@ -6,12 +6,6 @@ import { getUniqueTagsCached, } from '@/photo/cache'; import { - PATH_ADMIN_BASELINE, - PATH_ADMIN_CONFIGURATION, - PATH_ADMIN_PHOTOS, - PATH_ADMIN_TAGS, - PATH_ADMIN_UPLOADS, - PATH_SIGN_IN, pathForCamera, pathForFilmSimulation, pathForTag, @@ -20,13 +14,10 @@ import { formatCameraText } from '@/camera'; import { authCachedSafe } from '@/auth/cache'; import { photoQuantityText } from '@/photo'; import { formatCount, formatCountDescriptive } from '@/utility/string'; -import { BiLockAlt, BiSolidUser } from 'react-icons/bi'; import { sortTagsObject } from '@/tag'; import PhotoFilmSimulationIcon from '@/simulation/PhotoFilmSimulationIcon'; import { FaTag } from 'react-icons/fa'; import { IoMdCamera } from 'react-icons/io'; -import { HiDocumentText } from 'react-icons/hi'; -import { signOutAction } from '@/auth/actions'; import { ADMIN_DEBUG_TOOLS_ENABLED } from './config'; export default async function CommandK() { @@ -84,62 +75,11 @@ export default async function CommandK() { })), }; - const SECTION_PAGES: CommandKSection = { - heading: 'Pages', - accessory: , - items: ([{ - label: 'Home', - path: '/', - }, { - label: 'Grid', - path:'/grid', - }]), - }; - - const SECTION_ADMIN: CommandKSection = { - heading: 'Admin', - accessory: , - items: isAdminLoggedIn - ? [{ - label: 'Manage Photos', - annotation: , - path: PATH_ADMIN_PHOTOS, - }, { - label: 'Manage Uploads', - annotation: , - path: PATH_ADMIN_UPLOADS, - }, { - label: 'Manage Tags', - annotation: , - path: PATH_ADMIN_TAGS, - }, { - label: 'App Config', - annotation: , - path: PATH_ADMIN_CONFIGURATION, - }, { - label: 'Sign Out', - action: signOutAction, - }] - : [{ - label: 'Sign In', - path: PATH_SIGN_IN, - }], - }; - - if (isAdminLoggedIn && ADMIN_DEBUG_TOOLS_ENABLED) { - SECTION_ADMIN.items.push({ - label: 'Baseline Overview', - path: PATH_ADMIN_BASELINE, - }); - } - return + +
+ {isPathAdmin(pathname) + ? <> + {userEmail === undefined && + } + {userEmail && <> +
+ {userEmail} +
+
signOutAndRedirectAction() + .then(() => setUserEmail?.(undefined))}> + + Sign out + +
+ } + + : <> + + Admin + + {SHOW_REPO_LINK && + } + } +
+
+ +
+ ] + : []} + />} + /> ); } diff --git a/src/site/FooterClient.tsx b/src/site/FooterClient.tsx deleted file mode 100644 index 12f6a6b5..00000000 --- a/src/site/FooterClient.tsx +++ /dev/null @@ -1,75 +0,0 @@ -'use client'; - -import { clsx } from 'clsx/lite'; -import SiteGrid from '../components/SiteGrid'; -import ThemeSwitcher from '@/site/ThemeSwitcher'; -import Link from 'next/link'; -import { SHOW_REPO_LINK } from '@/site/config'; -import RepoLink from '../components/RepoLink'; -import { usePathname } from 'next/navigation'; -import { isPathAdmin, isPathSignIn, pathForAdminPhotos } from './paths'; -import SubmitButtonWithStatus from '@/components/SubmitButtonWithStatus'; -import { signOutAction } from '@/auth/actions'; -import Spinner from '@/components/Spinner'; -import AnimateItems from '@/components/AnimateItems'; - -export default function FooterClient({ - userEmail, -}: { - userEmail?: string | null | undefined -}) { - const pathname = usePathname(); - - const showFooter = !isPathSignIn(pathname); - - const shouldAnimate = !isPathAdmin(pathname); - - return ( - -
- {isPathAdmin(pathname) - ? <> - {userEmail === undefined && - } - {userEmail && <> -
- {userEmail} -
-
- - Sign out - -
- } - - : <> - - Admin - - {SHOW_REPO_LINK && - } - } -
-
- -
- ] - : []} - />} - /> - ); -} diff --git a/src/site/Nav.tsx b/src/site/Nav.tsx index f20874cc..36fa5c34 100644 --- a/src/site/Nav.tsx +++ b/src/site/Nav.tsx @@ -1,16 +1,76 @@ -import { authCachedSafe } from '@/auth/cache'; -import NavClient from './NavClient'; +'use client'; -export default async function Nav({ - animate, +import { clsx } from 'clsx/lite'; +import { usePathname } from 'next/navigation'; +import Link from 'next/link'; +import SiteGrid from '../components/SiteGrid'; +import { SITE_DOMAIN_OR_TITLE } from '@/site/config'; +import ViewSwitcher, { SwitcherSelection } from '@/site/ViewSwitcher'; +import { + PATH_ROOT, + isPathGrid, + isPathProtected, + isPathSignIn, +} from '@/site/paths'; +import AnimateItems from '../components/AnimateItems'; +import { useAppState } from '@/state/AppState'; + +export default function Nav({ + animate = true, }: { - animate?: boolean + animate?: boolean, }) { - const session = await authCachedSafe(); + const pathname = usePathname(); + + const { isUserSignedIn } = useAppState(); + + const showNav = !isPathSignIn(pathname); + + const renderLink = ( + text: string, + linkOrAction: string | (() => void), + ) => + typeof linkOrAction === 'string' + ? {text} + : ; + + const switcherSelectionForPath = (): SwitcherSelection | undefined => { + if (pathname === PATH_ROOT) { + return 'full-frame'; + } else if (isPathGrid(pathname)) { + return 'grid'; + } else if (isPathProtected(pathname)) { + return 'admin'; + } + }; + return ( - +
+ +
+
+ {renderLink(SITE_DOMAIN_OR_TITLE, PATH_ROOT)} +
+ ] + : []} + /> + } /> ); -} +}; diff --git a/src/site/NavClient.tsx b/src/site/NavClient.tsx deleted file mode 100644 index 87015bba..00000000 --- a/src/site/NavClient.tsx +++ /dev/null @@ -1,75 +0,0 @@ -'use client'; - -import { clsx } from 'clsx/lite'; -import { usePathname } from 'next/navigation'; -import Link from 'next/link'; -import SiteGrid from '../components/SiteGrid'; -import { SITE_DOMAIN_OR_TITLE } from '@/site/config'; -import ViewSwitcher, { SwitcherSelection } from '@/site/ViewSwitcher'; -import { - PATH_ROOT, - isPathGrid, - isPathProtected, - isPathSignIn, -} from '@/site/paths'; -import AnimateItems from '../components/AnimateItems'; - -export default function NavClient({ - showAdmin, - animate = true, -}: { - showAdmin?: boolean, - animate?: boolean, -}) { - const pathname = usePathname(); - - const showNav = !isPathSignIn(pathname); - - const renderLink = ( - text: string, - linkOrAction: string | (() => void), - ) => - typeof linkOrAction === 'string' - ? {text} - : ; - - const switcherSelectionForPath = (): SwitcherSelection | undefined => { - if (pathname === PATH_ROOT) { - return 'full-frame'; - } else if (isPathGrid(pathname)) { - return 'grid'; - } else if (isPathProtected(pathname)) { - return 'admin'; - } - }; - - return ( - -
- -
-
- {renderLink(SITE_DOMAIN_OR_TITLE, PATH_ROOT)} -
- ] - : []} - /> - } - /> - ); -}; diff --git a/src/state/AppState.ts b/src/state/AppState.ts index 18b08869..788502b1 100644 --- a/src/state/AppState.ts +++ b/src/state/AppState.ts @@ -4,6 +4,9 @@ import { AnimationConfig } from '@/components/AnimateItems'; export interface AppStateContext { previousPathname?: string hasLoaded?: boolean + userEmail?: string + setUserEmail?: Dispatch> + isUserSignedIn?: boolean setHasLoaded?: Dispatch> nextPhotoAnimation?: AnimationConfig setNextPhotoAnimation?: Dispatch> diff --git a/src/state/AppStateProvider.tsx b/src/state/AppStateProvider.tsx index c3675521..57770634 100644 --- a/src/state/AppStateProvider.tsx +++ b/src/state/AppStateProvider.tsx @@ -4,6 +4,7 @@ import { useState, useEffect, ReactNode } from 'react'; import { AppStateContext } from './AppState'; import { AnimationConfig } from '@/components/AnimateItems'; import usePathnames from '@/utility/usePathnames'; +import { getCurrentUser } from '@/auth/actions'; export default function AppStateProvider({ children, @@ -14,6 +15,8 @@ export default function AppStateProvider({ const [hasLoaded, setHasLoaded] = useState(false); + const [userEmail, setUserEmail] = + useState(); const [nextPhotoAnimation, setNextPhotoAnimation] = useState(); const [shouldRespondToKeyboardCommands, setShouldRespondToKeyboardCommands] = @@ -25,6 +28,7 @@ export default function AppStateProvider({ useEffect(() => { setHasLoaded?.(true); + getCurrentUser().then(user => setUserEmail?.(user?.email ?? undefined)); }, [setHasLoaded]); return ( @@ -33,6 +37,9 @@ export default function AppStateProvider({ previousPathname, hasLoaded, setHasLoaded, + isUserSignedIn: userEmail !== undefined, + userEmail, + setUserEmail, nextPhotoAnimation, setNextPhotoAnimation, shouldRespondToKeyboardCommands,