Clean up login and invite page UI

This commit is contained in:
Jackson Harper
2022-12-08 12:05:44 +08:00
parent 7b2549c6e7
commit e9757e3391
9 changed files with 418 additions and 224 deletions

View File

@ -33,10 +33,27 @@ export const FormInput = styled('input', {
},
})
export const FormLabel = styled('label', {
fontSize: '16px',
color: '$omnivoreGray',
})
export const BorderedFormInput = styled(FormInput, {
height: '40px',
margin: '0',
padding: '4px 11px',
color: 'rgba(0,0,0,.88)',
fontSize: '14px',
lineHeight: '1.6',
listStyle: 'none',
width: '100%',
minWidth: '0',
backgroundColor: '#fff',
borderWidth: '1px',
borderStyle: 'solid',
borderColor: '#d9d9d9',
borderRadius: '6px',
border: `1px solid $grayBorder`,
p: '$3',
transition: 'all .2s',
})
export function GeneralFormInput(props: FormInputProps): JSX.Element {
@ -53,7 +70,7 @@ export function GeneralFormInput(props: FormInputProps): JSX.Element {
return (
<VStack>
{input.options?.map((label, index) => (
<HStack key={index} alignment='center'>
<HStack key={index} alignment="center">
<Checkbox
key={index}
checked={input.value[index]}
@ -73,9 +90,19 @@ export function GeneralFormInput(props: FormInputProps): JSX.Element {
)
} else if (props.type === 'select') {
return (
<select onChange={input.onChange} style={{ padding: '8px', height: '38px', borderRadius: '6px', minWidth: '196px' }}>
<select
onChange={input.onChange}
style={{
padding: '8px',
height: '38px',
borderRadius: '6px',
minWidth: '196px',
}}
>
{input.options?.map((label, index) => (
<option key={index} value={label}>{label}</option>
<option key={index} value={label}>
{label}
</option>
))}
</select>
)

View File

@ -166,6 +166,8 @@ export const StyledText = styled('p', {
},
})
export const StyledTextSpan = styled('span', StyledText)
export const StyledListElement = styled('li', {
fontFamily: 'Inter',
fontWeight: 'normal',

View File

@ -1,5 +1,5 @@
import Link from 'next/link'
import { StyledText } from '../elements/StyledText'
import { StyledText, StyledTextSpan } from '../elements/StyledText'
import { VStack, Box, SpanBox } from '../elements/LayoutPrimitives'
import { styled } from '../tokens/stitches.config'
import {
@ -10,8 +10,6 @@ import {
import AppleLogin from 'react-apple-login'
import { AppleIdButton } from './auth/AppleIdButton'
const StyledTextSpan = styled('span', StyledText)
export type LoginFormProps = {
errorMessage?: string
}
@ -41,25 +39,30 @@ export function LoginForm(props: LoginFormProps): JSX.Element {
<StyledTextHeadline>
Read-it-later for serious readers.
</StyledTextHeadline>
<StyledText css={{ fontStyle: 'normal',
fontWeight: '400',
fontSize: '18px',
lineHeight: '120%',
m: '0px',
color: '$omnivoreGray',
}}>
<StyledText
css={{
fontStyle: 'normal',
fontWeight: '400',
fontSize: '18px',
lineHeight: '120%',
m: '0px',
color: '$omnivoreGray',
}}
>
Save articles and read them later in our distraction-free reader.
</StyledText>
<Link passHref href="/about">
<a style={{ textDecoration: 'none' }}>
<StyledText css={{
fontStyle: 'normal',
fontWeight: '400',
fontSize: '18px',
lineHeight: '120%',
m: '0px',
color: '$omnivoreGray',
}}>
<StyledText
css={{
fontStyle: 'normal',
fontWeight: '400',
fontSize: '18px',
lineHeight: '120%',
m: '0px',
color: '$omnivoreGray',
}}
>
Learn More -&gt;
</StyledText>
</a>
@ -68,41 +71,44 @@ export function LoginForm(props: LoginFormProps): JSX.Element {
<SpanBox css={{ height: '24px' }} />
<VStack alignment="start" distribution="center">
{googleID && (
<Box
style={{
cursor: 'pointer',
display: 'inline-flex',
height: '40px',
}}
>
<GoogleAuthButton />
</Box>
)}
{googleID && (
<Box
style={{
cursor: 'pointer',
display: 'inline-flex',
height: '40px',
}}
>
<GoogleAuthButton />
</Box>
)}
<Box style={{ height: '16px' }}></Box>
<Box style={{ height: '16px' }}></Box>
{appleAuthRedirectURI && (
<Box
style={{
cursor: 'pointer',
display: 'inline-flex',
width: '210px',
height: '40px',
}}
>
<AppleIdButton
clientId="app.omnivore"
scope="name email"
state="web:login"
redirectURI={appleAuthRedirectURI}
responseMode="form_post"
responseType="code id_token"
/>
</Box>
)}
{appleAuthRedirectURI && (
<Box
style={{
cursor: 'pointer',
display: 'inline-flex',
width: '210px',
height: '40px',
}}
>
<AppleIdButton
clientId="app.omnivore"
scope="name email"
state="web:login"
redirectURI={appleAuthRedirectURI}
responseMode="form_post"
responseType="code id_token"
/>
</Box>
)}
<Link href="/auth/email-login" passHref>
<StyledTextSpan style="actionLink" css={{ color: '$omnivoreGray', pt: '12px' }}>
<StyledTextSpan
style="actionLink"
css={{ color: '$omnivoreGray', pt: '12px' }}
>
Continue with Email
</StyledTextSpan>
</Link>
@ -114,7 +120,7 @@ export function LoginForm(props: LoginFormProps): JSX.Element {
function GoogleAuthButton() {
return (
<Box css={{ overflow: 'hidden' }}>
<Box css={{ overflow: 'hidden' }}>
<div
id="g_id_onload"
data-client_id={googleID}
@ -122,7 +128,6 @@ function GoogleAuthButton() {
data-ux_mode="popup"
data-login_uri={gauthRedirectURI}
data-auto_prompt="false"
/>
<div

View File

@ -2,32 +2,19 @@ import { SpanBox, VStack } from '../../elements/LayoutPrimitives'
import { Button } from '../../elements/Button'
import { StyledText } from '../../elements/StyledText'
import { useEffect, useState } from 'react'
import { FormInput } from '../../elements/FormElements'
import { BorderedFormInput, FormLabel } from '../../elements/FormElements'
import { fetchEndpoint } from '../../../lib/appConfig'
import { logoutMutation } from '../../../lib/networking/mutations/logoutMutation'
import { styled } from '@stitches/react'
import { useRouter } from 'next/router'
import { formatMessage } from '../../../locales/en/messages'
import { parseErrorCodes } from '../../../lib/queryParamParser'
const BorderedFormInput = styled(FormInput, {
height: '40px',
paddingLeft: '6px',
borderRadius: '6px',
background: 'white',
color: '$omnivoreGray',
border: `1px solid 1px solid rgba(0, 0, 0, 0.06)`,
})
const FormLabel = styled('label', {
fontSize: '16px',
color: '$omnivoreGray',
})
export function EmailForgotPassword(): JSX.Element {
const router = useRouter()
const [email, setEmail] = useState<string>('')
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined)
const [errorMessage, setErrorMessage] = useState<string | undefined>(
undefined
)
useEffect(() => {
if (!router.isReady) return
@ -40,9 +27,24 @@ export function EmailForgotPassword(): JSX.Element {
return (
<form action={`${fetchEndpoint}/auth/forgot-password`} method="POST">
<VStack alignment="center" css={{ padding: '16px' }}>
<StyledText style="subHeadline" css={{ color: '$omnivoreGray' }}>Reset your password</StyledText>
<VStack css={{ width: '100%', minWidth: '320px', gap: '16px', pb: '16px' }}>
<VStack
alignment="center"
css={{
padding: '16px',
background: 'white',
minWidth: '340px',
width: '70vw',
maxWidth: '576px',
borderRadius: '8px',
boxShadow: 'rgb(224 224 224) 9px 9px 9px -9px',
}}
>
<StyledText style="subHeadline" css={{ color: '$omnivoreGray' }}>
Reset your password
</StyledText>
<VStack
css={{ width: '100%', minWidth: '320px', gap: '16px', pb: '16px' }}
>
<SpanBox css={{ width: '100%' }}>
<FormLabel>Email</FormLabel>
<BorderedFormInput
@ -52,15 +54,16 @@ export function EmailForgotPassword(): JSX.Element {
value={email}
placeholder="Email"
css={{ bg: 'white', color: 'black' }}
onChange={(e) => { e.preventDefault(); setEmail(e.target.value); }}
onChange={(e) => {
e.preventDefault()
setEmail(e.target.value)
}}
/>
</SpanBox>
</VStack>
{errorMessage && (
<StyledText style="error">{errorMessage}</StyledText>
)}
<Button type="submit" style="ctaDarkYellow" css={{ my: '$2' }}>
{errorMessage && <StyledText style="error">{errorMessage}</StyledText>}
<Button type="submit" style="ctaDarkYellow" css={{ my: '$2' }}>
Reset Password
</Button>
<Button

View File

@ -1,9 +1,8 @@
import { HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
import { Button } from '../../elements/Button'
import { StyledText } from '../../elements/StyledText'
import { StyledText, StyledTextSpan } from '../../elements/StyledText'
import { useEffect, useState } from 'react'
import { FormInput } from '../../elements/FormElements'
import { TermAndConditionsFooter } from '../LoginForm'
import { BorderedFormInput, FormLabel } from '../../elements/FormElements'
import { fetchEndpoint } from '../../../lib/appConfig'
import { logoutMutation } from '../../../lib/networking/mutations/logoutMutation'
import { styled } from '@stitches/react'
@ -12,26 +11,13 @@ import { parseErrorCodes } from '../../../lib/queryParamParser'
import { formatMessage } from '../../../locales/en/messages'
import Link from 'next/link'
const StyledTextSpan = styled('span', StyledText)
const BorderedFormInput = styled(FormInput, {
height: '40px',
paddingLeft: '6px',
borderRadius: '6px',
background: 'white',
border: `1px solid 1px solid rgba(0, 0, 0, 0.06)`,
})
const FormLabel = styled('label', {
fontSize: '16px',
color: '$omnivoreGray',
})
export function EmailLogin(): JSX.Element {
const router = useRouter()
const [email, setEmail] = useState<string | undefined>(undefined)
const [password, setPassword] = useState<string | undefined>(undefined)
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined)
const [errorMessage, setErrorMessage] = useState<string | undefined>(
undefined
)
useEffect(() => {
if (!router.isReady) return
@ -44,9 +30,24 @@ export function EmailLogin(): JSX.Element {
return (
<form action={`${fetchEndpoint}/auth/email-login`} method="POST">
<VStack alignment="center" css={{ padding: '16px' }}>
<StyledText style="subHeadline" css={{ color: '$omnivoreGray' }}>Login</StyledText>
<VStack css={{ width: '100%', minWidth: '320px', gap: '16px', pb: '16px' }}>
<VStack
alignment="center"
css={{
padding: '16px',
background: 'white',
minWidth: '340px',
width: '70vw',
maxWidth: '576px',
borderRadius: '8px',
boxShadow: 'rgb(224 224 224) 9px 9px 9px -9px',
}}
>
<StyledText style="subHeadline" css={{ color: '$omnivoreGray' }}>
Login
</StyledText>
<VStack
css={{ width: '100%', minWidth: '320px', gap: '16px', pb: '16px' }}
>
<SpanBox css={{ width: '100%' }}>
<FormLabel>Email</FormLabel>
<BorderedFormInput
@ -56,7 +57,10 @@ export function EmailLogin(): JSX.Element {
value={email}
placeholder="Email"
css={{ backgroundColor: 'white', color: 'black' }}
onChange={(e) => { e.preventDefault(); setEmail(e.target.value); }}
onChange={(e) => {
e.preventDefault()
setEmail(e.target.value)
}}
/>
</SpanBox>
@ -73,10 +77,8 @@ export function EmailLogin(): JSX.Element {
/>
</SpanBox>
</VStack>
{errorMessage && (
<StyledText style="error">{errorMessage}</StyledText>
)}
{errorMessage && <StyledText style="error">{errorMessage}</StyledText>}
<HStack
alignment="center"
@ -87,7 +89,11 @@ export function EmailLogin(): JSX.Element {
height: '80px',
}}
>
<Button style={'ctaOutlineYellow'} css={{ color: '$omnivoreGray', borderColor: '$omnivoreLightGray' }} type="button" onClick={async (event) => {
<Button
style={'ctaOutlineYellow'}
css={{ color: '$omnivoreGray', borderColor: '$omnivoreLightGray' }}
type="button"
onClick={async (event) => {
window.localStorage.removeItem('authVerified')
window.localStorage.removeItem('authToken')
try {
@ -100,7 +106,9 @@ export function EmailLogin(): JSX.Element {
>
Cancel
</Button>
<Button type="submit" style={'ctaDarkYellow'}>Login</Button>
<Button type="submit" style={'ctaDarkYellow'}>
Login
</Button>
</HStack>
<StyledText
style="action"
@ -109,12 +117,15 @@ export function EmailLogin(): JSX.Element {
pt: '16px',
width: '100%',
color: '$omnivoreLightGray',
textAlign: 'center'
textAlign: 'center',
whiteSpace: 'normal',
}}
>
Don&apos;t have an account? {' '}
Don&apos;t have an account?{' '}
<Link href="/auth/email-signup" passHref>
<StyledTextSpan style="actionLink" css={{ color: '$omnivoreGray' }}>Sign up</StyledTextSpan>
<StyledTextSpan style="actionLink" css={{ color: '$omnivoreGray' }}>
Sign up
</StyledTextSpan>
</Link>
</StyledText>
<StyledText
@ -124,12 +135,15 @@ export function EmailLogin(): JSX.Element {
pt: '4px',
width: '100%',
color: '$omnivoreLightGray',
textAlign: 'center'
textAlign: 'center',
whiteSpace: 'normal',
}}
>
Forgot your password? {' '}
Forgot your password?{' '}
<Link href="/auth/forgot-password" passHref>
<StyledTextSpan style="actionLink" css={{ color: '$omnivoreGray' }}>Click here</StyledTextSpan>
<StyledTextSpan style="actionLink" css={{ color: '$omnivoreGray' }}>
Click here
</StyledTextSpan>
</Link>
</StyledText>
</VStack>

View File

@ -2,35 +2,20 @@ import { SpanBox, VStack } from '../../elements/LayoutPrimitives'
import { Button } from '../../elements/Button'
import { StyledText } from '../../elements/StyledText'
import { useEffect, useState } from 'react'
import { FormInput } from '../../elements/FormElements'
import { TermAndConditionsFooter } from '../LoginForm'
import { BorderedFormInput, FormLabel } from '../../elements/FormElements'
import { fetchEndpoint } from '../../../lib/appConfig'
import { logoutMutation } from '../../../lib/networking/mutations/logoutMutation'
import { styled } from '@stitches/react'
import { useRouter } from 'next/router'
import { formatMessage } from '../../../locales/en/messages'
import { parseErrorCodes } from '../../../lib/queryParamParser'
import { LoadingView } from '../../patterns/LoadingView'
const BorderedFormInput = styled(FormInput, {
height: '40px',
paddingLeft: '6px',
borderRadius: '6px',
background: 'white',
color: '$omnivoreGray',
border: `1px solid 1px solid rgba(0, 0, 0, 0.06)`,
})
const FormLabel = styled('label', {
fontSize: '16px',
color: '$omnivoreGray',
})
export function EmailResetPassword(): JSX.Element {
const router = useRouter()
const [token, setToken] = useState<string | undefined>(undefined)
const [password, setPassword] = useState<string>('')
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined)
const [errorMessage, setErrorMessage] = useState<string | undefined>(
undefined
)
useEffect(() => {
if (!router.isReady) return
@ -38,7 +23,7 @@ export function EmailResetPassword(): JSX.Element {
const errorMsg = errorCode
? formatMessage({ id: `error.${errorCode}` })
: undefined
console.log('errorCode', errorCode, errorMsg)
setErrorMessage(errorMsg)
@ -51,8 +36,21 @@ export function EmailResetPassword(): JSX.Element {
return (
<form action={`${fetchEndpoint}/auth/reset-password`} method="POST">
<VStack alignment="center" css={{ padding: '16px' }}>
<VStack css={{ width: '100%', minWidth: '320px', gap: '16px', pb: '16px' }}>
<VStack
alignment="center"
css={{
padding: '16px',
background: 'white',
minWidth: '340px',
width: '70vw',
maxWidth: '576px',
borderRadius: '8px',
boxShadow: 'rgb(224 224 224) 9px 9px 9px -9px',
}}
>
<VStack
css={{ width: '100%', minWidth: '320px', gap: '16px', pb: '16px' }}
>
<SpanBox css={{ width: '100%' }}>
<FormLabel>Enter new password</FormLabel>
<BorderedFormInput
@ -62,18 +60,21 @@ export function EmailResetPassword(): JSX.Element {
value={password}
placeholder="Password"
css={{ bg: 'white', color: 'black' }}
onChange={(e) => { e.preventDefault(); setPassword(e.target.value); }}
onChange={(e) => {
e.preventDefault()
setPassword(e.target.value)
}}
/>
<FormLabel css={{ fontSize: '12px' }}>(Password must be at least 8 chars)</FormLabel>
<FormLabel css={{ fontSize: '12px' }}>
(Password must be at least 8 chars)
</FormLabel>
<input type="hidden" name="token" value={token} />
</SpanBox>
</VStack>
{errorMessage && (
<StyledText style="error">{errorMessage}</StyledText>
)}
<Button type="submit" style="ctaDarkYellow" css={{ my: '$2' }}>
{errorMessage && <StyledText style="error">{errorMessage}</StyledText>}
<Button type="submit" style="ctaDarkYellow" css={{ my: '$2' }}>
Update Password
</Button>
</VStack>

View File

@ -1,8 +1,8 @@
import { HStack, SpanBox, VStack } from '../../elements/LayoutPrimitives'
import { Button } from '../../elements/Button'
import { StyledText } from '../../elements/StyledText'
import { useCallback, useEffect, useMemo, useState } from 'react'
import { FormInput } from '../../elements/FormElements'
import { StyledText, StyledTextSpan } from '../../elements/StyledText'
import { useCallback, useEffect, useState } from 'react'
import { BorderedFormInput, FormLabel } from '../../elements/FormElements'
import { TermAndConditionsFooter } from '../LoginForm'
import { fetchEndpoint } from '../../../lib/appConfig'
import { useValidateUsernameQuery } from '../../../lib/networking/queries/useValidateUsernameQuery'
@ -13,29 +13,18 @@ import { formatMessage } from '../../../locales/en/messages'
import { parseErrorCodes } from '../../../lib/queryParamParser'
import Link from 'next/link'
const StyledTextSpan = styled('span', StyledText)
const BorderedFormInput = styled(FormInput, {
height: '40px',
paddingLeft: '6px',
borderRadius: '6px',
background: 'white',
border: `1px solid 1px solid rgba(0, 0, 0, 0.06)`,
})
const FormLabel = styled('label', {
fontSize: '16px',
color: '$omnivoreGray',
})
export function EmailSignup(): JSX.Element {
const router = useRouter()
const [email, setEmail] = useState<string | undefined>(undefined)
const [password, setPassword] = useState<string | undefined>(undefined)
const [fullname, setFullname] = useState<string | undefined>(undefined)
const [username, setUsername] = useState<string | undefined>(undefined)
const [debouncedUsername, setDebouncedUsername] = useState<string | undefined>(undefined)
const [errorMessage, setErrorMessage] = useState<string | undefined>(undefined)
const [debouncedUsername, setDebouncedUsername] = useState<
string | undefined
>(undefined)
const [errorMessage, setErrorMessage] = useState<string | undefined>(
undefined
)
useEffect(() => {
if (!router.isReady) return
@ -62,9 +51,24 @@ export function EmailSignup(): JSX.Element {
return (
<form action={`${fetchEndpoint}/auth/email-signup`} method="POST">
<VStack alignment="center" css={{ padding: '16px' }}>
<StyledText style="subHeadline" css={{ color: '$omnivoreGray' }}>Sign Up</StyledText>
<VStack css={{ width: '100%', minWidth: '320px', gap: '16px', pb: '16px' }}>
<VStack
alignment="center"
css={{
padding: '16px',
background: 'white',
minWidth: '340px',
width: '70vw',
maxWidth: '576px',
borderRadius: '8px',
boxShadow: 'rgb(224 224 224) 9px 9px 9px -9px',
}}
>
<StyledText style="subHeadline" css={{ color: '$omnivoreGray' }}>
Sign Up
</StyledText>
<VStack
css={{ width: '100%', minWidth: '320px', gap: '16px', pb: '16px' }}
>
<SpanBox css={{ width: '100%' }}>
<FormLabel>Email</FormLabel>
<BorderedFormInput
@ -74,7 +78,10 @@ export function EmailSignup(): JSX.Element {
value={email}
placeholder="Email"
css={{ backgroundColor: 'white', color: 'black' }}
onChange={(e) => { e.preventDefault(); setEmail(e.target.value); }}
onChange={(e) => {
e.preventDefault()
setEmail(e.target.value)
}}
/>
</SpanBox>
@ -132,17 +139,20 @@ export function EmailSignup(): JSX.Element {
{isUsernameValid && (
<StyledText
style="caption"
css={{ m: 0, pl: '$2', alignSelf: 'flex-start', color: '$omnivoreGray' }}
css={{
m: 0,
pl: '$2',
alignSelf: 'flex-start',
color: '$omnivoreGray',
}}
>
Username is available.
</StyledText>
)}
</VStack>
{errorMessage && (
<StyledText style="error">{errorMessage}</StyledText>
)}
{errorMessage && <StyledText style="error">{errorMessage}</StyledText>}
<HStack
alignment="center"
distribution="end"
@ -152,7 +162,11 @@ export function EmailSignup(): JSX.Element {
height: '80px',
}}
>
<Button style={'ctaOutlineYellow'} css={{ color: '$omnivoreGray', borderColor: 'rgba(0, 0, 0, 0.06)' }} type="button" onClick={async (event) => {
<Button
style={'ctaOutlineYellow'}
css={{ color: '$omnivoreGray', borderColor: 'rgba(0, 0, 0, 0.06)' }}
type="button"
onClick={async (event) => {
window.localStorage.removeItem('authVerified')
window.localStorage.removeItem('authToken')
try {
@ -165,7 +179,9 @@ export function EmailSignup(): JSX.Element {
>
Cancel
</Button>
<Button type="submit" style={'ctaDarkYellow'}>Sign Up</Button>
<Button type="submit" style={'ctaDarkYellow'}>
Sign Up
</Button>
</HStack>
<StyledText
@ -173,12 +189,14 @@ export function EmailSignup(): JSX.Element {
css={{
pt: '16px',
color: '$omnivoreLightGray',
textAlign: 'center'
textAlign: 'center',
}}
>
Already have an account? {' '}
Already have an account?{' '}
<Link href="/auth/email-login" passHref>
<StyledTextSpan style="actionLink" css={{ color: '$omnivoreGray' }}>Login instead</StyledTextSpan>
<StyledTextSpan style="actionLink" css={{ color: '$omnivoreGray' }}>
Login instead
</StyledTextSpan>
</Link>
</StyledText>
<TermAndConditionsFooter />

View File

@ -1,7 +1,5 @@
import { Box, HStack, MediumBreakpointBox, SpanBox, VStack } from '../../elements/LayoutPrimitives'
import { Box, HStack } from '../../elements/LayoutPrimitives'
import type { LoginFormProps } from '../LoginForm'
import { OmnivoreNameLogo } from '../../elements/images/OmnivoreNameLogo'
import { theme } from '../../tokens/stitches.config'
export function VerifyEmail(props: LoginFormProps): JSX.Element {
return (
@ -13,23 +11,24 @@ export function VerifyEmail(props: LoginFormProps): JSX.Element {
width: '100vw',
height: '100vh',
bg: '$omnivoreYellow',
overflowY: 'clip'
overflowY: 'clip',
}}
>
<Box css={{
width: '100%',
margin: '40px',
color: '$omnivoreGray',
'@xl': { margin: '138px' },
}}>
<h1>Verify your email address</h1>
<Box>
We sent a verification link to the email you provided.
Click the link to verify your email. You may need to check
your spam folder.
<Box
css={{
width: '100%',
margin: '40px',
color: '$omnivoreGray',
'@xl': { margin: '138px' },
}}
>
<h1>Verify your email address</h1>
<Box>
We sent a verification link to the email you provided. Click the
link to verify your email. You may need to check your spam folder.
</Box>
</Box>
</Box>
</HStack>
</HStack>
</>
)
}

View File

@ -1,45 +1,170 @@
import Link from 'next/link'
import { useRouter } from 'next/router'
import { useEffect, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import { Button } from '../../components/elements/Button'
import { HStack, VStack } from '../../components/elements/LayoutPrimitives'
import {
StyledText,
StyledTextSpan,
} from '../../components/elements/StyledText'
import { PageMetaData } from '../../components/patterns/PageMetaData'
import { ProfileLayout } from '../../components/templates/ProfileLayout'
import { joinGroupMutation } from '../../lib/networking/mutations/joinGroupMutation'
import { useGetViewerQuery } from '../../lib/networking/queries/useGetViewerQuery'
import { showSuccessToast } from '../../lib/toastHelpers'
export default function InvitePage(): JSX.Element {
const router = useRouter()
const { viewerData, viewerDataError, isLoading } = useGetViewerQuery()
const { inviteCode } = router.query
const [errorMessage, setErrorMessage] = useState<string | undefined>(
undefined
)
const [error, setError] = useState<string | undefined>(undefined)
// Check if the user is logged in and display an error message if they are not
useEffect(() => {
if (!router.isReady) {
return
}
if (!inviteCode || typeof inviteCode != 'string') {
setError('No invite code provided')
}
const joinGroup = async () => {
try {
const result = await joinGroupMutation(inviteCode as string)
if (!result) {
throw 'Unknown error occurred.'
}
showSuccessToast(`You have been added to the ${result.name} group`)
} catch (error) {
setError('Unable to join group')
if (!isLoading && router.isReady) {
if (viewerDataError || !viewerData?.me) {
setErrorMessage(
'You are not logged in. You must log in or signup before accepting your invite.'
)
}
}
}, [isLoading, router, viewerData, viewerDataError])
const acceptClicked = useCallback(
(event) => {
event?.stopPropagation()
if (!router.isReady) {
return
}
if (!inviteCode || typeof inviteCode != 'string') {
setErrorMessage('No invite code provided')
}
const joinGroup = async () => {
try {
const result = await joinGroupMutation(inviteCode as string)
if (!result) {
throw 'Unknown error occurred.'
}
showSuccessToast(`You have been added to the ${result.name} group`)
router.push(`/home`)
} catch (error) {
console.log('error', error)
setErrorMessage('Unable to join group')
}
}
joinGroup().catch((error) => {
setErrorMessage(error)
})
},
[router, inviteCode]
)
joinGroup().catch((error) => {
setError(error)
})
}, [router, inviteCode])
return (
<>
<PageMetaData title="Accept Invite - Omnivore" path="/invite" />
<ProfileLayout>Accepting invite to join</ProfileLayout>
<div data-testid="invite-page-tag" />
<ProfileLayout>
<VStack
alignment="center"
css={{
padding: '16px',
background: 'white',
minWidth: '340px',
width: '70vw',
maxWidth: '576px',
borderRadius: '8px',
boxShadow: 'rgb(224 224 224) 9px 9px 9px -9px',
}}
>
<StyledText style="subHeadline" css={{ color: '$omnivoreGray' }}>
You're invited
</StyledText>
<StyledText
style="action"
css={{
mt: '0px',
pt: '4px',
width: '100%',
color: '$omnivoreLightGray',
textAlign: 'center',
whiteSpace: 'normal',
}}
>
You have been invited to join a recommendation group on Omnivore.
Recommendation groups allow you to share articles with other group
members.
</StyledText>
{errorMessage && (
<StyledText
style="error"
css={{
mt: '0px',
pt: '4px',
width: '100%',
textAlign: 'center',
whiteSpace: 'normal',
}}
>
{errorMessage}
</StyledText>
)}
<HStack
alignment="center"
distribution="center"
css={{
gap: '10px',
width: '100%',
height: '80px',
}}
>
{viewerData?.me ? (
<Button
type="submit"
style={'ctaDarkYellow'}
onClick={acceptClicked}
>
Accept Invite
</Button>
) : (
<Button
type="submit"
style={'ctaDarkYellow'}
onClick={() => router.push('/login')}
>
Login
</Button>
)}
</HStack>
<StyledText
style="action"
css={{
m: '0px',
pt: '16px',
width: '100%',
color: '$omnivoreLightGray',
textAlign: 'center',
whiteSpace: 'normal',
}}
>
Don&apos;t have an Omnivore account?{' '}
<Link href="/login" passHref>
<StyledTextSpan
style="actionLink"
css={{ color: '$omnivoreGray' }}
>
Signup
</StyledTextSpan>
</Link>
</StyledText>
</VStack>
<div data-testid="invite-page-tag" />
</ProfileLayout>
</>
)
}