Skip to content

Commit 9397242

Browse files
feat(a11y): inline errors deleteAccount (#6452)
* chore: started actionsheet refactor to use react-hook-form * feat: created ConfirmDeleteAccountContent * fix: formTextInput error color * fix: remove alert * fix: rollback actionsheet * chore: format code with Prettier [skip ci] * action: organized translations --------- Co-authored-by: OtavioStasiak <[email protected]>
1 parent 3296d4f commit 9397242

File tree

4 files changed

+237
-63
lines changed

4 files changed

+237
-63
lines changed
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import React from 'react';
2+
import { StyleSheet, Text, View } from 'react-native';
3+
import { useDispatch } from 'react-redux';
4+
5+
import i18n from '../../../../i18n';
6+
import sharedStyles from '../../../Styles';
7+
import FooterButtons from './FooterButtons';
8+
import AlertText from './AlertText';
9+
import { deleteOwnAccount } from '../../../../lib/services/restApi';
10+
import { deleteAccount } from '../../../../actions/login';
11+
import { CustomIcon } from '../../../../containers/CustomIcon';
12+
import { useTheme } from '../../../../theme';
13+
import { useActionSheet } from '../../../../containers/ActionSheet/Provider';
14+
15+
const styles = StyleSheet.create({
16+
subtitleText: {
17+
...sharedStyles.textRegular,
18+
fontSize: 16,
19+
lineHeight: 24
20+
},
21+
titleContainerText: {
22+
...sharedStyles.textBold,
23+
fontSize: 16,
24+
lineHeight: 24,
25+
paddingLeft: 12
26+
},
27+
titleContainer: {
28+
paddingRight: 80,
29+
marginBottom: 12,
30+
flexDirection: 'row',
31+
alignItems: 'center'
32+
},
33+
changeOwnerRoomsAlert: {
34+
marginTop: 24
35+
},
36+
removedRoomsAlert: {
37+
marginBottom: 36
38+
}
39+
});
40+
41+
interface IConfirmDeleteAccountContent {
42+
password: string;
43+
changeOwnerRooms: string;
44+
removedRooms: string;
45+
}
46+
47+
const ConfirmDeleteAccountContent = ({
48+
password,
49+
changeOwnerRooms,
50+
removedRooms
51+
}: IConfirmDeleteAccountContent): React.ReactElement => {
52+
const { colors } = useTheme();
53+
const dispatch = useDispatch();
54+
const { hideActionSheet } = useActionSheet();
55+
56+
const handleDeleteAccount = async () => {
57+
hideActionSheet();
58+
await deleteOwnAccount(password, true);
59+
dispatch(deleteAccount());
60+
};
61+
62+
return (
63+
<View style={sharedStyles.containerScrollView} testID='action-sheet-content-with-input-and-submit'>
64+
<View accessible accessibilityLabel={i18n.t('Are_you_sure_question_mark')} style={styles.titleContainer}>
65+
<CustomIcon name={'warning'} size={32} color={colors.buttonBackgroundDangerDefault} />
66+
<Text style={[styles.titleContainerText, { color: colors.fontDefault }]}>{i18n.t('Are_you_sure_question_mark')}</Text>
67+
</View>
68+
<Text style={[styles.subtitleText, { color: colors.fontTitlesLabels }]}>
69+
{i18n.t('Deleting_a_user_will_delete_all_messages')}
70+
</Text>
71+
{changeOwnerRooms ? <AlertText text={changeOwnerRooms} style={styles.changeOwnerRoomsAlert} /> : null}
72+
{removedRooms ? <AlertText text={removedRooms} style={styles.removedRoomsAlert} /> : null}
73+
74+
<FooterButtons
75+
confirmBackgroundColor={colors.buttonBackgroundDangerDefault}
76+
cancelAction={hideActionSheet}
77+
confirmAction={handleDeleteAccount}
78+
cancelTitle={i18n.t('Cancel')}
79+
confirmTitle={i18n.t('Delete_Account_confirm')}
80+
testID={'room-info-edit-view-name'}
81+
/>
82+
</View>
83+
);
84+
};
85+
86+
export default ConfirmDeleteAccountContent;
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import React from 'react';
2+
import { StyleSheet, View } from 'react-native';
3+
4+
import { useTheme } from '../../../../theme';
5+
import Button from '../../../../containers/Button';
6+
7+
const styles = StyleSheet.create({
8+
buttonSeparator: {
9+
marginRight: 12,
10+
flex: 1
11+
},
12+
footerButtonsContainer: {
13+
flexDirection: 'row',
14+
paddingTop: 16
15+
}
16+
});
17+
18+
interface IFooterButtons {
19+
cancelAction: () => void;
20+
confirmAction: () => void;
21+
cancelTitle: string;
22+
confirmTitle: string;
23+
disabled?: boolean;
24+
testID: string;
25+
cancelBackgroundColor?: string;
26+
confirmBackgroundColor?: string;
27+
}
28+
29+
const FooterButtons = ({
30+
cancelAction = () => {},
31+
confirmAction = () => {},
32+
cancelTitle = '',
33+
confirmTitle = '',
34+
disabled = false,
35+
cancelBackgroundColor = '',
36+
confirmBackgroundColor = '',
37+
testID = ''
38+
}: IFooterButtons): React.ReactElement => {
39+
const { colors } = useTheme();
40+
return (
41+
<View style={styles.footerButtonsContainer}>
42+
<Button
43+
style={[styles.buttonSeparator, { backgroundColor: cancelBackgroundColor || colors.buttonBackgroundSecondaryDefault }]}
44+
title={cancelTitle}
45+
color={colors.buttonFontSecondary}
46+
onPress={cancelAction}
47+
testID={`${testID}-cancel`}
48+
/>
49+
<Button
50+
style={{ flex: 1, backgroundColor: confirmBackgroundColor || colors.buttonBackgroundDangerDefault }}
51+
title={confirmTitle}
52+
onPress={confirmAction}
53+
disabled={disabled}
54+
testID={`${testID}-confirm`}
55+
/>
56+
</View>
57+
);
58+
};
59+
60+
export default FooterButtons;
Lines changed: 90 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,101 +1,129 @@
1-
import { sha256 } from 'js-sha256';
21
import React from 'react';
3-
import { Keyboard } from 'react-native';
2+
import { AccessibilityInfo, Keyboard, StyleSheet, Text, View } from 'react-native';
3+
import { sha256 } from 'js-sha256';
4+
import { useForm } from 'react-hook-form';
45
import { useDispatch } from 'react-redux';
56

6-
import { deleteAccount } from '../../../../actions/login';
7-
import { useActionSheet } from '../../../../containers/ActionSheet';
8-
import ActionSheetContentWithInputAndSubmit from '../../../../containers/ActionSheet/ActionSheetContentWithInputAndSubmit';
97
import i18n from '../../../../i18n';
10-
import { showErrorAlert } from '../../../../lib/methods/helpers';
11-
import { events, logEvent } from '../../../../lib/methods/helpers/log';
8+
import sharedStyles from '../../../Styles';
9+
import FooterButtons from './FooterButtons';
10+
import ConfirmDeleteAccountContent from './ConfirmDeleteAccountContent';
1211
import { deleteOwnAccount } from '../../../../lib/services/restApi';
12+
import { deleteAccount } from '../../../../actions/login';
13+
import { CustomIcon } from '../../../../containers/CustomIcon';
14+
import { isIOS } from '../../../../lib/methods/helpers';
1315
import { useTheme } from '../../../../theme';
16+
import { ControlledFormTextInput } from '../../../../containers/TextInput';
17+
import { useActionSheet } from '../../../../containers/ActionSheet/Provider';
18+
import { events, logEvent } from '../../../../lib/methods/helpers/log';
1419
import { getTranslations } from './getTranslations';
15-
import AlertText from './AlertText';
1620

17-
export function DeleteAccountActionSheetContent(): React.ReactElement {
21+
const styles = StyleSheet.create({
22+
subtitleText: {
23+
...sharedStyles.textRegular,
24+
fontSize: 16,
25+
lineHeight: 24
26+
},
27+
titleContainerText: {
28+
...sharedStyles.textBold,
29+
fontSize: 16,
30+
lineHeight: 24,
31+
paddingLeft: 12
32+
},
33+
titleContainer: {
34+
paddingRight: 80,
35+
marginBottom: 12,
36+
flexDirection: 'row',
37+
alignItems: 'center'
38+
},
39+
inputContainer: {
40+
marginTop: 12,
41+
marginBottom: 36
42+
}
43+
});
44+
45+
const DeleteAccountActionSheetContent = (): React.ReactElement => {
46+
const { colors } = useTheme();
1847
const { hideActionSheet, showActionSheet } = useActionSheet();
1948
const dispatch = useDispatch();
20-
const { colors } = useTheme();
49+
const {
50+
control,
51+
getValues,
52+
setError,
53+
formState: { errors }
54+
} = useForm({
55+
defaultValues: {
56+
password: ''
57+
}
58+
});
2159

22-
const handleDeleteAccount = async (password: string) => {
60+
const handleDeleteAccount = async () => {
61+
const { password } = getValues();
2362
Keyboard.dismiss();
2463
try {
2564
await deleteOwnAccount(sha256(password));
2665
hideActionSheet();
2766
} catch (error: any) {
28-
hideActionSheet();
2967
if (error.data.errorType === 'user-last-owner') {
3068
const { shouldChangeOwner, shouldBeRemoved } = error.data.details;
3169
const { changeOwnerRooms, removedRooms } = getTranslations({ shouldChangeOwner, shouldBeRemoved });
32-
70+
hideActionSheet();
3371
setTimeout(() => {
3472
showActionSheet({
3573
children: (
36-
<ConfirmDeleteAccountActionSheetContent
37-
changeOwnerRooms={changeOwnerRooms}
38-
removedRooms={removedRooms}
39-
password={sha256(password)}
40-
/>
74+
<ConfirmDeleteAccountContent changeOwnerRooms={changeOwnerRooms} password={password} removedRooms={removedRooms} />
4175
)
4276
});
4377
}, 250); // timeout for hide effect
4478
} else if (error.data.errorType === 'error-invalid-password') {
4579
logEvent(events.DELETE_OWN_ACCOUNT_F);
46-
showErrorAlert(i18n.t('error-invalid-password'));
80+
setError('password', { message: i18n.t('error-invalid-password'), type: 'validate' });
81+
AccessibilityInfo.announceForAccessibility(i18n.t('error-invalid-password'));
4782
} else {
4883
logEvent(events.DELETE_OWN_ACCOUNT_F);
49-
showErrorAlert(i18n.t(error.data.details));
84+
setError('password', { message: i18n.t(error.data.details), type: 'validate' });
85+
AccessibilityInfo.announceForAccessibility(i18n.t(error.data.details));
5086
}
5187
return;
5288
}
5389
dispatch(deleteAccount());
5490
};
5591

5692
return (
57-
<ActionSheetContentWithInputAndSubmit
58-
title={i18n.t('Are_you_sure_you_want_to_delete_your_account')}
59-
description={i18n.t('For_your_security_you_must_enter_your_current_password_to_continue')}
60-
onCancel={hideActionSheet}
61-
onSubmit={password => handleDeleteAccount(password as string)}
62-
placeholder={i18n.t('Password')}
63-
autoComplete='password'
64-
testID='profile-view-delete-account-sheet'
65-
iconName='warning'
66-
confirmTitle={i18n.t('Delete_Account')}
67-
confirmBackgroundColor={colors.buttonBackgroundDangerDefault}
68-
/>
69-
);
70-
}
93+
<View style={sharedStyles.containerScrollView} testID='action-sheet-content-with-input-and-submit'>
94+
<View accessible accessibilityLabel={i18n.t('Are_you_sure_you_want_to_delete_your_account')} style={styles.titleContainer}>
95+
<CustomIcon name={'warning'} size={32} color={colors.buttonBackgroundDangerDefault} />
96+
<Text style={[styles.titleContainerText, { color: colors.fontDefault }]}>
97+
{i18n.t('Are_you_sure_you_want_to_delete_your_account')}
98+
</Text>
99+
</View>
100+
<Text style={[styles.subtitleText, { color: colors.fontTitlesLabels }]}>
101+
{i18n.t('For_your_security_you_must_enter_your_current_password_to_continue')}
102+
</Text>
71103

72-
function ConfirmDeleteAccountActionSheetContent({ changeOwnerRooms = '', removedRooms = '', password = '' }) {
73-
const { hideActionSheet } = useActionSheet();
74-
const dispatch = useDispatch();
75-
const { colors } = useTheme();
76-
const handleDeleteAccount = async () => {
77-
hideActionSheet();
78-
await deleteOwnAccount(password, true);
79-
dispatch(deleteAccount());
80-
};
104+
<ControlledFormTextInput
105+
control={control}
106+
name='password'
107+
onSubmitEditing={handleDeleteAccount}
108+
accessibilityLabel={i18n.t('Password')}
109+
autoComplete='password'
110+
testID='profile-view-delete-account-sheet-input'
111+
secureTextEntry
112+
bottomSheet={isIOS}
113+
containerStyle={styles.inputContainer}
114+
error={errors.password?.message}
115+
/>
81116

82-
return (
83-
<ActionSheetContentWithInputAndSubmit
84-
title={i18n.t('Are_you_sure_question_mark')}
85-
iconName='warning'
86-
description={i18n.t('Deleting_a_user_will_delete_all_messages')}
87-
onCancel={hideActionSheet}
88-
onSubmit={handleDeleteAccount}
89-
testID='room-info-edit-view-name'
90-
confirmTitle={i18n.t('Delete_Account_confirm')}
91-
confirmBackgroundColor={colors.buttonBackgroundDangerDefault}
92-
showInput={false}
93-
customText={
94-
<>
95-
{!!changeOwnerRooms && <AlertText text={changeOwnerRooms} style={{ marginTop: 24 }} />}
96-
{!!removedRooms && <AlertText text={removedRooms} style={{ marginBottom: 36 }} />}
97-
</>
98-
}
99-
/>
117+
<FooterButtons
118+
confirmBackgroundColor={colors.buttonBackgroundDangerDefault}
119+
cancelAction={hideActionSheet}
120+
confirmAction={handleDeleteAccount}
121+
cancelTitle={i18n.t('Cancel')}
122+
confirmTitle={i18n.t('Delete_Account')}
123+
testID={'profile-view-delete-account-sheet'}
124+
/>
125+
</View>
100126
);
101-
}
127+
};
128+
129+
export default DeleteAccountActionSheetContent;

app/views/ProfileView/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import { getUserSelector } from '../../selectors/login';
3232
import { ProfileStackParamList } from '../../stacks/types';
3333
import { useTheme } from '../../theme';
3434
import sharedStyles from '../Styles';
35-
import { DeleteAccountActionSheetContent } from './components/DeleteAccountActionSheetContent';
35+
import DeleteAccountActionSheetContent from './components/DeleteAccountActionSheetContent';
3636
import styles from './styles';
3737
import { useAppSelector } from '../../lib/hooks';
3838
import useParsedCustomFields from '../../lib/hooks/useParsedCustomFields';

0 commit comments

Comments
 (0)