Bluesky app fork with some witchin' additions 💫

Modernise change password dialog (#8269)

* alf change password dialog

* rm old modal

* move dialog

* fix buttons on native

* lowercase

* fix dupe import

* Apply suggestions from code review

Thanks @surfdude29 :)

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

* use primary_subtle, change web layout

* move to into non-network err, warn -> error

* error -> safeMessage

* better message than token is invalid

* cancel button native only

* move close to end of focus priority

---------

Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com>

authored by samuel.fm

surfdude29 and committed by
GitHub
98d96bd2 dd2830ba

+304 -363
+4 -1
src/screens/Settings/AccountSettings.tsx
··· 25 25 import {Trash_Stroke2_Corner2_Rounded} from '#/components/icons/Trash' 26 26 import * as Layout from '#/components/Layout' 27 27 import {ChangeHandleDialog} from './components/ChangeHandleDialog' 28 + import {ChangePasswordDialog} from './components/ChangePasswordDialog' 28 29 import {DeactivateAccountDialog} from './components/DeactivateAccountDialog' 29 30 import {ExportCarDialog} from './components/ExportCarDialog' 30 31 ··· 37 38 const emailDialogControl = useEmailDialogControl() 38 39 const birthdayControl = useDialogControl() 39 40 const changeHandleControl = useDialogControl() 41 + const changePasswordControl = useDialogControl() 40 42 const exportCarControl = useDialogControl() 41 43 const deactivateAccountControl = useDialogControl() 42 44 ··· 117 119 <SettingsList.Divider /> 118 120 <SettingsList.PressableItem 119 121 label={_(msg`Password`)} 120 - onPress={() => openModal({name: 'change-password'})}> 122 + onPress={() => changePasswordControl.open()}> 121 123 <SettingsList.ItemIcon icon={LockIcon} /> 122 124 <SettingsList.ItemText> 123 125 <Trans>Password</Trans> ··· 180 182 181 183 <BirthDateSettingsDialog control={birthdayControl} /> 182 184 <ChangeHandleDialog control={changeHandleControl} /> 185 + <ChangePasswordDialog control={changePasswordControl} /> 183 186 <ExportCarDialog control={exportCarControl} /> 184 187 <DeactivateAccountDialog control={deactivateAccountControl} /> 185 188 </Layout.Screen>
+300
src/screens/Settings/components/ChangePasswordDialog.tsx
··· 1 + import {useState} from 'react' 2 + import {useWindowDimensions, View} from 'react-native' 3 + import {msg, Trans} from '@lingui/macro' 4 + import {useLingui} from '@lingui/react' 5 + import * as EmailValidator from 'email-validator' 6 + 7 + import {cleanError, isNetworkError} from '#/lib/strings/errors' 8 + import {checkAndFormatResetCode} from '#/lib/strings/password' 9 + import {logger} from '#/logger' 10 + import {isNative} from '#/platform/detection' 11 + import {useAgent, useSession} from '#/state/session' 12 + import {ErrorMessage} from '#/view/com/util/error/ErrorMessage' 13 + import {android, atoms as a, web} from '#/alf' 14 + import {Button, ButtonIcon, ButtonText} from '#/components/Button' 15 + import * as Dialog from '#/components/Dialog' 16 + import * as TextField from '#/components/forms/TextField' 17 + import {Loader} from '#/components/Loader' 18 + import {Text} from '#/components/Typography' 19 + 20 + enum Stages { 21 + RequestCode = 'RequestCode', 22 + ChangePassword = 'ChangePassword', 23 + Done = 'Done', 24 + } 25 + 26 + export function ChangePasswordDialog({ 27 + control, 28 + }: { 29 + control: Dialog.DialogControlProps 30 + }) { 31 + const {height} = useWindowDimensions() 32 + 33 + return ( 34 + <Dialog.Outer 35 + control={control} 36 + nativeOptions={android({minHeight: height / 2})}> 37 + <Dialog.Handle /> 38 + <Inner /> 39 + </Dialog.Outer> 40 + ) 41 + } 42 + 43 + function Inner() { 44 + const {_} = useLingui() 45 + const {currentAccount} = useSession() 46 + const agent = useAgent() 47 + const control = Dialog.useDialogContext() 48 + 49 + const [stage, setStage] = useState(Stages.RequestCode) 50 + const [isProcessing, setIsProcessing] = useState(false) 51 + const [resetCode, setResetCode] = useState('') 52 + const [newPassword, setNewPassword] = useState('') 53 + const [error, setError] = useState('') 54 + 55 + const uiStrings = { 56 + RequestCode: { 57 + title: _(msg`Change your password`), 58 + message: _( 59 + msg`If you want to change your password, we will send you a code to verify that this is your account.`, 60 + ), 61 + }, 62 + ChangePassword: { 63 + title: _(msg`Enter code`), 64 + message: _( 65 + msg`Please enter the code you received and the new password you would like to use.`, 66 + ), 67 + }, 68 + Done: { 69 + title: _(msg`Password changed`), 70 + message: _( 71 + msg`Your password has been changed successfully! Please use your new password when you sign in to Bluesky from now on.`, 72 + ), 73 + }, 74 + } 75 + 76 + const onRequestCode = async () => { 77 + if ( 78 + !currentAccount?.email || 79 + !EmailValidator.validate(currentAccount.email) 80 + ) { 81 + return setError(_(msg`Your email appears to be invalid.`)) 82 + } 83 + 84 + setError('') 85 + setIsProcessing(true) 86 + try { 87 + await agent.com.atproto.server.requestPasswordReset({ 88 + email: currentAccount.email, 89 + }) 90 + setStage(Stages.ChangePassword) 91 + } catch (e: any) { 92 + if (isNetworkError(e)) { 93 + setError( 94 + _( 95 + msg`Unable to contact your service. Please check your internet connection and try again.`, 96 + ), 97 + ) 98 + } else { 99 + logger.error('Failed to request password reset', {safeMessage: e}) 100 + setError(cleanError(e)) 101 + } 102 + } finally { 103 + setIsProcessing(false) 104 + } 105 + } 106 + 107 + const onChangePassword = async () => { 108 + const formattedCode = checkAndFormatResetCode(resetCode) 109 + if (!formattedCode) { 110 + setError( 111 + _( 112 + msg`You have entered an invalid code. It should look like XXXXX-XXXXX.`, 113 + ), 114 + ) 115 + return 116 + } 117 + if (!newPassword) { 118 + setError( 119 + _(msg`Please enter a password. It must be at least 8 characters long.`), 120 + ) 121 + return 122 + } 123 + if (newPassword.length < 8) { 124 + setError(_(msg`Password must be at least 8 characters long.`)) 125 + return 126 + } 127 + 128 + setError('') 129 + setIsProcessing(true) 130 + try { 131 + await agent.com.atproto.server.resetPassword({ 132 + token: formattedCode, 133 + password: newPassword, 134 + }) 135 + setStage(Stages.Done) 136 + } catch (e: any) { 137 + if (isNetworkError(e)) { 138 + setError( 139 + _( 140 + msg`Unable to contact your service. Please check your internet connection and try again.`, 141 + ), 142 + ) 143 + } else if (e?.toString().includes('Token is invalid')) { 144 + setError(_(msg`This confirmation code is not valid. Please try again.`)) 145 + } else { 146 + logger.error('Failed to set new password', {safeMessage: e}) 147 + setError(cleanError(e)) 148 + } 149 + } finally { 150 + setIsProcessing(false) 151 + } 152 + } 153 + 154 + const onBlur = () => { 155 + const formattedCode = checkAndFormatResetCode(resetCode) 156 + if (!formattedCode) { 157 + return 158 + } 159 + setResetCode(formattedCode) 160 + } 161 + 162 + return ( 163 + <Dialog.ScrollableInner 164 + label={_(msg`Change password dialog`)} 165 + style={web({maxWidth: 400})}> 166 + <View style={[a.gap_xl]}> 167 + <View style={[a.gap_sm]}> 168 + <Text style={[a.font_heavy, a.text_2xl]}> 169 + {uiStrings[stage].title} 170 + </Text> 171 + {error ? ( 172 + <View style={[a.rounded_sm, a.overflow_hidden]}> 173 + <ErrorMessage message={error} /> 174 + </View> 175 + ) : null} 176 + 177 + <Text style={[a.text_md, a.leading_snug]}> 178 + {uiStrings[stage].message} 179 + </Text> 180 + </View> 181 + 182 + {stage === Stages.ChangePassword && ( 183 + <View style={[a.gap_md]}> 184 + <View> 185 + <TextField.LabelText> 186 + <Trans>Confirmation code</Trans> 187 + </TextField.LabelText> 188 + <TextField.Root> 189 + <TextField.Input 190 + label={_(msg`Confirmation code`)} 191 + placeholder="XXXXX-XXXXX" 192 + value={resetCode} 193 + onChangeText={setResetCode} 194 + onBlur={onBlur} 195 + autoCapitalize="none" 196 + autoCorrect={false} 197 + autoComplete="one-time-code" 198 + /> 199 + </TextField.Root> 200 + </View> 201 + <View> 202 + <TextField.LabelText> 203 + <Trans>New password</Trans> 204 + </TextField.LabelText> 205 + <TextField.Root> 206 + <TextField.Input 207 + label={_(msg`New password`)} 208 + placeholder={_(msg`At least 8 characters`)} 209 + value={newPassword} 210 + onChangeText={setNewPassword} 211 + secureTextEntry 212 + autoCapitalize="none" 213 + autoComplete="new-password" 214 + /> 215 + </TextField.Root> 216 + </View> 217 + </View> 218 + )} 219 + 220 + <View style={[a.gap_sm]}> 221 + {stage === Stages.RequestCode ? ( 222 + <> 223 + <Button 224 + label={_(msg`Request code`)} 225 + color="primary" 226 + size="large" 227 + disabled={isProcessing} 228 + onPress={onRequestCode}> 229 + <ButtonText> 230 + <Trans>Request code</Trans> 231 + </ButtonText> 232 + {isProcessing && <ButtonIcon icon={Loader} />} 233 + </Button> 234 + <Button 235 + label={_(msg`Already have a code?`)} 236 + onPress={() => setStage(Stages.ChangePassword)} 237 + size="large" 238 + color="primary_subtle" 239 + disabled={isProcessing}> 240 + <ButtonText> 241 + <Trans>Already have a code?</Trans> 242 + </ButtonText> 243 + </Button> 244 + {isNative && ( 245 + <Button 246 + label={_(msg`Cancel`)} 247 + color="secondary" 248 + size="large" 249 + disabled={isProcessing} 250 + onPress={() => control.close()}> 251 + <ButtonText> 252 + <Trans>Cancel</Trans> 253 + </ButtonText> 254 + </Button> 255 + )} 256 + </> 257 + ) : stage === Stages.ChangePassword ? ( 258 + <> 259 + <Button 260 + label={_(msg`Change password`)} 261 + color="primary" 262 + size="large" 263 + disabled={isProcessing} 264 + onPress={onChangePassword}> 265 + <ButtonText> 266 + <Trans>Change password</Trans> 267 + </ButtonText> 268 + {isProcessing && <ButtonIcon icon={Loader} />} 269 + </Button> 270 + <Button 271 + label={_(msg`Back`)} 272 + color="secondary" 273 + size="large" 274 + disabled={isProcessing} 275 + onPress={() => { 276 + setResetCode('') 277 + setStage(Stages.RequestCode) 278 + }}> 279 + <ButtonText> 280 + <Trans>Back</Trans> 281 + </ButtonText> 282 + </Button> 283 + </> 284 + ) : stage === Stages.Done ? ( 285 + <Button 286 + label={_(msg`Close`)} 287 + color="primary" 288 + size="large" 289 + onPress={() => control.close()}> 290 + <ButtonText> 291 + <Trans>Close</Trans> 292 + </ButtonText> 293 + </Button> 294 + ) : null} 295 + </View> 296 + </View> 297 + <Dialog.Close /> 298 + </Dialog.ScrollableInner> 299 + ) 300 + }
-5
src/state/modals/index.tsx
··· 39 39 name: 'post-languages-settings' 40 40 } 41 41 42 - export interface ChangePasswordModal { 43 - name: 'change-password' 44 - } 45 - 46 42 /** 47 43 * @deprecated DO NOT ADD NEW MODALS 48 44 */ 49 45 export type Modal = 50 46 // Account 51 47 | DeleteAccountModal 52 - | ChangePasswordModal 53 48 54 49 // Curation 55 50 | ContentLanguagesSettingsModal
-350
src/view/com/modals/ChangePassword.tsx
··· 1 - import {useState} from 'react' 2 - import { 3 - ActivityIndicator, 4 - SafeAreaView, 5 - StyleSheet, 6 - TouchableOpacity, 7 - View, 8 - } from 'react-native' 9 - import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 10 - import {msg, Trans} from '@lingui/macro' 11 - import {useLingui} from '@lingui/react' 12 - import * as EmailValidator from 'email-validator' 13 - 14 - import {usePalette} from '#/lib/hooks/usePalette' 15 - import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 16 - import {cleanError, isNetworkError} from '#/lib/strings/errors' 17 - import {checkAndFormatResetCode} from '#/lib/strings/password' 18 - import {colors, s} from '#/lib/styles' 19 - import {logger} from '#/logger' 20 - import {isAndroid, isWeb} from '#/platform/detection' 21 - import {useModalControls} from '#/state/modals' 22 - import {useAgent, useSession} from '#/state/session' 23 - import {ErrorMessage} from '../util/error/ErrorMessage' 24 - import {Button} from '../util/forms/Button' 25 - import {Text} from '../util/text/Text' 26 - import {ScrollView} from './util' 27 - import {TextInput} from './util' 28 - 29 - enum Stages { 30 - RequestCode, 31 - ChangePassword, 32 - Done, 33 - } 34 - 35 - export const snapPoints = isAndroid ? ['90%'] : ['45%'] 36 - 37 - export function Component() { 38 - const pal = usePalette('default') 39 - const {currentAccount} = useSession() 40 - const agent = useAgent() 41 - const {_} = useLingui() 42 - const [stage, setStage] = useState<Stages>(Stages.RequestCode) 43 - const [isProcessing, setIsProcessing] = useState<boolean>(false) 44 - const [resetCode, setResetCode] = useState<string>('') 45 - const [newPassword, setNewPassword] = useState<string>('') 46 - const [error, setError] = useState<string>('') 47 - const {isMobile} = useWebMediaQueries() 48 - const {closeModal} = useModalControls() 49 - 50 - const onRequestCode = async () => { 51 - if ( 52 - !currentAccount?.email || 53 - !EmailValidator.validate(currentAccount.email) 54 - ) { 55 - return setError(_(msg`Your email appears to be invalid.`)) 56 - } 57 - 58 - setError('') 59 - setIsProcessing(true) 60 - try { 61 - await agent.com.atproto.server.requestPasswordReset({ 62 - email: currentAccount.email, 63 - }) 64 - setStage(Stages.ChangePassword) 65 - } catch (e: any) { 66 - const errMsg = e.toString() 67 - logger.warn('Failed to request password reset', {error: e}) 68 - if (isNetworkError(e)) { 69 - setError( 70 - _( 71 - msg`Unable to contact your service. Please check your Internet connection.`, 72 - ), 73 - ) 74 - } else { 75 - setError(cleanError(errMsg)) 76 - } 77 - } finally { 78 - setIsProcessing(false) 79 - } 80 - } 81 - 82 - const onChangePassword = async () => { 83 - const formattedCode = checkAndFormatResetCode(resetCode) 84 - if (!formattedCode) { 85 - setError( 86 - _( 87 - msg`You have entered an invalid code. It should look like XXXXX-XXXXX.`, 88 - ), 89 - ) 90 - return 91 - } 92 - if (!newPassword) { 93 - setError( 94 - _(msg`Please enter a password. It must be at least 8 characters long.`), 95 - ) 96 - return 97 - } 98 - if (newPassword.length < 8) { 99 - setError(_(msg`Password must be at least 8 characters long.`)) 100 - return 101 - } 102 - 103 - setError('') 104 - setIsProcessing(true) 105 - try { 106 - await agent.com.atproto.server.resetPassword({ 107 - token: formattedCode, 108 - password: newPassword, 109 - }) 110 - setStage(Stages.Done) 111 - } catch (e: any) { 112 - const errMsg = e.toString() 113 - logger.warn('Failed to set new password', {error: e}) 114 - if (isNetworkError(e)) { 115 - setError( 116 - _( 117 - msg`Unable to contact your service. Please check your Internet connection.`, 118 - ), 119 - ) 120 - } else { 121 - setError(cleanError(errMsg)) 122 - } 123 - } finally { 124 - setIsProcessing(false) 125 - } 126 - } 127 - 128 - const onBlur = () => { 129 - const formattedCode = checkAndFormatResetCode(resetCode) 130 - if (!formattedCode) { 131 - setError( 132 - _( 133 - msg`You have entered an invalid code. It should look like XXXXX-XXXXX.`, 134 - ), 135 - ) 136 - return 137 - } 138 - setResetCode(formattedCode) 139 - } 140 - 141 - return ( 142 - <SafeAreaView style={[pal.view, s.flex1]}> 143 - <ScrollView 144 - contentContainerStyle={[ 145 - styles.container, 146 - isMobile && styles.containerMobile, 147 - ]} 148 - keyboardShouldPersistTaps="handled"> 149 - <View> 150 - <View style={styles.titleSection}> 151 - <Text type="title-lg" style={[pal.text, styles.title]}> 152 - {stage !== Stages.Done 153 - ? _(msg`Change Password`) 154 - : _(msg`Password Changed`)} 155 - </Text> 156 - </View> 157 - 158 - <Text type="lg" style={[pal.textLight, {marginBottom: 10}]}> 159 - {stage === Stages.RequestCode ? ( 160 - <Trans> 161 - If you want to change your password, we will send you a code to 162 - verify that this is your account. 163 - </Trans> 164 - ) : stage === Stages.ChangePassword ? ( 165 - <Trans> 166 - Enter the code you received to change your password. 167 - </Trans> 168 - ) : ( 169 - <Trans>Your password has been changed successfully!</Trans> 170 - )} 171 - </Text> 172 - 173 - {stage === Stages.RequestCode && ( 174 - <View style={[s.flexRow, s.justifyCenter, s.mt10]}> 175 - <TouchableOpacity 176 - testID="skipSendEmailButton" 177 - onPress={() => setStage(Stages.ChangePassword)} 178 - accessibilityRole="button" 179 - accessibilityLabel={_(msg`Go to next`)} 180 - accessibilityHint={_(msg`Navigates to the next screen`)}> 181 - <Text type="xl" style={[pal.link, s.pr5]}> 182 - <Trans>Already have a code?</Trans> 183 - </Text> 184 - </TouchableOpacity> 185 - </View> 186 - )} 187 - {stage === Stages.ChangePassword && ( 188 - <View style={[pal.border, styles.group]}> 189 - <View style={[styles.groupContent]}> 190 - <FontAwesomeIcon 191 - icon="ticket" 192 - style={[pal.textLight, styles.groupContentIcon]} 193 - /> 194 - <TextInput 195 - testID="codeInput" 196 - style={[pal.text, styles.textInput]} 197 - placeholder={_(msg`Reset code`)} 198 - placeholderTextColor={pal.colors.textLight} 199 - value={resetCode} 200 - onChangeText={setResetCode} 201 - onFocus={() => setError('')} 202 - onBlur={onBlur} 203 - accessible={true} 204 - accessibilityLabel={_(msg`Reset Code`)} 205 - accessibilityHint="" 206 - autoCapitalize="none" 207 - autoCorrect={false} 208 - autoComplete="off" 209 - /> 210 - </View> 211 - <View 212 - style={[ 213 - pal.borderDark, 214 - styles.groupContent, 215 - styles.groupBottom, 216 - ]}> 217 - <FontAwesomeIcon 218 - icon="lock" 219 - style={[pal.textLight, styles.groupContentIcon]} 220 - /> 221 - <TextInput 222 - testID="codeInput" 223 - style={[pal.text, styles.textInput]} 224 - placeholder={_(msg`New password`)} 225 - placeholderTextColor={pal.colors.textLight} 226 - onChangeText={setNewPassword} 227 - secureTextEntry 228 - accessible={true} 229 - accessibilityLabel={_(msg`New Password`)} 230 - accessibilityHint="" 231 - autoCapitalize="none" 232 - autoComplete="new-password" 233 - /> 234 - </View> 235 - </View> 236 - )} 237 - {error ? ( 238 - <ErrorMessage message={error} style={styles.error} /> 239 - ) : undefined} 240 - </View> 241 - <View style={[styles.btnContainer]}> 242 - {isProcessing ? ( 243 - <View style={styles.btn}> 244 - <ActivityIndicator color="#fff" /> 245 - </View> 246 - ) : ( 247 - <View style={{gap: 6}}> 248 - {stage === Stages.RequestCode && ( 249 - <Button 250 - testID="requestChangeBtn" 251 - type="primary" 252 - onPress={onRequestCode} 253 - accessibilityLabel={_(msg`Request Code`)} 254 - accessibilityHint="" 255 - label={_(msg`Request Code`)} 256 - labelContainerStyle={{justifyContent: 'center', padding: 4}} 257 - labelStyle={[s.f18]} 258 - /> 259 - )} 260 - {stage === Stages.ChangePassword && ( 261 - <Button 262 - testID="confirmBtn" 263 - type="primary" 264 - onPress={onChangePassword} 265 - accessibilityLabel={_(msg`Next`)} 266 - accessibilityHint="" 267 - label={_(msg`Next`)} 268 - labelContainerStyle={{justifyContent: 'center', padding: 4}} 269 - labelStyle={[s.f18]} 270 - /> 271 - )} 272 - <Button 273 - testID="cancelBtn" 274 - type={stage !== Stages.Done ? 'default' : 'primary'} 275 - onPress={() => { 276 - closeModal() 277 - }} 278 - accessibilityLabel={ 279 - stage !== Stages.Done ? _(msg`Cancel`) : _(msg`Close`) 280 - } 281 - accessibilityHint="" 282 - label={stage !== Stages.Done ? _(msg`Cancel`) : _(msg`Close`)} 283 - labelContainerStyle={{justifyContent: 'center', padding: 4}} 284 - labelStyle={[s.f18]} 285 - /> 286 - </View> 287 - )} 288 - </View> 289 - </ScrollView> 290 - </SafeAreaView> 291 - ) 292 - } 293 - 294 - const styles = StyleSheet.create({ 295 - container: { 296 - justifyContent: 'space-between', 297 - }, 298 - containerMobile: { 299 - paddingHorizontal: 18, 300 - paddingBottom: 35, 301 - }, 302 - titleSection: { 303 - paddingTop: isWeb ? 0 : 4, 304 - paddingBottom: isWeb ? 14 : 10, 305 - }, 306 - title: { 307 - textAlign: 'center', 308 - fontWeight: '600', 309 - marginBottom: 5, 310 - }, 311 - error: { 312 - borderRadius: 6, 313 - }, 314 - textInput: { 315 - width: '100%', 316 - paddingHorizontal: 14, 317 - paddingVertical: 10, 318 - fontSize: 16, 319 - }, 320 - btn: { 321 - flexDirection: 'row', 322 - alignItems: 'center', 323 - justifyContent: 'center', 324 - borderRadius: 32, 325 - padding: 14, 326 - backgroundColor: colors.blue3, 327 - }, 328 - btnContainer: { 329 - paddingTop: 20, 330 - }, 331 - group: { 332 - borderWidth: 1, 333 - borderRadius: 10, 334 - marginVertical: 20, 335 - }, 336 - groupLabel: { 337 - paddingHorizontal: 20, 338 - paddingBottom: 5, 339 - }, 340 - groupContent: { 341 - flexDirection: 'row', 342 - alignItems: 'center', 343 - }, 344 - groupBottom: { 345 - borderTopWidth: 1, 346 - }, 347 - groupContentIcon: { 348 - marginLeft: 10, 349 - }, 350 - })
-4
src/view/com/modals/Modal.tsx
··· 7 7 import {useModalControls, useModals} from '#/state/modals' 8 8 import {FullWindowOverlay} from '#/components/FullWindowOverlay' 9 9 import {createCustomBackdrop} from '../util/BottomSheetCustomBackdrop' 10 - import * as ChangePasswordModal from './ChangePassword' 11 10 import * as CreateOrEditListModal from './CreateOrEditList' 12 11 import * as DeleteAccountModal from './DeleteAccount' 13 12 import * as InviteCodesModal from './InviteCodes' ··· 64 63 } else if (activeModal?.name === 'post-languages-settings') { 65 64 snapPoints = PostLanguagesSettingsModal.snapPoints 66 65 element = <PostLanguagesSettingsModal.Component /> 67 - } else if (activeModal?.name === 'change-password') { 68 - snapPoints = ChangePasswordModal.snapPoints 69 - element = <ChangePasswordModal.Component /> 70 66 } else { 71 67 return null 72 68 }
-3
src/view/com/modals/Modal.web.tsx
··· 6 6 import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 7 7 import {type Modal as ModalIface} from '#/state/modals' 8 8 import {useModalControls, useModals} from '#/state/modals' 9 - import * as ChangePasswordModal from './ChangePassword' 10 9 import * as CreateOrEditListModal from './CreateOrEditList' 11 10 import * as DeleteAccountModal from './DeleteAccount' 12 11 import * as InviteCodesModal from './InviteCodes' ··· 62 61 element = <ContentLanguagesSettingsModal.Component /> 63 62 } else if (modal.name === 'post-languages-settings') { 64 63 element = <PostLanguagesSettingsModal.Component /> 65 - } else if (modal.name === 'change-password') { 66 - element = <ChangePasswordModal.Component /> 67 64 } else { 68 65 return null 69 66 }