-
Notifications
You must be signed in to change notification settings - Fork 3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #2169 from Maftalion/matt-2029-secondary-logins
[User Settings] Allow users to add secondary logins
- Loading branch information
Showing
11 changed files
with
405 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
import React, {Component} from 'react'; | ||
import Onyx, {withOnyx} from 'react-native-onyx'; | ||
import PropTypes from 'prop-types'; | ||
import {View, TextInput} from 'react-native'; | ||
import _ from 'underscore'; | ||
import Str from 'expensify-common/lib/str'; | ||
import HeaderWithCloseButton from '../../components/HeaderWithCloseButton'; | ||
import Navigation from '../../libs/Navigation/Navigation'; | ||
import ScreenWrapper from '../../components/ScreenWrapper'; | ||
import Text from '../../components/Text'; | ||
import styles from '../../styles/styles'; | ||
import {setSecondaryLogin} from '../../libs/actions/User'; | ||
import ONYXKEYS from '../../ONYXKEYS'; | ||
import ButtonWithLoader from '../../components/ButtonWithLoader'; | ||
import ROUTES from '../../ROUTES'; | ||
import CONST from '../../CONST'; | ||
|
||
const propTypes = { | ||
/* Onyx Props */ | ||
// The details about the user that is signed in | ||
user: PropTypes.shape({ | ||
// error associated with adding a secondary login | ||
error: PropTypes.string, | ||
|
||
// Whether the form is being submitted | ||
loading: PropTypes.bool, | ||
|
||
// Whether or not the user is subscribed to news updates | ||
loginList: PropTypes.arrayOf(PropTypes.shape({ | ||
|
||
// Value of partner name | ||
partnerName: PropTypes.string, | ||
|
||
// Phone/Email associated with user | ||
partnerUserID: PropTypes.string, | ||
|
||
// Date of when login was validated | ||
validatedDate: PropTypes.string, | ||
})), | ||
}), | ||
|
||
// Route object from navigation | ||
route: PropTypes.shape({ | ||
params: PropTypes.shape({ | ||
type: PropTypes.string, | ||
}), | ||
}), | ||
}; | ||
|
||
const defaultProps = { | ||
user: {}, | ||
route: {}, | ||
}; | ||
|
||
class AddSecondaryLoginPage extends Component { | ||
constructor(props) { | ||
super(props); | ||
|
||
this.state = { | ||
login: '', | ||
password: '', | ||
}; | ||
this.formType = props.route.params.type; | ||
this.submitForm = this.submitForm.bind(this); | ||
this.validateForm = this.validateForm.bind(this); | ||
} | ||
|
||
componentWillUnmount() { | ||
Onyx.merge(ONYXKEYS.USER, {error: ''}); | ||
} | ||
|
||
submitForm() { | ||
setSecondaryLogin(this.state.login, this.state.password) | ||
.then((response) => { | ||
if (response.jsonCode === 200) { | ||
Navigation.navigate(ROUTES.SETTINGS_PROFILE); | ||
} | ||
}); | ||
} | ||
|
||
// Determines whether form is valid | ||
validateForm() { | ||
const validationMethod = this.formType === CONST.LOGIN_TYPE.PHONE ? Str.isValidPhone : Str.isValidEmail; | ||
return !this.state.password || !validationMethod(this.state.login); | ||
} | ||
|
||
render() { | ||
return ( | ||
<ScreenWrapper> | ||
<HeaderWithCloseButton | ||
title={this.formType === CONST.LOGIN_TYPE.PHONE ? 'Add Phone Number' : 'Add Email Address'} | ||
shouldShowBackButton | ||
onBackButtonPress={() => Navigation.navigate(ROUTES.SETTINGS_PROFILE)} | ||
onCloseButtonPress={Navigation.dismissModal} | ||
/> | ||
<View style={[styles.p5, styles.flex1, styles.overflowScroll]}> | ||
<View style={styles.flexGrow1}> | ||
<Text style={[styles.mb6, styles.textP]}> | ||
{this.formType === CONST.LOGIN_TYPE.PHONE | ||
? 'Enter your preferred phone number and password to send a validation link.' | ||
: 'Enter your preferred email address and password to send a validation link.'} | ||
</Text> | ||
<View style={styles.mb6}> | ||
<Text style={[styles.mb1, styles.formLabel]}> | ||
{this.formType === CONST.LOGIN_TYPE.PHONE ? 'Phone Number' : 'Email Address'} | ||
</Text> | ||
<TextInput | ||
style={styles.textInput} | ||
value={this.state.login} | ||
onChangeText={login => this.setState({login})} | ||
autoFocus | ||
keyboardType={this.formType === CONST.LOGIN_TYPE.PHONE | ||
? CONST.KEYBOARD_TYPE.PHONE_PAD : undefined} | ||
returnKeyType="done" | ||
/> | ||
</View> | ||
<View style={styles.mb6}> | ||
<Text style={[styles.mb1, styles.formLabel]}>Password</Text> | ||
<TextInput | ||
style={styles.textInput} | ||
value={this.state.password} | ||
onChangeText={password => this.setState({password})} | ||
secureTextEntry | ||
autoCompleteType="password" | ||
textContentType="password" | ||
onSubmitEditing={this.submitForm} | ||
/> | ||
</View> | ||
{!_.isEmpty(this.props.user.error) && ( | ||
<Text style={styles.formError}> | ||
{this.props.user.error} | ||
</Text> | ||
)} | ||
</View> | ||
<View style={[styles.flexGrow0]}> | ||
<ButtonWithLoader | ||
isDisabled={this.validateForm()} | ||
isLoading={this.props.user.loading} | ||
text="Send Validation" | ||
onClick={this.submitForm} | ||
/> | ||
</View> | ||
</View> | ||
</ScreenWrapper> | ||
); | ||
} | ||
} | ||
|
||
AddSecondaryLoginPage.propTypes = propTypes; | ||
AddSecondaryLoginPage.defaultProps = defaultProps; | ||
AddSecondaryLoginPage.displayName = 'AddSecondaryLoginPage'; | ||
|
||
export default withOnyx({ | ||
user: { | ||
key: ONYXKEYS.USER, | ||
}, | ||
})(AddSecondaryLoginPage); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import React, {Component} from 'react'; | ||
import {View, Pressable} from 'react-native'; | ||
import PropTypes from 'prop-types'; | ||
import Text from '../../../components/Text'; | ||
import styles from '../../../styles/styles'; | ||
import colors from '../../../styles/colors'; | ||
import {Plus, Checkmark} from '../../../components/Icon/Expensicons'; | ||
import Icon from '../../../components/Icon'; | ||
import ROUTES from '../../../ROUTES'; | ||
import CONST from '../../../CONST'; | ||
import Navigation from '../../../libs/Navigation/Navigation'; | ||
import {resendValidateCode} from '../../../libs/actions/User'; | ||
|
||
const propTypes = { | ||
// Label to display on login form | ||
label: PropTypes.string.isRequired, | ||
|
||
// Type associated with the login | ||
type: PropTypes.oneOf([CONST.LOGIN_TYPE.EMAIL, CONST.LOGIN_TYPE.PHONE]).isRequired, | ||
|
||
// Login associated with the user | ||
login: PropTypes.shape({ | ||
partnerUserID: PropTypes.string, | ||
validatedDate: PropTypes.string, | ||
}).isRequired, | ||
}; | ||
|
||
export default class LoginField extends Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
showCheckmarkIcon: false, | ||
}; | ||
this.timeout = null; | ||
this.onResendClicked = this.onResendClicked.bind(this); | ||
} | ||
|
||
onResendClicked() { | ||
resendValidateCode(this.props.login.partnerUserID); | ||
this.setState({showCheckmarkIcon: true}); | ||
|
||
// Revert checkmark back to "Resend" after 5 seconds | ||
if (!this.timeout) { | ||
this.timeout = setTimeout(() => { | ||
if (this.timeout) { | ||
this.setState({showCheckmarkIcon: false}); | ||
this.timeout = null; | ||
} | ||
}, 5000); | ||
} | ||
} | ||
|
||
render() { | ||
let note; | ||
if (this.props.type === CONST.LOGIN_TYPE.PHONE) { | ||
// No phone number | ||
if (!this.props.login.partnerUserID) { | ||
note = 'Add your phone number to settle up via Venmo.'; | ||
|
||
// Has unvalidated phone number | ||
} else if (!this.props.login.validatedDate) { | ||
// eslint-disable-next-line max-len | ||
note = 'The number has not yet been validated. Click the button to resend the validation link via text.'; | ||
|
||
// Has verified phone number | ||
} else { | ||
note = 'Use your phone number to settle up via Venmo.'; | ||
} | ||
|
||
// Has unvalidated email | ||
} else if (this.props.login.partnerUserID && !this.props.login.validatedDate) { | ||
note = 'The email has not yet been validated. Click the button to resend the validation link via text.'; | ||
} | ||
|
||
return ( | ||
<View style={styles.mb6}> | ||
<Text style={styles.formLabel}>{this.props.label}</Text> | ||
{!this.props.login.partnerUserID ? ( | ||
<Pressable | ||
style={[styles.createMenuItem, styles.ph0]} | ||
onPress={() => Navigation.navigate(ROUTES.getSettingsAddLoginRoute(this.props.type))} | ||
> | ||
<View style={styles.flexRow}> | ||
<View style={styles.createMenuIcon}> | ||
<Icon src={Plus} /> | ||
</View> | ||
<View style={styles.justifyContentCenter}> | ||
<Text style={[styles.createMenuText, styles.ml3]}> | ||
{`Add ${this.props.label}`} | ||
</Text> | ||
</View> | ||
</View> | ||
</Pressable> | ||
) : ( | ||
<View style={[styles.flexRow, styles.justifyContentBetween, styles.alignItemsCenter]}> | ||
<Text style={[styles.textP]} numberOfLines={1}> | ||
{this.props.login.partnerUserID} | ||
</Text> | ||
{!this.props.login.validatedDate && ( | ||
<Pressable | ||
style={[styles.button, styles.mb2]} | ||
onPress={this.onResendClicked} | ||
> | ||
{this.state.showCheckmarkIcon ? ( | ||
<Icon fill={colors.black} src={Checkmark} /> | ||
) : ( | ||
<Text style={styles.createMenuText}> | ||
Resend | ||
</Text> | ||
)} | ||
</Pressable> | ||
)} | ||
</View> | ||
)} | ||
{note && ( | ||
<Text style={[styles.textLabel, styles.colorMuted]}> | ||
{note} | ||
</Text> | ||
)} | ||
</View> | ||
); | ||
} | ||
} | ||
|
||
LoginField.propTypes = propTypes; | ||
LoginField.displayName = 'LoginField'; |
Oops, something went wrong.