@@ -256,7 +250,9 @@ function DonationPage({ fund: fundSlug, slug, project }: Props) {
name="taxDeductible"
render={({ field }) => (
- Do you want this donation to potentially qualify for a tax deduction? (US only)
+
+ Do you want this donation to potentially qualify for a tax deduction? (US only)
+
Want to support more projects and receive optional perks?
-
+
diff --git a/components/PasswordResetFormModal.tsx b/pages/[fund]/forgot-password.tsx
similarity index 62%
rename from components/PasswordResetFormModal.tsx
rename to pages/[fund]/forgot-password.tsx
index 4df690c..9df0360 100644
--- a/components/PasswordResetFormModal.tsx
+++ b/pages/[fund]/forgot-password.tsx
@@ -2,16 +2,25 @@ import { useRef } from 'react'
import { zodResolver } from '@hookform/resolvers/zod'
import { Turnstile, TurnstileInstance } from '@marsidev/react-turnstile'
import { useForm } from 'react-hook-form'
+import { GetServerSidePropsContext } from 'next'
+import { getServerSession } from 'next-auth'
import { z } from 'zod'
-import { DialogDescription, DialogHeader, DialogTitle } from './ui/dialog'
-import { Input } from './ui/input'
-import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from './ui/form'
-import { Button } from './ui/button'
-import { useToast } from './ui/use-toast'
-import { trpc } from '../utils/trpc'
-import Spinner from './Spinner'
-import { env } from '../env.mjs'
+import { Input } from '../../components/ui/input'
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from '../../components/ui/form'
+import { Button } from '../../components/ui/button'
+import { useToast } from '../../components/ui/use-toast'
+import { trpc } from '../../utils/trpc'
+import Spinner from '../../components/Spinner'
+import { env } from '../../env.mjs'
+import { authOptions } from '../api/auth/[...nextauth]'
const schema = z.object({
turnstileToken: z.string().min(1),
@@ -20,9 +29,7 @@ const schema = z.object({
type PasswordResetFormInputs = z.infer
-type Props = { close: () => void }
-
-function PasswordResetFormModal({ close }: Props) {
+function ForgotPassword() {
const { toast } = useToast()
const turnstileRef = useRef()
@@ -34,22 +41,18 @@ function PasswordResetFormModal({ close }: Props) {
try {
await requestPasswordResetMutation.mutateAsync(data)
- toast({ title: 'A password reset link has been sent to your email.' })
- close()
+ toast({ title: 'Success', description: 'A password reset link has been sent to your email.' })
form.reset({ email: '' })
} catch (error) {
- toast({ title: 'Sorry, something went wrong.', variant: 'destructive' })
+ toast({ title: 'Error', description: 'Sorry, something went wrong.', variant: 'destructive' })
}
turnstileRef.current?.reset()
}
return (
- <>
-
- Reset Password
- Recover your account.
-
+
+
Request password reset
- >
+
)
}
-export default PasswordResetFormModal
+export default ForgotPassword
+
+export async function getServerSideProps({ params, req, res }: GetServerSidePropsContext) {
+ const session = await getServerSession(req, res, authOptions)
+
+ if (session) {
+ return { redirect: { destination: `/${params?.fund!}` } }
+ }
+}
diff --git a/components/LoginFormModal.tsx b/pages/[fund]/login.tsx
similarity index 69%
rename from components/LoginFormModal.tsx
rename to pages/[fund]/login.tsx
index 568aec6..f2dea90 100644
--- a/components/LoginFormModal.tsx
+++ b/pages/[fund]/login.tsx
@@ -1,19 +1,28 @@
import { useEffect, useRef } from 'react'
import { useRouter } from 'next/router'
import { zodResolver } from '@hookform/resolvers/zod'
-import { signIn, useSession } from 'next-auth/react'
+import { signIn } from 'next-auth/react'
import { useForm } from 'react-hook-form'
import { Turnstile, TurnstileInstance } from '@marsidev/react-turnstile'
+import { getServerSession } from 'next-auth'
+import { GetServerSidePropsContext } from 'next'
import { z } from 'zod'
-import { DialogContent, DialogDescription, DialogHeader, DialogTitle } from './ui/dialog'
-import { Input } from './ui/input'
-import { Form, FormControl, FormField, FormItem, FormLabel, FormMessage } from './ui/form'
-import { Button } from './ui/button'
-import { useToast } from './ui/use-toast'
-import { useFundSlug } from '../utils/use-fund-slug'
-import Spinner from './Spinner'
-import { env } from '../env.mjs'
+import { Input } from '../../components/ui/input'
+import {
+ Form,
+ FormControl,
+ FormField,
+ FormItem,
+ FormLabel,
+ FormMessage,
+} from '../../components/ui/form'
+import { Button } from '../../components/ui/button'
+import { useToast } from '../../components/ui/use-toast'
+import { useFundSlug } from '../../utils/use-fund-slug'
+import Spinner from '../../components/Spinner'
+import { env } from '../../env.mjs'
+import { authOptions } from '../api/auth/[...nextauth]'
const schema = z.object({
email: z.string().email(),
@@ -23,13 +32,7 @@ const schema = z.object({
type LoginFormInputs = z.infer
-type Props = {
- close: () => void
- openPasswordResetModal: () => void
- openRegisterModal: () => void
-}
-
-function LoginFormModal({ close, openPasswordResetModal, openRegisterModal }: Props) {
+function Login() {
const { toast } = useToast()
const router = useRouter()
const fundSlug = useFundSlug()
@@ -43,12 +46,11 @@ function LoginFormModal({ close, openPasswordResetModal, openRegisterModal }: Pr
useEffect(() => {
if (!fundSlug) return
- if (router.query.loginEmail) {
- form.setValue('email', router.query.loginEmail as string)
+ if (router.query.email) {
+ form.setValue('email', router.query.email as string)
setTimeout(() => form.setFocus('password'), 100)
- router.replace(`/${fundSlug}`)
}
- }, [router.query.loginEmail])
+ }, [router.query.email])
async function onSubmit(data: LoginFormInputs) {
const result = await signIn('credentials', {
@@ -69,25 +71,24 @@ function LoginFormModal({ close, openPasswordResetModal, openRegisterModal }: Pr
)
}
- return toast({
- title: 'Sorry, something went wrong.',
- variant: 'destructive',
- })
+ toast({ title: 'Error', description: 'Sorry, something went wrong.', variant: 'destructive' })
}
toast({
- title: 'Successfully logged in!',
+ title: 'Success',
+ description: 'Successfully logged in!',
})
- close()
+ if (router.query.nextAction === 'membership') {
+ router.push(`/${fundSlug}/membership`)
+ } else {
+ router.push(`/${fundSlug}`)
+ }
}
return (
- <>
-
- Login
- Log into your account.
-
+
+
Login
- >
+
)
}
-export default LoginFormModal
+export default Login
+
+export async function getServerSideProps({ params, req, res }: GetServerSidePropsContext) {
+ const session = await getServerSession(req, res, authOptions)
+
+ if (session) {
+ return { redirect: { destination: `/${params?.fund!}` } }
+ }
+}
diff --git a/pages/[fund]/membership/[slug].tsx b/pages/[fund]/membership.tsx
similarity index 90%
rename from pages/[fund]/membership/[slug].tsx
rename to pages/[fund]/membership.tsx
index 492735f..d889e0e 100644
--- a/pages/[fund]/membership/[slug].tsx
+++ b/pages/[fund]/membership.tsx
@@ -6,16 +6,17 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { DollarSign } from 'lucide-react'
import { useSession } from 'next-auth/react'
import { FundSlug } from '@prisma/client'
-import { GetStaticPropsContext } from 'next'
+import { GetServerSidePropsContext, GetStaticPropsContext } from 'next'
import Image from 'next/image'
+import Head from 'next/head'
import { z } from 'zod'
-import { MAX_AMOUNT } from '../../../config'
-import Spinner from '../../../components/Spinner'
-import { trpc } from '../../../utils/trpc'
-import { useToast } from '../../../components/ui/use-toast'
-import { Button } from '../../../components/ui/button'
-import { RadioGroup, RadioGroupItem } from '../../../components/ui/radio-group'
+import { MAX_AMOUNT } from '../../config'
+import Spinner from '../../components/Spinner'
+import { trpc } from '../../utils/trpc'
+import { useToast } from '../../components/ui/use-toast'
+import { Button } from '../../components/ui/button'
+import { RadioGroup, RadioGroupItem } from '../../components/ui/radio-group'
import {
Form,
FormControl,
@@ -23,18 +24,21 @@ import {
FormItem,
FormLabel,
FormMessage,
-} from '../../../components/ui/form'
-import { Input } from '../../../components/ui/input'
-import { ProjectItem } from '../../../utils/types'
-import { getProjectBySlug, getProjects } from '../../../utils/md'
-import { funds, fundSlugs } from '../../../utils/funds'
-import Head from 'next/head'
+} from '../../components/ui/form'
+import { Input } from '../../components/ui/input'
+import { ProjectItem } from '../../utils/types'
+import { funds, fundSlugs } from '../../utils/funds'
+import { getServerSession } from 'next-auth'
+import { authOptions } from '../api/auth/[...nextauth]'
+import { useEffect } from 'react'
+import { useRouter } from 'next/router'
type QueryParams = { fund: FundSlug; slug: string }
type Props = { project: ProjectItem } & QueryParams
function MembershipPage({ fund: fundSlug, project }: Props) {
const session = useSession()
+ const router = useRouter()
const isAuthed = session.status === 'authenticated'
const schema = z
@@ -95,10 +99,7 @@ function MembershipPage({ fund: fundSlug, project }: Props) {
window.location.assign(result.url)
} catch (e) {
- toast({
- title: 'Sorry, something went wrong.',
- variant: 'destructive',
- })
+ toast({ title: 'Error', description: 'Sorry, something went wrong.', variant: 'destructive' })
}
}
@@ -121,21 +122,24 @@ function MembershipPage({ fund: fundSlug, project }: Props) {
window.location.assign(result.url)
} catch (e) {
- toast({
- title: 'Sorry, something went wrong.',
- variant: 'destructive',
- })
+ toast({ title: 'Error', description: 'Sorry, something went wrong.', variant: 'destructive' })
}
}
- if (!project) return <>>
+ useEffect(() => {
+ if (session.status === 'unauthenticated') {
+ router.push(`/${fundSlug}/login?nextAction=membership`)
+ }
+ }, [session])
+
+ if (!project || session.status === 'loading') return <>>
return (
<>
Membership to {project.title}
-
- Donate to
-
- {' '}
- Privacy Guides
- and support our mission to defend digital rights and
- spread the word about mass surveillance programs and other daily privacy invasions.
- You can help Privacy Guides researchers, activists, and maintainers create informative content,
- host private digital services, and protect privacy rights at a time when the world needs it most.
+ Donate to
+
+ {' '}
+ Privacy Guides
+ {' '}
+ and support our mission to defend digital rights and spread the word about mass
+ surveillance programs and other daily privacy invasions. You can help Privacy Guides
+ researchers, activists, and maintainers create informative content, host private
+ digital services, and protect privacy rights at a time when the world needs it most.