Skip to content

Commit a5ff6aa

Browse files
authored
Merge pull request #1858 from HuanFengYeh/1795_auth_redirect_page
1795 auth redirect page
2 parents e425294 + 9aae601 commit a5ff6aa

File tree

13 files changed

+154
-64
lines changed

13 files changed

+154
-64
lines changed

components/EditProfilePage/EditProfilePage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
} from "./StyledEditProfileComponents"
2626
import { TestimoniesTab } from "./TestimoniesTab"
2727
import { useFlags } from "components/featureFlags"
28-
import LoginPage from "components/Login/login"
28+
import LoginPage from "components/Login/Login"
2929
import { PendingUpgradeBanner } from "components/PendingUpgradeBanner"
3030

3131
const tabTitle = ["about-you", "testimonies", "following"] as const

components/Login/Login.tsx

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import { useForm } from "react-hook-form"
2+
import { useRouter } from "next/router"
3+
import {
4+
useSignInWithEmailAndPassword,
5+
SignInWithEmailAndPasswordData
6+
} from "../auth/hooks"
7+
import { Form, Stack, Alert, Container, Row, Col, Card } from "../bootstrap"
8+
import Input from "../forms/Input"
9+
import PasswordInput from "../forms/PasswordInput"
10+
import { useTranslation } from "next-i18next"
11+
import { LoadingButton } from "../buttons"
12+
import SocialSignOnButtons from "components/auth/SocialSignOnButtons"
13+
import Divider from "../Divider/Divider"
14+
15+
export default function Login() {
16+
const router = useRouter()
17+
const redirect = (router.query.redirect as string) || "/"
18+
const { t } = useTranslation("auth")
19+
20+
const {
21+
register,
22+
handleSubmit,
23+
formState: { errors }
24+
} = useForm<SignInWithEmailAndPasswordData>()
25+
26+
const signIn = useSignInWithEmailAndPassword()
27+
28+
const success = () => {
29+
const safeRedirect = redirect.startsWith("/") ? redirect : "/"
30+
router.replace(safeRedirect)
31+
}
32+
const onSubmit = handleSubmit(credentials => {
33+
signIn.execute(credentials).then(success)
34+
})
35+
36+
return (
37+
<Container className="py-5">
38+
<Row className="justify-content-center">
39+
<Col xs={12} sm={10} md={8} lg={6}>
40+
<Card className="p-4 shadow-lg">
41+
<h2 className="text-center">{t("signInToAccess")}</h2>
42+
43+
<Form onSubmit={onSubmit} noValidate>
44+
{signIn.error && (
45+
<Alert variant="danger">{signIn.error.message}</Alert>
46+
)}
47+
48+
<Stack gap={3}>
49+
<Input
50+
label={t("email")}
51+
type="email"
52+
{...register("email", {
53+
required: t("emailIsRequired") ?? "Email is required"
54+
})}
55+
error={errors.email?.message}
56+
/>
57+
58+
<PasswordInput
59+
label={t("password")}
60+
{...register("password", {
61+
required: t("passwordRequired") ?? "Password is required"
62+
})}
63+
error={errors.password?.message}
64+
/>
65+
66+
<LoadingButton
67+
type="submit"
68+
className="w-100"
69+
loading={signIn.loading}
70+
>
71+
{t("signIn")}
72+
</LoadingButton>
73+
74+
<Divider className="px-4">{t("or")}</Divider>
75+
76+
<SocialSignOnButtons
77+
onComplete={() => router.replace(redirect)}
78+
/>
79+
</Stack>
80+
</Form>
81+
</Card>
82+
</Col>
83+
</Row>
84+
</Container>
85+
)
86+
}

components/Login/login.tsx

Lines changed: 0 additions & 50 deletions
This file was deleted.

components/Newsfeed/Newsfeed.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
StyledContainer
1515
} from "./StyledNewsfeedComponents"
1616
import ProfileSettingsModal from "components/EditProfilePage/ProfileSettingsModal"
17-
import LoginPage from "components/Login/login"
17+
import LoginPage from "components/Login/Login"
1818
import { NewsfeedCard } from "components/NewsfeedCard/NewsfeedCard"
1919
import { ProfileButtons } from "components/ProfilePage/ProfileButtons"
2020

