diff --git a/app/actions/login.js b/app/actions/login.js deleted file mode 100644 index e0f4c1e28..000000000 --- a/app/actions/login.js +++ /dev/null @@ -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 - }; -} diff --git a/app/actions/login.ts b/app/actions/login.ts new file mode 100644 index 000000000..dead31433 --- /dev/null +++ b/app/actions/login.ts @@ -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; +} + +interface ILoginFailure extends Action { + err: Partial; +} + +interface ILogout extends Action { + forcedByServer: boolean; +} + +interface ISetUser extends Action { + user: Partial; +} + +interface ISetServices extends Action { + data: Record; +} + +interface ISetPreference extends Action { + preference: Record; +} + +interface ISetLocalAuthenticated extends Action { + isLocalAuthenticated: boolean; +} + +export type TActionsLogin = ILoginRequest & + ILoginSuccess & + ILoginFailure & + ILogout & + ISetUser & + ISetServices & + ISetPreference & + ISetLocalAuthenticated; + +export function loginRequest( + credentials: Partial, + logoutOnError?: boolean, + isFromWebView?: boolean +): ILoginRequest { + return { + type: types.LOGIN.REQUEST, + credentials, + logoutOnError, + isFromWebView + }; +} + +export function loginSuccess(user: Partial): ILoginSuccess { + return { + type: types.LOGIN.SUCCESS, + user + }; +} + +export function loginFailure(err: Record): ILoginFailure { + return { + type: types.LOGIN.FAILURE, + err + }; +} + +export function logout(forcedByServer = false): ILogout { + return { + type: types.LOGOUT, + forcedByServer + }; +} + +export function setUser(user: Partial): ISetUser { + return { + type: types.USER.SET, + user + }; +} + +export function setLoginServices(data: Record): ISetServices { + return { + type: types.LOGIN.SET_SERVICES, + data + }; +} + +export function setPreference(preference: Record): ISetPreference { + return { + type: types.LOGIN.SET_PREFERENCE, + preference + }; +} + +export function setLocalAuthenticated(isLocalAuthenticated: boolean): ISetLocalAuthenticated { + return { + type: types.LOGIN.SET_LOCAL_AUTHENTICATED, + isLocalAuthenticated + }; +} diff --git a/app/definitions/IUser.ts b/app/definitions/IUser.ts index 06aaffa1f..c486a56ed 100644 --- a/app/definitions/IUser.ts +++ b/app/definitions/IUser.ts @@ -2,6 +2,7 @@ import Model from '@nozbe/watermelondb/Model'; import { UserStatus } from './UserStatus'; import { IRocketChatRecord } from './IRocketChatRecord'; +import { ILoggedUser } from './ILoggedUser'; export interface ILoginToken { hashedToken: string; @@ -93,8 +94,10 @@ export interface IUserSettings { }; } -export interface IUser extends IRocketChatRecord { +export interface IUser extends IRocketChatRecord, Omit { _id: string; + id: string; + token: string; createdAt: Date; roles: string[]; type: string; diff --git a/app/definitions/redux/index.ts b/app/definitions/redux/index.ts index b7927a927..7b4e52db0 100644 --- a/app/definitions/redux/index.ts +++ b/app/definitions/redux/index.ts @@ -23,6 +23,7 @@ import { ICreateChannel } from '../../reducers/createChannel'; import { ICreateDiscussion } from '../../reducers/createDiscussion'; import { IEncryption } from '../../reducers/encryption'; import { IInviteLinks } from '../../reducers/inviteLinks'; +import { ILogin } from '../../reducers/login'; import { IRoles } from '../../reducers/roles'; import { IRoom } from '../../reducers/room'; import { ISelectedUsers } from '../../reducers/selectedUsers'; @@ -34,8 +35,8 @@ import { IEnterpriseModules } from '../../reducers/enterpriseModules'; export interface IApplicationState { settings: ISettings; - login: any; meteor: IConnect; + login: ILogin; server: IServer; selectedUsers: ISelectedUsers; app: IApp; diff --git a/app/reducers/activeUsers.test.ts b/app/reducers/activeUsers.test.ts index b78db6db4..42a10d402 100644 --- a/app/reducers/activeUsers.test.ts +++ b/app/reducers/activeUsers.test.ts @@ -1,4 +1,5 @@ import { clearActiveUsers, setActiveUsers } from '../actions/activeUsers'; +import { UserStatus } from '../definitions/UserStatus'; import { IActiveUsers, initialState } from './activeUsers'; import { mockedStore } from './mockedStore'; @@ -8,7 +9,7 @@ describe('test reducer', () => { expect(state).toEqual(initialState); }); 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)); const state = mockedStore.getState().activeUsers; expect(state).toEqual({ ...activeUsers }); diff --git a/app/reducers/activeUsers.ts b/app/reducers/activeUsers.ts index bfd765654..34a55ce6d 100644 --- a/app/reducers/activeUsers.ts +++ b/app/reducers/activeUsers.ts @@ -1,9 +1,9 @@ -import { TApplicationActions } from '../definitions'; import { ACTIVE_USERS } from '../actions/actionsTypes'; +import { TApplicationActions } from '../definitions'; +import { UserStatus } from '../definitions/UserStatus'; -type TUserStatus = 'online' | 'offline' | 'away' | 'busy'; export interface IActiveUser { - status: TUserStatus; + status: UserStatus; statusText: string; } diff --git a/app/reducers/login.test.ts b/app/reducers/login.test.ts new file mode 100644 index 000000000..abab2ebaf --- /dev/null +++ b/app/reducers/login.test.ts @@ -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); + }); +}); diff --git a/app/reducers/login.js b/app/reducers/login.ts similarity index 50% rename from app/reducers/login.js rename to app/reducers/login.ts index 664718c4c..2969d0c28 100644 --- a/app/reducers/login.js +++ b/app/reducers/login.ts @@ -1,15 +1,47 @@ +import { UserStatus } from '../definitions/UserStatus'; 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[]; + customFields: Record; + settings?: Record; +} + +export interface ILogin { + user: Partial; + isLocalAuthenticated: boolean; + isAuthenticated: boolean; + isFetching: boolean; + error: Record; + services: Record; + failure: boolean; +} + +export const initialState: ILogin = { isLocalAuthenticated: true, isAuthenticated: false, isFetching: false, user: {}, error: {}, - services: {} + services: {}, + failure: false }; -export default function login(state = initialState, action) { +export default function login(state = initialState, action: TActionsLogin): ILogin { switch (action.type) { case types.APP.INIT: return initialState; @@ -60,13 +92,14 @@ export default function login(state = initialState, action) { ...state, user: { ...state.user, - settings: { - ...state.user.settings, - preferences: { - ...state.user.settings.preferences, - ...action.preference - } - } + settings: state.user?.settings + ? { + ...state.user?.settings, + preferences: state.user?.settings?.preferences + ? { ...state.user.settings.preferences, ...action.preference } + : { ...action.preference } + } + : { profile: {}, preferences: {} } } }; case types.LOGIN.SET_LOCAL_AUTHENTICATED: diff --git a/app/selectors/login.ts b/app/selectors/login.ts index d9524b6e4..2b634d6ab 100644 --- a/app/selectors/login.ts +++ b/app/selectors/login.ts @@ -1,7 +1,7 @@ import { createSelector } from 'reselect'; import isEmpty from 'lodash/isEmpty'; -import { IApplicationState } from '../definitions'; +import { IApplicationState, IUser } from '../definitions'; interface IServices { facebook: { clientId: string }; @@ -13,7 +13,7 @@ interface IServices { wordpress: { clientId: string; serverURL: string }; } -const getUser = (state: IApplicationState) => { +const getUser = (state: IApplicationState): Partial => { if (!isEmpty(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 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( [getLoginServices, getShowFormLoginSetting, getIframeEnabledSetting], diff --git a/app/views/LoginView.tsx b/app/views/LoginView.tsx index e43505f3f..a8bcba275 100644 --- a/app/views/LoginView.tsx +++ b/app/views/LoginView.tsx @@ -1,21 +1,20 @@ +import { dequal } from 'dequal'; import React from 'react'; import { Alert, Keyboard, StyleSheet, Text, View } from 'react-native'; 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 I18n from '../i18n'; -import * as HeaderButton from '../containers/HeaderButton'; +import { loginRequest } from '../actions/login'; import { themes } from '../constants/colors'; -import { withTheme } from '../theme'; +import Button from '../containers/Button'; import FormContainer, { FormContainerInner } from '../containers/FormContainer'; -import TextInput from '../containers/TextInput'; -import { loginRequest as loginRequestAction } from '../actions/login'; +import * as HeaderButton from '../containers/HeaderButton'; 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 { withTheme } from '../theme'; +import sharedStyles from './Styles'; const styles = StyleSheet.create({ registerDisabled: { @@ -48,9 +47,7 @@ const styles = StyleSheet.create({ } }); -interface ILoginViewProps { - navigation: StackNavigationProp; - route: RouteProp; +interface ILoginViewProps extends IBaseScreen { Site_Name: string; Accounts_RegistrationForm: string; Accounts_RegistrationForm_LinkReplacementText: string; @@ -63,7 +60,6 @@ interface ILoginViewProps { error: string; }; failure: boolean; - theme: string; loginRequest: Function; inviteLinkToken: string; } @@ -132,9 +128,9 @@ class LoginView extends React.Component { } const { user, password } = this.state; - const { loginRequest } = this.props; + const { dispatch } = this.props; Keyboard.dismiss(); - loginRequest({ user, password }); + dispatch(loginRequest({ user, password })); }; renderUserForm = () => { @@ -243,23 +239,19 @@ class LoginView extends React.Component { } } -const mapStateToProps = (state: any) => ({ +const mapStateToProps = (state: IApplicationState) => ({ server: state.server.server, - Site_Name: state.settings.Site_Name, - Accounts_ShowFormLogin: state.settings.Accounts_ShowFormLogin, - Accounts_RegistrationForm: state.settings.Accounts_RegistrationForm, - Accounts_RegistrationForm_LinkReplacementText: state.settings.Accounts_RegistrationForm_LinkReplacementText, + Site_Name: state.settings.Site_Name as string, + Accounts_ShowFormLogin: state.settings.Accounts_ShowFormLogin as boolean, + Accounts_RegistrationForm: state.settings.Accounts_RegistrationForm as string, + Accounts_RegistrationForm_LinkReplacementText: state.settings.Accounts_RegistrationForm_LinkReplacementText as string, isFetching: state.login.isFetching, failure: state.login.failure, error: state.login.error && state.login.error.data, - Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder, - Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder, - Accounts_PasswordReset: state.settings.Accounts_PasswordReset, + Accounts_EmailOrUsernamePlaceholder: state.settings.Accounts_EmailOrUsernamePlaceholder as string, + Accounts_PasswordPlaceholder: state.settings.Accounts_PasswordPlaceholder as string, + Accounts_PasswordReset: state.settings.Accounts_PasswordReset as boolean, inviteLinkToken: state.inviteLinks.token }); -const mapDispatchToProps = (dispatch: any) => ({ - loginRequest: (params: any) => dispatch(loginRequestAction(params)) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(withTheme(LoginView)); +export default connect(mapStateToProps)(withTheme(LoginView)); diff --git a/app/views/RegisterView.tsx b/app/views/RegisterView.tsx index ae5f46bd9..a4f1d025d 100644 --- a/app/views/RegisterView.tsx +++ b/app/views/RegisterView.tsx @@ -1,26 +1,25 @@ import React from 'react'; 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 { connect } from 'react-redux'; -import { OutsideParamList } from '../stacks/types'; -import log, { events, logEvent } from '../utils/log'; -import Button from '../containers/Button'; -import I18n from '../i18n'; -import * as HeaderButton from '../containers/HeaderButton'; +import { loginRequest } from '../actions/login'; import { themes } from '../constants/colors'; -import { withTheme } from '../theme'; +import Button from '../containers/Button'; import FormContainer, { FormContainerInner } from '../containers/FormContainer'; -import TextInput from '../containers/TextInput'; -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 * as HeaderButton from '../containers/HeaderButton'; 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 { 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'; const styles = StyleSheet.create({ @@ -51,9 +50,7 @@ const styles = StyleSheet.create({ } }); -interface IProps { - navigation: StackNavigationProp; - route: RouteProp; +interface IProps extends IBaseScreen { server: string; Site_Name: string; Gitlab_URL: string; @@ -63,8 +60,6 @@ interface IProps { Accounts_EmailVerification: boolean; Accounts_ManuallyApproveNewUsers: boolean; showLoginButton: boolean; - loginRequest: Function; - theme: string; } class RegisterView extends React.Component { @@ -130,7 +125,7 @@ class RegisterView extends React.Component { Keyboard.dismiss(); 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 { await RocketChat.register({ @@ -148,11 +143,11 @@ class RegisterView extends React.Component { await navigation.goBack(); showErrorAlert(I18n.t('Wait_activation_warning'), I18n.t('Registration_Succeeded')); } else { - await loginRequest({ user: email, password }); + dispatch(loginRequest({ user: email, password })); } } catch (e: any) { if (e.data?.errorType === 'username-invalid') { - return loginRequest({ user: email, password }); + return dispatch(loginRequest({ user: email, password })); } if (e.data?.error) { logEvent(events.REGISTER_DEFAULT_SIGN_UP_F); @@ -349,20 +344,16 @@ class RegisterView extends React.Component { } } -const mapStateToProps = (state: any) => ({ +const mapStateToProps = (state: IApplicationState) => ({ server: state.server.server, - Site_Name: state.settings.Site_Name, - Gitlab_URL: state.settings.API_Gitlab_URL, - CAS_enabled: state.settings.CAS_enabled, - CAS_login_url: state.settings.CAS_login_url, - Accounts_CustomFields: state.settings.Accounts_CustomFields, - Accounts_EmailVerification: state.settings.Accounts_EmailVerification, - Accounts_ManuallyApproveNewUsers: state.settings.Accounts_ManuallyApproveNewUsers, + Site_Name: state.settings.Site_Name as string, + Gitlab_URL: state.settings.API_Gitlab_URL as string, + CAS_enabled: state.settings.CAS_enabled as boolean, + CAS_login_url: state.settings.CAS_login_url as string, + Accounts_CustomFields: state.settings.Accounts_CustomFields as string, + Accounts_EmailVerification: state.settings.Accounts_EmailVerification as boolean, + Accounts_ManuallyApproveNewUsers: state.settings.Accounts_ManuallyApproveNewUsers as boolean, showLoginButton: getShowLoginButton(state) }); -const mapDispatchToProps = (dispatch: any) => ({ - loginRequest: (params: any) => dispatch(loginRequestAction(params)) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(withTheme(RegisterView)); +export default connect(mapStateToProps)(withTheme(RegisterView)); diff --git a/app/views/SetUsernameView.tsx b/app/views/SetUsernameView.tsx index 221561697..4c9aa91f0 100644 --- a/app/views/SetUsernameView.tsx +++ b/app/views/SetUsernameView.tsx @@ -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 { 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 TextInput from '../containers/TextInput'; +import { loginRequest } from '../actions/login'; +import { themes } from '../constants/colors'; import Button from '../containers/Button'; -import KeyboardView from '../presentation/KeyboardView'; -import scrollPersistTaps from '../utils/scrollPersistTaps'; +import SafeAreaView from '../containers/SafeAreaView'; +import StatusBar from '../containers/StatusBar'; +import TextInput from '../containers/TextInput'; +import { IApplicationState } from '../definitions'; import I18n from '../i18n'; import RocketChat from '../lib/rocketchat'; -import StatusBar from '../containers/StatusBar'; -import { withTheme } from '../theme'; -import { themes } from '../constants/colors'; -import { isTablet } from '../utils/deviceInfo'; +import KeyboardView from '../presentation/KeyboardView'; import { getUserSelector } from '../selectors/login'; +import { withTheme } from '../theme'; +import { isTablet } from '../utils/deviceInfo'; import { showErrorAlert } from '../utils/info'; -import SafeAreaView from '../containers/SafeAreaView'; +import scrollPersistTaps from '../utils/scrollPersistTaps'; import sharedStyles from './Styles'; const styles = StyleSheet.create({ @@ -39,9 +40,9 @@ interface ISetUsernameViewProps { route: RouteProp<{ SetUsernameView: { title: string } }, 'SetUsernameView'>; server: string; userId: string; - loginRequest: ({ resume }: { resume: string }) => void; token: string; theme: string; + dispatch: Dispatch; } class SetUsernameView extends React.Component { @@ -86,7 +87,7 @@ class SetUsernameView extends React.Component { const { username } = this.state; - const { loginRequest, token } = this.props; + const { dispatch, token } = this.props; if (!username.trim()) { return; @@ -95,7 +96,7 @@ class SetUsernameView extends React.Component ({ +const mapStateToProps = (state: IApplicationState) => ({ server: state.server.server, token: getUserSelector(state).token }); -const mapDispatchToProps = (dispatch: Dispatch) => ({ - loginRequest: (params: { resume: string }) => dispatch(loginRequestAction(params)) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(withTheme(SetUsernameView)); +export default connect(mapStateToProps)(withTheme(SetUsernameView)); diff --git a/app/views/StatusView.tsx b/app/views/StatusView.tsx index 23fc619df..a8822df00 100644 --- a/app/views/StatusView.tsx +++ b/app/views/StatusView.tsx @@ -1,24 +1,24 @@ import React from 'react'; -import { StackNavigationProp } from '@react-navigation/stack'; import { FlatList, StyleSheet } from 'react-native'; -import { Dispatch } from '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 Loading from '../containers/Loading'; +import SafeAreaView from '../containers/SafeAreaView'; import Status from '../containers/Status/Status'; 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 { showErrorAlert } from '../utils/info'; -import Loading from '../containers/Loading'; -import RocketChat from '../lib/rocketchat'; 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 = [ { @@ -65,12 +65,9 @@ interface IStatusViewState { loading: boolean; } -interface IStatusViewProps { - navigation: StackNavigationProp; +interface IStatusViewProps extends IBaseScreen { user: IUser; - theme: string; isMasterDetail: boolean; - setUser: (user: IUser) => void; Accounts_AllowInvisibleStatusOption: boolean; } @@ -111,7 +108,7 @@ class StatusView extends React.Component { }; setCustomStatus = async (statusText: string) => { - const { user, setUser } = this.props; + const { user, dispatch } = this.props; this.setState({ loading: true }); @@ -119,7 +116,7 @@ class StatusView extends React.Component { const result = await RocketChat.setUserStatus(user.status, statusText); if (result.success) { logEvent(events.STATUS_CUSTOM); - setUser({ statusText }); + dispatch(setUser({ statusText })); EventEmitter.emit(LISTENER, { message: I18n.t('Status_saved_successfully') }); } else { logEvent(events.STATUS_CUSTOM_F); @@ -156,7 +153,7 @@ class StatusView extends React.Component { renderItem = ({ item }: { item: { id: string; name: string } }) => { const { statusText } = this.state; - const { user, setUser } = this.props; + const { user, dispatch } = this.props; const { id, name } = item; return ( { try { const result = await RocketChat.setUserStatus(item.id, statusText); if (result.success) { - setUser({ status: item.id }); + dispatch(setUser({ status: item.id as UserStatus })); } } catch (e: any) { showErrorAlert(I18n.t(e.data.errorType)); @@ -205,14 +202,10 @@ class StatusView extends React.Component { } } -const mapStateToProps = (state: any) => ({ +const mapStateToProps = (state: IApplicationState) => ({ user: getUserSelector(state), isMasterDetail: state.app.isMasterDetail, - Accounts_AllowInvisibleStatusOption: state.settings.Accounts_AllowInvisibleStatusOption ?? true + Accounts_AllowInvisibleStatusOption: (state.settings.Accounts_AllowInvisibleStatusOption as boolean) ?? true }); -const mapDispatchToProps = (dispatch: Dispatch) => ({ - setUser: (user: IUser) => dispatch(setUserAction(user)) -}); - -export default connect(mapStateToProps, mapDispatchToProps)(withTheme(StatusView)); +export default connect(mapStateToProps)(withTheme(StatusView));