MM_35115: Login screen - email - login api call [IN PROGRESS]

This commit is contained in:
Avinash Lingaloo
2021-05-14 17:48:40 +04:00
parent d05b3f2b91
commit a5e8a77d6e
8 changed files with 99 additions and 57 deletions

View File

@@ -2,7 +2,8 @@
"extends": [
"plugin:mattermost/react",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended"
],
"parser": "@typescript-eslint/parser",
"plugins": [

View File

@@ -4,6 +4,7 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {ClientOptions} from '@typings/api/client4';
import urlParse from 'url-parse';
import {Analytics, create} from '@init/analytics';
@@ -12,7 +13,7 @@ import * as ClientConstants from './constants';
import ClientError from './error';
export default class ClientBase {
analitics: Analytics|undefined;
analytics: Analytics|undefined;
clusterId = '';
csrf = '';
defaultHeaders: {[x: string]: string} = {};
@@ -127,7 +128,7 @@ export default class ClientBase {
setUrl(url: string) {
this.url = url.replace(/\/+$/, '');
this.analitics = create(this.url);
this.analytics = create(this.url);
}
// Routes

View File

@@ -47,7 +47,7 @@ const ErrorText = ({error, testID, textStyle, theme}: ErrorProps) => {
const getStyleSheet = makeStyleSheetFromTheme((theme: Theme) => {
return {
errorLabel: {
color: (theme.errorTextColor || '#DA4A4A'),
color: (theme?.errorTextColor || '#DA4A4A'),
marginTop: 15,
marginBottom: 15,
fontSize: 12,

View File

@@ -7,10 +7,12 @@ import DatabaseConnectionException from '@database/exceptions/database_connectio
import DatabaseManager from '@database/manager';
import {Q} from '@nozbe/watermelondb';
import {Client4Error} from '@typings/api/client4';
import Global from '@typings/database/global';
const HTTP_UNAUTHORIZED = 401;
//fixme: this file needs to be finalized
//todo: retrieve deviceToken from default database - Global entity
export const logout = async (skipServerLogout = false) => {
return async () => {
@@ -42,3 +44,41 @@ export const forceLogoutIfNecessary = async (err: Client4Error) => {
logout(false);
}
};
type LoginArgs = {loginId: string, password: string, mfaToken?: string, ldapOnly?: boolean}
export const login = async ({loginId, password, mfaToken, ldapOnly = false}: LoginArgs) => {
const database = await DatabaseManager.getDefaultDatabase();
if (!database) {
throw new DatabaseConnectionException('DatabaseManager.getActiveServerDatabase returned undefined');
}
let deviceToken;
let user;
try {
const tokens = await database.collections.get(MM_TABLES.DEFAULT.GLOBAL).query(Q.where('name', 'deviceToken')).fetch() as Global[];
console.log('called login api method with ', loginId, password);
deviceToken = tokens?.[0]?.value ?? '';
user = await Client4.login(loginId, password, mfaToken, deviceToken, ldapOnly);
console.log('user =>> ', user);
//todo : setCSRFFromCookie
// await setCSRFFromCookie(Client4.getUrl());
} catch (error) {
return {error};
}
//todo : loadMe
// const result = await dispatch(loadMe(user));
// if (!result.error) {
// //todo: completeLogin
// // dispatch(completeLogin(user, deviceToken));
// }
// return user;
// return result;
return user;
};

View File

@@ -1,11 +1,11 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
import {login} from '@requests/remote/user';
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {useIntl} from 'react-intl';
import {
ActivityIndicator,
Dimensions,
Image,
InteractionManager,
Keyboard,
@@ -53,14 +53,22 @@ const Login: NavigationFunctionComponent = ({config, license, theme}: LoginProps
const managedConfig = useManagedConfig();
const [isLoading, setIsLoading] = useState<boolean>(false);
const [error, setError] = useState<ClientErrorWithIntl | string | undefined | null>();
const [loginId, setLoginId] = useState<string>('');
const [password, setPassword] = useState<string>('');
//fixme: remove hardcoded value for loginId and password
const [loginId, setLoginId] = useState<string>('avinash.lingaloo@mattermost.com');
const [password, setPassword] = useState<string>('AluminiumZ545*');
//fixme: is this necessary ?
// useEffect for orientation change
useEffect(() => {
Dimensions.addEventListener('change', handleOrientationDidChange);
return () => Dimensions.removeEventListener('change', handleOrientationDidChange);
}, []);
// useEffect(() => {
// const handleOrientationDidChange = () => {
// if (this.scroll.current) {
// this.scroll.current.scrollTo({x: 0, y: 0, animated: true});
// }
// };
// Dimensions.addEventListener('change', handleOrientationDidChange);
// return () => Dimensions.removeEventListener('change', handleOrientationDidChange);
// }, []);
// useEffect to set userName for EMM
useEffect(() => {
@@ -74,14 +82,10 @@ const Login: NavigationFunctionComponent = ({config, license, theme}: LoginProps
setEmmUsernameIfAvailable();
}, []);
const handleOrientationDidChange = () => {
if (this.scroll.current) {
this.scroll.current.scrollTo({x: 0, y: 0, animated: true});
}
};
const preSignIn = preventDoubleTap(() => {
const preSignIn = preventDoubleTap(async () => {
setIsLoading(true);
setError(null);
Keyboard.dismiss();
InteractionManager.runAfterInteractions(async () => {
if (!loginId) {
@@ -111,43 +115,35 @@ const Login: NavigationFunctionComponent = ({config, license, theme}: LoginProps
});
setIsLoading(false);
setError(
intl.formatMessage(
{
id: msgId,
defaultMessage: '',
},
{
ldapUsername: config.LdapLoginFieldName || ldapUsername,
},
),
);
setError(intl.formatMessage(
{
id: msgId,
defaultMessage: '',
},
{
ldapUsername: config.LdapLoginFieldName || ldapUsername,
},
));
return;
}
if (!password) {
setIsLoading(false);
setError(
intl.formatMessage(
{
id: t('login.noPassword'),
defaultMessage: 'Please enter your password',
},
),
);
setError(intl.formatMessage({
id: t('login.noPassword'),
defaultMessage: 'Please enter your password',
}));
return;
}
signIn();
});
});
const signIn = async () => {
if (isLoading) {
const result = await login(loginId.toLowerCase(), password);
if (checkLoginResponse(result)) {
goToChannel();
}
const result = await login({loginId: loginId.toLowerCase(), password});
if (checkLoginResponse(result)) {
goToChannel();
}
};
@@ -160,10 +156,9 @@ const Login: NavigationFunctionComponent = ({config, license, theme}: LoginProps
scheduleExpiredNotification(intl);
};
const checkLoginResponse = (data) => {
const checkLoginResponse = (data: any) => {
if (MFA_EXPECTED_ERRORS.includes(data?.error?.server_error_id)) { // eslint-disable-line camelcase
goToMfa();
setIsLoading(false);
return false;
}
@@ -184,11 +179,11 @@ const Login: NavigationFunctionComponent = ({config, license, theme}: LoginProps
goToScreen(screen, title, {goToChannel, loginId, password});
};
const getLoginErrorMessage = (loginError) => {
const getLoginErrorMessage = (loginError: any) => {
return (getServerErrorForLogin(loginError) || loginError);
};
const getServerErrorForLogin = (serverError) => {
const getServerErrorForLogin = (serverError: any) => {
if (!serverError) {
return null;
}
@@ -338,14 +333,14 @@ const Login: NavigationFunctionComponent = ({config, license, theme}: LoginProps
source={require('@assets/images/logo.png')}
style={{height: 72, resizeMode: 'contain'}}
/>
<View testID='login.screen'>
<Text style={GlobalStyles.header}>{config.SiteName}</Text>
{config?.SiteName && (<View testID='login.screen'>
<Text style={GlobalStyles.header}>{config?.SiteName}</Text>
<FormattedText
style={GlobalStyles.subheader}
id='web.root.signup_info'
defaultMessage='All team communication in one place, searchable and accessible anywhere'
/>
</View>
</View>)}
{error && (
<ErrorText
testID='login.error.text'
@@ -368,6 +363,7 @@ const Login: NavigationFunctionComponent = ({config, license, theme}: LoginProps
returnKeyType='next'
style={GlobalStyles.inputBox}
underlineColorAndroid='transparent'
value={loginId} //to remove
/>
<TextInput
testID='login.password.input'
@@ -386,6 +382,7 @@ const Login: NavigationFunctionComponent = ({config, license, theme}: LoginProps
returnKeyType='go'
secureTextEntry={true}
underlineColorAndroid='transparent'
value={password} //to remove
/>
{getProceed()}
{(config.EnableSignInWithEmail === 'true' || config.EnableSignInWithUsername === 'true') && (

View File

@@ -37,7 +37,9 @@ const Server: NavigationFunctionComponent = ({theme}: ServerProps) => {
const input = useRef<TextInput>(null);
const [connecting, setConnecting] = useState(false);
const [error, setError] = useState<ClientErrorWithIntl|string|undefined>();
const [url, setUrl] = useState<string>('');
//fixme: remove hardcoded url
const [url, setUrl] = useState<string>('https://rc.test.mattermost.com');
const styles = getStyleSheet(theme);
const {formatMessage} = intl;

View File

@@ -119,6 +119,7 @@
"eslint-plugin-jest": "24.3.5",
"eslint-plugin-mattermost": "github:mattermost/eslint-plugin-mattermost#070ce792d105482ffb2b27cfc0b7e78b3d20acee",
"eslint-plugin-react": "7.23.2",
"eslint-plugin-react-hooks": "4.2.0",
"husky": "6.0.0",
"isomorphic-fetch": "3.0.0",
"jest": "26.6.3",

View File

@@ -1,30 +1,30 @@
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
declare type logLevel = 'ERROR' | 'WARNING' | 'INFO';
declare type GenericClientResponse = {
export type logLevel = 'ERROR' | 'WARNING' | 'INFO';
export type GenericClientResponse = {
response: any;
headers: Map<string, string>;
data: any;
};
declare type ErrorOffline = {
export type ErrorOffline = {
message: string;
url: string;
};
declare type ErrorInvalidResponse = {
export type ErrorInvalidResponse = {
intl: {
id: string;
defaultMessage: string;
};
};
declare type ErrorApi = {
export type ErrorApi = {
message: string;
server_error_id: string;
status_code: number;
url: string;
};
declare type Client4Error = ErrorOffline | ErrorInvalidResponse | ErrorApi;
declare type ClientOptions = {
export type Client4Error = ErrorOffline | ErrorInvalidResponse | ErrorApi;
export type ClientOptions = {
headers?: {
[x: string]: string;
};