Chore: Migrate redux module login to typescript (#3647)

* chore: migrate redux module login to typescript

chore: update redux module login tests

* update workers

* wip

* fix type

* remove partial

* add more status

* migrate the rest of the stuff to typescript

* fix tests and types

* fix types and tests
This commit is contained in:
Gleidson Daniel Silva 2022-02-18 10:53:36 -03:00 committed by GitHub
parent d73886bd60
commit 219462ba3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 370 additions and 193 deletions

View File

@ -1,59 +0,0 @@
import * as types from './actionsTypes';
export function loginRequest(credentials, logoutOnError, isFromWebView) {
return {
type: types.LOGIN.REQUEST,
credentials,
logoutOnError,
isFromWebView
};
}
export function loginSuccess(user) {
return {
type: types.LOGIN.SUCCESS,
user
};
}
export function loginFailure(err) {
return {
type: types.LOGIN.FAILURE,
err
};
}
export function logout(forcedByServer = false) {
return {
type: types.LOGOUT,
forcedByServer
};
}
export function setUser(user) {
return {
type: types.USER.SET,
user
};
}
export function setLoginServices(data) {
return {
type: types.LOGIN.SET_SERVICES,
data
};
}
export function setPreference(preference) {
return {
type: types.LOGIN.SET_PREFERENCE,
preference
};
}
export function setLocalAuthenticated(isLocalAuthenticated) {
return {
type: types.LOGIN.SET_LOCAL_AUTHENTICATED,
isLocalAuthenticated
};
}

115
app/actions/login.ts Normal file
View File

@ -0,0 +1,115 @@
import { Action } from 'redux';
import { IUser } from '../definitions';
import * as types from './actionsTypes';
interface ICredentials {
resume: string;
user: string;
password: string;
}
interface ILoginRequest extends Action {
credentials: any;
logoutOnError?: boolean;
isFromWebView?: boolean;
}
interface ILoginSuccess extends Action {
user: Partial<IUser>;
}
interface ILoginFailure extends Action {
err: Partial<IUser>;
}
interface ILogout extends Action {
forcedByServer: boolean;
}
interface ISetUser extends Action {
user: Partial<IUser>;
}
interface ISetServices extends Action {
data: Record<string, string>;
}
interface ISetPreference extends Action {
preference: Record<string, any>;
}
interface ISetLocalAuthenticated extends Action {
isLocalAuthenticated: boolean;
}
export type TActionsLogin = ILoginRequest &
ILoginSuccess &
ILoginFailure &
ILogout &
ISetUser &
ISetServices &
ISetPreference &
ISetLocalAuthenticated;
export function loginRequest(
credentials: Partial<ICredentials>,
logoutOnError?: boolean,
isFromWebView?: boolean
): ILoginRequest {
return {
type: types.LOGIN.REQUEST,
credentials,
logoutOnError,
isFromWebView
};
}
export function loginSuccess(user: Partial<IUser>): ILoginSuccess {
return {
type: types.LOGIN.SUCCESS,
user
};
}
export function loginFailure(err: Record<string, any>): ILoginFailure {
return {
type: types.LOGIN.FAILURE,
err
};
}
export function logout(forcedByServer = false): ILogout {
return {
type: types.LOGOUT,
forcedByServer
};
}
export function setUser(user: Partial<IUser>): ISetUser {
return {
type: types.USER.SET,
user
};
}
export function setLoginServices(data: Record<string, any>): ISetServices {
return {
type: types.LOGIN.SET_SERVICES,
data
};
}
export function setPreference(preference: Record<string, any>): ISetPreference {
return {
type: types.LOGIN.SET_PREFERENCE,
preference
};
}
export function setLocalAuthenticated(isLocalAuthenticated: boolean): ISetLocalAuthenticated {
return {
type: types.LOGIN.SET_LOCAL_AUTHENTICATED,
isLocalAuthenticated
};
}

View File

@ -2,6 +2,7 @@ import Model from '@nozbe/watermelondb/Model';
import { UserStatus } from './UserStatus'; import { UserStatus } from './UserStatus';
import { IRocketChatRecord } from './IRocketChatRecord'; import { IRocketChatRecord } from './IRocketChatRecord';
import { ILoggedUser } from './ILoggedUser';
export interface ILoginToken { export interface ILoginToken {
hashedToken: string; hashedToken: string;
@ -93,8 +94,10 @@ export interface IUserSettings {
}; };
} }
export interface IUser extends IRocketChatRecord { export interface IUser extends IRocketChatRecord, Omit<ILoggedUser, 'username' | 'name' | 'status'> {
_id: string; _id: string;
id: string;
token: string;
createdAt: Date; createdAt: Date;
roles: string[]; roles: string[];
type: string; type: string;

View File

@ -23,6 +23,7 @@ import { ICreateChannel } from '../../reducers/createChannel';
import { ICreateDiscussion } from '../../reducers/createDiscussion'; import { ICreateDiscussion } from '../../reducers/createDiscussion';
import { IEncryption } from '../../reducers/encryption'; import { IEncryption } from '../../reducers/encryption';
import { IInviteLinks } from '../../reducers/inviteLinks'; import { IInviteLinks } from '../../reducers/inviteLinks';
import { ILogin } from '../../reducers/login';
import { IRoles } from '../../reducers/roles'; import { IRoles } from '../../reducers/roles';
import { IRoom } from '../../reducers/room'; import { IRoom } from '../../reducers/room';
import { ISelectedUsers } from '../../reducers/selectedUsers'; import { ISelectedUsers } from '../../reducers/selectedUsers';
@ -34,8 +35,8 @@ import { IEnterpriseModules } from '../../reducers/enterpriseModules';
export interface IApplicationState { export interface IApplicationState {
settings: ISettings; settings: ISettings;
login: any;
meteor: IConnect; meteor: IConnect;
login: ILogin;
server: IServer; server: IServer;
selectedUsers: ISelectedUsers; selectedUsers: ISelectedUsers;
app: IApp; app: IApp;

View File

@ -1,4 +1,5 @@
import { clearActiveUsers, setActiveUsers } from '../actions/activeUsers'; import { clearActiveUsers, setActiveUsers } from '../actions/activeUsers';
import { UserStatus } from '../definitions/UserStatus';
import { IActiveUsers, initialState } from './activeUsers'; import { IActiveUsers, initialState } from './activeUsers';
import { mockedStore } from './mockedStore'; import { mockedStore } from './mockedStore';
@ -8,7 +9,7 @@ describe('test reducer', () => {
expect(state).toEqual(initialState); expect(state).toEqual(initialState);
}); });
it('should return modified store after action', () => { it('should return modified store after action', () => {
const activeUsers: IActiveUsers = { any: { status: 'online', statusText: 'any' } }; const activeUsers: IActiveUsers = { any: { status: UserStatus.ONLINE, statusText: 'any' } };
mockedStore.dispatch(setActiveUsers(activeUsers)); mockedStore.dispatch(setActiveUsers(activeUsers));
const state = mockedStore.getState().activeUsers; const state = mockedStore.getState().activeUsers;
expect(state).toEqual({ ...activeUsers }); expect(state).toEqual({ ...activeUsers });

View File

@ -1,9 +1,9 @@
import { TApplicationActions } from '../definitions';
import { ACTIVE_USERS } from '../actions/actionsTypes'; import { ACTIVE_USERS } from '../actions/actionsTypes';
import { TApplicationActions } from '../definitions';
import { UserStatus } from '../definitions/UserStatus';
type TUserStatus = 'online' | 'offline' | 'away' | 'busy';
export interface IActiveUser { export interface IActiveUser {
status: TUserStatus; status: UserStatus;
statusText: string; statusText: string;
} }

109
app/reducers/login.test.ts Normal file
View File

@ -0,0 +1,109 @@
import {
loginFailure,
loginRequest,
loginSuccess,
logout,
setLocalAuthenticated,
setLoginServices,
setUser
} from '../actions/login';
import { UserStatus } from '../definitions/UserStatus';
import { initialState } from './login';
import { mockedStore } from './mockedStore';
describe('test selectedUsers reducer', () => {
it('should return initial state', () => {
const state = mockedStore.getState().login;
expect(state).toEqual(initialState);
});
it('should return modified store after loginRequest', () => {
mockedStore.dispatch(loginRequest({ user: 'carlitos@email.com', password: '123456' }));
const state = mockedStore.getState().login;
expect(state).toEqual({ ...initialState, isFetching: true, isAuthenticated: false, failure: false, error: {} });
});
it('should return modified store after loginFailure', () => {
mockedStore.dispatch(loginFailure({ error: 'error' }));
const state = mockedStore.getState().login.error.error;
expect(state).toEqual('error');
});
it('should return modified store after loginSuccess', () => {
const user = {
id: 'ajhsiahsa',
token: 'asdasdasdas',
username: 'carlitos',
name: 'Carlitos',
customFields: {
phonenumber: ''
},
emails: [
{
address: 'carlitos@email.com',
verified: true
}
],
roles: ['user'],
isFromWebView: false,
showMessageInMainThread: false,
enableMessageParserEarlyAdoption: false,
status: UserStatus.ONLINE,
statusText: 'online'
};
mockedStore.dispatch(loginSuccess(user));
const state = mockedStore.getState().login.user;
expect(state).toEqual(user);
});
it('should return modified store after setUser', () => {
const user = {
id: 'ajhsiahsa',
token: 'asdasdasdas',
username: 'carlito',
name: 'Carlitos',
customFields: {
phonenumber: ''
},
emails: [
{
address: 'carlitos@email.com',
verified: true
}
],
roles: ['user'],
isFromWebView: false,
showMessageInMainThread: false,
enableMessageParserEarlyAdoption: false
};
mockedStore.dispatch(setUser(user));
const state = mockedStore.getState().login.user.username;
expect(state).toEqual(user.username);
});
// TODO PREFERENCE REDUCER WITH EMPTY PREFERENCE - NON USED?
// it('should return modified store after setPreference', () => {
// mockedStore.dispatch(setPreference({ showAvatar: true }));
// const state = mockedStore.getState().login;
// console.log(state);
// expect(state).toEqual('error');
// });
it('should return modified store after setLocalAuthenticated', () => {
mockedStore.dispatch(setLocalAuthenticated(true));
const state = mockedStore.getState().login.isLocalAuthenticated;
expect(state).toEqual(true);
});
it('should return modified store after setLoginServices', () => {
mockedStore.dispatch(setLoginServices({ facebook: { clientId: 'xxx' } }));
const state = mockedStore.getState().login.services.facebook.clientId;
expect(state).toEqual('xxx');
});
it('should return modified store after logout', () => {
mockedStore.dispatch(logout());
const state = mockedStore.getState().login;
expect(state).toEqual(initialState);
});
});

View File

@ -1,15 +1,47 @@
import { UserStatus } from '../definitions/UserStatus';
import * as types from '../actions/actionsTypes'; import * as types from '../actions/actionsTypes';
import { TActionsLogin } from '../actions/login';
import { IUser } from '../definitions';
const initialState = { export interface IUserLogin {
id: string;
token: string;
username: string;
name: string;
language?: string;
status: UserStatus;
statusText: string;
roles: string[];
avatarETag?: string;
isFromWebView: boolean;
showMessageInMainThread: boolean;
enableMessageParserEarlyAdoption: boolean;
emails: Record<string, any>[];
customFields: Record<string, string>;
settings?: Record<string, string>;
}
export interface ILogin {
user: Partial<IUser>;
isLocalAuthenticated: boolean;
isAuthenticated: boolean;
isFetching: boolean;
error: Record<string, any>;
services: Record<string, any>;
failure: boolean;
}
export const initialState: ILogin = {
isLocalAuthenticated: true, isLocalAuthenticated: true,
isAuthenticated: false, isAuthenticated: false,
isFetching: false, isFetching: false,
user: {}, user: {},
error: {}, error: {},
services: {} services: {},
failure: false
}; };
export default function login(state = initialState, action) { export default function login(state = initialState, action: TActionsLogin): ILogin {
switch (action.type) { switch (action.type) {
case types.APP.INIT: case types.APP.INIT:
return initialState; return initialState;
@ -60,13 +92,14 @@ export default function login(state = initialState, action) {
...state, ...state,
user: { user: {
...state.user, ...state.user,
settings: { settings: state.user?.settings
...state.user.settings, ? {
preferences: { ...state.user?.settings,
...state.user.settings.preferences, preferences: state.user?.settings?.preferences
...action.preference ? { ...state.user.settings.preferences, ...action.preference }
} : { ...action.preference }
} }
: { profile: {}, preferences: {} }
} }
}; };
case types.LOGIN.SET_LOCAL_AUTHENTICATED: case types.LOGIN.SET_LOCAL_AUTHENTICATED:

View File

@ -1,7 +1,7 @@
import { createSelector } from 'reselect'; import { createSelector } from 'reselect';
import isEmpty from 'lodash/isEmpty'; import isEmpty from 'lodash/isEmpty';
import { IApplicationState } from '../definitions'; import { IApplicationState, IUser } from '../definitions';
interface IServices { interface IServices {
facebook: { clientId: string }; facebook: { clientId: string };
@ -13,7 +13,7 @@ interface IServices {
wordpress: { clientId: string; serverURL: string }; wordpress: { clientId: string; serverURL: string };
} }
const getUser = (state: IApplicationState) => { const getUser = (state: IApplicationState): Partial<IUser> => {
if (!isEmpty(state.share?.user)) { if (!isEmpty(state.share?.user)) {
return state.share.user; return state.share.user;
} }
@ -23,7 +23,8 @@ const getLoginServices = (state: IApplicationState) => (state.login.services as
const getShowFormLoginSetting = (state: IApplicationState) => (state.settings.Accounts_ShowFormLogin as boolean) || false; const getShowFormLoginSetting = (state: IApplicationState) => (state.settings.Accounts_ShowFormLogin as boolean) || false;
const getIframeEnabledSetting = (state: IApplicationState) => (state.settings.Accounts_iframe_enabled as boolean) || false; const getIframeEnabledSetting = (state: IApplicationState) => (state.settings.Accounts_iframe_enabled as boolean) || false;
export const getUserSelector = createSelector([getUser], user => user); // TODO: we need to change 42 files to fix a correct type, i believe is better to do this later
export const getUserSelector = createSelector([getUser], user => user) as any;
export const getShowLoginButton = createSelector( export const getShowLoginButton = createSelector(
[getLoginServices, getShowFormLoginSetting, getIframeEnabledSetting], [getLoginServices, getShowFormLoginSetting, getIframeEnabledSetting],

View File

@ -1,21 +1,20 @@
import { dequal } from 'dequal';
import React from 'react'; import React from 'react';
import { Alert, Keyboard, StyleSheet, Text, View } from 'react-native'; import { Alert, Keyboard, StyleSheet, Text, View } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { dequal } from 'dequal';
import { StackNavigationProp } from '@react-navigation/stack';
import { RouteProp } from '@react-navigation/core';
import Button from '../containers/Button'; import { loginRequest } from '../actions/login';
import I18n from '../i18n';
import * as HeaderButton from '../containers/HeaderButton';
import { themes } from '../constants/colors'; import { themes } from '../constants/colors';
import { withTheme } from '../theme'; import Button from '../containers/Button';
import FormContainer, { FormContainerInner } from '../containers/FormContainer'; import FormContainer, { FormContainerInner } from '../containers/FormContainer';
import TextInput from '../containers/TextInput'; import * as HeaderButton from '../containers/HeaderButton';
import { loginRequest as loginRequestAction } from '../actions/login';
import LoginServices from '../containers/LoginServices'; import LoginServices from '../containers/LoginServices';
import sharedStyles from './Styles'; import TextInput from '../containers/TextInput';
import { IApplicationState, IBaseScreen } from '../definitions';
import I18n from '../i18n';
import { OutsideParamList } from '../stacks/types'; import { OutsideParamList } from '../stacks/types';
import { withTheme } from '../theme';
import sharedStyles from './Styles';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
registerDisabled: { registerDisabled: {
@ -48,9 +47,7 @@ const styles = StyleSheet.create({
} }
}); });
interface ILoginViewProps { interface ILoginViewProps extends IBaseScreen<OutsideParamList, 'LoginView'> {
navigation: StackNavigationProp<OutsideParamList, 'LoginView'>;
route: RouteProp<OutsideParamList, 'LoginView'>;
Site_Name: string; Site_Name: string;
Accounts_RegistrationForm: string; Accounts_RegistrationForm: string;
Accounts_RegistrationForm_LinkReplacementText: string; Accounts_RegistrationForm_LinkReplacementText: string;
@ -63,7 +60,6 @@ interface ILoginViewProps {
error: string; error: string;
}; };
failure: boolean; failure: boolean;
theme: string;
loginRequest: Function; loginRequest: Function;
inviteLinkToken: string; inviteLinkToken: string;
} }
@ -132,9 +128,9 @@ class LoginView extends React.Component<ILoginViewProps, any> {
} }
const { user, password } = this.state; const { user, password } = this.state;
const { loginRequest } = this.props; const { dispatch } = this.props;
Keyboard.dismiss(); Keyboard.dismiss();
loginRequest({ user, password }); dispatch(loginRequest({ user, password }));
}; };
renderUserForm = () => { renderUserForm = () => {
@ -243,23 +239,19 @@ class LoginView extends React.Component<ILoginViewProps, any> {
} }
} }
const mapStateToProps = (state: any) => ({ const mapStateToProps = (state: IApplicationState) => ({
server: state.server.server, server: state.server.server,
Site_Name: state.settings.Site_Name, Site_Name: state.settings.Site_Name as string,
Accounts_ShowFormLogin: state.settings.Accounts_ShowFormLogin, Accounts_ShowFormLogin: state.settings.Accounts_ShowFormLogin as boolean,
Accounts_RegistrationForm: state.settings.Accounts_RegistrationForm, Accounts_RegistrationForm: state.settings.Accounts_RegistrationForm as string,
Accounts_RegistrationForm_LinkReplacementText: state.settings.Accounts_RegistrationForm_LinkReplacementText, Accounts_RegistrationForm_LinkReplacementText: state.settings.Accounts_RegistrationForm_LinkReplacementText as string,
isFetching: state.login.isFetching, isFetching: state.login.isFetching,
failure: state.login.failure, failure: state.login.failure,
error: state.login.error && state.login.error.data, error: state.login.error && state.login.error.data,
Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder, Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder as string,
Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder, Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder as string,
Accounts_PasswordReset: state.settings.Accounts_PasswordReset, Accounts_PasswordReset: state.settings.Accounts_PasswordReset as boolean,
inviteLinkToken: state.inviteLinks.token inviteLinkToken: state.inviteLinks.token
}); });
const mapDispatchToProps = (dispatch: any) => ({ export default connect(mapStateToProps)(withTheme(LoginView));
loginRequest: (params: any) => dispatch(loginRequestAction(params))
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(LoginView));

View File

@ -1,26 +1,25 @@
import React from 'react'; import React from 'react';
import { Keyboard, StyleSheet, Text, View } from 'react-native'; import { Keyboard, StyleSheet, Text, View } from 'react-native';
import { StackNavigationProp } from '@react-navigation/stack';
import { RouteProp } from '@react-navigation/core';
import { connect } from 'react-redux';
import RNPickerSelect from 'react-native-picker-select'; import RNPickerSelect from 'react-native-picker-select';
import { connect } from 'react-redux';
import { OutsideParamList } from '../stacks/types'; import { loginRequest } from '../actions/login';
import log, { events, logEvent } from '../utils/log';
import Button from '../containers/Button';
import I18n from '../i18n';
import * as HeaderButton from '../containers/HeaderButton';
import { themes } from '../constants/colors'; import { themes } from '../constants/colors';
import { withTheme } from '../theme'; import Button from '../containers/Button';
import FormContainer, { FormContainerInner } from '../containers/FormContainer'; import FormContainer, { FormContainerInner } from '../containers/FormContainer';
import TextInput from '../containers/TextInput'; import * as HeaderButton from '../containers/HeaderButton';
import isValidEmail from '../utils/isValidEmail';
import { showErrorAlert } from '../utils/info';
import RocketChat from '../lib/rocketchat';
import { loginRequest as loginRequestAction } from '../actions/login';
import openLink from '../utils/openLink';
import LoginServices from '../containers/LoginServices'; import LoginServices from '../containers/LoginServices';
import TextInput from '../containers/TextInput';
import { IApplicationState, IBaseScreen } from '../definitions';
import I18n from '../i18n';
import RocketChat from '../lib/rocketchat';
import { getShowLoginButton } from '../selectors/login'; import { getShowLoginButton } from '../selectors/login';
import { OutsideParamList } from '../stacks/types';
import { withTheme } from '../theme';
import { showErrorAlert } from '../utils/info';
import isValidEmail from '../utils/isValidEmail';
import log, { events, logEvent } from '../utils/log';
import openLink from '../utils/openLink';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@ -51,9 +50,7 @@ const styles = StyleSheet.create({
} }
}); });
interface IProps { interface IProps extends IBaseScreen<OutsideParamList, 'RegisterView'> {
navigation: StackNavigationProp<OutsideParamList, 'RegisterView'>;
route: RouteProp<OutsideParamList, 'RegisterView'>;
server: string; server: string;
Site_Name: string; Site_Name: string;
Gitlab_URL: string; Gitlab_URL: string;
@ -63,8 +60,6 @@ interface IProps {
Accounts_EmailVerification: boolean; Accounts_EmailVerification: boolean;
Accounts_ManuallyApproveNewUsers: boolean; Accounts_ManuallyApproveNewUsers: boolean;
showLoginButton: boolean; showLoginButton: boolean;
loginRequest: Function;
theme: string;
} }
class RegisterView extends React.Component<IProps, any> { class RegisterView extends React.Component<IProps, any> {
@ -130,7 +125,7 @@ class RegisterView extends React.Component<IProps, any> {
Keyboard.dismiss(); Keyboard.dismiss();
const { name, email, password, username, customFields } = this.state; const { name, email, password, username, customFields } = this.state;
const { loginRequest, Accounts_EmailVerification, navigation, Accounts_ManuallyApproveNewUsers } = this.props; const { dispatch, Accounts_EmailVerification, navigation, Accounts_ManuallyApproveNewUsers } = this.props;
try { try {
await RocketChat.register({ await RocketChat.register({
@ -148,11 +143,11 @@ class RegisterView extends React.Component<IProps, any> {
await navigation.goBack(); await navigation.goBack();
showErrorAlert(I18n.t('Wait_activation_warning'), I18n.t('Registration_Succeeded')); showErrorAlert(I18n.t('Wait_activation_warning'), I18n.t('Registration_Succeeded'));
} else { } else {
await loginRequest({ user: email, password }); dispatch(loginRequest({ user: email, password }));
} }
} catch (e: any) { } catch (e: any) {
if (e.data?.errorType === 'username-invalid') { if (e.data?.errorType === 'username-invalid') {
return loginRequest({ user: email, password }); return dispatch(loginRequest({ user: email, password }));
} }
if (e.data?.error) { if (e.data?.error) {
logEvent(events.REGISTER_DEFAULT_SIGN_UP_F); logEvent(events.REGISTER_DEFAULT_SIGN_UP_F);
@ -349,20 +344,16 @@ class RegisterView extends React.Component<IProps, any> {
} }
} }
const mapStateToProps = (state: any) => ({ const mapStateToProps = (state: IApplicationState) => ({
server: state.server.server, server: state.server.server,
Site_Name: state.settings.Site_Name, Site_Name: state.settings.Site_Name as string,
Gitlab_URL: state.settings.API_Gitlab_URL, Gitlab_URL: state.settings.API_Gitlab_URL as string,
CAS_enabled: state.settings.CAS_enabled, CAS_enabled: state.settings.CAS_enabled as boolean,
CAS_login_url: state.settings.CAS_login_url, CAS_login_url: state.settings.CAS_login_url as string,
Accounts_CustomFields: state.settings.Accounts_CustomFields, Accounts_CustomFields: state.settings.Accounts_CustomFields as string,
Accounts_EmailVerification: state.settings.Accounts_EmailVerification, Accounts_EmailVerification: state.settings.Accounts_EmailVerification as boolean,
Accounts_ManuallyApproveNewUsers: state.settings.Accounts_ManuallyApproveNewUsers, Accounts_ManuallyApproveNewUsers: state.settings.Accounts_ManuallyApproveNewUsers as boolean,
showLoginButton: getShowLoginButton(state) showLoginButton: getShowLoginButton(state)
}); });
const mapDispatchToProps = (dispatch: any) => ({ export default connect(mapStateToProps)(withTheme(RegisterView));
loginRequest: (params: any) => dispatch(loginRequestAction(params))
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(RegisterView));

View File

@ -1,25 +1,26 @@
import React from 'react';
import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import { Dispatch } from 'redux';
import { ScrollView, StyleSheet, Text } from 'react-native';
import { connect } from 'react-redux';
import Orientation from 'react-native-orientation-locker';
import { RouteProp } from '@react-navigation/native'; import { RouteProp } from '@react-navigation/native';
import { StackNavigationOptions, StackNavigationProp } from '@react-navigation/stack';
import React from 'react';
import { ScrollView, StyleSheet, Text } from 'react-native';
import Orientation from 'react-native-orientation-locker';
import { connect } from 'react-redux';
import { Dispatch } from 'redux';
import { loginRequest as loginRequestAction } from '../actions/login'; import { loginRequest } from '../actions/login';
import TextInput from '../containers/TextInput'; import { themes } from '../constants/colors';
import Button from '../containers/Button'; import Button from '../containers/Button';
import KeyboardView from '../presentation/KeyboardView'; import SafeAreaView from '../containers/SafeAreaView';
import scrollPersistTaps from '../utils/scrollPersistTaps'; import StatusBar from '../containers/StatusBar';
import TextInput from '../containers/TextInput';
import { IApplicationState } from '../definitions';
import I18n from '../i18n'; import I18n from '../i18n';
import RocketChat from '../lib/rocketchat'; import RocketChat from '../lib/rocketchat';
import StatusBar from '../containers/StatusBar'; import KeyboardView from '../presentation/KeyboardView';
import { withTheme } from '../theme';
import { themes } from '../constants/colors';
import { isTablet } from '../utils/deviceInfo';
import { getUserSelector } from '../selectors/login'; import { getUserSelector } from '../selectors/login';
import { withTheme } from '../theme';
import { isTablet } from '../utils/deviceInfo';
import { showErrorAlert } from '../utils/info'; import { showErrorAlert } from '../utils/info';
import SafeAreaView from '../containers/SafeAreaView'; import scrollPersistTaps from '../utils/scrollPersistTaps';
import sharedStyles from './Styles'; import sharedStyles from './Styles';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
@ -39,9 +40,9 @@ interface ISetUsernameViewProps {
route: RouteProp<{ SetUsernameView: { title: string } }, 'SetUsernameView'>; route: RouteProp<{ SetUsernameView: { title: string } }, 'SetUsernameView'>;
server: string; server: string;
userId: string; userId: string;
loginRequest: ({ resume }: { resume: string }) => void;
token: string; token: string;
theme: string; theme: string;
dispatch: Dispatch;
} }
class SetUsernameView extends React.Component<ISetUsernameViewProps, ISetUsernameViewState> { class SetUsernameView extends React.Component<ISetUsernameViewProps, ISetUsernameViewState> {
@ -86,7 +87,7 @@ class SetUsernameView extends React.Component<ISetUsernameViewProps, ISetUsernam
submit = async () => { submit = async () => {
const { username } = this.state; const { username } = this.state;
const { loginRequest, token } = this.props; const { dispatch, token } = this.props;
if (!username.trim()) { if (!username.trim()) {
return; return;
@ -95,7 +96,7 @@ class SetUsernameView extends React.Component<ISetUsernameViewProps, ISetUsernam
this.setState({ saving: true }); this.setState({ saving: true });
try { try {
await RocketChat.saveUserProfile({ username }); await RocketChat.saveUserProfile({ username });
await loginRequest({ resume: token }); dispatch(loginRequest({ resume: token }));
} catch (e: any) { } catch (e: any) {
showErrorAlert(e.message, I18n.t('Oops')); showErrorAlert(e.message, I18n.t('Oops'));
} }
@ -144,13 +145,9 @@ class SetUsernameView extends React.Component<ISetUsernameViewProps, ISetUsernam
} }
} }
const mapStateToProps = (state: any) => ({ const mapStateToProps = (state: IApplicationState) => ({
server: state.server.server, server: state.server.server,
token: getUserSelector(state).token token: getUserSelector(state).token
}); });
const mapDispatchToProps = (dispatch: Dispatch) => ({ export default connect(mapStateToProps)(withTheme(SetUsernameView));
loginRequest: (params: { resume: string }) => dispatch(loginRequestAction(params))
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(SetUsernameView));

View File

@ -1,24 +1,24 @@
import React from 'react'; import React from 'react';
import { StackNavigationProp } from '@react-navigation/stack';
import { FlatList, StyleSheet } from 'react-native'; import { FlatList, StyleSheet } from 'react-native';
import { Dispatch } from 'redux';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import I18n from '../i18n'; import { UserStatus } from '../definitions/UserStatus';
import { setUser } from '../actions/login';
import * as HeaderButton from '../containers/HeaderButton';
import * as List from '../containers/List'; import * as List from '../containers/List';
import Loading from '../containers/Loading';
import SafeAreaView from '../containers/SafeAreaView';
import Status from '../containers/Status/Status'; import Status from '../containers/Status/Status';
import TextInput from '../containers/TextInput'; import TextInput from '../containers/TextInput';
import { LISTENER } from '../containers/Toast';
import { IApplicationState, IBaseScreen } from '../definitions';
import I18n from '../i18n';
import RocketChat from '../lib/rocketchat';
import { getUserSelector } from '../selectors/login';
import { withTheme } from '../theme';
import EventEmitter from '../utils/events'; import EventEmitter from '../utils/events';
import { showErrorAlert } from '../utils/info'; import { showErrorAlert } from '../utils/info';
import Loading from '../containers/Loading';
import RocketChat from '../lib/rocketchat';
import log, { events, logEvent } from '../utils/log'; import log, { events, logEvent } from '../utils/log';
import { LISTENER } from '../containers/Toast';
import { withTheme } from '../theme';
import { getUserSelector } from '../selectors/login';
import * as HeaderButton from '../containers/HeaderButton';
import { setUser as setUserAction } from '../actions/login';
import SafeAreaView from '../containers/SafeAreaView';
const STATUS = [ const STATUS = [
{ {
@ -65,12 +65,9 @@ interface IStatusViewState {
loading: boolean; loading: boolean;
} }
interface IStatusViewProps { interface IStatusViewProps extends IBaseScreen<any, 'StatusView'> {
navigation: StackNavigationProp<any, 'StatusView'>;
user: IUser; user: IUser;
theme: string;
isMasterDetail: boolean; isMasterDetail: boolean;
setUser: (user: IUser) => void;
Accounts_AllowInvisibleStatusOption: boolean; Accounts_AllowInvisibleStatusOption: boolean;
} }
@ -111,7 +108,7 @@ class StatusView extends React.Component<IStatusViewProps, IStatusViewState> {
}; };
setCustomStatus = async (statusText: string) => { setCustomStatus = async (statusText: string) => {
const { user, setUser } = this.props; const { user, dispatch } = this.props;
this.setState({ loading: true }); this.setState({ loading: true });
@ -119,7 +116,7 @@ class StatusView extends React.Component<IStatusViewProps, IStatusViewState> {
const result = await RocketChat.setUserStatus(user.status, statusText); const result = await RocketChat.setUserStatus(user.status, statusText);
if (result.success) { if (result.success) {
logEvent(events.STATUS_CUSTOM); logEvent(events.STATUS_CUSTOM);
setUser({ statusText }); dispatch(setUser({ statusText }));
EventEmitter.emit(LISTENER, { message: I18n.t('Status_saved_successfully') }); EventEmitter.emit(LISTENER, { message: I18n.t('Status_saved_successfully') });
} else { } else {
logEvent(events.STATUS_CUSTOM_F); logEvent(events.STATUS_CUSTOM_F);
@ -156,7 +153,7 @@ class StatusView extends React.Component<IStatusViewProps, IStatusViewState> {
renderItem = ({ item }: { item: { id: string; name: string } }) => { renderItem = ({ item }: { item: { id: string; name: string } }) => {
const { statusText } = this.state; const { statusText } = this.state;
const { user, setUser } = this.props; const { user, dispatch } = this.props;
const { id, name } = item; const { id, name } = item;
return ( return (
<List.Item <List.Item
@ -168,7 +165,7 @@ class StatusView extends React.Component<IStatusViewProps, IStatusViewState> {
try { try {
const result = await RocketChat.setUserStatus(item.id, statusText); const result = await RocketChat.setUserStatus(item.id, statusText);
if (result.success) { if (result.success) {
setUser({ status: item.id }); dispatch(setUser({ status: item.id as UserStatus }));
} }
} catch (e: any) { } catch (e: any) {
showErrorAlert(I18n.t(e.data.errorType)); showErrorAlert(I18n.t(e.data.errorType));
@ -205,14 +202,10 @@ class StatusView extends React.Component<IStatusViewProps, IStatusViewState> {
} }
} }
const mapStateToProps = (state: any) => ({ const mapStateToProps = (state: IApplicationState) => ({
user: getUserSelector(state), user: getUserSelector(state),
isMasterDetail: state.app.isMasterDetail, isMasterDetail: state.app.isMasterDetail,
Accounts_AllowInvisibleStatusOption: state.settings.Accounts_AllowInvisibleStatusOption ?? true Accounts_AllowInvisibleStatusOption: (state.settings.Accounts_AllowInvisibleStatusOption as boolean) ?? true
}); });
const mapDispatchToProps = (dispatch: Dispatch) => ({ export default connect(mapStateToProps)(withTheme(StatusView));
setUser: (user: IUser) => dispatch(setUserAction(user))
});
export default connect(mapStateToProps, mapDispatchToProps)(withTheme(StatusView));