Skip to content
Merged
2 changes: 1 addition & 1 deletion components/EditProfilePage/EditProfilePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import {
} from "./StyledEditProfileComponents"
import { TestimoniesTab } from "./TestimoniesTab"
import { useFlags } from "components/featureFlags"
import LoginPage from "components/Login/login"
import LoginPage from "components/Login/Login"
import { PendingUpgradeBanner } from "components/PendingUpgradeBanner"

const tabTitle = ["about-you", "testimonies", "following"] as const
Expand Down
86 changes: 86 additions & 0 deletions components/Login/Login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { useForm } from "react-hook-form"
import { useRouter } from "next/router"
import {
useSignInWithEmailAndPassword,
SignInWithEmailAndPasswordData
} from "../auth/hooks"
import { Form, Stack, Alert, Container, Row, Col, Card } from "../bootstrap"
import Input from "../forms/Input"
import PasswordInput from "../forms/PasswordInput"
import { useTranslation } from "next-i18next"
import { LoadingButton } from "../buttons"
import SocialSignOnButtons from "components/auth/SocialSignOnButtons"
import Divider from "../Divider/Divider"

export default function Login() {
const router = useRouter()
const redirect = (router.query.redirect as string) || "/"
const { t } = useTranslation("auth")

const {
register,
handleSubmit,
formState: { errors }
} = useForm<SignInWithEmailAndPasswordData>()

const signIn = useSignInWithEmailAndPassword()

const success = () => {
const safeRedirect = redirect.startsWith("/") ? redirect : "/"
router.replace(safeRedirect)
}
const onSubmit = handleSubmit(credentials => {
signIn.execute(credentials).then(success)
})

return (
<Container className="py-5">
<Row className="justify-content-center">
<Col xs={12} sm={10} md={8} lg={6}>
<Card className="p-4 shadow-lg">
<h2 className="text-center">{t("signInToAccess")}</h2>

<Form onSubmit={onSubmit} noValidate>
{signIn.error && (
<Alert variant="danger">{signIn.error.message}</Alert>
)}

<Stack gap={3}>
<Input
label={t("email")}
type="email"
{...register("email", {
required: t("emailIsRequired") ?? "Email is required"
})}
error={errors.email?.message}
/>

<PasswordInput
label={t("password")}
{...register("password", {
required: t("passwordRequired") ?? "Password is required"
})}
error={errors.password?.message}
/>

<LoadingButton
type="submit"
className="w-100"
loading={signIn.loading}
>
{t("signIn")}
</LoadingButton>

<Divider className="px-4">{t("or")}</Divider>

<SocialSignOnButtons
onComplete={() => router.replace(redirect)}
/>
</Stack>
</Form>
</Card>
</Col>
</Row>
</Container>
)
}
50 changes: 0 additions & 50 deletions components/Login/login.tsx

This file was deleted.

2 changes: 1 addition & 1 deletion components/Newsfeed/Newsfeed.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
StyledContainer
} from "./StyledNewsfeedComponents"
import ProfileSettingsModal from "components/EditProfilePage/ProfileSettingsModal"
import LoginPage from "components/Login/login"
import LoginPage from "components/Login/Login"
import { NewsfeedCard } from "components/NewsfeedCard/NewsfeedCard"
import { ProfileButtons } from "components/ProfilePage/ProfileButtons"

Expand Down
21 changes: 21 additions & 0 deletions components/auth/Provider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useEffect } from "react"
import { getAuth, onAuthStateChanged, User } from "firebase/auth"
import { useDispatch } from "react-redux"
import { authChanged } from "./redux"

