diff --git a/src/admin/AdminCTA.tsx b/src/admin/AdminCTA.tsx deleted file mode 100644 index 589bc20d..00000000 --- a/src/admin/AdminCTA.tsx +++ /dev/null @@ -1,36 +0,0 @@ -'use client'; - -import PhotoUploadWithStatus from '@/photo/PhotoUploadWithStatus'; -import { PATH_ADMIN_PHOTOS } from '@/app/paths'; -import { useAppState } from '@/state/AppState'; -import Link from 'next/link'; -import { FaArrowRight } from 'react-icons/fa'; - -export default function AdminCTA({ - shouldResize, - onLastUpload, -}: { - shouldResize: boolean - onLastUpload: () => Promise -}) { - const { isUserSignedIn } = useAppState(); - - return ( -
- {isUserSignedIn - ? - : - Admin Dashboard - - } -
- ); -} diff --git a/src/admin/SignInOrUploadClient.tsx b/src/admin/SignInOrUploadClient.tsx new file mode 100644 index 00000000..6823f0f7 --- /dev/null +++ b/src/admin/SignInOrUploadClient.tsx @@ -0,0 +1,45 @@ +'use client'; + +import { useAppState } from '@/state/AppState'; +import SignInForm from '@/auth/SignInForm'; +import clsx from 'clsx'; +import PhotoUploadWithStatus from '@/photo/PhotoUploadWithStatus'; + +export default function SignInOrUploadClient({ + shouldResize, + onLastUpload, +}: { + shouldResize: boolean + onLastUpload: () => Promise +}) { + const { isUserSignedIn, isCheckingAuth } = useAppState(); + + return ( +
+
+ {isCheckingAuth + ? 'Loading ...' + : isUserSignedIn + ? 'Add your first photo' + : 'Sign in to upload photos'} +
+ {!isCheckingAuth && isUserSignedIn === false && +
+ +
} + {isUserSignedIn === true && + } +
+ ); +} diff --git a/src/auth/SignInForm.tsx b/src/auth/SignInForm.tsx index 8adef8af..041f8863 100644 --- a/src/auth/SignInForm.tsx +++ b/src/auth/SignInForm.tsx @@ -11,13 +11,26 @@ import { } from 'react'; import { getAuthAction, signInAction } from './actions'; import ErrorNote from '@/components/ErrorNote'; -import { KEY_CALLBACK_URL, KEY_CREDENTIALS_SIGN_IN_ERROR } from '.'; +import { + KEY_CALLBACK_URL, + KEY_CREDENTIALS_SIGN_IN_ERROR, + KEY_CREDENTIALS_SUCCESS, +} from '.'; import { useSearchParams } from 'next/navigation'; import { useAppState } from '@/state/AppState'; import { clsx } from 'clsx/lite'; import { FiLock } from 'react-icons/fi'; +import { PATH_ADMIN_PHOTOS } from '@/app/paths'; -export default function SignInForm() { +export default function SignInForm({ + includeTitle = true, + shouldRedirect = true, + className, +}: { + includeTitle?: boolean + shouldRedirect?: boolean + className?: string +}) { const params = useSearchParams(); const { setUserEmail } = useAppState(); @@ -33,12 +46,15 @@ export default function SignInForm() { }, []); useEffect(() => { + if (response === KEY_CREDENTIALS_SUCCESS) { + setUserEmail?.(email); + } return () => { // Capture user email before unmounting getAuthAction().then(auth => setUserEmail?.(auth?.user?.email ?? undefined)); }; - }, [setUserEmail]); + }, [setUserEmail, response, email]); const isFormValid = email.length > 0 && @@ -48,20 +64,19 @@ export default function SignInForm() { -

- - - Sign in - -

-
+ {includeTitle && +

+ + + Sign in + +

} +
{response === KEY_CREDENTIALS_SIGN_IN_ERROR && @@ -83,11 +98,12 @@ export default function SignInForm() { value={password} onChange={setPassword} /> - + {shouldRedirect && + }
Sign in diff --git a/src/auth/actions.ts b/src/auth/actions.ts index 60a4c0fe..e2a61d1f 100644 --- a/src/auth/actions.ts +++ b/src/auth/actions.ts @@ -5,12 +5,12 @@ import { KEY_CREDENTIALS_CALLBACK_ROUTE_ERROR_URL, KEY_CREDENTIALS_SIGN_IN_ERROR, KEY_CREDENTIALS_SIGN_IN_ERROR_URL, + KEY_CREDENTIALS_SUCCESS, auth, generateAuthSecret, signIn, signOut, } from '@/auth'; -import { PATH_ADMIN_PHOTOS } from '@/app/paths'; import type { Session } from 'next-auth'; import { redirect } from 'next/navigation'; @@ -38,7 +38,10 @@ export const signInAction = async ( throw error; } } - redirect(formData.get(KEY_CALLBACK_URL) as string || PATH_ADMIN_PHOTOS); + if (formData.get(KEY_CALLBACK_URL)) { + redirect(formData.get(KEY_CALLBACK_URL) as string); + } + return KEY_CREDENTIALS_SUCCESS; }; export const signOutAction = async () => diff --git a/src/auth/index.ts b/src/auth/index.ts index a207d8cd..b1978b19 100644 --- a/src/auth/index.ts +++ b/src/auth/index.ts @@ -7,6 +7,7 @@ export const KEY_CREDENTIALS_SIGN_IN_ERROR_URL = 'https://errors.authjs.dev#credentialssignin'; export const KEY_CREDENTIALS_CALLBACK_ROUTE_ERROR_URL = 'https://errors.authjs.dev#callbackrouteerror'; +export const KEY_CREDENTIALS_SUCCESS = 'success'; export const KEY_CALLBACK_URL = 'callbackUrl'; export const { diff --git a/src/photo/PhotosEmptyState.tsx b/src/photo/PhotosEmptyState.tsx index c8377e23..bdd01be5 100644 --- a/src/photo/PhotosEmptyState.tsx +++ b/src/photo/PhotosEmptyState.tsx @@ -1,13 +1,13 @@ -import AdminCTA from '@/admin/AdminCTA'; import Container from '@/components/Container'; import SiteGrid from '@/components/SiteGrid'; import { IS_SITE_READY, PRESERVE_ORIGINAL_UPLOADS } from '@/app/config'; -import { PATH_ADMIN_CONFIGURATION } from '@/app/paths'; import AdminAppConfiguration from '@/admin/AdminAppConfiguration'; import { clsx } from 'clsx/lite'; -import Link from 'next/link'; import { HiOutlinePhotograph } from 'react-icons/hi'; import { revalidatePath } from 'next/cache'; +import SignInOrUploadClient from '@/admin/SignInOrUploadClient'; +import Link from 'next/link'; +import { PATH_ADMIN_CONFIGURATION } from '@/app/paths'; export default function PhotosEmptyState() { return ( @@ -30,19 +30,14 @@ export default function PhotosEmptyState() { {!IS_SITE_READY ? :
-
-
- Add your first photo: -
- { - 'use server'; - // Update upload count in admin nav - revalidatePath('/admin', 'layout'); - }} - /> -
+ { + 'use server'; + // Update upload count in admin nav + revalidatePath('/admin', 'layout'); + }} + />
Change the name of this blog and other configuration by editing environment variables referenced in diff --git a/src/state/AppState.ts b/src/state/AppState.ts index 65ecb848..47578319 100644 --- a/src/state/AppState.ts +++ b/src/state/AppState.ts @@ -44,6 +44,7 @@ export type AppStateContext = { isUserSignedInEager?: boolean clearAuthStateAndRedirect?: () => void // ADMIN + isCheckingAuth?: boolean adminUpdateTimes?: Date[] registerAdminUpdate?: () => void refreshAdminData?: () => void diff --git a/src/state/AppStateProvider.tsx b/src/state/AppStateProvider.tsx index f17b7b16..5dae4a2f 100644 --- a/src/state/AppStateProvider.tsx +++ b/src/state/AppStateProvider.tsx @@ -91,7 +91,11 @@ export default function AppStateProvider({ const invalidateSwr = useCallback(() => setSwrTimestamp(Date.now()), []); - const { data: auth, error: authError } = useSWR('getAuth', getAuthAction); + const { + data: auth, + error: authError, + isLoading: isCheckingAuth, + } = useSWR('getAuth', getAuthAction); useEffect(() => { setIsUserSignedInEager(hasAuthEmailCookie()); if (!authError) { @@ -176,6 +180,7 @@ export default function AppStateProvider({ recipeModalProps, setRecipeModalProps, // AUTH + isCheckingAuth, userEmail, setUserEmail, isUserSignedIn,