add: `AvatarEditView`

This commit is contained in:
Gerzon Z 2022-05-05 13:51:14 -04:00
parent 305f360b40
commit 98bdd552cd
12 changed files with 130 additions and 88 deletions

View File

@ -1,4 +1,4 @@
import { IParams } from '../../IProfileViewInterfaces';
import { IParams } from '../../../views/ProfileView/interfaces';
import type { ITeam } from '../../ITeam';
import type { IUser } from '../../IUser';
import { INotificationPreferences, IUserPreferences, IUserRegistered } from '../../IUser';

View File

@ -114,6 +114,7 @@
"Auto_Translate": "Auto-Translate",
"Avatar_changed_successfully": "Avatar changed successfully!",
"Avatar_Url": "Avatar URL",
"Avatar_Url_Insert_Image": "Insert image URL here",
"Away": "Away",
"Back": "Back",
"Black": "Black",

View File

@ -7,7 +7,7 @@ import {
SubscriptionType,
IUser
} from '../../definitions';
import { IAvatarSuggestion, IParams } from '../../definitions/IProfileViewInterfaces';
import { IAvatarSuggestion, IParams } from '../../views/ProfileView/interfaces';
import { ISpotlight } from '../../definitions/ISpotlight';
import { TEAM_TYPE } from '../../definitions/ITeam';
import { Encryption } from '../encryption';

View File

@ -33,6 +33,7 @@ import CannedResponseDetail from '../views/CannedResponseDetail';
import { themes } from '../lib/constants';
// Profile Stack
import ProfileView from '../views/ProfileView';
import AvatarEditView from '../views/AvatarEditView';
import UserPreferencesView from '../views/UserPreferencesView';
import UserNotificationPrefView from '../views/UserNotificationPreferencesView';
// Display Preferences View
@ -148,6 +149,7 @@ const ProfileStackNavigator = () => {
<ProfileStack.Navigator
screenOptions={{ ...defaultHeader, ...themedHeader(theme), ...StackAnimation } as StackNavigationOptions}>
<ProfileStack.Screen name='ProfileView' component={ProfileView} options={ProfileView.navigationOptions} />
<ProfileStack.Screen name='AvatarEditView' component={AvatarEditView} />
<ProfileStack.Screen name='UserPreferencesView' component={UserPreferencesView} />
<ProfileStack.Screen
name='UserNotificationPrefView'

View File

@ -44,6 +44,7 @@ import UserNotificationPrefView from '../../views/UserNotificationPreferencesVie
import SecurityPrivacyView from '../../views/SecurityPrivacyView';
import E2EEncryptionSecurityView from '../../views/E2EEncryptionSecurityView';
// InsideStackNavigator
import AvatarEditView from '../../views/AvatarEditView';
import AttachmentView from '../../views/AttachmentView';
import ModalBlockView from '../../views/ModalBlockView';
import JitsiMeetView from '../../views/JitsiMeetView';
@ -195,6 +196,7 @@ const ModalStackNavigator = React.memo(({ navigation }: INavigation) => {
component={ProfileView}
options={props => ProfileView.navigationOptions!({ ...props, isMasterDetail: true })}
/>
<ModalStack.Screen name='AvatarEditView' component={AvatarEditView} />
<ModalStack.Screen name='DisplayPrefsView' component={DisplayPrefsView} />
<ModalStack.Screen
name='AdminPanelView'

View File

@ -150,6 +150,7 @@ export type ModalStackParamList = {
ScreenLockConfigView: undefined;
StatusView: undefined;
ProfileView: undefined;
AvatarEditView: undefined;
DisplayPrefsView: undefined;
AdminPanelView: undefined;
NewMessageView: undefined;

View File

@ -157,6 +157,7 @@ export type ChatsStackParamList = {
export type ProfileStackParamList = {
ProfileView: undefined;
AvatarEditView: undefined;
UserPreferencesView: undefined;
UserNotificationPrefView: undefined;
PickerView: {

View File

@ -0,0 +1,53 @@
import React, { useLayoutEffect, useState } from 'react';
// import { useSelector } from 'react-redux';
import { ProfileStackParamList } from '../../stacks/types';
import { IBaseScreen } from '../../definitions';
import SafeAreaView from '../../containers/SafeAreaView';
import Button from '../../containers/Button';
import { useTheme } from '../../theme';
import i18n from '../../i18n';
import RCTextInput from '../../containers/TextInput';
// import { getUserSelector } from '../../selectors/login';
// import Avatar from '../../containers/Avatar';
const AvatarEditView = ({ navigation }: IBaseScreen<ProfileStackParamList, 'AvatarEditView'>): React.ReactElement => {
const [avatarUrl, setAvatarUrl] = useState('');
const [saving] = useState(false);
// const username = useSelector((state: IApplicationState) => getUserSelector(state).username);
// const url = useSelector((state: IApplicationState) => getUserSelector(state).username);
const { theme } = useTheme();
useLayoutEffect(() => {
navigation.setOptions({
title: 'Avatar'
});
}, []);
return (
<SafeAreaView>
{/* <Avatar text={username} avatar={avatar?.url} isStatic={avatar?.url} size={120} /> */}
<RCTextInput
label={i18n.t('Avatar_Url')}
placeholder={i18n.t('Avatar_Url_Insert_Image')}
value={avatarUrl}
onChangeText={value => setAvatarUrl(value)}
// onSubmitEditing={this.submit}
testID='profile-view-avatar-url'
theme={theme}
/>
<Button
title={i18n.t('Save_Changes')}
type='primary'
// onPress={this.submit}
// disabled={!this.formIsChanged()}
testID='profile-view-submit'
loading={saving}
theme={theme}
/>
</SafeAreaView>
);
};
export default AvatarEditView;

View File

@ -1,5 +1,5 @@
import React from 'react';
import { Keyboard, ScrollView, View } from 'react-native';
import { Keyboard, ScrollView, Text, View } from 'react-native';
import { connect } from 'react-redux';
import prompt from 'react-native-prompt-android';
import { sha256 } from 'js-sha256';
@ -22,7 +22,6 @@ import I18n from '../../i18n';
import Button from '../../containers/Button';
import Avatar from '../../containers/Avatar';
import { setUser as setUserAction } from '../../actions/login';
import { CustomIcon } from '../../containers/CustomIcon';
import * as HeaderButton from '../../containers/HeaderButton';
import StatusBar from '../../containers/StatusBar';
import { themes } from '../../lib/constants';
@ -30,14 +29,7 @@ import { withTheme } from '../../theme';
import { getUserSelector } from '../../selectors/login';
import SafeAreaView from '../../containers/SafeAreaView';
import styles from './styles';
import {
IAvatar,
IAvatarButton,
INavigationOptions,
IParams,
IProfileViewProps,
IProfileViewState
} from '../../definitions/IProfileViewInterfaces';
import { IAvatar, IAvatarButton, IParams, IProfileViewProps, IProfileViewState } from './interfaces';
import { IUser } from '../../definitions';
import { Services } from '../../lib/services';
@ -48,7 +40,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
private avatarUrl: any;
private newPassword: any;
static navigationOptions = ({ navigation, isMasterDetail }: INavigationOptions) => {
static navigationOptions = ({ navigation, isMasterDetail }: IProfileViewProps) => {
const options: StackNavigationOptions = {
title: I18n.t('Profile')
};
@ -319,48 +311,48 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
);
};
renderAvatarButtons = () => {
const { avatarUrl, avatarSuggestions } = this.state;
const { user, theme, Accounts_AllowUserAvatarChange } = this.props;
// 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: () => this.pickImageWithURL(avatarUrl!),
disabled: !avatarUrl,
key: 'profile-view-avatar-url-button'
})}
{Object.keys(avatarSuggestions).map(service => {
const { url, blob, contentType } = avatarSuggestions[service];
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>
);
};
// 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: () => this.pickImageWithURL(avatarUrl!),
// disabled: !avatarUrl,
// key: 'profile-view-avatar-url-button'
// })}
// {Object.keys(avatarSuggestions).map(service => {
// const { url, blob, contentType } = avatarSuggestions[service];
// 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 = () => {
const { customFields } = this.state;
@ -448,7 +440,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
};
render() {
const { name, username, email, newPassword, avatarUrl, customFields, avatar, saving } = this.state;
const { name, username, email, newPassword, customFields, avatar, saving } = this.state;
const {
user,
theme,
@ -457,7 +449,8 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
Accounts_AllowRealNameChange,
Accounts_AllowUserAvatarChange,
Accounts_AllowUsernameChange,
Accounts_CustomFields
Accounts_CustomFields,
navigation
} = this.props;
return (
@ -469,7 +462,17 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
<SafeAreaView testID='profile-view'>
<ScrollView contentContainerStyle={sharedStyles.containerScrollView} testID='profile-view-list' {...scrollPersistTaps}>
<View style={styles.avatarContainer} testID='profile-view-avatar'>
<Avatar text={user.username} avatar={avatar?.url} isStatic={avatar?.url} size={100} />
<Avatar text={user.username} avatar={avatar?.url} isStatic={avatar?.url} size={120} />
{Accounts_AllowUserAvatarChange ? (
<Touch
key={'profile-view-edit-avatar'}
testID={'profile-view-edit-avatar'}
onPress={() => navigation.navigate('AvatarEditView')}
style={[styles.avatarButton, { backgroundColor: themes[theme].borderColor }]}
theme={theme}>
<Text style={{ color: themes[theme].bodyText }}>Edit</Text>
</Touch>
) : null}
</View>
<RCTextInput
editable={Accounts_AllowRealNameChange}
@ -534,28 +537,13 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
// @ts-ignore
return this[Object.keys(customFields)[0]].focus();
}
this.avatarUrl.focus();
this.submit();
}}
secureTextEntry
testID='profile-view-new-password'
theme={theme}
/>
{this.renderCustomFields()}
<RCTextInput
editable={Accounts_AllowUserAvatarChange}
inputStyle={[!Accounts_AllowUserAvatarChange && styles.disabled]}
inputRef={e => {
this.avatarUrl = e;
}}
label={I18n.t('Avatar_Url')}
placeholder={I18n.t('Avatar_Url')}
value={avatarUrl!}
onChangeText={value => this.setState({ avatarUrl: value })}
onSubmitEditing={this.submit}
testID='profile-view-avatar-url'
theme={theme}
/>
{this.renderAvatarButtons()}
<Button
title={I18n.t('Save_Changes')}
type='primary'

View File

@ -1,9 +1,8 @@
import { StackNavigationProp } from '@react-navigation/stack';
import React from 'react';
import { TSupportedThemes } from '../theme';
import { ProfileStackParamList } from '../stacks/types';
import { IUser } from './IUser';
import { IBaseScreen, IUser } from '../../definitions';
import { TSupportedThemes } from '../../theme';
import { ProfileStackParamList } from '../../stacks/types';
export interface IParams {
name: string;
@ -20,12 +19,7 @@ export interface IAvatarButton {
disabled: boolean;
}
export interface INavigationOptions {
navigation: StackNavigationProp<ProfileStackParamList, 'ProfileView'>;
isMasterDetail?: boolean;
}
export interface IProfileViewProps {
export interface IProfileViewProps extends IBaseScreen<ProfileStackParamList, 'ProfileView'> {
user: IUser;
baseUrl: string;
Accounts_AllowEmailChange: boolean;

View File

@ -16,12 +16,12 @@ export default StyleSheet.create({
},
avatarButton: {
backgroundColor: '#e1e5e8',
width: 50,
height: 50,
width: 48,
height: 32,
alignItems: 'center',
justifyContent: 'center',
marginRight: 15,
marginBottom: 15,
marginTop: 16,
marginBottom: 24,
borderRadius: 2
}
});

View File

@ -32,7 +32,7 @@ import log, { events, logEvent } from '../../utils/log';
import { MessageTypeValues } from '../../utils/messageTypes';
import random from '../../utils/random';
import scrollPersistTaps from '../../utils/scrollPersistTaps';
import { IAvatar } from '../../definitions/IProfileViewInterfaces';
import { IAvatar } from '../ProfileView/interfaces';
import sharedStyles from '../Styles';
import styles from './styles';
import SwitchContainer from './SwitchContainer';