components/auth/Provider.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { useEffect } from "react"
2+
import { getAuth, onAuthStateChanged, User } from "firebase/auth"
3+
import { useDispatch } from "react-redux"
4+
import { authChanged } from "./redux"
5+
6+
export const Provider: React.FC<React.PropsWithChildren<unknown>> = ({
7+
children
8+
}) => {
9+
const dispatch = useDispatch()
10+
11+
useEffect(() => {
12+
const auth = getAuth()
13+
const unsubscribe = onAuthStateChanged(auth, (user: User | null) => {
14+
dispatch(authChanged({ user }))
15+
})
16+
17+
return () => unsubscribe()
18+
}, [])
19+
20+
return <>{children}</>
21+
}

components/auth/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from "./service"
33
export { default as SignInWithButton } from "./SignInWithButton"
44
export { default as SignOut } from "./SignOut"
55
export * from "./types"
6+
export { Provider } from "./Provider"

components/auth/redux.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ export interface State {
2323
/** True iff user is signed in */
2424
authenticated: boolean
2525
authFlowStep: AuthFlowStep
26+
loading: boolean
2627
}
2728

2829
const initialState: State = {
2930
authenticated: false,
3031
user: undefined,
31-
authFlowStep: null
32+
authFlowStep: null,
33+
loading: true
3234
}
3335

3436
export const {
@@ -45,6 +47,7 @@ export const {
4547
state.user = payload.user
4648
state.claims = payload.claims
4749
state.authenticated = Boolean(payload.user)
50+
state.loading = false
4851
},
4952
authStepChanged(state, action: PayloadAction<AuthFlowStep>) {
5053
state.authFlowStep = action.payload

components/auth/service.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { useAppDispatch } from "../hooks"
88
import { createService } from "../service"
99
import { authChanged, useAuth } from "./redux"
1010
import { Claim } from "./types"
11+
import { Row, Spinner } from "../bootstrap"
1112

1213
export const { Provider } = createService(() => {
1314
const dispatch = useAppDispatch()
@@ -76,13 +77,22 @@ export function requireAuth(
7677
Component: React.FC<React.PropsWithChildren<{ user: User }>>
7778
) {
7879
return function ProtectedRoute() {
79-
const { user } = useAuth()
80+
const { user, loading } = useAuth()
8081
const router = useRouter()
8182
useEffect(() => {
82-
if (user === null) {
83-
router.push({ pathname: "/" })
83+
if (!loading && !user) {
84+
const currentPath = router.asPath
85+
router.replace(`/login?redirect=${encodeURIComponent(currentPath)}`)
8486
}
85-
}, [router, user])
87+
}, [user, loading, router])
88+
89+
if (loading) {
90+
return (
91+
<Row>
92+
<Spinner animation="border" className="mx-auto" />
93+
</Row>
94+
)
95+
}
8696

8797
return user ? <Component user={user} /> : null
8898
}
@@ -92,6 +102,6 @@ export function requireAuth(
92102
* Redirects user after logging out.
93103
*/
94104
export async function signOutAndRedirectToHome() {
95-
await auth.signOut()
96105
Router.push("/")
106+
await auth.signOut()
97107
}

pages/edit-profile/[[...docName]].tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,10 @@ const Query = z.object({
1414

1515
export default createPage({
1616
titleI18nKey: "navigation.editProfile",
17-
Page: () => {
18-
// Page: requireAuth(({ user }) => {
17+
Page: requireAuth(() => {
1918
const tabTitle = Query.parse(useRouter().query).docName?.[0] || "about-you"
2019
return <EditProfile tabTitle={tabTitle as TabTitles} />
21-
}
20+
})
2221
})
2322

2423
export const getStaticPaths: GetStaticPaths = async ctx => {

pages/login.tsx

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { createPage } from "../components/page"
2+
import Login from "components/Login/Login"
3+
4+
export default createPage({
5+
titleI18nKey: "navigation.login",
6+
Page: () => <Login />
7+
})
8+
import { serverSideTranslations } from "next-i18next/serverSideTranslations"
9+
export async function getStaticProps({ locale }: any) {
10+
return {
11+
props: {
12+
...(await serverSideTranslations(locale, ["auth", "common"]))
13+
}
14+
}
15+
}

0 commit comments

Comments
 (0)