Skip to content

Commit

Permalink
Episodio 1
Browse files Browse the repository at this point in the history
  • Loading branch information
durancristhian committed Aug 22, 2020
1 parent 88ae575 commit 4497690
Show file tree
Hide file tree
Showing 9 changed files with 342 additions and 8 deletions.
76 changes: 76 additions & 0 deletions components/Layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import Link from 'next/link'
import React, { ReactNode } from 'react'
import A from '../ui/A'
import { useAuth } from '../hooks/useAuth'

type Props = {
children: ReactNode
}

const Layout = ({ children }: Props) => {
const { user } = useAuth()

return (
<div className="flex flex-col min-h-screen">
<header className="p-4 shadow">
<div className="flex items-center justify-between">
<Link href="/" passHref>
<A href="#!">
<img src="logo.png" alt="Cremona" className="h-12" />
</A>
</Link>
<div className="flex">{user ? <LoggedOut /> : <LoggedIn />}</div>
</div>
</header>
<main className="flex-1 p-4">{children}</main>
<footer className="p-4">
<p className="text-center">
<span>Created by </span>
<Link href="/signin" passHref>
<A
href="http://twitter.com/durancristhian"
target="_blank"
rel="noopener noreferrer"
>
@durancristhian
</A>
</Link>
</p>
</footer>
</div>
)
}

export default Layout

function LoggedIn() {
return (
<>
<Link href="/signin" passHref>
<A href="#!">Sign in</A>
</Link>
<div className="ml-4">
<Link href="/signup" passHref>
<A href="#!">Sign up</A>
</Link>
</div>
</>
)
}

function LoggedOut() {
const { signout } = useAuth()

return (
<>
<button
onClick={() => {
signout()
}}
className="text-blue-700"
>
Sing out
</button>
</>
)
}
105 changes: 105 additions & 0 deletions components/Login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
import { useRouter } from 'next/router'
import React, { FormEvent, useState } from 'react'
import { useAuth } from '../hooks/useAuth'

type LoginMode = 'signin' | 'signup'

type Props = {
mode: LoginMode
}

const Login = ({ mode }: Props) => {
const router = useRouter()
const [formData, setFormData] = useState({
email: '',
password: '',
})
const [inProgress, setInProgress] = useState(false)
const [message, setMessage] = useState('')
const { signin, signup } = useAuth()

const onSubmit = (event: FormEvent) => {
event.preventDefault()

setInProgress(true)
setMessage('Processing...')

const method = mode === 'signin' ? signin : signup

method(formData.email, formData.password)
.then(() => {
setInProgress(false)
setMessage('')

router.push('/')
})
.catch((error: Error) => {
console.error(error)

setInProgress(false)
setMessage(error.message)
})
}

return (
<>
<div className="mb-4">
<h1>{mode === 'signin' ? 'Sign In' : 'Sign Up'}</h1>
</div>
<form onSubmit={onSubmit}>
<fieldset disabled={inProgress}>
<div className="mb-4">
<input
type="email"
name="email"
id="email"
value={formData.email}
onChange={({ target }) => {
setFormData({
...formData,
email: target.value,
})
}}
placeholder="Email"
autoComplete="email"
className="block px-4 py-2 shadow w-full"
/>
</div>
<div className="mb-4">
<input
type="password"
name="password"
id="password"
value={formData.password}
onChange={({ target }) => {
setFormData({
...formData,
password: target.value,
})
}}
placeholder="Password"
autoComplete="current-password"
className="block px-4 py-2 shadow w-full"
/>
</div>
<div className="text-center">
<button
type="submit"
className="border bg-blue-200 border-blue-500 px-4 py-2 disabled:opacity-50"
disabled={!formData.email || !formData.password}
>
{mode === 'signin' ? 'Sign In' : 'Sign Up'}
</button>
</div>
{message && (
<div className="mt-4 text-center">
<p className="italic">{message}</p>
</div>
)}
</fieldset>
</form>
</>
)
}

export default Login
86 changes: 86 additions & 0 deletions hooks/useAuth.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { fuego } from '@nandorojo/swr-firestore'
import React, {
createContext,
ReactNode,
useContext,
useEffect,
useState,
} from 'react'

type ContextData = {
user: firebase.User | null
signin: (email: string, password: string) => Promise<firebase.User | null>
signup: (email: string, password: string) => Promise<firebase.User | null>
signout: () => Promise<void>
}

