add Implement custom sign-out functionality with loading state and error handling
```
This commit is contained in:
2025-05-16 13:26:17 +08:00
parent 57e25ef65f
commit 3556e77a7c
3 changed files with 26 additions and 14 deletions

View File

@@ -2,7 +2,11 @@
import * as React from 'react'; import * as React from 'react';
import { useRouter } from 'next/navigation'; import { useRouter } from 'next/navigation';
import { LoadingButton } from '@mui/lab';
import { Button } from '@mui/material';
import MenuItem from '@mui/material/MenuItem'; import MenuItem from '@mui/material/MenuItem';
import { SignOut as SignOutIcon } from '@phosphor-icons/react/dist/ssr/SignOut';
import { useTranslation } from 'react-i18next';
import { authClient } from '@/lib/auth/custom/client'; import { authClient } from '@/lib/auth/custom/client';
import { logger } from '@/lib/default-logger'; import { logger } from '@/lib/default-logger';
@@ -10,39 +14,44 @@ import { useUser } from '@/hooks/use-user';
import { toast } from '@/components/core/toaster'; import { toast } from '@/components/core/toaster';
export function CustomSignOut(): React.JSX.Element { export function CustomSignOut(): React.JSX.Element {
const { t } = useTranslation('sign_in');
const { checkSession } = useUser(); const { checkSession } = useUser();
const [buttonShowLoading, setButtonShowLoading] = React.useState<boolean>(false);
const router = useRouter(); const router = useRouter();
const handleSignOut = React.useCallback(async (): Promise<void> => { const handleSignOut = React.useCallback(async (): Promise<void> => {
setButtonShowLoading(true);
try { try {
const { error } = await authClient.signOut(); const { error } = await authClient.signOut();
if (error) { if (error) {
logger.error('Sign out error', error); logger.error('Sign out error', error);
toast.error('Something went wrong, unable to sign out'); toast.error(t('something-went-wrong-unable-to-sign-out'));
return; return;
} }
// Refresh the auth state // Refresh the auth state
await checkSession?.(); await checkSession?.();
// UserProvider, for this case, will not refresh the router and we need to do it manually // UserProvider, for this case, will not refresh the router and we need to do it manually
router.refresh(); router.refresh();
// After refresh, AuthGuard will handle the redirect // After refresh, AuthGuard will handle the redirect
} catch (err) { } catch (err) {
logger.error('Sign out error', err); logger.error('Sign out error', err);
toast.error('Something went wrong, unable to sign out'); toast.error(t('something-went-wrong-unable-to-sign-out'));
} }
}, [checkSession, router]); }, [checkSession, router, t]);
return ( return (
<MenuItem <LoadingButton
component="div"
onClick={handleSignOut} onClick={handleSignOut}
sx={{ justifyContent: 'center' }} sx={{ width: '100%' }}
variant="text"
disabled={buttonShowLoading}
loading={buttonShowLoading}
startIcon={<SignOutIcon />}
color="secondary"
> >
Sign out {t('sign-out')}
</MenuItem> </LoadingButton>
); );
} }

View File

@@ -16,15 +16,15 @@ import { User as UserIcon } from '@phosphor-icons/react/dist/ssr/User';
import type { User } from '@/types/user'; import type { User } from '@/types/user';
import { config } from '@/config'; import { config } from '@/config';
import { paths } from '@/paths'; import { paths } from '@/paths';
import { authClient } from '@/lib/auth/custom/client';
import { AuthStrategy } from '@/lib/auth/strategy'; import { AuthStrategy } from '@/lib/auth/strategy';
import { logger } from '@/lib/default-logger';
import { Auth0SignOut } from './auth0-sign-out'; import { Auth0SignOut } from './auth0-sign-out';
import { CognitoSignOut } from './cognito-sign-out'; import { CognitoSignOut } from './cognito-sign-out';
import { CustomSignOut } from './custom-sign-out'; import { CustomSignOut } from './custom-sign-out';
import { FirebaseSignOut } from './firebase-sign-out'; import { FirebaseSignOut } from './firebase-sign-out';
import { SupabaseSignOut } from './supabase-sign-out'; import { SupabaseSignOut } from './supabase-sign-out';
import { authClient } from '@/lib/auth/custom/client';
import { logger } from '@/lib/default-logger';
const defaultUser = { const defaultUser = {
id: 'USR-000', id: 'USR-000',
@@ -55,7 +55,8 @@ export function UserPopover({ anchorEl, onClose, open }: UserPopoverProps): Reac
void loadUserMeta(); void loadUserMeta();
}, []); }, []);
if (!userMeta) return <>loading</>; // NOTE: delay when userMeta is null, used for sign-out
if (!userMeta) return <></>;
return ( return (
<Popover <Popover

View File

@@ -66,6 +66,7 @@ export function SideNav({ color = 'evident', items = [] }: SideNavProps): React.
spacing={2} spacing={2}
sx={{ p: 2 }} sx={{ p: 2 }}
> >
{/* NOTE: hide logo
<div> <div>
<Box <Box
component={RouterLink} component={RouterLink}
@@ -79,6 +80,7 @@ export function SideNav({ color = 'evident', items = [] }: SideNavProps): React.
/> />
</Box> </Box>
</div> </div>
*/}
<WorkspacesSwitch /> <WorkspacesSwitch />
</Stack> </Stack>
<Box <Box