change avatar for profile it's done, missing fix revalidate the avatar in profile and drawer
This commit is contained in:
parent
5e1f1c89da
commit
7813efbb26
|
@ -880,5 +880,9 @@
|
||||||
"Upload_image": "Upload image",
|
"Upload_image": "Upload image",
|
||||||
"Delete_image": "Delete image",
|
"Delete_image": "Delete image",
|
||||||
"Images_uploaded": "Images uploaded",
|
"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."
|
||||||
}
|
}
|
|
@ -2,6 +2,16 @@ import { Alert } from 'react-native';
|
||||||
|
|
||||||
import I18n from '../../../i18n';
|
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 =>
|
export const showErrorAlert = (message: string, title?: string, onPress = () => {}): void =>
|
||||||
Alert.alert(title || '', message, [{ text: 'OK', onPress }], { cancelable: true });
|
Alert.alert(title || '', message, [{ text: 'OK', onPress }], { cancelable: true });
|
||||||
|
|
||||||
|
|
|
@ -1,47 +1,47 @@
|
||||||
import React from 'react';
|
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 I18n from '../../i18n';
|
||||||
import Avatar from '../../containers/Avatar';
|
import Avatar from '../../containers/Avatar';
|
||||||
// import Touch from '../../containers/Touch';
|
// import Touch from '../../containers/Touch';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { useTheme } from '../../theme';
|
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 { 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 (
|
return (
|
||||||
<View style={{ flex: 1 }}>
|
<TouchableOpacity
|
||||||
<Text style={styles.itemLabel}>{I18n.t('Images_uploaded')}</Text>
|
key={item?.service}
|
||||||
<View style={styles.containerAvatarSuggestion}>{renderItem()}</View>
|
testID={`${item?.service}-avatar-suggestion`}
|
||||||
</View>
|
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;
|
export default AvatarSuggestion;
|
||||||
|
|
|
@ -17,7 +17,7 @@ const AvatarUrl = ({ submit }: { submit: (value: string) => void }) => {
|
||||||
return (
|
return (
|
||||||
<FormTextInput
|
<FormTextInput
|
||||||
label={I18n.t('Avatar_Url')}
|
label={I18n.t('Avatar_Url')}
|
||||||
placeholder={I18n.t('Avatar_Url')}
|
placeholder={I18n.t('insert_Avatar_URL')}
|
||||||
onChangeText={handleChangeText}
|
onChangeText={handleChangeText}
|
||||||
testID='change-avatar-view-avatar-url'
|
testID='change-avatar-view-avatar-url'
|
||||||
containerStyle={{ marginBottom: 0 }}
|
containerStyle={{ marginBottom: 0 }}
|
||||||
|
|
|
@ -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 { ScrollView, View } from 'react-native';
|
||||||
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
|
import { RouteProp, useNavigation, useRoute } from '@react-navigation/native';
|
||||||
import { StackNavigationProp } from '@react-navigation/stack';
|
import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
|
@ -6,6 +6,7 @@ import { StackNavigationProp } from '@react-navigation/stack';
|
||||||
import KeyboardView from '../../containers/KeyboardView';
|
import KeyboardView from '../../containers/KeyboardView';
|
||||||
import sharedStyles from '../Styles';
|
import sharedStyles from '../Styles';
|
||||||
import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps';
|
import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps';
|
||||||
|
import { showConfirmationAlert, handleError } from '../../lib/methods/helpers/info';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import { useTheme } from '../../theme';
|
import { useTheme } from '../../theme';
|
||||||
import SafeAreaView from '../../containers/SafeAreaView';
|
import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
|
@ -21,19 +22,49 @@ import { ChatsStackParamList } from '../../stacks/types';
|
||||||
import { IAvatar } from '../../definitions';
|
import { IAvatar } from '../../definitions';
|
||||||
import { Services } from '../../lib/services';
|
import { Services } from '../../lib/services';
|
||||||
import AvatarSuggestion from './AvatarSuggestion';
|
import AvatarSuggestion from './AvatarSuggestion';
|
||||||
|
import log from '../../lib/methods/helpers/log';
|
||||||
|
|
||||||
const ChangeAvatarView = () => {
|
const ChangeAvatarView = () => {
|
||||||
const [avatarUrl, setAvatarUrl] = useState('');
|
const [avatar, setAvatarState] = useState<IAvatar>();
|
||||||
const [avatarSuggestions, setAvatarSuggestions] = useState<IAvatar[]>([]);
|
const [avatarSuggestions, setAvatarSuggestions] = useState<IAvatar[]>([]);
|
||||||
|
const [textAvatar, setTextAvatar] = useState('');
|
||||||
|
const [saving, setSaving] = useState(false);
|
||||||
const { colors } = useTheme();
|
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 navigation = useNavigation<StackNavigationProp<ChatsStackParamList, 'ChangeAvatarView'>>();
|
||||||
const { fromUser, titleHeader } = useRoute<RouteProp<ChatsStackParamList, 'ChangeAvatarView'>>().params;
|
const { fromUser, titleHeader } = useRoute<RouteProp<ChatsStackParamList, 'ChangeAvatarView'>>().params;
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
titleHeader ? navigation.setOptions({ title: titleHeader }) : navigation.setOptions({ title: I18n.t('Avatar') });
|
navigation.setOptions({
|
||||||
|
title: titleHeader || I18n.t('Avatar')
|
||||||
|
});
|
||||||
}, [titleHeader, navigation]);
|
}, [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 getAvatarSuggestion = async () => {
|
||||||
const result = await Services.getAvatarSuggestion();
|
const result = await Services.getAvatarSuggestion();
|
||||||
const suggestions = Object.keys(result).map(service => {
|
const suggestions = Object.keys(result).map(service => {
|
||||||
|
@ -54,7 +85,45 @@ const ChangeAvatarView = () => {
|
||||||
}
|
}
|
||||||
}, [fromUser]);
|
}, [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 (
|
return (
|
||||||
<KeyboardView
|
<KeyboardView
|
||||||
|
@ -70,27 +139,40 @@ const ChangeAvatarView = () => {
|
||||||
{...scrollPersistTaps}
|
{...scrollPersistTaps}
|
||||||
>
|
>
|
||||||
<View style={styles.avatarContainer} testID='change-avatar-view-avatar'>
|
<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>
|
</View>
|
||||||
<AvatarUrl submit={setAvatarUrl} />
|
<AvatarUrl submit={value => setAvatar({ url: value, data: value, service: 'url' })} />
|
||||||
<List.Separator style={styles.separator} />
|
<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
|
<Button
|
||||||
title={I18n.t('Upload_image')}
|
title={I18n.t('Upload_image')}
|
||||||
type='secondary'
|
type='secondary'
|
||||||
|
disabled={saving}
|
||||||
backgroundColor={colors.chatComponentBackground}
|
backgroundColor={colors.chatComponentBackground}
|
||||||
onPress={() => {}}
|
onPress={() => {}}
|
||||||
testID='change-avatar-view-logout-other-locations'
|
testID='change-avatar-view-logout-other-locations'
|
||||||
/>
|
/>
|
||||||
|
{!fromUser ? (
|
||||||
<Button
|
<Button
|
||||||
title={I18n.t('Delete_image')}
|
title={I18n.t('Delete_image')}
|
||||||
type='primary'
|
type='primary'
|
||||||
|
disabled={saving}
|
||||||
backgroundColor={colors.dangerColor}
|
backgroundColor={colors.dangerColor}
|
||||||
onPress={() => {}}
|
onPress={() => {}}
|
||||||
testID='change-avatar-view-delete-my-account'
|
testID='change-avatar-view-delete-my-account'
|
||||||
/>
|
/>
|
||||||
<Button title={I18n.t('Save')} type='primary' onPress={() => {}} testID='change-avatar-view-submit' />
|
) : null}
|
||||||
|
<Button
|
||||||
|
title={I18n.t('Save')}
|
||||||
|
disabled={!avatarUrl.current || saving}
|
||||||
|
type='primary'
|
||||||
|
loading={saving}
|
||||||
|
onPress={submit}
|
||||||
|
testID='change-avatar-view-submit'
|
||||||
|
/>
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
</KeyboardView>
|
</KeyboardView>
|
||||||
|
|
|
@ -12,7 +12,7 @@ import Touch from '../../containers/Touch';
|
||||||
import KeyboardView from '../../containers/KeyboardView';
|
import KeyboardView from '../../containers/KeyboardView';
|
||||||
import sharedStyles from '../Styles';
|
import sharedStyles from '../Styles';
|
||||||
import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps';
|
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 { LISTENER } from '../../containers/Toast';
|
||||||
import EventEmitter from '../../lib/methods/helpers/events';
|
import EventEmitter from '../../lib/methods/helpers/events';
|
||||||
import { FormTextInput } from '../../containers/TextInput';
|
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> => {
|
submit = async (): Promise<void> => {
|
||||||
Keyboard.dismiss();
|
Keyboard.dismiss();
|
||||||
|
|
||||||
|
@ -280,7 +270,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logEvent(events.PROFILE_SAVE_AVATAR_F);
|
logEvent(events.PROFILE_SAVE_AVATAR_F);
|
||||||
this.setState({ saving: false, currentPassword: null });
|
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);
|
logEvent(events.PROFILE_SAVE_CHANGES_F);
|
||||||
this.setState({ saving: false, currentPassword: null, twoFactorCode: null });
|
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') });
|
EventEmitter.emit(LISTENER, { message: I18n.t('Avatar_changed_successfully') });
|
||||||
this.init();
|
this.init();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.handleError(e, 'resetAvatar', 'changing_avatar');
|
handleError(e, 'resetAvatar', 'changing_avatar');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue