Chore: Evaluate ProfileView - Typescript (#4091)

This commit is contained in:
Alex Junior 2022-05-06 23:11:07 -03:00 committed by GitHub
parent 1346154b65
commit 55d66c85db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 121 additions and 122 deletions

View File

@ -0,0 +1,31 @@
import React from 'react';
export interface IProfileParams {
name: string;
username: string;
email: string | null;
newPassword: string;
currentPassword: string;
}
export interface IAvatarButton {
key: string;
child: React.ReactNode;
onPress: () => void;
disabled: boolean;
}
export interface IAvatar {
data: {} | string | null;
url?: string;
contentType?: string;
service?: any;
}
export interface IAvatarSuggestion {
[service: string]: {
url: string;
blob: string;
contentType: string;
};
}

View File

@ -1,69 +0,0 @@
import { StackNavigationProp } from '@react-navigation/stack';
import React from 'react';
import { TSupportedThemes } from '../theme';
import { ProfileStackParamList } from '../stacks/types';
import { IUser } from './IUser';
export interface IParams {
name: string;
username: string;
email: string | null;
newPassword: string;
currentPassword: string;
}
export interface IAvatarButton {
key: string;
child: React.ReactNode;
onPress: () => void;
disabled: boolean;
}
export interface INavigationOptions {
navigation: StackNavigationProp<ProfileStackParamList, 'ProfileView'>;
isMasterDetail?: boolean;
}
export interface IProfileViewProps {
user: IUser;
baseUrl: string;
Accounts_AllowEmailChange: boolean;
Accounts_AllowPasswordChange: boolean;
Accounts_AllowRealNameChange: boolean;
Accounts_AllowUserAvatarChange: boolean;
Accounts_AllowUsernameChange: boolean;
Accounts_CustomFields: string;
setUser: Function;
theme: TSupportedThemes;
}
export interface IAvatar {
data: {} | string | null;
url?: string;
contentType?: string;
service?: any;
}
export interface IAvatarSuggestion {
[service: string]: {
url: string;
blob: string;
contentType: string;
};
}
export interface IProfileViewState {
saving: boolean;
name: string;
username: string;
email: string | null;
newPassword: string | null;
currentPassword: string | null;
avatarUrl: string | null;
avatar: IAvatar;
avatarSuggestions: IAvatarSuggestion;
customFields: {
[key: string | number]: string;
};
}

View File

@ -28,6 +28,7 @@ export * from './IUrl';
export * from './ICredentials'; export * from './ICredentials';
export * from './ISearch'; export * from './ISearch';
export * from './TUserStatus'; export * from './TUserStatus';
export * from './IProfile';
export interface IBaseScreen<T extends Record<string, object | undefined>, S extends string> { export interface IBaseScreen<T extends Record<string, object | undefined>, S extends string> {
navigation: StackNavigationProp<T, S>; navigation: StackNavigationProp<T, S>;

View File

@ -1,4 +1,4 @@
import { IParams } from '../../IProfileViewInterfaces'; import { IProfileParams } from '../../IProfile';
import type { ITeam } from '../../ITeam'; import type { ITeam } from '../../ITeam';
import type { IUser } from '../../IUser'; import type { IUser } from '../../IUser';
import { INotificationPreferences, IUserPreferences, IUserRegistered } from '../../IUser'; import { INotificationPreferences, IUserPreferences, IUserRegistered } from '../../IUser';
@ -39,7 +39,10 @@ export type UsersEndpoints = {
POST: (params: { status: string; message: string }) => {}; POST: (params: { status: string; message: string }) => {};
}; };
'users.updateOwnBasicInfo': { 'users.updateOwnBasicInfo': {
POST: (params: { data: IParams | Pick<IParams, 'username'>; customFields?: { [key: string | number]: string } }) => { POST: (params: {
data: IProfileParams | Pick<IProfileParams, 'username'>;
customFields?: { [key: string | number]: string };
}) => {
user: IUser; user: IUser;
}; };
}; };

View File

@ -5,9 +5,10 @@ import {
IRoom, IRoom,
IRoomNotifications, IRoomNotifications,
SubscriptionType, SubscriptionType,
IUser IUser,
IAvatarSuggestion,
IProfileParams
} from '../../definitions'; } from '../../definitions';
import { IAvatarSuggestion, IParams } from '../../definitions/IProfileViewInterfaces';
import { ISpotlight } from '../../definitions/ISpotlight'; import { ISpotlight } from '../../definitions/ISpotlight';
import { TEAM_TYPE } from '../../definitions/ITeam'; import { TEAM_TYPE } from '../../definitions/ITeam';
import { Encryption } from '../encryption'; import { Encryption } from '../encryption';
@ -561,7 +562,10 @@ export const saveRoomSettings = (
// RC 0.55.0 // RC 0.55.0
sdk.methodCallWrapper('saveRoomSettings', rid, params); sdk.methodCallWrapper('saveRoomSettings', rid, params);
export const saveUserProfile = (data: IParams | Pick<IParams, 'username'>, customFields?: { [key: string | number]: string }) => export const saveUserProfile = (
data: IProfileParams | Pick<IProfileParams, 'username'>,
customFields?: { [key: string | number]: string }
) =>
// RC 0.62.2 // RC 0.62.2
sdk.post('users.updateOwnBasicInfo', { data, customFields }); sdk.post('users.updateOwnBasicInfo', { data, customFields });

View File

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import { Keyboard, ScrollView, View } from 'react-native'; import { Keyboard, ScrollView, TextInput, View } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import prompt from 'react-native-prompt-android'; import prompt from 'react-native-prompt-android';
import { sha256 } from 'js-sha256'; import { sha256 } from 'js-sha256';
@ -21,34 +21,62 @@ import log, { events, logEvent } from '../../utils/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 as setUserAction } from '../../actions/login'; import { setUser } from '../../actions/login';
import { CustomIcon } from '../../containers/CustomIcon'; 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';
import { withTheme } from '../../theme'; import { TSupportedThemes, withTheme } from '../../theme';
import { getUserSelector } from '../../selectors/login'; import { getUserSelector } from '../../selectors/login';
import SafeAreaView from '../../containers/SafeAreaView'; import SafeAreaView from '../../containers/SafeAreaView';
import styles from './styles'; import styles from './styles';
import { ProfileStackParamList } from '../../stacks/types';
import { Services } from '../../lib/services';
import { import {
IApplicationState,
IAvatar, IAvatar,
IAvatarButton, IAvatarButton,
INavigationOptions, IAvatarSuggestion,
IParams, IBaseScreen,
IProfileViewProps, IProfileParams,
IProfileViewState IUser
} from '../../definitions/IProfileViewInterfaces'; } from '../../definitions';
import { IUser } from '../../definitions';
import { Services } from '../../lib/services'; interface IProfileViewProps extends IBaseScreen<ProfileStackParamList, 'ProfileView'> {
user: IUser;
baseUrl: string;
Accounts_AllowEmailChange: boolean;
Accounts_AllowPasswordChange: boolean;
Accounts_AllowRealNameChange: boolean;
Accounts_AllowUserAvatarChange: boolean;
Accounts_AllowUsernameChange: boolean;
Accounts_CustomFields: string;
theme: TSupportedThemes;
}
interface IProfileViewState {
saving: boolean;
name: string;
username: string;
email: string | null;
newPassword: string | null;
currentPassword: string | null;
avatarUrl: string | null;
avatar: IAvatar;
avatarSuggestions: IAvatarSuggestion;
customFields: {
[key: string | number]: string;
};
}
class ProfileView extends React.Component<IProfileViewProps, IProfileViewState> { class ProfileView extends React.Component<IProfileViewProps, IProfileViewState> {
private name: any; private name?: TextInput | null;
private username: any; private username?: TextInput | null;
private email: any; private email?: TextInput;
private avatarUrl: any; private avatarUrl?: TextInput;
private newPassword: any; private newPassword?: TextInput;
static navigationOptions = ({ navigation, isMasterDetail }: INavigationOptions) => { static navigationOptions = ({ navigation, isMasterDetail }: IProfileViewProps) => {
const options: StackNavigationOptions = { const options: StackNavigationOptions = {
title: I18n.t('Profile') title: I18n.t('Profile')
}; };
@ -150,7 +178,7 @@ 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 && !avatar.data &&
!customFieldsChanged !customFieldsChanged
); );
}; };
@ -172,8 +200,8 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
this.setState({ saving: true }); this.setState({ saving: true });
const { name, username, email, newPassword, currentPassword, avatar, customFields } = this.state; const { name, username, email, newPassword, currentPassword, avatar, customFields } = this.state;
const { user, setUser } = this.props; const { user, dispatch } = this.props;
const params = {} as IParams; const params = {} as IProfileParams;
// Name // Name
if (user.name !== name) { if (user.name !== name) {
@ -225,7 +253,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
} }
try { try {
if (avatar!.url) { if (avatar.url) {
try { try {
logEvent(events.PROFILE_SAVE_AVATAR); logEvent(events.PROFILE_SAVE_AVATAR);
await Services.setAvatarFromService(avatar); await Services.setAvatarFromService(avatar);
@ -241,9 +269,9 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
if (result.success) { if (result.success) {
logEvent(events.PROFILE_SAVE_CHANGES); logEvent(events.PROFILE_SAVE_CHANGES);
if (customFields) { if (customFields) {
setUser({ customFields, ...params }); dispatch(setUser({ customFields, ...params }));
} else { } else {
setUser({ ...params }); dispatch(setUser({ ...params }));
} }
EventEmitter.emit(LISTENER, { message: I18n.t('Profile_saved_successfully') }); EventEmitter.emit(LISTENER, { message: I18n.t('Profile_saved_successfully') });
this.init(); this.init();
@ -339,7 +367,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
})} })}
{this.renderAvatarButton({ {this.renderAvatarButton({
child: <CustomIcon name='link' size={30} color={themes[theme].bodyText} />, child: <CustomIcon name='link' size={30} color={themes[theme].bodyText} />,
onPress: () => this.pickImageWithURL(avatarUrl!), onPress: () => (avatarUrl ? this.pickImageWithURL(avatarUrl) : null),
disabled: !avatarUrl, disabled: !avatarUrl,
key: 'profile-view-avatar-url-button' key: 'profile-view-avatar-url-button'
})} })}
@ -419,7 +447,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
// @ts-ignore // @ts-ignore
return this[array[index + 1]].focus(); return this[array[index + 1]].focus();
} }
this.avatarUrl.focus(); this.avatarUrl?.focus();
}} }}
theme={theme} theme={theme}
/> />
@ -482,7 +510,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
value={name} value={name}
onChangeText={(value: string) => this.setState({ name: value })} onChangeText={(value: string) => this.setState({ name: value })}
onSubmitEditing={() => { onSubmitEditing={() => {
this.username.focus(); this.username?.focus();
}} }}
testID='profile-view-name' testID='profile-view-name'
theme={theme} theme={theme}
@ -498,7 +526,7 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
value={username} value={username}
onChangeText={value => this.setState({ username: value })} onChangeText={value => this.setState({ username: value })}
onSubmitEditing={() => { onSubmitEditing={() => {
this.email.focus(); this.email?.focus();
}} }}
testID='profile-view-username' testID='profile-view-username'
theme={theme} theme={theme}
@ -507,14 +535,16 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
editable={Accounts_AllowEmailChange} editable={Accounts_AllowEmailChange}
inputStyle={[!Accounts_AllowEmailChange && styles.disabled]} inputStyle={[!Accounts_AllowEmailChange && styles.disabled]}
inputRef={e => { inputRef={e => {
this.email = e; if (e) {
this.email = e;
}
}} }}
label={I18n.t('Email')} label={I18n.t('Email')}
placeholder={I18n.t('Email')} placeholder={I18n.t('Email')}
value={email!} value={email || undefined}
onChangeText={value => this.setState({ email: value })} onChangeText={value => this.setState({ email: value })}
onSubmitEditing={() => { onSubmitEditing={() => {
this.newPassword.focus(); this.newPassword?.focus();
}} }}
testID='profile-view-email' testID='profile-view-email'
theme={theme} theme={theme}
@ -523,18 +553,20 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
editable={Accounts_AllowPasswordChange} editable={Accounts_AllowPasswordChange}
inputStyle={[!Accounts_AllowPasswordChange && styles.disabled]} inputStyle={[!Accounts_AllowPasswordChange && styles.disabled]}
inputRef={e => { inputRef={e => {
this.newPassword = e; if (e) {
this.newPassword = e;
}
}} }}
label={I18n.t('New_Password')} label={I18n.t('New_Password')}
placeholder={I18n.t('New_Password')} placeholder={I18n.t('New_Password')}
value={newPassword!} value={newPassword || undefined}
onChangeText={value => this.setState({ newPassword: value })} onChangeText={value => this.setState({ newPassword: value })}
onSubmitEditing={() => { onSubmitEditing={() => {
if (Accounts_CustomFields && Object.keys(customFields).length) { if (Accounts_CustomFields && Object.keys(customFields).length) {
// @ts-ignore // @ts-ignore
return this[Object.keys(customFields)[0]].focus(); return this[Object.keys(customFields)[0]].focus();
} }
this.avatarUrl.focus(); this.avatarUrl?.focus();
}} }}
secureTextEntry secureTextEntry
testID='profile-view-new-password' testID='profile-view-new-password'
@ -545,11 +577,13 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
editable={Accounts_AllowUserAvatarChange} editable={Accounts_AllowUserAvatarChange}
inputStyle={[!Accounts_AllowUserAvatarChange && styles.disabled]} inputStyle={[!Accounts_AllowUserAvatarChange && styles.disabled]}
inputRef={e => { inputRef={e => {
this.avatarUrl = e; if (e) {
this.avatarUrl = e;
}
}} }}
label={I18n.t('Avatar_Url')} label={I18n.t('Avatar_Url')}
placeholder={I18n.t('Avatar_Url')} placeholder={I18n.t('Avatar_Url')}
value={avatarUrl!} value={avatarUrl || undefined}
onChangeText={value => this.setState({ avatarUrl: value })} onChangeText={value => this.setState({ avatarUrl: value })}
onSubmitEditing={this.submit} onSubmitEditing={this.submit}
testID='profile-view-avatar-url' testID='profile-view-avatar-url'
@ -580,19 +614,15 @@ class ProfileView extends React.Component<IProfileViewProps, IProfileViewState>
} }
} }
const mapStateToProps = (state: any) => ({ const mapStateToProps = (state: IApplicationState) => ({
user: getUserSelector(state), user: getUserSelector(state),
Accounts_AllowEmailChange: state.settings.Accounts_AllowEmailChange, Accounts_AllowEmailChange: state.settings.Accounts_AllowEmailChange as boolean,
Accounts_AllowPasswordChange: state.settings.Accounts_AllowPasswordChange, Accounts_AllowPasswordChange: state.settings.Accounts_AllowPasswordChange as boolean,
Accounts_AllowRealNameChange: state.settings.Accounts_AllowRealNameChange, Accounts_AllowRealNameChange: state.settings.Accounts_AllowRealNameChange as boolean,
Accounts_AllowUserAvatarChange: state.settings.Accounts_AllowUserAvatarChange, Accounts_AllowUserAvatarChange: state.settings.Accounts_AllowUserAvatarChange as boolean,
Accounts_AllowUsernameChange: state.settings.Accounts_AllowUsernameChange, Accounts_AllowUsernameChange: state.settings.Accounts_AllowUsernameChange as boolean,
Accounts_CustomFields: state.settings.Accounts_CustomFields, Accounts_CustomFields: state.settings.Accounts_CustomFields as string,
baseUrl: state.server.server baseUrl: state.server.server
}); });
const mapDispatchToProps = (dispatch: any) => ({ export default connect(mapStateToProps)(withTheme(ProfileView));
setUser: (params: any) => dispatch(setUserAction(params))
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(ProfileView));

View File

@ -16,7 +16,7 @@ import StatusBar from '../../containers/StatusBar';
import RCTextInput from '../../containers/TextInput'; import RCTextInput from '../../containers/TextInput';
import { LISTENER } from '../../containers/Toast'; import { LISTENER } from '../../containers/Toast';
import { MultiSelect } from '../../containers/UIKit/MultiSelect'; import { MultiSelect } from '../../containers/UIKit/MultiSelect';
import { IApplicationState, IBaseScreen, ISubscription, SubscriptionType, TSubscriptionModel } from '../../definitions'; import { IApplicationState, IBaseScreen, ISubscription, SubscriptionType, TSubscriptionModel, IAvatar } from '../../definitions';
import { ERoomType } from '../../definitions/ERoomType'; import { ERoomType } from '../../definitions/ERoomType';
import I18n from '../../i18n'; import I18n from '../../i18n';
import database from '../../lib/database'; import database from '../../lib/database';
@ -32,7 +32,6 @@ import log, { events, logEvent } from '../../utils/log';
import { MessageTypeValues } from '../../utils/messageTypes'; import { MessageTypeValues } from '../../utils/messageTypes';
import random from '../../utils/random'; import random from '../../utils/random';
import scrollPersistTaps from '../../utils/scrollPersistTaps'; import scrollPersistTaps from '../../utils/scrollPersistTaps';
import { IAvatar } from '../../definitions/IProfileViewInterfaces';
import sharedStyles from '../Styles'; import sharedStyles from '../Styles';
import styles from './styles'; import styles from './styles';
import SwitchContainer from './SwitchContainer'; import SwitchContainer from './SwitchContainer';