Improve auth ux

This commit is contained in:
Sam Becker 2024-05-08 23:57:01 -05:00
parent 760f63f570
commit da86fc601a
6 changed files with 40 additions and 15 deletions

View File

@ -4,12 +4,14 @@ import FieldSetWithStatus from '@/components/FieldSetWithStatus';
import InfoBlock from '@/components/InfoBlock';
import SubmitButtonWithStatus from '@/components/SubmitButtonWithStatus';
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
import { getCurrentUser, signInAction } from './actions';
import { getAuthAction, signInAction } from './actions';
import { useFormState } from 'react-dom';
import ErrorNote from '@/components/ErrorNote';
import { KEY_CALLBACK_URL, KEY_CREDENTIALS_SIGN_IN_ERROR } from '.';
import { useSearchParams } from 'next/navigation';
import { useAppState } from '@/state/AppState';
import { clsx } from 'clsx/lite';
import { FiLock } from 'react-icons/fi';
export default function SignInForm() {
const params = useSearchParams();
@ -28,7 +30,8 @@ export default function SignInForm() {
useEffect(() => {
return () => {
// Capture user email before unmounting
getCurrentUser().then(user => setUserEmail?.(user?.email ?? undefined));
getAuthAction().then(auth =>
setUserEmail?.(auth?.user?.email ?? undefined));
};
}, [setUserEmail]);
@ -37,14 +40,29 @@ export default function SignInForm() {
password.length > 0;
return (
<InfoBlock>
<form action={action}>
<div className="space-y-8">
<InfoBlock className={clsx(
'w-[calc(100vw-1.5rem)] sm:w-[min(360px,90vw)]',
'px-6 py-5',
)}>
<h1 className={clsx(
'flex gap-3 items-center justify-center',
'self-start text-2xl mb-3.5',
)}>
<FiLock className="text-main translate-y-[0.5px]" />
<span className="text-main">
Sign in
</span>
</h1>
<form
action={action}
className="w-full"
>
<div className="space-y-6 w-full -translate-y-0.5">
{response === KEY_CREDENTIALS_SIGN_IN_ERROR &&
<ErrorNote>
Invalid email/password
</ErrorNote>}
<div className="space-y-4">
<div className="space-y-4 w-full">
<FieldSetWithStatus
id="email"
inputRef={emailRef}

View File

@ -39,4 +39,4 @@ export const signInAction = async (
export const signOutAndRedirectAction = async () =>
signOut({ redirectTo: PATH_ROOT });
export const getCurrentUser = async () => (await auth())?.user;
export const getAuthAction = () => auth();

View File

@ -140,7 +140,11 @@ export default function FieldSetWithStatus({
autoCapitalize={!capitalize ? 'off' : undefined}
readOnly={readOnly || pending || loading}
className={clsx(
type === 'text' && 'w-full',
(
type === 'text' ||
type === 'email' ||
type === 'password'
) && 'w-full',
Boolean(error) && 'error',
)}
/>}

View File

@ -26,9 +26,12 @@ export default function LoaderButton(props: {
type={type}
className={clsx(
className,
styleAsLink
? 'link h-4 hover:text-dim active:text-medium'
: 'h-9',
...(styleAsLink
? [
'link h-4 hover:text-dim active:text-medium',
'disabled:!bg-transparent',
]
: ['h-9']),
'inline-flex items-center gap-2 self-start',
)}
disabled={isLoading || disabled}

View File

@ -41,7 +41,7 @@ export default function Footer() {
{isPathAdmin(pathname)
? <>
{userEmail === undefined &&
<Spinner />}
<Spinner size={14} className="translate-y-[2px]" />}
{userEmail && <>
<div className={clsx(
'truncate max-w-full',

View File

@ -4,7 +4,7 @@ import { useState, useEffect, ReactNode, useCallback } from 'react';
import { AppStateContext } from './AppState';
import { AnimationConfig } from '@/components/AnimateItems';
import usePathnames from '@/utility/usePathnames';
import { getCurrentUser } from '@/auth/actions';
import { getAuthAction } from '@/auth/actions';
import useSWR from 'swr';
export default function AppStateProvider({
@ -34,8 +34,8 @@ export default function AppStateProvider({
const invalidateSwr = useCallback(() => setSwrTimestamp(Date.now()), []);
const { data } = useSWR('getCurrentUser', getCurrentUser);
useEffect(() => setUserEmail(data?.email ?? undefined), [data]);
const { data } = useSWR('getAuth', getAuthAction);
useEffect(() => setUserEmail(data?.user?.email ?? undefined), [data]);
const registerAdminUpdate = useCallback(() =>
setAdminUpdateTimes(updates => [...updates, new Date()])