210 lines
6.7 KiB
TypeScript
210 lines
6.7 KiB
TypeScript
import {useCallback, useMemo, useState} from 'react'
|
|
import {View} from 'react-native'
|
|
import {msg} from '@lingui/core/macro'
|
|
import {useLingui} from '@lingui/react'
|
|
import {Trans} from '@lingui/react/macro'
|
|
|
|
import {useCleanError} from '#/lib/hooks/useCleanError'
|
|
import {isAppPassword} from '#/lib/jwt'
|
|
import {getAge, getDateAgo} from '#/lib/strings/time'
|
|
import {logger} from '#/logger'
|
|
import {
|
|
useBirthdateMutation,
|
|
useIsBirthdateUpdateAllowed,
|
|
} from '#/state/birthdate'
|
|
import {
|
|
usePreferencesQuery,
|
|
type UsePreferencesQueryResponse,
|
|
} from '#/state/queries/preferences'
|
|
import {useSession} from '#/state/session'
|
|
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
|
|
import {atoms as a, useTheme, web} from '#/alf'
|
|
import {Admonition} from '#/components/Admonition'
|
|
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
|
|
import * as Dialog from '#/components/Dialog'
|
|
import {DateField} from '#/components/forms/DateField'
|
|
import {SimpleInlineLinkText} from '#/components/Link'
|
|
import {Loader} from '#/components/Loader'
|
|
import {Span, Text} from '#/components/Typography'
|
|
import {IS_IOS, IS_WEB} from '#/env'
|
|
|
|
export function BirthDateSettingsDialog({
|
|
control,
|
|
}: {
|
|
control: Dialog.DialogControlProps
|
|
}) {
|
|
const t = useTheme()
|
|
const {_} = useLingui()
|
|
const {isLoading, error, data: preferences} = usePreferencesQuery()
|
|
const isBirthdateUpdateAllowed = useIsBirthdateUpdateAllowed()
|
|
const {currentAccount} = useSession()
|
|
const isUsingAppPassword = isAppPassword(currentAccount?.accessJwt || '')
|
|
|
|
return (
|
|
<Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}>
|
|
<Dialog.Handle />
|
|
{isBirthdateUpdateAllowed ? (
|
|
<Dialog.ScrollableInner
|
|
label={_(msg`My Birthdate`)}
|
|
style={web({maxWidth: 400})}>
|
|
<View style={[a.gap_md]}>
|
|
<Text style={[a.text_xl, a.font_semi_bold]}>
|
|
<Trans>My Birthdate</Trans>
|
|
</Text>
|
|
<Text
|
|
style={[a.text_md, a.leading_snug, t.atoms.text_contrast_medium]}>
|
|
<Trans>
|
|
This information is private and not shared with other users.
|
|
</Trans>
|
|
</Text>
|
|
|
|
{isLoading ? (
|
|
<Loader size="xl" />
|
|
) : error || !preferences ? (
|
|
<ErrorMessage
|
|
message={
|
|
error?.toString() ||
|
|
_(
|
|
msg`We were unable to load your birthdate preferences. Please try again.`,
|
|
)
|
|
}
|
|
style={[a.rounded_sm]}
|
|
/>
|
|
) : isUsingAppPassword ? (
|
|
<Admonition type="info">
|
|
<Trans>
|
|
Hmm, it looks like you're signed in with an{' '}
|
|
<Span style={[a.italic]}>App Password</Span>. To set your
|
|
birthdate, you'll need to sign in with your main account
|
|
password, or ask whomever controls this account to do so.
|
|
</Trans>
|
|
</Admonition>
|
|
) : (
|
|
<BirthdayInner control={control} preferences={preferences} />
|
|
)}
|
|
</View>
|
|
|
|
<Dialog.Close />
|
|
</Dialog.ScrollableInner>
|
|
) : (
|
|
<Dialog.ScrollableInner
|
|
label={_(msg`You recently changed your birthdate`)}
|
|
style={web({maxWidth: 400})}>
|
|
<View style={[a.gap_sm]}>
|
|
<Text
|
|
style={[
|
|
a.text_xl,
|
|
a.font_semi_bold,
|
|
a.leading_snug,
|
|
{paddingRight: 32},
|
|
]}>
|
|
<Trans>You recently changed your birthdate</Trans>
|
|
</Text>
|
|
<Text
|
|
style={[a.text_md, a.leading_snug, t.atoms.text_contrast_medium]}>
|
|
<Trans>
|
|
There is a limit to how often you can change your birthdate. You
|
|
may need to wait a day or two before updating it again.
|
|
</Trans>
|
|
</Text>
|
|
</View>
|
|
|
|
<Dialog.Close />
|
|
</Dialog.ScrollableInner>
|
|
)}
|
|
</Dialog.Outer>
|
|
)
|
|
}
|
|
|
|
function BirthdayInner({
|
|
control,
|
|
preferences,
|
|
}: {
|
|
control: Dialog.DialogControlProps
|
|
preferences: UsePreferencesQueryResponse
|
|
}) {
|
|
const {_} = useLingui()
|
|
const cleanError = useCleanError()
|
|
const [date, setDate] = useState(preferences.birthDate || getDateAgo(18))
|
|
const {isPending, error, mutateAsync: setBirthDate} = useBirthdateMutation()
|
|
const hasChanged = date !== preferences.birthDate
|
|
const errorMessage = useMemo(() => {
|
|
if (error) {
|
|
const {raw, clean} = cleanError(error)
|
|
return clean || raw || error.toString()
|
|
}
|
|
}, [error, cleanError])
|
|
|
|
const age = getAge(new Date(date))
|
|
const isUnder13 = age < 13
|
|
const isUnder18 = age >= 13 && age < 18
|
|
|
|
const onSave = useCallback(async () => {
|
|
try {
|
|
// skip if date is the same
|
|
if (hasChanged) {
|
|
await setBirthDate({birthDate: date})
|
|
}
|
|
control.close()
|
|
} catch (e: any) {
|
|
logger.error(`setBirthDate failed`, {message: e.message})
|
|
}
|
|
}, [date, setBirthDate, control, hasChanged])
|
|
|
|
return (
|
|
<View style={a.gap_lg} testID="birthDateSettingsDialog">
|
|
<View style={IS_IOS && [a.w_full, a.align_center]}>
|
|
<DateField
|
|
testID="birthdayInput"
|
|
value={date}
|
|
onChangeDate={newDate => setDate(new Date(newDate))}
|
|
label={_(msg`Birthdate`)}
|
|
accessibilityHint={_(msg`Enter your birthdate`)}
|
|
/>
|
|
</View>
|
|
|
|
{isUnder18 && hasChanged && (
|
|
<Admonition type="info">
|
|
<Trans>
|
|
The birthdate you've entered means you are under 18 years old.
|
|
Certain content and features may be unavailable to you.
|
|
</Trans>
|
|
</Admonition>
|
|
)}
|
|
|
|
{isUnder13 && (
|
|
<Admonition type="error">
|
|
<Trans>
|
|
You must be at least 13 years old to use Bluesky. Read our{' '}
|
|
<SimpleInlineLinkText
|
|
to="https://bsky.social/about/support/tos"
|
|
label={_(msg`Terms of Service`)}>
|
|
Terms of Service
|
|
</SimpleInlineLinkText>{' '}
|
|
for more information.
|
|
</Trans>
|
|
</Admonition>
|
|
)}
|
|
|
|
{errorMessage ? (
|
|
<ErrorMessage message={errorMessage} style={[a.rounded_sm]} />
|
|
) : undefined}
|
|
|
|
<View style={IS_WEB && [a.flex_row, a.justify_end]}>
|
|
<Button
|
|
label={hasChanged ? _(msg`Save birthdate`) : _(msg`Done`)}
|
|
size="large"
|
|
onPress={onSave}
|
|
variant="solid"
|
|
color="primary"
|
|
disabled={isUnder13}>
|
|
<ButtonText>
|
|
{hasChanged ? <Trans>Save</Trans> : <Trans>Done</Trans>}
|
|
</ButtonText>
|
|
{isPending && <ButtonIcon icon={Loader} />}
|
|
</Button>
|
|
</View>
|
|
</View>
|
|
)
|
|
}
|