clean profile view
This commit is contained in:
parent
c4f09d8b7a
commit
90984de444
|
@ -2,7 +2,6 @@ import React from 'react';
|
||||||
import { Keyboard, ScrollView, TextInput, View } from 'react-native';
|
import { Keyboard, ScrollView, TextInput, View } from 'react-native';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { sha256 } from 'js-sha256';
|
import { sha256 } from 'js-sha256';
|
||||||
import ImagePicker, { Image } from 'react-native-image-crop-picker';
|
|
||||||
import RNPickerSelect from 'react-native-picker-select';
|
import RNPickerSelect from 'react-native-picker-select';
|
||||||
import { dequal } from 'dequal';
|
import { dequal } from 'dequal';
|
||||||
import omit from 'lodash/omit';
|
import omit from 'lodash/omit';
|
||||||
|
@ -16,12 +15,11 @@ 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';
|
||||||
import log, { events, logEvent } from '../../lib/methods/helpers/log';
|
import { events, logEvent } from '../../lib/methods/helpers/log';
|
||||||
import I18n from '../../i18n';
|
import I18n from '../../i18n';
|
||||||
import Button from '../../containers/Button';
|
import Button from '../../containers/Button';
|
||||||
import Avatar from '../../containers/Avatar';
|
import Avatar from '../../containers/Avatar';
|
||||||
import { setUser } from '../../actions/login';
|
import { setUser } from '../../actions/login';
|
||||||
import { CustomIcon } from '../../containers/CustomIcon';
|
|
||||||
import * as HeaderButton from '../../containers/HeaderButton';
|
import * as HeaderButton from '../../containers/HeaderButton';
|
||||||
import StatusBar from '../../containers/StatusBar';
|
import StatusBar from '../../containers/StatusBar';
|
||||||
import { themes } from '../../lib/constants';
|
import { themes } from '../../lib/constants';
|
||||||
|
@ -31,15 +29,7 @@ import SafeAreaView from '../../containers/SafeAreaView';
|
||||||
import styles from './styles';
|
import styles from './styles';
|
||||||
import { ProfileStackParamList } from '../../stacks/types';
|
import { ProfileStackParamList } from '../../stacks/types';
|
||||||
import { Services } from '../../lib/services';
|
import { Services } from '../../lib/services';
|
||||||
import {
|
import { IApplicationState, IAvatarButton, IBaseScreen, IProfileParams, IUser } from '../../definitions';
|
||||||
IApplicationState,
|
|
||||||
IAvatar,
|
|
||||||
IAvatarButton,
|
|
||||||
IAvatarSuggestion,
|
|
||||||
IBaseScreen,
|
|
||||||
IProfileParams,
|
|
||||||
IUser
|
|
||||||
} from '../../definitions';
|
|
||||||
import { twoFactor } from '../../lib/services/twoFactor';
|
import { twoFactor } from '../../lib/services/twoFactor';
|
||||||
import { TwoFactorMethods } from '../../definitions/ITotp';
|
import { TwoFactorMethods } from '../../definitions/ITotp';
|
||||||
import { withActionSheet, IActionSheetProvider } from '../../containers/ActionSheet';
|
import { withActionSheet, IActionSheetProvider } from '../../containers/ActionSheet';
|
||||||
|
@ -67,9 +57,6 @@ interface IProfileViewState {
|
||||||
email: string | null;
|
email: string | null;
|
||||||
newPassword: string | null;
|
newPassword: string | null;
|
||||||
currentPassword: string | null;
|
currentPassword: string | null;
|
||||||
avatarUrl: string | null;
|
|
||||||
avatar: IAvatar;
|
|
||||||
avatarSuggestions: { [service: string]: IAvatarSuggestion };
|
|
||||||
customFields: {
|
customFields: {
|
||||||
[key: string | number]: string;
|
[key: string | number]: string;
|
||||||
};
|
};
|
||||||
|
@ -113,25 +100,12 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
||||||
email: '',
|
email: '',
|
||||||
newPassword: '',
|
newPassword: '',
|
||||||
currentPassword: '',
|
currentPassword: '',
|
||||||
avatarUrl: '',
|
|
||||||
avatar: {
|
|
||||||
data: {},
|
|
||||||
url: ''
|
|
||||||
},
|
|
||||||
avatarSuggestions: {},
|
|
||||||
customFields: {},
|
customFields: {},
|
||||||
twoFactorCode: null
|
twoFactorCode: null
|
||||||
};
|
};
|
||||||
|
|
||||||
async componentDidMount() {
|
componentDidMount() {
|
||||||
this.init();
|
this.init();
|
||||||
|
|
||||||
try {
|
|
||||||
const result = await Services.getAvatarSuggestion();
|
|
||||||
this.setState({ avatarSuggestions: result });
|
|
||||||
} catch (e) {
|
|
||||||
log(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UNSAFE_componentWillReceiveProps(nextProps: IProfileViewProps) {
|
UNSAFE_componentWillReceiveProps(nextProps: IProfileViewProps) {
|
||||||
|
@ -147,16 +121,6 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
setAvatar = (avatar: IAvatar) => {
|
|
||||||
const { Accounts_AllowUserAvatarChange } = this.props;
|
|
||||||
|
|
||||||
if (!Accounts_AllowUserAvatarChange) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState({ avatar });
|
|
||||||
};
|
|
||||||
|
|
||||||
init = (user?: IUser) => {
|
init = (user?: IUser) => {
|
||||||
const { user: userProps } = this.props;
|
const { user: userProps } = this.props;
|
||||||
const { name, username, emails, customFields } = user || userProps;
|
const { name, username, emails, customFields } = user || userProps;
|
||||||
|
@ -167,17 +131,12 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
||||||
email: emails ? emails[0].address : null,
|
email: emails ? emails[0].address : null,
|
||||||
newPassword: null,
|
newPassword: null,
|
||||||
currentPassword: null,
|
currentPassword: null,
|
||||||
avatarUrl: null,
|
|
||||||
avatar: {
|
|
||||||
data: {},
|
|
||||||
url: ''
|
|
||||||
},
|
|
||||||
customFields: customFields || {}
|
customFields: customFields || {}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
formIsChanged = () => {
|
formIsChanged = () => {
|
||||||
const { name, username, email, newPassword, avatar, customFields } = this.state;
|
const { name, username, email, newPassword, customFields } = this.state;
|
||||||
const { user } = this.props;
|
const { user } = this.props;
|
||||||
let customFieldsChanged = false;
|
let customFieldsChanged = false;
|
||||||
|
|
||||||
|
@ -196,7 +155,6 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
||||||
!newPassword &&
|
!newPassword &&
|
||||||
user.emails &&
|
user.emails &&
|
||||||
user.emails[0].address === email &&
|
user.emails[0].address === email &&
|
||||||
!avatar.data &&
|
|
||||||
!customFieldsChanged
|
!customFieldsChanged
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -210,7 +168,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
||||||
|
|
||||||
this.setState({ saving: true });
|
this.setState({ saving: true });
|
||||||
|
|
||||||
const { name, username, email, newPassword, currentPassword, avatar, customFields, twoFactorCode } = this.state;
|
const { name, username, email, newPassword, currentPassword, customFields, twoFactorCode } = this.state;
|
||||||
const { user, dispatch } = this.props;
|
const { user, dispatch } = this.props;
|
||||||
const params = {} as IProfileParams;
|
const params = {} as IProfileParams;
|
||||||
|
|
||||||
|
@ -263,17 +221,6 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (avatar.url) {
|
|
||||||
try {
|
|
||||||
logEvent(events.PROFILE_SAVE_AVATAR);
|
|
||||||
await Services.setAvatarFromService(avatar);
|
|
||||||
} catch (e) {
|
|
||||||
logEvent(events.PROFILE_SAVE_AVATAR_F);
|
|
||||||
this.setState({ saving: false, currentPassword: null });
|
|
||||||
return handleError(e, 'setAvatarFromService', 'changing_avatar');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const twoFactorOptions = params.currentPassword
|
const twoFactorOptions = params.currentPassword
|
||||||
? {
|
? {
|
||||||
twoFactorCode: params.currentPassword,
|
twoFactorCode: params.currentPassword,
|
||||||
|
@ -328,37 +275,6 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pickImage = async () => {
|
|
||||||
const { Accounts_AllowUserAvatarChange } = this.props;
|
|
||||||
|
|
||||||
if (!Accounts_AllowUserAvatarChange) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = {
|
|
||||||
cropping: true,
|
|
||||||
compressImageQuality: 0.8,
|
|
||||||
freeStyleCropEnabled: true,
|
|
||||||
cropperAvoidEmptySpaceAroundImage: false,
|
|
||||||
cropperChooseText: I18n.t('Choose'),
|
|
||||||
cropperCancelText: I18n.t('Cancel'),
|
|
||||||
includeBase64: true
|
|
||||||
};
|
|
||||||
try {
|
|
||||||
logEvent(events.PROFILE_PICK_AVATAR);
|
|
||||||
const response: Image = await ImagePicker.openPicker(options);
|
|
||||||
this.setAvatar({ url: response.path, data: `data:image/jpeg;base64,${response.data}`, service: 'upload' });
|
|
||||||
} catch (error) {
|
|
||||||
logEvent(events.PROFILE_PICK_AVATAR_F);
|
|
||||||
console.warn(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
pickImageWithURL = (avatarUrl: string) => {
|
|
||||||
logEvent(events.PROFILE_PICK_AVATAR_WITH_URL);
|
|
||||||
this.setAvatar({ url: avatarUrl, data: avatarUrl, service: 'url' });
|
|
||||||
};
|
|
||||||
|
|
||||||
handleEditAvatar = () => {
|
handleEditAvatar = () => {
|
||||||
const { navigation } = this.props;
|
const { navigation } = this.props;
|
||||||
navigation.navigate('ChangeAvatarView', { fromUser: true });
|
navigation.navigate('ChangeAvatarView', { fromUser: true });
|
||||||
|
@ -379,50 +295,6 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderAvatarButtons = () => {
|
|
||||||
const { avatarUrl, avatarSuggestions } = this.state;
|
|
||||||
const { user, theme, Accounts_AllowUserAvatarChange } = this.props;
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View style={styles.avatarButtons}>
|
|
||||||
{this.renderAvatarButton({
|
|
||||||
child: <Avatar text={`@${user.username}`} size={50} />,
|
|
||||||
onPress: () => this.resetAvatar(),
|
|
||||||
disabled: !Accounts_AllowUserAvatarChange,
|
|
||||||
key: 'profile-view-reset-avatar'
|
|
||||||
})}
|
|
||||||
{this.renderAvatarButton({
|
|
||||||
child: <CustomIcon name='upload' size={30} color={themes[theme].bodyText} />,
|
|
||||||
onPress: () => this.pickImage(),
|
|
||||||
disabled: !Accounts_AllowUserAvatarChange,
|
|
||||||
key: 'profile-view-upload-avatar'
|
|
||||||
})}
|
|
||||||
{this.renderAvatarButton({
|
|
||||||
child: <CustomIcon name='link' size={30} color={themes[theme].bodyText} />,
|
|
||||||
onPress: () => (avatarUrl ? this.pickImageWithURL(avatarUrl) : null),
|
|
||||||
disabled: !avatarUrl,
|
|
||||||
key: 'profile-view-avatar-url-button'
|
|
||||||
})}
|
|
||||||
{Object.keys(avatarSuggestions).map(service => {
|
|
||||||
const { url, blob, contentType } = avatarSuggestions[service];
|
|
||||||
console.log('🚀 ~ file: index.tsx:418 ~ ProfileView ~ {Object.keys ~ url', url);
|
|
||||||
return this.renderAvatarButton({
|
|
||||||
disabled: !Accounts_AllowUserAvatarChange,
|
|
||||||
key: `profile-view-avatar-${service}`,
|
|
||||||
child: <Avatar avatar={url} size={50} />,
|
|
||||||
onPress: () =>
|
|
||||||
this.setAvatar({
|
|
||||||
url,
|
|
||||||
data: blob,
|
|
||||||
service,
|
|
||||||
contentType
|
|
||||||
})
|
|
||||||
});
|
|
||||||
})}
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
renderCustomFields = () => {
|
renderCustomFields = () => {
|
||||||
const { customFields } = this.state;
|
const { customFields } = this.state;
|
||||||
const { Accounts_CustomFields } = this.props;
|
const { Accounts_CustomFields } = this.props;
|
||||||
|
@ -516,7 +388,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { name, username, email, newPassword, avatarUrl, customFields, avatar, saving } = this.state;
|
const { name, username, email, newPassword, customFields, saving } = this.state;
|
||||||
const {
|
const {
|
||||||
user,
|
user,
|
||||||
theme,
|
theme,
|
||||||
|
@ -541,8 +413,6 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
||||||
<View style={styles.avatarContainer} testID='profile-view-avatar'>
|
<View style={styles.avatarContainer} testID='profile-view-avatar'>
|
||||||
<Avatar
|
<Avatar
|
||||||
text={user.username}
|
text={user.username}
|
||||||
avatar={avatar?.url}
|
|
||||||
isStatic={avatar?.url}
|
|
||||||
size={100}
|
size={100}
|
||||||
handleEdit={Accounts_AllowUserAvatarChange ? this.handleEditAvatar : undefined}
|
handleEdit={Accounts_AllowUserAvatarChange ? this.handleEditAvatar : undefined}
|
||||||
/>
|
/>
|
||||||
|
@ -617,22 +487,6 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
|
||||||
testID='profile-view-new-password'
|
testID='profile-view-new-password'
|
||||||
/>
|
/>
|
||||||
{this.renderCustomFields()}
|
{this.renderCustomFields()}
|
||||||
<FormTextInput
|
|
||||||
editable={Accounts_AllowUserAvatarChange}
|
|
||||||
inputStyle={[!Accounts_AllowUserAvatarChange && styles.disabled]}
|
|
||||||
inputRef={e => {
|
|
||||||
if (e) {
|
|
||||||
this.avatarUrl = e;
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
label={I18n.t('Avatar_Url')}
|
|
||||||
placeholder={I18n.t('Avatar_Url')}
|
|
||||||
value={avatarUrl || undefined}
|
|
||||||
onChangeText={value => this.setState({ avatarUrl: value })}
|
|
||||||
onSubmitEditing={this.submit}
|
|
||||||
testID='profile-view-avatar-url'
|
|
||||||
/>
|
|
||||||
{this.renderAvatarButtons()}
|
|
||||||
<Button
|
<Button
|
||||||
title={I18n.t('Save_Changes')}
|
title={I18n.t('Save_Changes')}
|
||||||
type='primary'
|
type='primary'
|
||||||
|
|
Loading…
Reference in New Issue