change avatar for profile it's done, missing fix revalidate the avatar in profile and drawer

This commit is contained in:
Reinaldo Neto 2022-12-07 20:17:08 -03:00
parent 5e1f1c89da
commit 7813efbb26
6 changed files with 146 additions and 60 deletions

View File

@ -880,5 +880,9 @@
"Upload_image": "Upload image",
"Delete_image": "Delete image",
"Images_uploaded": "Images uploaded",
"Avatar": "Avatar"
"Avatar": "Avatar",
"insert_Avatar_URL": "insert image URL here",
"Discard_changes":"Discard changes?",
"Discard":"Discard",
"Discard_changes_description":"All changes will be lost if you go back without saving."
}

View File

@ -2,6 +2,16 @@ import { Alert } from 'react-native';
import I18n from '../../../i18n';
export const handleError = (e: any, _func: string, action: string) => {
if (e.data && e.data.error.includes('[error-too-many-requests]')) {
return showErrorAlert(e.data.error);
}
if (I18n.isTranslated(e.error)) {
return showErrorAlert(I18n.t(e.error));
}
showErrorAlert(I18n.t('There_was_an_error_while_action', { action: I18n.t(action) }));
};
export const showErrorAlert = (message: string, title?: string, onPress = () => {}): void =>
Alert.alert(title || '', message, [{ text: 'OK', onPress }], { cancelable: true });

View File