const defaultContextData = {
user: null,
signin: () => Promise.resolve(null),
signup: () => Promise.resolve(null),
signout: () => Promise.resolve(void 0),
}

const AuthContext = createContext<ContextData>(defaultContextData)

type Props = {
children: ReactNode
}

export function ProvideAuth({ children }: Props) {
const auth = useProvideAuth()

return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>
}

export const useAuth = () => {
return useContext(AuthContext)
}

function useProvideAuth() {
const [user, setUser] = useState<firebase.User | null>(null)

const signin = (email: string, password: string) => {
return fuego
.auth()
.signInWithEmailAndPassword(email, password)
.then((response) => {
setUser(response.user)
return response.user
})
}

const signup = (email: string, password: string) => {
return fuego
.auth()
.createUserWithEmailAndPassword(email, password)
.then((response) => {
setUser(response.user)
return response.user
})
}

const signout = () => {
return fuego
.auth()
.signOut()
.then(() => {
setUser(null)
})
}

useEffect(() => {
const unsubscribe = fuego.auth().onAuthStateChanged((user) => {
setUser(user)
})

return () => unsubscribe()
}, [])

return {
user,
signin,
signup,
signout,
}
}
28 changes: 27 additions & 1 deletion pages/_app.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,35 @@
import { Fuego, FuegoProvider } from '@nandorojo/swr-firestore'
import 'firebase/auth'
import 'firebase/firestore'
import { AppProps } from 'next/app'
import React from 'react'
import Layout from '../components/Layout'
import { ProvideAuth } from '../hooks/useAuth'
import '../styles/globals.css'

const firebaseConfig = {
apiKey: process.env.NEXT_PUBLIC_API_KEY,
authDomain: process.env.NEXT_PUBLIC_AUTH_DOMAIN,
databaseURL: process.env.NEXT_PUBLIC_DATABASE_URL,
projectId: process.env.NEXT_PUBLIC_PROJECT_ID,
storageBucket: process.env.NEXT_PUBLIC_STORAGE_BUCKET,
messagingSenderId: process.env.NEXT_PUBLIC_MESSAGING_SENDER_ID,
appId: process.env.NEXT_PUBLIC_APP_ID,
measurementId: process.env.NEXT_PUBLIC_MEASUREMENT_ID,
}

const fuego = new Fuego(firebaseConfig)

const App = ({ Component, pageProps }: AppProps) => {
return <Component {...pageProps} />
return (
<FuegoProvider fuego={fuego}>
<ProvideAuth>
<Layout>
<Component {...pageProps} />
</Layout>
</ProvideAuth>
</FuegoProvider>
)
}

export default App
2 changes: 1 addition & 1 deletion pages/_document.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class MyDocument extends Document {
<Head>
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
</Head>
<body className="debug">
<body className="bg-orange-100">
<Main />
<NextScript />
</body>
Expand Down
10 changes: 4 additions & 6 deletions pages/index.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import React from 'react'
import { useAuth } from '../hooks/useAuth'

export default function Index() {
return (
<>
<img src="logo.png" alt="Cremona" />
<h1>Cremona</h1>
</>
)
const { user } = useAuth()

return <div className="p-4">{user ? 'Hello' : null}</div>
}
12 changes: 12 additions & 0 deletions pages/signin.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react'
import Login from '../components/Login'

const SignIn = () => {
return (
<>
<Login mode="signin"></Login>
</>
)
}

export default SignIn
12 changes: 12 additions & 0 deletions pages/signup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import React from 'react'
import Login from '../components/Login'

const SignUp = () => {
return (
<>
<Login mode="signup"></Login>
</>
)
}

export default SignUp
19 changes: 19 additions & 0 deletions ui/A.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, { AnchorHTMLAttributes, forwardRef, ReactNode, Ref } from 'react'

type Props = AnchorHTMLAttributes<HTMLAnchorElement> & {
children: ReactNode
}

const A = forwardRef(
({ children, ...rest }: Props, ref: Ref<HTMLAnchorElement>) => {
return (
<a ref={ref} {...rest} className="text-blue-700">
{children}
</a>
)
},
)

A.displayName = 'A'

export default A

1 comment on commit 4497690

@vercel
Copy link

@vercel vercel bot commented on 4497690 Aug 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.