import React, { useEffect, useLayoutEffect, useReducer, useRef, useState } from 'react'; import { ScrollView, View } from 'react-native'; import { RouteProp, useNavigation, useRoute } from '@react-navigation/native'; import { StackNavigationProp } from '@react-navigation/stack'; import ImagePicker, { Image } from 'react-native-image-crop-picker'; import { shallowEqual } from 'react-redux'; import { compareServerVersion } from '../../lib/methods/helpers'; import KeyboardView from '../../containers/KeyboardView'; import sharedStyles from '../Styles'; import scrollPersistTaps from '../../lib/methods/helpers/scrollPersistTaps'; import { showConfirmationAlert, showErrorAlert } from '../../lib/methods/helpers/info'; import StatusBar from '../../containers/StatusBar'; import { useTheme } from '../../theme'; import SafeAreaView from '../../containers/SafeAreaView'; import * as List from '../../containers/List'; import styles from './styles'; import { useAppSelector } from '../../lib/hooks'; import { getUserSelector } from '../../selectors/login'; import Avatar from '../../containers/Avatar'; import AvatarUrl from './AvatarUrl'; import Button from '../../containers/Button'; import I18n from '../../i18n'; 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'; enum AvatarStateActions { CHANGE_AVATAR = 'CHANGE_AVATAR', RESET_USER_AVATAR = 'RESET_USER_AVATAR', RESET_ROOM_AVATAR = 'RESET_ROOM_AVATAR' } interface IReducerAction { type: AvatarStateActions; payload?: Partial; } interface IState extends IAvatar { resetUserAvatar: string; } const initialState = { data: '', url: '', contentType: '', service: '', resetUserAvatar: '' }; function reducer(state: IState, action: IReducerAction) { const { type, payload } = action; if (type in AvatarStateActions) { return { ...initialState, ...payload }; } return state; } const ChangeAvatarView = () => { const [state, dispatch] = useReducer(reducer, initialState); const [saving, setSaving] = useState(false); const { colors } = useTheme(); const { userId, username, serverVersion } = useAppSelector( state => ({ userId: getUserSelector(state).id, username: getUserSelector(state).username, serverVersion: state.server.version }), shallowEqual ); const isDirty = useRef(false); const navigation = useNavigation>(); const { context, titleHeader, room, t } = useRoute>().params; useLayoutEffect(() => { navigation.setOptions({ title: titleHeader || I18n.t('Avatar') }); }, [titleHeader, navigation]); useEffect(() => { navigation.addListener('beforeRemove', e => { if (!isDirty.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 dispatchAvatar = (action: IReducerAction) => { isDirty.current = true; dispatch(action); }; const submit = async () => { try { setSaving(true); console.log('🚀 ~ file: index.tsx:117 ~ submit ~ state', state); if (context === 'room' && room?.rid) { // Change Rooms Avatar await changeRoomsAvatar(room.rid); } else if (state?.url) { // Change User's Avatar await changeUserAvatar(state); } else if (state.resetUserAvatar) { // Change User's Avatar await resetUserAvatar(); } isDirty.current = false; } catch (e: any) { log(e); return showErrorAlert(e.message, I18n.t('Oops')); } finally { setSaving(false); } return navigation.goBack(); }; const changeRoomsAvatar = async (rid: string) => { try { console.log('🚀 ~ file: index.tsx:135 ~ changeRoomsAvatar ~ rid', rid, state); await Services.saveRoomSettings(rid, { roomAvatar: state?.data }); } catch (e) { log(e); return handleError(e, 'changing_avatar'); } }; const changeUserAvatar = async (avatarUpload: IAvatar) => { try { console.log('🚀 ~ file: index.tsx:144 ~ changeUserAvatar ~ avatarUpload', avatarUpload); await Services.setAvatarFromService(avatarUpload); } catch (e) { return handleError(e, 'changing_avatar'); } }; const resetUserAvatar = async () => { try { console.log('🚀 ~ file: index.tsx:154 ~ resetUserAvatar ~ userId', userId); await Services.resetAvatar(userId); } catch (e) { return handleError(e, 'changing_avatar'); } }; const handleError = (e: any, action: string) => { if (e.data && e.data.error.includes('[error-too-many-requests]')) { throw new Error(e.data.error); } if (e.error && e.error === 'error-avatar-invalid-url') { throw new Error(I18n.t(e.error, { url: e.details.url })); } if (I18n.isTranslated(e.error)) { throw new Error(I18n.t(e.error)); } throw new Error(I18n.t('There_was_an_error_while_action', { action: I18n.t(action) })); }; const pickImage = async () => { const options = { cropping: true, compressImageQuality: 0.8, freeStyleCropEnabled: true, cropperAvoidEmptySpaceAroundImage: false, cropperChooseText: I18n.t('Choose'), cropperCancelText: I18n.t('Cancel'), includeBase64: true }; try { const response: Image = await ImagePicker.openPicker(options); dispatchAvatar({ type: AvatarStateActions.CHANGE_AVATAR, payload: { url: response.path, data: `data:image/jpeg;base64,${response.data}`, service: 'upload' } }); } catch (error) { log(error); } }; const deletingRoomAvatar = context === 'room' && state.data === null ? {} : { rid: room?.rid }; return ( {context === 'profile' ? ( dispatchAvatar({ type: AvatarStateActions.CHANGE_AVATAR, payload: { url: value, data: value, service: 'url' } }) } /> ) : null} {context === 'profile' ? ( dispatchAvatar({ type: AvatarStateActions.RESET_USER_AVATAR, payload: { resetUserAvatar: `@${username}` } }) } username={username} onPress={value => dispatchAvatar({ type: AvatarStateActions.CHANGE_AVATAR, payload: value }) } /> ) : null}