@ -1,47 +1,47 @@
import React from 'react';
import { Text, View, Alert, TouchableOpacity } from 'react-native';
import { Text, View, TouchableOpacity } from 'react-native';
import { IAvatar } from '../../definitions';
import { IAvatar, IUser } from '../../definitions';
import I18n from '../../i18n';
import Avatar from '../../containers/Avatar';
// import Touch from '../../containers/Touch';
import styles from './styles';
import { useTheme } from '../../theme';
const AvatarSuggestion = ({ avatarSuggestions }: { avatarSuggestions: IAvatar[] }) => {
const Item = ({ item, onPress, text }: { item?: IAvatar; onPress: (value?: IAvatar) => void; text?: string }) => {
const { colors } = useTheme();
const test = [
...avatarSuggestions,
...avatarSuggestions,
...avatarSuggestions,
...avatarSuggestions,
...avatarSuggestions,
...avatarSuggestions,
...avatarSuggestions,
...avatarSuggestions,
...avatarSuggestions,
...avatarSuggestions,
...avatarSuggestions,
...avatarSuggestions
];
const renderItem = () =>
test.slice(0, 8).map(item => (
<TouchableOpacity
key={item.service}
testID={`${item.service}-avatar-suggestion`}
onPress={() => Alert.alert('aqui')}
style={[styles.avatarButton, { backgroundColor: colors.borderColor }]}
>
<Avatar avatar={item.url} size={64} />
</TouchableOpacity>
));
return (
<View style={{ flex: 1 }}>
<Text style={styles.itemLabel}>{I18n.t('Images_uploaded')}</Text>
<View style={styles.containerAvatarSuggestion}>{renderItem()}</View>
</View>
<TouchableOpacity
key={item?.service}
testID={`${item?.service}-avatar-suggestion`}
onPress={() => onPress(item)}
style={[styles.avatarButton, { backgroundColor: colors.borderColor }]}
>
<Avatar avatar={item?.url} text={text} size={64} />
</TouchableOpacity>
);
};
const AvatarSuggestion = ({
avatarSuggestions,
onPress,
user,
resetAvatar
}: {
avatarSuggestions: IAvatar[];
onPress: (value?: IAvatar) => void;
user?: IUser;
resetAvatar?: () => void;
}) => (
<View style={{ flex: 1 }}>
<Text style={styles.itemLabel}>{I18n.t('Images_uploaded')}</Text>
<View style={styles.containerAvatarSuggestion}>
{user?.username && resetAvatar ? <Item text={`@${user.username}`} onPress={resetAvatar} /> : null}
{avatarSuggestions.slice(0, 7).map(item => (
<Item item={item} onPress={onPress} />
))}
</View>
</View>
);
export default AvatarSuggestion;

View File

@ -17,7 +17,7 @@ const AvatarUrl = ({ submit }: { submit: (value: string) => void }) => {
return (
<FormTextInput
label={I18n.t('Avatar_Url')}
placeholder={I18n.t('Avatar_Url')}
placeholder={I18n.t('insert_Avatar_URL')}
onChangeText={handleChangeText}
testID='change-avatar-view-avatar-url'
containerStyle={{ marginBottom: 0 }}

View File

@ -1,4 +1,4 @@
import React, { useEffect, useLayoutEffect, useState } from 'react';
import React, { useEffect, useLayoutEffect, useRef, useState } from 'react';
import { ScrollView, View } from 'react-native';
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack';
@ -6,6 +6,7 @@ import { StackNavigationProp } from '@react-navigation/stack';
import KeyboardView from '../../containers/KeyboardView';
import sharedStyles from '../Styles';
import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps';
import { showConfirmationAlert, handleError } from '../../lib/methods/helpers/info';
import StatusBar from '../../containers/StatusBar';
import { useTheme } from '../../theme';
import SafeAreaView from '../../containers/SafeAreaView';
@ -21,19 +22,49 @@ import { ChatsStackParamList } from '../../stacks/types';
import { IAvatar } from '../../definitions';
import { Services } from '../../lib/services';
import AvatarSuggestion from './AvatarSuggestion';
import log from '../../lib/methods/helpers/log';
const ChangeAvatarView = () => {
const [avatarUrl, setAvatarUrl] = useState('');
const [avatar, setAvatarState] = useState<IAvatar>();
const [avatarSuggestions, setAvatarSuggestions] = useState<IAvatar[]>([]);
const [textAvatar, setTextAvatar] = useState('');
const [saving, setSaving] = useState(false);
const { colors } = useTheme();
const { user } = useAppSelector(state => ({
user: getUserSelector(state),
isMasterDetail: state.app.isMasterDetail
}));
const avatarUrl = useRef<string | undefined>('');
const navigation = useNavigation<StackNavigationProp<ChatsStackParamList, 'ChangeAvatarView'>>();
const { fromUser, titleHeader } = useRoute<RouteProp<ChatsStackParamList, 'ChangeAvatarView'>>().params;
useLayoutEffect(() => {
titleHeader ? navigation.setOptions({ title: titleHeader }) : navigation.setOptions({ title: I18n.t('Avatar') });
navigation.setOptions({
title: titleHeader || I18n.t('Avatar')
});
}, [titleHeader, navigation]);
useEffect(() => {
navigation.addListener('beforeRemove', e => {
if (!avatarUrl.current) {
return;
}
e.preventDefault();
showConfirmationAlert({
title: I18n.t('Discard_changes'),
message: I18n.t('Discard_changes_description'),
confirmationText: I18n.t('Discard'),
onPress: () => {
navigation.dispatch(e.data.action);
}
});
});
}, [navigation]);
const getAvatarSuggestion = async () => {
const result = await Services.getAvatarSuggestion();
const suggestions = Object.keys(result).map(service => {
@ -54,7 +85,45 @@ const ChangeAvatarView = () => {
}
}, [fromUser]);
const user = useAppSelector(state => getUserSelector(state));
const setAvatar = (value?: IAvatar) => {
avatarUrl.current = value?.url;
setAvatarState(value);
};
const submit = async () => {
if (avatar?.url) {
try {
setSaving(true);
await Services.setAvatarFromService(avatar);
setSaving(false);
avatarUrl.current = '';
return navigation.goBack();
} catch (e) {
log(e);
setSaving(false);
return handleError(e, 'setAvatarFromService', 'changing_avatar');
}
}
if (textAvatar) {
try {
setSaving(true);
await Services.resetAvatar(user.id);
setSaving(false);
avatarUrl.current = '';
return navigation.goBack();
} catch (e) {
setSaving(false);
handleError(e, 'resetAvatar', 'changing_avatar');
}
}
};
const resetAvatar = () => {
setAvatar(undefined);
setTextAvatar(`@${user.username}`);
avatarUrl.current = `@${user.username}`;
};
return (
<KeyboardView
@ -70,27 +139,40 @@ const ChangeAvatarView = () => {
{...scrollPersistTaps}
>
<View style={styles.avatarContainer} testID='change-avatar-view-avatar'>
<Avatar text={user.username} avatar={avatarUrl} isStatic={avatarUrl} size={100} />
<Avatar text={textAvatar || user.username} avatar={avatar?.url} isStatic={avatar?.url} size={100} />
</View>
<AvatarUrl submit={setAvatarUrl} />
<AvatarUrl submit={value => setAvatar({ url: value, data: value, service: 'url' })} />
<List.Separator style={styles.separator} />
{fromUser && avatarSuggestions.length ? <AvatarSuggestion avatarSuggestions={avatarSuggestions} /> : null}
{fromUser && avatarSuggestions.length ? (
<AvatarSuggestion resetAvatar={resetAvatar} user={user} onPress={setAvatar} avatarSuggestions={avatarSuggestions} />
) : null}
<Button
title={I18n.t('Upload_image')}
type='secondary'
disabled={saving}
backgroundColor={colors.chatComponentBackground}
onPress={() => {}}
testID='change-avatar-view-logout-other-locations'
/>
{!fromUser ? (
<Button
title={I18n.t('Delete_image')}
type='primary'
disabled={saving}
backgroundColor={colors.dangerColor}
onPress={() => {}}
testID='change-avatar-view-delete-my-account'
/>
) : null}
<Button
title={I18n.t('Delete_image')}
title={I18n.t('Save')}
disabled={!avatarUrl.current || saving}
type='primary'
backgroundColor={colors.dangerColor}
onPress={() => {}}
testID='change-avatar-view-delete-my-account'
loading={saving}
onPress={submit}
testID='change-avatar-view-submit'
/>
<Button title={I18n.t('Save')} type='primary' onPress={() => {}} testID='change-avatar-view-submit' />
</ScrollView>
</SafeAreaView>
</KeyboardView>

View File

@ -12,7 +12,7 @@ import Touch from '../../containers/Touch';
import KeyboardView from '../../containers/KeyboardView';
import sharedStyles from '../Styles';
import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps';
import { showConfirmationAlert, showErrorAlert } from '../../lib/methods/helpers';
import { handleError, showConfirmationAlert } from '../../lib/methods/helpers';
import { LISTENER } from '../../containers/Toast';
import EventEmitter from '../../lib/methods/helpers/events';
import { FormTextInput } from '../../containers/TextInput';
@ -201,16 +201,6 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
);
};
handleError = (e: any, _func: string, action: string) => {
if (e.data && e.data.error.includes('[error-too-many-requests]')) {
return showErrorAlert(e.data.error);
}
if (I18n.isTranslated(e.error)) {
return showErrorAlert(I18n.t(e.error));
}
showErrorAlert(I18n.t('There_was_an_error_while_action', { action: I18n.t(action) }));
};
submit = async (): Promise<void> => {
Keyboard.dismiss();
@ -280,7 +270,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
} catch (e) {
logEvent(events.PROFILE_SAVE_AVATAR_F);
this.setState({ saving: false, currentPassword: null });
return this.handleError(e, 'setAvatarFromService', 'changing_avatar');
return handleError(e, 'setAvatarFromService', 'changing_avatar');
}
}
@ -317,7 +307,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
}
logEvent(events.PROFILE_SAVE_CHANGES_F);
this.setState({ saving: false, currentPassword: null, twoFactorCode: null });
this.handleError(e, 'saveUserProfile', 'saving_profile');
handleError(e, 'saveUserProfile', 'saving_profile');
}
};
@ -334,7 +324,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
EventEmitter.emit(LISTENER, { message: I18n.t('Avatar_changed_successfully') });
this.init();
} catch (e) {
this.handleError(e, 'resetAvatar', 'changing_avatar');
handleError(e, 'resetAvatar', 'changing_avatar');
}
};