Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

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

Merge pull request #3218 from bluesky-social/samuel/alf-birthday

Use ALF for the birthday modal and remove legacy one

authored by

Samuel Newman and committed by
GitHub
e30b3d9b 4f838167

+152 -171
+3 -1
src/components/Dialog/index.tsx
··· 23 23 DialogInnerProps, 24 24 } from '#/components/Dialog/types' 25 25 import {Context} from '#/components/Dialog/context' 26 + import {isNative} from 'platform/detection' 26 27 27 28 export {useDialogControl, useDialogContext} from '#/components/Dialog/context' 28 29 export * from '#/components/Dialog/types' ··· 221 222 borderTopRightRadius: 40, 222 223 }, 223 224 flatten(style), 224 - ]}> 225 + ]} 226 + contentContainerStyle={isNative ? a.pb_4xl : undefined}> 225 227 {children} 226 228 <View style={{height: insets.bottom + a.pt_5xl.paddingTop}} /> 227 229 </BottomSheetScrollView>
-2
src/components/Prompt.tsx
··· 3 3 import {msg} from '@lingui/macro' 4 4 import {useLingui} from '@lingui/react' 5 5 6 - import {isNative} from '#/platform/detection' 7 6 import {useTheme, atoms as a, useBreakpoints} from '#/alf' 8 7 import {Text} from '#/components/Typography' 9 8 import {Button, ButtonColor, ButtonText} from '#/components/Button' ··· 86 85 gtMobile 87 86 ? [a.flex_row, a.flex_row_reverse, a.justify_start] 88 87 : [a.flex_col], 89 - isNative && [a.pb_4xl], 90 88 ]}> 91 89 {children} 92 90 </View>
+124
src/components/dialogs/BirthDateSettings.tsx
··· 1 + import React from 'react' 2 + import {useLingui} from '@lingui/react' 3 + import {Trans, msg} from '@lingui/macro' 4 + 5 + import * as Dialog from '#/components/Dialog' 6 + import {Text} from '../Typography' 7 + import {DateInput} from '#/view/com/util/forms/DateInput' 8 + import {logger} from '#/logger' 9 + import { 10 + usePreferencesSetBirthDateMutation, 11 + UsePreferencesQueryResponse, 12 + } from '#/state/queries/preferences' 13 + import {Button, ButtonText} from '../Button' 14 + import {atoms as a, useTheme} from '#/alf' 15 + import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' 16 + import {cleanError} from '#/lib/strings/errors' 17 + import {ActivityIndicator, View} from 'react-native' 18 + import {isIOS, isWeb} from '#/platform/detection' 19 + 20 + export function BirthDateSettingsDialog({ 21 + control, 22 + preferences, 23 + }: { 24 + control: Dialog.DialogControlProps 25 + preferences: UsePreferencesQueryResponse | undefined 26 + }) { 27 + const {_} = useLingui() 28 + const {isPending, isError, error, mutateAsync} = 29 + usePreferencesSetBirthDateMutation() 30 + 31 + return ( 32 + <Dialog.Outer control={control}> 33 + <Dialog.Handle /> 34 + <Dialog.ScrollableInner label={_(msg`My Birthday`)}> 35 + {preferences && !isPending ? ( 36 + <BirthdayInner 37 + control={control} 38 + preferences={preferences} 39 + isError={isError} 40 + error={error} 41 + setBirthDate={mutateAsync} 42 + /> 43 + ) : ( 44 + <ActivityIndicator size="large" style={a.my_5xl} /> 45 + )} 46 + </Dialog.ScrollableInner> 47 + </Dialog.Outer> 48 + ) 49 + } 50 + 51 + function BirthdayInner({ 52 + control, 53 + preferences, 54 + isError, 55 + error, 56 + setBirthDate, 57 + }: { 58 + control: Dialog.DialogControlProps 59 + preferences: UsePreferencesQueryResponse 60 + isError: boolean 61 + error: unknown 62 + setBirthDate: (args: {birthDate: Date}) => Promise<unknown> 63 + }) { 64 + const {_} = useLingui() 65 + const [date, setDate] = React.useState(preferences.birthDate || new Date()) 66 + const t = useTheme() 67 + 68 + const hasChanged = date !== preferences.birthDate 69 + 70 + const onSave = React.useCallback(async () => { 71 + try { 72 + // skip if date is the same 73 + if (hasChanged) { 74 + await setBirthDate({birthDate: date}) 75 + } 76 + control.close() 77 + } catch (e) { 78 + logger.error(`setBirthDate failed`, {message: e}) 79 + } 80 + }, [date, setBirthDate, control, hasChanged]) 81 + 82 + return ( 83 + <View style={a.gap_lg} testID="birthDateSettingsDialog"> 84 + <View style={[a.gap_sm]}> 85 + <Text style={[a.text_2xl, a.font_bold]}> 86 + <Trans>My Birthday</Trans> 87 + </Text> 88 + <Text style={t.atoms.text_contrast_medium}> 89 + <Trans>This information is not shared with other users.</Trans> 90 + </Text> 91 + </View> 92 + <View style={isIOS && [a.w_full, a.align_center]}> 93 + <DateInput 94 + handleAsUTC 95 + testID="birthdayInput" 96 + value={date} 97 + onChange={setDate} 98 + buttonType="default-light" 99 + buttonStyle={[a.rounded_sm]} 100 + buttonLabelType="lg" 101 + accessibilityLabel={_(msg`Birthday`)} 102 + accessibilityHint={_(msg`Enter your birth date`)} 103 + accessibilityLabelledBy="birthDate" 104 + /> 105 + </View> 106 + {isError ? ( 107 + <ErrorMessage message={cleanError(error)} style={[a.rounded_sm]} /> 108 + ) : undefined} 109 + 110 + <View style={isWeb && [a.flex_row, a.justify_end]}> 111 + <Button 112 + label={hasChanged ? _(msg`Save birthday`) : _(msg`Done`)} 113 + size={isWeb ? 'small' : 'medium'} 114 + onPress={onSave} 115 + variant="solid" 116 + color="primary"> 117 + <ButtonText> 118 + {hasChanged ? <Trans>Save</Trans> : <Trans>Done</Trans>} 119 + </ButtonText> 120 + </Button> 121 + </View> 122 + </View> 123 + ) 124 + }
-5
src/state/modals/index.tsx
··· 135 135 name: 'post-languages-settings' 136 136 } 137 137 138 - export interface BirthDateSettingsModal { 139 - name: 'birth-date-settings' 140 - } 141 - 142 138 export interface VerifyEmailModal { 143 139 name: 'verify-email' 144 140 showReminder?: boolean ··· 179 175 | ChangeHandleModal 180 176 | DeleteAccountModal 181 177 | EditProfileModal 182 - | BirthDateSettingsModal 183 178 | VerifyEmailModal 184 179 | ChangeEmailModal 185 180 | ChangePasswordModal
-151
src/view/com/modals/BirthDateSettings.tsx
··· 1 - import React, {useState} from 'react' 2 - import { 3 - ActivityIndicator, 4 - StyleSheet, 5 - TouchableOpacity, 6 - View, 7 - } from 'react-native' 8 - import {Text} from '../util/text/Text' 9 - import {DateInput} from '../util/forms/DateInput' 10 - import {ErrorMessage} from '../util/error/ErrorMessage' 11 - import {s, colors} from 'lib/styles' 12 - import {usePalette} from 'lib/hooks/usePalette' 13 - import {isWeb} from 'platform/detection' 14 - import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 15 - import {cleanError} from 'lib/strings/errors' 16 - import {Trans, msg} from '@lingui/macro' 17 - import {useLingui} from '@lingui/react' 18 - import {useModalControls} from '#/state/modals' 19 - import { 20 - usePreferencesQuery, 21 - usePreferencesSetBirthDateMutation, 22 - UsePreferencesQueryResponse, 23 - } from '#/state/queries/preferences' 24 - import {logger} from '#/logger' 25 - 26 - export const snapPoints = ['50%', '90%'] 27 - 28 - function Inner({preferences}: {preferences: UsePreferencesQueryResponse}) { 29 - const pal = usePalette('default') 30 - const {isMobile} = useWebMediaQueries() 31 - const {_} = useLingui() 32 - const { 33 - isPending, 34 - isError, 35 - error, 36 - mutateAsync: setBirthDate, 37 - } = usePreferencesSetBirthDateMutation() 38 - const [date, setDate] = useState(preferences.birthDate || new Date()) 39 - const {closeModal} = useModalControls() 40 - 41 - const onSave = React.useCallback(async () => { 42 - try { 43 - await setBirthDate({birthDate: date}) 44 - closeModal() 45 - } catch (e) { 46 - logger.error(`setBirthDate failed`, {message: e}) 47 - } 48 - }, [date, setBirthDate, closeModal]) 49 - 50 - return ( 51 - <View 52 - testID="birthDateSettingsModal" 53 - style={[pal.view, styles.container, isMobile && {paddingHorizontal: 18}]}> 54 - <View style={styles.titleSection}> 55 - <Text type="title-lg" style={[pal.text, styles.title]}> 56 - <Trans>My Birthday</Trans> 57 - </Text> 58 - </View> 59 - 60 - <Text type="lg" style={[pal.textLight, {marginBottom: 10}]}> 61 - <Trans>This information is not shared with other users.</Trans> 62 - </Text> 63 - 64 - <View> 65 - <DateInput 66 - handleAsUTC 67 - testID="birthdayInput" 68 - value={date} 69 - onChange={setDate} 70 - buttonType="default-light" 71 - buttonStyle={[pal.border, styles.dateInputButton]} 72 - buttonLabelType="lg" 73 - accessibilityLabel={_(msg`Birthday`)} 74 - accessibilityHint={_(msg`Enter your birth date`)} 75 - accessibilityLabelledBy="birthDate" 76 - /> 77 - </View> 78 - 79 - {isError ? ( 80 - <ErrorMessage message={cleanError(error)} style={styles.error} /> 81 - ) : undefined} 82 - 83 - <View style={[styles.btnContainer, pal.borderDark]}> 84 - {isPending ? ( 85 - <View style={styles.btn}> 86 - <ActivityIndicator color="#fff" /> 87 - </View> 88 - ) : ( 89 - <TouchableOpacity 90 - testID="confirmBtn" 91 - onPress={onSave} 92 - style={styles.btn} 93 - accessibilityRole="button" 94 - accessibilityLabel={_(msg`Save`)} 95 - accessibilityHint=""> 96 - <Text style={[s.white, s.bold, s.f18]}> 97 - <Trans>Save</Trans> 98 - </Text> 99 - </TouchableOpacity> 100 - )} 101 - </View> 102 - </View> 103 - ) 104 - } 105 - 106 - export function Component({}: {}) { 107 - const {data: preferences} = usePreferencesQuery() 108 - 109 - return !preferences ? ( 110 - <ActivityIndicator /> 111 - ) : ( 112 - <Inner preferences={preferences} /> 113 - ) 114 - } 115 - 116 - const styles = StyleSheet.create({ 117 - container: { 118 - flex: 1, 119 - paddingBottom: isWeb ? 0 : 40, 120 - }, 121 - titleSection: { 122 - paddingTop: isWeb ? 0 : 4, 123 - paddingBottom: isWeb ? 14 : 10, 124 - }, 125 - title: { 126 - textAlign: 'center', 127 - fontWeight: '600', 128 - marginBottom: 5, 129 - }, 130 - error: { 131 - borderRadius: 6, 132 - marginTop: 10, 133 - }, 134 - dateInputButton: { 135 - borderWidth: 1, 136 - borderRadius: 6, 137 - paddingVertical: 14, 138 - }, 139 - btn: { 140 - flexDirection: 'row', 141 - alignItems: 'center', 142 - justifyContent: 'center', 143 - borderRadius: 32, 144 - padding: 14, 145 - backgroundColor: colors.blue3, 146 - }, 147 - btnContainer: { 148 - paddingTop: 20, 149 - paddingHorizontal: 20, 150 - }, 151 - })
+9 -3
src/view/com/modals/ContentFilteringSettings.tsx
··· 24 24 CONFIGURABLE_LABEL_GROUPS, 25 25 UsePreferencesQueryResponse, 26 26 } from '#/state/queries/preferences' 27 + import {useDialogControl} from '#/components/Dialog' 28 + import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings' 27 29 28 30 export const snapPoints = ['90%'] 29 31 ··· 107 109 const {_} = useLingui() 108 110 const {data: preferences} = usePreferencesQuery() 109 111 const {mutate, variables} = usePreferencesSetAdultContentMutation() 110 - const {openModal} = useModalControls() 112 + const bithdayDialogControl = useDialogControl() 111 113 112 114 const onSetAge = React.useCallback( 113 - () => openModal({name: 'birth-date-settings'}), 114 - [openModal], 115 + () => bithdayDialogControl.open(), 116 + [bithdayDialogControl], 115 117 ) 116 118 117 119 const onToggleAdultContent = React.useCallback(async () => { ··· 135 137 136 138 return ( 137 139 <View style={s.mb10}> 140 + <BirthDateSettingsDialog 141 + control={bithdayDialogControl} 142 + preferences={preferences} 143 + /> 138 144 {isIOS ? ( 139 145 preferences?.adultContentEnabled ? null : ( 140 146 <Text type="md" style={pal.textLight}>
-4
src/view/com/modals/Modal.tsx
··· 25 25 import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguagesSettings' 26 26 import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettings' 27 27 import * as ModerationDetailsModal from './ModerationDetails' 28 - import * as BirthDateSettingsModal from './BirthDateSettings' 29 28 import * as VerifyEmailModal from './VerifyEmail' 30 29 import * as ChangeEmailModal from './ChangeEmail' 31 30 import * as ChangePasswordModal from './ChangePassword' ··· 122 121 } else if (activeModal?.name === 'moderation-details') { 123 122 snapPoints = ModerationDetailsModal.snapPoints 124 123 element = <ModerationDetailsModal.Component {...activeModal} /> 125 - } else if (activeModal?.name === 'birth-date-settings') { 126 - snapPoints = BirthDateSettingsModal.snapPoints 127 - element = <BirthDateSettingsModal.Component /> 128 124 } else if (activeModal?.name === 'verify-email') { 129 125 snapPoints = VerifyEmailModal.snapPoints 130 126 element = <VerifyEmailModal.Component {...activeModal} />
-3
src/view/com/modals/Modal.web.tsx
··· 27 27 import * as ContentLanguagesSettingsModal from './lang-settings/ContentLanguagesSettings' 28 28 import * as PostLanguagesSettingsModal from './lang-settings/PostLanguagesSettings' 29 29 import * as ModerationDetailsModal from './ModerationDetails' 30 - import * as BirthDateSettingsModal from './BirthDateSettings' 31 30 import * as VerifyEmailModal from './VerifyEmail' 32 31 import * as ChangeEmailModal from './ChangeEmail' 33 32 import * as ChangePasswordModal from './ChangePassword' ··· 117 116 element = <EditImageModal.Component {...modal} /> 118 117 } else if (modal.name === 'moderation-details') { 119 118 element = <ModerationDetailsModal.Component {...modal} /> 120 - } else if (modal.name === 'birth-date-settings') { 121 - element = <BirthDateSettingsModal.Component /> 122 119 } else if (modal.name === 'verify-email') { 123 120 element = <VerifyEmailModal.Component {...modal} /> 124 121 } else if (modal.name === 'change-email') {
+16 -2
src/view/screens/Settings/index.tsx
··· 40 40 } from '#/state/preferences' 41 41 import {useSession, useSessionApi, SessionAccount} from '#/state/session' 42 42 import {useProfileQuery} from '#/state/queries/profile' 43 - import {useClearPreferencesMutation} from '#/state/queries/preferences' 43 + import { 44 + useClearPreferencesMutation, 45 + usePreferencesQuery, 46 + } from '#/state/queries/preferences' 44 47 // TODO import {useInviteCodesQuery} from '#/state/queries/invites' 45 48 import {clear as clearStorage} from '#/state/persisted/store' 46 49 import {clearLegacyStorage} from '#/state/persisted/legacy' ··· 68 71 import {AccountDropdownBtn} from 'view/com/util/AccountDropdownBtn' 69 72 import {SimpleViewHeader} from 'view/com/util/SimpleViewHeader' 70 73 import {ExportCarDialog} from './ExportCarDialog' 74 + import {BirthDateSettingsDialog} from '#/components/dialogs/BirthDateSettings' 71 75 72 76 function SettingsAccountCard({account}: {account: SessionAccount}) { 73 77 const pal = usePalette('default') ··· 152 156 const {screen, track} = useAnalytics() 153 157 const {openModal} = useModalControls() 154 158 const {isSwitchingAccounts, accounts, currentAccount} = useSession() 159 + const {data: preferences} = usePreferencesQuery() 155 160 const {mutate: clearPreferences} = useClearPreferencesMutation() 156 161 // TODO 157 162 // const {data: invites} = useInviteCodesQuery() ··· 159 164 const {setShowLoggedOut} = useLoggedOutViewControls() 160 165 const closeAllActiveElements = useCloseAllActiveElements() 161 166 const exportCarControl = useDialogControl() 167 + const birthdayControl = useDialogControl() 162 168 163 169 // const primaryBg = useCustomPalette<ViewStyle>({ 164 170 // light: {backgroundColor: colors.blue0}, ··· 269 275 Linking.openURL(STATUS_PAGE_URL) 270 276 }, []) 271 277 278 + const onPressBirthday = React.useCallback(() => { 279 + birthdayControl.open() 280 + }, [birthdayControl]) 281 + 272 282 const clearAllStorage = React.useCallback(async () => { 273 283 await clearStorage() 274 284 Toast.show(_(msg`Storage cleared, you need to restart the app now.`)) ··· 281 291 return ( 282 292 <View style={s.hContentRegion} testID="settingsScreen"> 283 293 <ExportCarDialog control={exportCarControl} /> 294 + <BirthDateSettingsDialog 295 + control={birthdayControl} 296 + preferences={preferences} 297 + /> 284 298 285 299 <SimpleViewHeader 286 300 showBackButton={isMobile} ··· 339 353 <Text type="lg-medium" style={pal.text}> 340 354 <Trans>Birthday:</Trans>{' '} 341 355 </Text> 342 - <Link onPress={() => openModal({name: 'birth-date-settings'})}> 356 + <Link onPress={onPressBirthday}> 343 357 <Text type="lg" style={pal.link}> 344 358 <Trans>Show</Trans> 345 359 </Text>