Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at cope-settings-sync 205 lines 6.9 kB view raw
1import {useCallback, useMemo, useState} from 'react' 2import {View} from 'react-native' 3import {Trans, useLingui} from '@lingui/react/macro' 4 5import {useCleanError} from '#/lib/hooks/useCleanError' 6import {isAppPassword} from '#/lib/jwt' 7import {getAge, getDateAgo} from '#/lib/strings/time' 8import {logger} from '#/logger' 9import { 10 useBirthdateMutation, 11 useIsBirthdateUpdateAllowed, 12} from '#/state/birthdate' 13import { 14 usePreferencesQuery, 15 type UsePreferencesQueryResponse, 16} from '#/state/queries/preferences' 17import {useSession} from '#/state/session' 18import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' 19import {atoms as a, useTheme, web} from '#/alf' 20import {Admonition} from '#/components/Admonition' 21import {Button, ButtonIcon, ButtonText} from '#/components/Button' 22import * as Dialog from '#/components/Dialog' 23import {DateField} from '#/components/forms/DateField' 24import {SimpleInlineLinkText} from '#/components/Link' 25import {Loader} from '#/components/Loader' 26import {Span, Text} from '#/components/Typography' 27import {IS_IOS, IS_WEB} from '#/env' 28 29export function BirthDateSettingsDialog({ 30 control, 31}: { 32 control: Dialog.DialogControlProps 33}) { 34 const t = useTheme() 35 const {t: l} = useLingui() 36 const {isLoading, error, data: preferences} = usePreferencesQuery() 37 const isBirthdateUpdateAllowed = useIsBirthdateUpdateAllowed() 38 const {currentAccount} = useSession() 39 const isUsingAppPassword = currentAccount?.accessJwt 40 ? isAppPassword(currentAccount?.accessJwt) 41 : false 42 43 return ( 44 <Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}> 45 <Dialog.Handle /> 46 {isBirthdateUpdateAllowed ? ( 47 <Dialog.ScrollableInner 48 label={l`My birthdate`} 49 style={web({maxWidth: 400})}> 50 <View style={[a.gap_md]}> 51 <Text style={[a.text_xl, a.font_semi_bold]}> 52 <Trans>My birthdate</Trans> 53 </Text> 54 <Text 55 style={[a.text_md, a.leading_snug, t.atoms.text_contrast_medium]}> 56 <Trans> 57 This information is private and not shared with other users. 58 </Trans> 59 </Text> 60 61 {isLoading ? ( 62 <Loader size="xl" /> 63 ) : error || !preferences ? ( 64 <ErrorMessage 65 message={ 66 error?.toString() || 67 l`We were unable to load your birthdate preferences. Please try again.` 68 } 69 style={[a.rounded_sm]} 70 /> 71 ) : isUsingAppPassword ? ( 72 <Admonition type="info"> 73 <Trans> 74 Hmm, it looks like you're signed in with an{' '} 75 <Span style={[a.italic]}>App Password</Span>. To set your 76 birthdate, you'll need to sign in with your main account 77 password, or ask whomever controls this account to do so. 78 </Trans> 79 </Admonition> 80 ) : ( 81 <BirthdayInner control={control} preferences={preferences} /> 82 )} 83 </View> 84 85 <Dialog.Close /> 86 </Dialog.ScrollableInner> 87 ) : ( 88 <Dialog.ScrollableInner 89 label={l`You recently changed your birthdate`} 90 style={web({maxWidth: 400})}> 91 <View style={[a.gap_sm]}> 92 <Text 93 style={[ 94 a.text_xl, 95 a.font_semi_bold, 96 a.leading_snug, 97 {paddingRight: 32}, 98 ]}> 99 <Trans>You recently changed your birthdate</Trans> 100 </Text> 101 <Text 102 style={[a.text_md, a.leading_snug, t.atoms.text_contrast_medium]}> 103 <Trans> 104 There is a limit to how often you can change your birthdate. You 105 may need to wait a day or two before updating it again. 106 </Trans> 107 </Text> 108 </View> 109 110 <Dialog.Close /> 111 </Dialog.ScrollableInner> 112 )} 113 </Dialog.Outer> 114 ) 115} 116 117function BirthdayInner({ 118 control, 119 preferences, 120}: { 121 control: Dialog.DialogControlProps 122 preferences: UsePreferencesQueryResponse 123}) { 124 const {t: l} = useLingui() 125 const cleanError = useCleanError() 126 const [date, setDate] = useState(preferences.birthDate || getDateAgo(18)) 127 const {isPending, error, mutateAsync: setBirthDate} = useBirthdateMutation() 128 const hasChanged = date !== preferences.birthDate 129 const errorMessage = useMemo(() => { 130 if (error) { 131 const e = error as Error 132 const {raw, clean} = cleanError(e) 133 return clean || raw || e.toString() 134 } 135 }, [error, cleanError]) 136 137 const age = getAge(new Date(date)) 138 const isUnder13 = age < 13 139 const isUnder18 = age >= 13 && age < 18 140 141 const onSave = useCallback(async () => { 142 try { 143 // skip if date is the same 144 if (hasChanged) { 145 await setBirthDate({birthDate: date}) 146 } 147 control.close() 148 } catch (error) { 149 const e = error as Error 150 logger.error(`setBirthDate failed`, {message: e.message}) 151 } 152 }, [date, setBirthDate, control, hasChanged]) 153 154 return ( 155 <View style={a.gap_lg} testID="birthDateSettingsDialog"> 156 <View style={IS_IOS && [a.w_full, a.align_center]}> 157 <DateField 158 testID="birthdayInput" 159 value={date} 160 onChangeDate={newDate => setDate(new Date(newDate))} 161 label={l`Birthdate`} 162 accessibilityHint={l`Enter your birthdate`} 163 /> 164 </View> 165 {isUnder18 && hasChanged && ( 166 <Admonition type="info"> 167 <Trans> 168 The birthdate you've entered means you are under 18 years old. 169 Certain content and features may be unavailable to you. 170 </Trans> 171 </Admonition> 172 )} 173 {isUnder13 && ( 174 <Admonition type="error"> 175 <Trans> 176 You must be at least 13 years old to use Bluesky. Read our{' '} 177 <SimpleInlineLinkText 178 to="https://bsky.social/about/support/tos" 179 label={l`Terms of Service`}> 180 Terms of Service 181 </SimpleInlineLinkText>{' '} 182 for more information. 183 </Trans> 184 </Admonition> 185 )} 186 {errorMessage ? ( 187 <ErrorMessage message={errorMessage} style={[a.rounded_sm]} /> 188 ) : undefined} 189 <View style={IS_WEB && [a.flex_row, a.justify_end]}> 190 <Button 191 label={hasChanged ? l`Save birthdate` : l`Done`} 192 size="large" 193 onPress={() => void onSave()} 194 variant="solid" 195 color="primary" 196 disabled={isUnder13}> 197 <ButtonText> 198 {hasChanged ? <Trans>Save</Trans> : <Trans>Done</Trans>} 199 </ButtonText> 200 {isPending && <ButtonIcon icon={Loader} />} 201 </Button> 202 </View> 203 </View> 204 ) 205}