Skip to content

Commit

Permalink
Merge pull request #111 from hanzoai/auth
Browse files Browse the repository at this point in the history
share auth info
  • Loading branch information
artemis-prime committed Jul 12, 2024
2 parents 2ce6c04 + a374eaa commit 177ed9a
Show file tree
Hide file tree
Showing 8 changed files with 380 additions and 77 deletions.
6 changes: 4 additions & 2 deletions packages/auth/components/email-password-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ const EmailPasswordForm: React.FC<{
isLoading: boolean
className?: string
inputClx?: string
prompt?: string
}> = ({
onSubmit,
isLoading,
className,
inputClx
inputClx,
prompt='Login'
}) => {
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
Expand Down Expand Up @@ -82,7 +84,7 @@ const EmailPasswordForm: React.FC<{
</FormItem>
)}
/>
<Button type='submit' className='w-full' disabled={isLoading}>Login</Button>
<Button type='submit' className='w-full' disabled={isLoading}>{prompt}</Button>
</form>
</Form>
)
Expand Down
3 changes: 2 additions & 1 deletion packages/auth/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export { default as LoginPanel } from './login-panel'
export { default as EmailPasswordForm } from './email-password-form'
export { default as AuthWidget } from './auth-widget'
export { default as AuthWidget } from './auth-widget'
export { default as SignupPanel } from './signup-panel'
73 changes: 48 additions & 25 deletions packages/auth/components/login-panel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useRouter } from 'next/navigation'
import { observer } from 'mobx-react-lite'
import Link from 'next/link'

import { ApplyTypography, Button, Separator, toast } from '@hanzo/ui/primitives'
import { ApplyTypography, Button, Separator, toast, Toaster } from '@hanzo/ui/primitives'
import { cn } from '@hanzo/ui/util'

import { useAuth, type AuthProvider } from '../service'
Expand All @@ -23,18 +23,18 @@ const ProviderLoginButton: React.FC<PropsWithChildren & {
isLoading,
children
}) => {
return (
<Button
onClick={() => loginWithProvider(provider)}
className='w-full mx-auto flex items-center gap-2'
disabled={isLoading}
variant='outline'
>
{children}
</Button>
)
}

return (
<Button
onClick={() => {loginWithProvider(provider)}}
className='w-full mx-auto flex items-center gap-2'
disabled={isLoading}
variant='outline'
>
{children}
</Button>
)
}