export const Provider: React.FC<React.PropsWithChildren<unknown>> = ({
children
}) => {
const dispatch = useDispatch()

useEffect(() => {
const auth = getAuth()
const unsubscribe = onAuthStateChanged(auth, (user: User | null) => {
dispatch(authChanged({ user }))
})

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

return <>{children}</>
}
1 change: 1 addition & 0 deletions components/auth/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ export * from "./service"
export { default as SignInWithButton } from "./SignInWithButton"
export { default as SignOut } from "./SignOut"
export * from "./types"
export { Provider } from "./Provider"
5 changes: 4 additions & 1 deletion components/auth/redux.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@ export interface State {
/** True iff user is signed in */
authenticated: boolean
authFlowStep: AuthFlowStep
loading: boolean
}

const initialState: State = {
authenticated: false,
user: undefined,
authFlowStep: null
authFlowStep: null,
loading: true
}

export const {
Expand All @@ -45,6 +47,7 @@ export const {
state.user = payload.user
state.claims = payload.claims
state.authenticated = Boolean(payload.user)
state.loading = false
},
authStepChanged(state, action: PayloadAction<AuthFlowStep>) {
state.authFlowStep = action.payload
Expand Down
20 changes: 15 additions & 5 deletions components/auth/service.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { useAppDispatch } from "../hooks"
import { createService } from "../service"
import { authChanged, useAuth } from "./redux"
import { Claim } from "./types"
import { Row, Spinner } from "../bootstrap"

export const { Provider } = createService(() => {
const dispatch = useAppDispatch()
Expand Down Expand Up @@ -76,13 +77,22 @@ export function requireAuth(
Component: React.FC<React.PropsWithChildren<{ user: User }>>
) {
return function ProtectedRoute() {
const { user } = useAuth()
const { user, loading } = useAuth()
const router = useRouter()
useEffect(() => {
if (user === null) {
router.push({ pathname: "/" })
if (!loading && !user) {
const currentPath = router.asPath
router.replace(`/login?redirect=${encodeURIComponent(currentPath)}`)
}
}, [router, user])
}, [user, loading, router])

if (loading) {
return (
<Row>
<Spinner animation="border" className="mx-auto" />
</Row>
)
}

return user ? <Component user={user} /> : null
}
Expand All @@ -92,6 +102,6 @@ export function requireAuth(
* Redirects user after logging out.
*/
export async function signOutAndRedirectToHome() {
await auth.signOut()
Router.push("/")
await auth.signOut()
}
2 changes: 1 addition & 1 deletion next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
// see https://nextjs.org/docs/basic-features/typescript for more information.
5 changes: 2 additions & 3 deletions pages/edit-profile/[[...docName]].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@ const Query = z.object({

export default createPage({
titleI18nKey: "navigation.editProfile",
Page: () => {
// Page: requireAuth(({ user }) => {
Page: requireAuth(() => {
const tabTitle = Query.parse(useRouter().query).docName?.[0] || "about-you"
return <EditProfile tabTitle={tabTitle as TabTitles} />
}
})
})

export const getStaticPaths: GetStaticPaths = async ctx => {
Expand Down
15 changes: 15 additions & 0 deletions pages/login.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createPage } from "../components/page"
import Login from "components/Login/Login"

export default createPage({
titleI18nKey: "navigation.login",
Page: () => <Login />
})
import { serverSideTranslations } from "next-i18next/serverSideTranslations"
export async function getStaticProps({ locale }: any) {
return {
props: {
...(await serverSideTranslations(locale, ["auth", "common"]))
}
}
}
5 changes: 3 additions & 2 deletions pages/newsfeed.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { createPage } from "../components/page"
import Newsfeed from "components/Newsfeed/Newsfeed"
import { requireAuth } from "../components/auth"

export default createPage({
titleI18nKey: "navigation.newsfeed",
Page: () => {
Page: requireAuth(() => {
return <Newsfeed />
}
})
})

// this must only be on pages in the pages folder
Expand Down
3 changes: 3 additions & 0 deletions public/locales/en/auth.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@
"verifyEmail": "Verify your email address",
"verifyLinkSent": "Please verify your email for your account by clicking the verification link we sent to your email. You will be required to verify your email before submitting testimony.",
"setUpProfile": "Set Up Your Profile",
"probablySignedOut": "You were possibly signed out while trying to go to a page that needs to be signed in to function",
"pleaseConsider": "Please consider logging in first:",
"signInToAccess": "Login required to access the content",
"almostThere": "Almost there!",
"justLogIn": "You’ll be redirected automatically after logging in."
}
3 changes: 2 additions & 1 deletion public/locales/en/common.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,8 @@
"supportMaple": "Support MAPLE",
"aboutTestimony": "About Testimony",
"viewProfile": "View Profile",
"whyUseMaple": "Why Use MAPLE"
"whyUseMaple": "Why Use MAPLE",
"login": "Login"
},
"newcomer": "New to MAPLE? For extra support, check",
"newsletter": "Click Here to Subscribe to Our Newsletter",
Expand Down