[web] auth: use toast component for one click sign up popup (#1393)

This commit is contained in:
Nitesh Seram 2025-04-21 11:40:41 +05:30 committed by GitHub
parent b597fff269
commit 47f4c6edb8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 67 additions and 42 deletions

View File

@ -1,10 +1,10 @@
'use client';
import clsx from 'clsx';
import { motion } from 'framer-motion';
import { useEffect, useState } from 'react';
import { useCallback, useEffect, useRef } from 'react';
import { useSessionStorage } from 'usehooks-ts';
import { useToast } from '~/components/global/toasts/useToast';
import { useI18nPathname } from '~/next-i18nostic/src';
import AuthOneClickSignupCard from './AuthOneClickSignupCard';
@ -12,55 +12,61 @@ import AuthOneClickSignupCard from './AuthOneClickSignupCard';
import { useSessionContext } from '@supabase/auth-helpers-react';
const POPUP_DURATION = 15_000;
const TOAST_DURATION = 60 * 60 * 1000; // 1 hour
export default function AuthOneClickSignup() {
const { pathname } = useI18nPathname();
const lastToastId = useRef<string | null>(null);
const { showToast, dismissToast } = useToast();
const { isLoading: isUserLoading, session } = useSessionContext();
const [isVisible, setIsVisible] = useState(false);
const [dismissedSignUpPrompt, setDismissedSignUpPrompt] =
useSessionStorage<boolean>('gfe:auth:sign-up-prompt', false);
// Don't show it on homepage
const isHomepage = pathname === '/' || pathname === '/projects';
const handleClose = useCallback(() => {
setDismissedSignUpPrompt(true);
}, [setDismissedSignUpPrompt]);
useEffect(() => {
if (session || isUserLoading || dismissedSignUpPrompt || isHomepage) {
if (lastToastId.current) {
dismissToast(lastToastId.current);
}
return;
}
// Show popup after 15 seconds
const timer = setTimeout(() => {
setIsVisible(true);
const { id } = showToast({
animateFrom: 'bottom',
customComponent: () => <AuthOneClickSignupCard onClose={handleClose} />,
duration: TOAST_DURATION,
side: 'end',
variant: 'custom',
});
lastToastId.current = id;
}, POPUP_DURATION);
return () => {
setIsVisible(false);
if (lastToastId.current) {
dismissToast(lastToastId.current);
}
clearTimeout(timer);
};
}, [session, isUserLoading, dismissedSignUpPrompt, isHomepage]);
}, [
session,
isUserLoading,
dismissedSignUpPrompt,
isHomepage,
showToast,
handleClose,
dismissToast,
]);
if (!isVisible || session || isUserLoading || isHomepage) {
return null;
}
function handleClose() {
setIsVisible(false);
setDismissedSignUpPrompt(true);
}
return (
<motion.div
animate={{ opacity: 1, y: 0 }}
className={clsx(
'fixed bottom-6 right-6',
'z-popover shadow-lg',
'hidden sm:block',
)}
exit={{ opacity: 0, y: 100 }}
initial={{ opacity: 0, y: 100 }}
transition={{ duration: 0.3, ease: 'easeOut' }}>
<AuthOneClickSignupCard onClose={handleClose} />
</motion.div>
);
return null;
}

View File

@ -65,11 +65,11 @@ export default function AuthOneClickSignupCard({ onClose }: Props) {
})}
</Text>
<Button
className="group transition-all"
className="group/button transition-all"
icon={RiCloseLine}
iconClassName={clsx(
themeTextInvertColor,
'group-hover:text-neutral-900 dark:group-hover:text-white',
'group-hover/button:text-neutral-900 dark:group-hover/button:text-white',
)}
isLabelHidden={true}
label="Close"
@ -105,11 +105,11 @@ export default function AuthOneClickSignupCard({ onClose }: Props) {
onClick={() => signInWithProvider('google')}
/>
<Button
className="group transition-all"
className="group/button transition-all"
icon={RiGithubFill}
iconClassName={clsx(
themeTextInvertColor,
'group-hover:text-neutral-900 dark:group-hover:text-white',
'group-hover/button:text-neutral-900 dark:group-hover/button:text-white',
)}
isLabelHidden={true}
isLoading={loading}
@ -129,7 +129,7 @@ export default function AuthOneClickSignupCard({ onClose }: Props) {
})}
</Text>
<Button
className="group transition-all"
className="group/button transition-all"
href={signInUpHref({
query: {
view: 'email',
@ -138,7 +138,7 @@ export default function AuthOneClickSignupCard({ onClose }: Props) {
icon={RiMailLine}
iconClassName={clsx(
themeTextInvertColor,
'group-hover:text-neutral-900 dark:group-hover:text-white',
'group-hover/button:text-neutral-900 dark:group-hover/button:text-white',
)}
isDisabled={loading}
isLabelHidden={true}

View File

@ -194,10 +194,14 @@ export const ToastImpl = React.forwardRef<
'data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)]',
'data-[swipe=move]:transition-none',
'data-[state=open]:animate-in',
'data-[state=open]:sm:slide-in-from-left-full',
props.animateFrom === 'left'
? 'data-[state=open]:sm:slide-in-from-left-full'
: 'data-[state=open]:sm:slide-in-from-bottom-full',
'data-[state=closed]:animate-out',
'data-[state=closed]:fade-out-80',
'data-[state=closed]:slide-out-to-left-full',
props.animateFrom === 'left'
? 'data-[state=closed]:slide-out-to-left-full'
: 'data-[state=closed]:slide-out-to-bottom-full',
'flex',
props.side === 'start' && 'self-start',
props.side === 'end' && 'self-end justify-end',
@ -207,7 +211,7 @@ export const ToastImpl = React.forwardRef<
const { customComponent: Component, ...remainingProps } = props;
return (
<li ref={ref} className={commonClass} {...remainingProps}>
<li ref={ref} {...remainingProps} className={commonClass}>
<Component />
</li>
);
@ -312,7 +316,7 @@ type BaseToastProps = Omit<
React.ComponentPropsWithoutRef<typeof ToastPrimitive.Root>,
'children' | 'title'
> &
Readonly<{ side?: 'end' | 'start' }>;
Readonly<{ animateFrom?: 'bottom' | 'left'; side?: 'end' | 'start' }>;
export type DefaultToastProps = BaseToastProps & DefaultProps;
@ -325,11 +329,24 @@ const Toast = React.forwardRef<
ToastProps
>((props, ref) => {
if (props.variant === 'custom') {
const { customComponent, variant, ...remainingProps } = props;
const {
customComponent,
variant,
className,
side,
animateFrom = 'left',
...remainingProps
} = props;
return (
<ToastPrimitive.Root ref={ref} asChild={true} {...remainingProps}>
<ToastImpl customComponent={customComponent} variant={variant} />
<ToastImpl
animateFrom={animateFrom}
className={className}
customComponent={customComponent}
side={side}
variant={variant}
/>
</ToastPrimitive.Root>
);
}
@ -345,6 +362,7 @@ const Toast = React.forwardRef<
variant,
onClose,
side = 'start',
animateFrom = 'left',
...remainingProps
} = props;
@ -353,6 +371,7 @@ const Toast = React.forwardRef<
<ToastImpl
addOnIcon={addOnIcon}
addOnLabel={addOnLabel}
animateFrom={animateFrom}
className={className}
description={description}
icon={icon}