const LoginPanel: React.FC<PropsWithChildren & {
redirectUrl?: string,
Expand All @@ -45,6 +45,7 @@ const LoginPanel: React.FC<PropsWithChildren & {
onLoginChanged?: (token: string) => void
termsOfServiceUrl?: string
privacyPolicyUrl?: string
setIsLogin?: React.Dispatch<React.SetStateAction<boolean>>
}> = observer(({
children,
redirectUrl,
Expand All @@ -54,14 +55,15 @@ const LoginPanel: React.FC<PropsWithChildren & {
noHeading,
onLoginChanged,
termsOfServiceUrl,
privacyPolicyUrl
privacyPolicyUrl,
setIsLogin
}) => {

const router = useRouter()
const auth = useAuth()
const [isLoading, setIsLoading] = useState(false)

const succeed = async (loginMethod: AuthProvider | 'email' | null ) => {
const succeed = async (loginMethod: AuthProvider | 'email' | null) => {

if (loginMethod) {
sendGAEvent('login', { method: loginMethod })
Expand All @@ -86,8 +88,13 @@ const LoginPanel: React.FC<PropsWithChildren & {
setIsLoading(true)
try {
const res = await auth.loginEmailAndPassword(email, password)
if (res.success) { succeed('email') }
}
if (res.success) {
succeed('email'); toast.success(res.message)
}
else {
toast.error(res.message)
}
}
catch (e) {
toast('User with this email already signed up using a different provider')
}
Expand All @@ -113,17 +120,27 @@ const LoginPanel: React.FC<PropsWithChildren & {
const loginWithProvider = async (provider: AuthProvider) => {
setIsLoading(true)
const res = await auth.loginWithProvider(provider)
if (res.success) { succeed(provider) }
if (res.success) {
succeed(provider)
}
setIsLoading(false)
}

const logout = async () => {
setIsLoading(true)
const res = await auth.logout()
if (res.success) { succeed(null) }
if (res.success) {
succeed(null)
}
setIsLoading(false)
}

const handleOnClick = () => {
if (setIsLogin) {
setIsLogin(false)
}
}

return (
<ApplyTypography className={cn('w-full flex flex-col text-center !gap-3 LOGIN_OUTER', className)}>
{auth.loggedIn && !redirectUrl ? (
Expand All @@ -133,7 +150,7 @@ const LoginPanel: React.FC<PropsWithChildren & {
<p>You are signed in as {auth.user?.displayName ?? auth.user?.email}</p>
<div className='flex flex-col md:flex-row gap-3 items-center justify-center'>
{getStartedUrl && <Button variant='primary' onClick={() => router.push(getStartedUrl)} className='w-full'>GET STARTED</Button>}
<Button onClick={() => logout()} variant='outline' disabled={isLoading} className='w-full'>Sign Out</Button>
<Button onClick={logout} variant='outline' disabled={isLoading} className='w-full'>Sign Out</Button>
</div>
</>)}
</>
Expand All @@ -143,25 +160,31 @@ const LoginPanel: React.FC<PropsWithChildren & {
<h4 className='text-center'>Login</h4>
)}
{children}
<EmailPasswordForm onSubmit={loginWithEmailPassword} isLoading={isLoading} className='mb-4' inputClx={inputClx}/>
<EmailPasswordForm onSubmit={loginWithEmailPassword} isLoading={isLoading} className='mb-4' inputClx={inputClx} />

<div className='flex flex-row gap-4 justify-center'>
<div className='text-muted-2 text-sm'>Don't have an account?</div>
<button className='bg-transparent text-foreground text-sm' onClick={handleOnClick}>Sign Up</button>
</div>

<div className='flex gap-2 whitespace-nowrap items-center my-1 sm:my-3 text-xs text-muted'>
<Separator className='grow w-auto'/><div className='shrink-0 mx-1'>or continue with</div><Separator className='grow w-auto'/>
<Separator className='grow w-auto' /><div className='shrink-0 mx-1'>or continue with</div><Separator className='grow w-auto' />
</div>

{/* <Button onClick={loginWithEthereum} className='w-full mx-auto flex items-center gap-2' disabled={isLoading}>
<Ethereum height={20}/>Login with your wallet
</Button> */}
<ProviderLoginButton provider='google' isLoading={isLoading} loginWithProvider={loginWithProvider}>
<Google height={20}/>Google
<Google height={20} />Google
</ProviderLoginButton>
<ProviderLoginButton provider='facebook' isLoading={isLoading} loginWithProvider={loginWithProvider}>
<Facebook height={20}/>Facebook
<Facebook height={20} />Facebook
</ProviderLoginButton>
<ProviderLoginButton provider='github' isLoading={isLoading} loginWithProvider={loginWithProvider}>
<GitHub height={20}/>Github
<GitHub height={20} />Github
</ProviderLoginButton>
<p className='text-sm text-muted-2'>By logging in, you agree to our <Link href={termsOfServiceUrl ?? ''} target='_blank'>Terms of Service</Link> and <Link href={privacyPolicyUrl ?? ''} target='_blank'>Privacy Policy</Link>.</p>
<Toaster></Toaster>
</>
)}
</ApplyTypography>
Expand Down
200 changes: 200 additions & 0 deletions packages/auth/components/signup-panel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
'use client'
import { useState, type PropsWithChildren } from 'react'
import { useRouter } from 'next/navigation'
import { observer } from 'mobx-react-lite'
import Link from 'next/link'

import { ApplyTypography, Button, Separator, toast, Toaster } from '@hanzo/ui/primitives'
import { cn } from '@hanzo/ui/util'

import { useAuth, type AuthProvider } from '../service'
import { Facebook, Google, GitHub } from '../icons'
import EmailPasswordForm from './email-password-form'
import { sendGAEvent } from '../util/analytics'


const ProviderLoginButton: React.FC<PropsWithChildren & {
provider: AuthProvider,
loginWithProvider: (provider: AuthProvider) => Promise<void>,
isLoading: boolean
}> = ({
provider,
loginWithProvider,
isLoading,
children
}) => {

return (
<Button
onClick={() => loginWithProvider(provider)}
className='w-full mx-auto flex items-center gap-2'
disabled={isLoading}
variant='outline'
>
{children}
</Button>
)
}

const SignupPanel: React.FC<PropsWithChildren & {
redirectUrl?: string,
getStartedUrl?: string,
className?: string,
inputClx?: string,
noHeading?: boolean
onLoginChanged?: (token: string) => void
termsOfServiceUrl?: string
privacyPolicyUrl?: string
setIsLogin?: React.Dispatch<React.SetStateAction<boolean>>
}> = observer(({
children,
redirectUrl,
getStartedUrl,
className,
inputClx,
noHeading,
onLoginChanged,
termsOfServiceUrl,
privacyPolicyUrl,
setIsLogin
}) => {

const router = useRouter()
const auth = useAuth()
const [isLoading, setIsLoading] = useState(false)

const succeed = async (loginMethod: AuthProvider | 'email' | null) => {

if (loginMethod) {
sendGAEvent('login', { method: loginMethod })
}

// If a callback is provided, don't redirect.
// Assume host code is handling (eg, mobile menu)
if (onLoginChanged) {
const res = await fetch(
'/api/auth/generate-custom-token',
{ method: 'POST' }
).then(res => res.json())
onLoginChanged(res.token?.token ?? null)
}
else if (redirectUrl) {
// TODO :aa shouldn't the token thing happen in this case too??
router.push(redirectUrl)
}
}

const signupWithEmailPassword = async (email: string, password: string) => {
setIsLoading(true)
try {
const res = await auth.signupEmailAndPassword(email, password)
if (res.success) {
succeed('email')
toast.success(res.message)
}
else {
toast.error(res.message)
}
}
catch (e) {
toast.success('User with this email already signed up')
}
setIsLoading(false)
}

// const loginWithEthereum = async () => {
// setIsLoading(true)
// try {
// const res = await signInWithEthereum()
// if (res.success && res.user) {
// setUser({email: res.user?.email, displayName: res.user?.displayName, walletAddress: res.user?.walletAddress})
// if (redirectUrl) {
// router.push(redirectUrl)
// }
// }
// } catch (e) {
// toast({title: 'No Ethereum provider found'})
// }
// setIsLoading(false)
// }

const loginWithProvider = async (provider: AuthProvider) => {
setIsLoading(true)
const res = await auth.loginWithProvider(provider)
if (res.success) {
succeed(provider)
}
setIsLoading(false)
}

const logout = async () => {
setIsLoading(true)
const res = await auth.logout()
if (res.success) {
succeed(null)
}
setIsLoading(false)
}

const handleOnClick = () => {
if (setIsLogin) {
setIsLogin(true)
}
}

return (
<ApplyTypography className={cn('w-full flex flex-col text-center !gap-3', className)}>
{auth.loggedIn && !redirectUrl ? (
<>
<h4>Welcome!</h4>
{auth.user && (<> {/* this means the hanzo user isn't loaded yet ...*/}
<p>You are signed in as {auth.user?.displayName ?? auth.user?.email}</p>
<div className='flex flex-col md:flex-row gap-3 items-center justify-center'>
{getStartedUrl && <Button variant='primary' onClick={() => {router.push(getStartedUrl)}} className='w-full'>GET STARTED</Button>}
<Button onClick={logout} variant='outline' disabled={isLoading} className='w-full'>Sign Out</Button>
</div>
</>)}
</>
) : (
<>
{!noHeading && (
<h4 className='text-center'>Sign Up</h4>
)}
{children}
<EmailPasswordForm
onSubmit={signupWithEmailPassword}
isLoading={isLoading}
className='mb-4'
inputClx={inputClx}
prompt='SignUp'
/>
<div className='flex flex-row gap-4 justify-center'>
<div className='text-muted-2 text-sm'>Already have an account?</div>
<button className='bg-transparent text-foreground text-sm' onClick={handleOnClick}>Log In</button>
</div>

<div className='flex gap-2 whitespace-nowrap items-center my-1 sm:my-3 text-xs text-muted'>
<Separator className='grow w-auto' /><div className='shrink-0 mx-1'>or continue with</div><Separator className='grow w-auto' />
</div>

{/* <Button onClick={loginWithEthereum} className='w-full mx-auto flex items-center gap-2' disabled={isLoading}>
<Ethereum height={20}/>Login with your wallet
</Button> */}
<ProviderLoginButton provider='google' isLoading={isLoading} loginWithProvider={loginWithProvider}>
<Google height={20} />Google
</ProviderLoginButton>
<ProviderLoginButton provider='facebook' isLoading={isLoading} loginWithProvider={loginWithProvider}>
<Facebook height={20} />Facebook
</ProviderLoginButton>
<ProviderLoginButton provider='github' isLoading={isLoading} loginWithProvider={loginWithProvider}>
<GitHub height={20} />Github
</ProviderLoginButton>
<p className='text-sm text-muted-2'>By logging in, you agree to our <Link href={termsOfServiceUrl ?? ''} target='_blank'>Terms of Service</Link> and <Link href={privacyPolicyUrl ?? ''} target='_blank'>Privacy Policy</Link>.</p>
<Toaster />
</>
)}
</ApplyTypography>
)
})

export default SignupPanel
2 changes: 1 addition & 1 deletion packages/auth/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@hanzo/auth",
"version": "2.4.12",
"version": "2.4.14",
"description": "Library with Firebase authentication.",
"publishConfig": {
"registry": "https://registry.npmjs.org/",
Expand Down
Loading

0 comments on commit 177ed9a

Please sign in to comment.