-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create the RequestCallPage #3697
Changes from all commits
36fcc06
5dddf4e
0099005
dc5487d
95e29ab
6658651
47e5009
842fe58
c86a520
e0ddd97
3c14352
ab198b7
e25abcc
595d09e
7992059
8e4cff8
96e7f26
fb5c2c3
aa9b5df
9ceb2d4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
import PropTypes from 'prop-types'; | ||
import {TextInput, View} from 'react-native'; | ||
import _ from 'underscore'; | ||
import styles from '../styles/styles'; | ||
import Text from './Text'; | ||
import themeColors from '../styles/themes/default'; | ||
import withLocalize, {withLocalizePropTypes} from './withLocalize'; | ||
|
||
const propTypes = { | ||
...withLocalizePropTypes, | ||
|
||
/** Called when text is entered into the firstName input */ | ||
onChangeFirstName: PropTypes.func.isRequired, | ||
|
||
/** Called when text is entered into the lastName input */ | ||
onChangeLastName: PropTypes.func.isRequired, | ||
|
||
/** Used to prefill the firstName input, can also be used to make it a controlled input */ | ||
firstName: PropTypes.string, | ||
|
||
/** Placeholder text for the firstName input */ | ||
firstNamePlaceholder: PropTypes.string, | ||
|
||
/** Used to prefill the lastName input, can also be used to make it a controlled input */ | ||
lastName: PropTypes.string, | ||
|
||
/** Placeholder text for the lastName input */ | ||
lastNamePlaceholder: PropTypes.string, | ||
|
||
/** Additional styles to add after local styles */ | ||
style: PropTypes.oneOfType([ | ||
PropTypes.arrayOf(PropTypes.object), | ||
PropTypes.object, | ||
]), | ||
}; | ||
const defaultProps = { | ||
firstName: '', | ||
firstNamePlaceholder: null, | ||
lastName: '', | ||
lastNamePlaceholder: null, | ||
style: {}, | ||
}; | ||
|
||
const FullNameInputRow = ({ | ||
translate, | ||
onChangeFirstName, onChangeLastName, | ||
firstName, lastName, | ||
firstNamePlaceholder, | ||
lastNamePlaceholder, | ||
style, | ||
}) => { | ||
const additionalStyles = _.isArray(style) ? style : [style]; | ||
return ( | ||
<View style={[styles.flexRow, ...additionalStyles]}> | ||
<View style={styles.flex1}> | ||
<Text style={[styles.mb1, styles.formLabel]}> | ||
{translate('common.firstName')} | ||
</Text> | ||
<TextInput | ||
style={styles.textInput} | ||
value={firstName} | ||
onChangeText={onChangeFirstName} | ||
placeholder={firstNamePlaceholder ?? translate('profilePage.john')} | ||
placeholderTextColor={themeColors.placeholderText} | ||
/> | ||
</View> | ||
<View style={[styles.flex1, styles.ml2]}> | ||
<Text style={[styles.mb1, styles.formLabel]}> | ||
{translate('common.lastName')} | ||
</Text> | ||
<TextInput | ||
style={styles.textInput} | ||
value={lastName} | ||
onChangeText={onChangeLastName} | ||
placeholder={lastNamePlaceholder ?? translate('profilePage.doe')} | ||
placeholderTextColor={themeColors.placeholderText} | ||
/> | ||
</View> | ||
</View> | ||
); | ||
}; | ||
|
||
FullNameInputRow.displayName = 'FullNameInputRow'; | ||
FullNameInputRow.propTypes = propTypes; | ||
FullNameInputRow.defaultProps = defaultProps; | ||
export default withLocalize(FullNameInputRow); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
import {Component} from 'react'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey @marcaaron, sorry for the late reply! I noticed that this was broken and pushed a fix. It seems like it was broken when I merged a remote and local version of my branch. |
||
import {View, Text, TextInput} from 'react-native'; | ||
import _ from 'underscore'; | ||
import {withOnyx} from 'react-native-onyx'; | ||
import PropTypes from 'prop-types'; | ||
import Str from 'expensify-common/lib/str'; | ||
import HeaderWithCloseButton from '../components/HeaderWithCloseButton'; | ||
import Navigation from '../libs/Navigation/Navigation'; | ||
import styles from '../styles/styles'; | ||
import ScreenWrapper from '../components/ScreenWrapper'; | ||
import withLocalize, {withLocalizePropTypes} from '../components/withLocalize'; | ||
import ONYXKEYS from '../ONYXKEYS'; | ||
import compose from '../libs/compose'; | ||
import FullNameInputRow from '../components/FullNameInputRow'; | ||
import Button from '../components/Button'; | ||
import FixedFooter from '../components/FixedFooter'; | ||
import CONST from '../CONST'; | ||
import Growl from '../libs/Growl'; | ||
import {requestConciergeDMCall} from '../libs/actions/Inbox'; | ||
import {fetchOrCreateChatReport} from '../libs/actions/Report'; | ||
import personalDetailsPropType from './personalDetailsPropType'; | ||
|
||
const propTypes = { | ||
...withLocalizePropTypes, | ||
|
||
/** The personal details of the person who is logged in */ | ||
myPersonalDetails: personalDetailsPropType.isRequired, | ||
|
||
/** Current user session */ | ||
session: PropTypes.shape({ | ||
email: PropTypes.string.isRequired, | ||
}).isRequired, | ||
|
||
/** The details about the user that is signed in */ | ||
user: PropTypes.shape({ | ||
/** Whether or not the user is subscribed to news updates */ | ||
loginList: PropTypes.arrayOf(PropTypes.shape({ | ||
|
||
/** Phone/Email associated with user */ | ||
partnerUserID: PropTypes.string, | ||
})), | ||
}).isRequired, | ||
}; | ||
|
||
class RequestCallPage extends Component { | ||
constructor(props) { | ||
super(props); | ||
|
||
// The displayName defaults to the user's login if they haven't set a first and last name, | ||
// which we can't use to prefill the input fields | ||
const [firstName, lastName] = props.myPersonalDetails.displayName !== props.myPersonalDetails.login | ||
? props.myPersonalDetails.displayName.split(' ') | ||
: []; | ||
this.state = { | ||
firstName: firstName ?? '', | ||
lastName: lastName ?? '', | ||
phoneNumber: this.getPhoneNumber(props.user.loginList) ?? '', | ||
isLoading: false, | ||
}; | ||
|
||
this.onSubmit = this.onSubmit.bind(this); | ||
this.getPhoneNumber = this.getPhoneNumber.bind(this); | ||
} | ||
|
||
onSubmit() { | ||
this.setState({isLoading: true}); | ||
if (!this.state.firstName.length || !this.state.lastName.length) { | ||
Growl.show(this.props.translate('requestCallPage.growlMessageEmptyName'), CONST.GROWL.ERROR); | ||
this.setState({isLoading: false}); | ||
return; | ||
} | ||
|
||
requestConciergeDMCall('', this.state.firstName, this.state.lastName, this.state.phoneNumber) | ||
.then((result) => { | ||
this.setState({isLoading: false}); | ||
if (result.jsonCode === 200) { | ||
Growl.show(this.props.translate('requestCallPage.growlMessageOnSave'), CONST.GROWL.SUCCESS); | ||
fetchOrCreateChatReport([this.props.session.email, CONST.EMAIL.CONCIERGE], true); | ||
return; | ||
} | ||
|
||
// Phone number validation is handled by the API | ||
Growl.show(result.message, CONST.GROWL.ERROR, 3000); | ||
}); | ||
} | ||
|
||
/** | ||
* Gets the user's phone number from their secondary login. | ||
* Returns null if it doesn't exist. | ||
* @param {Array<Object>} loginList | ||
* | ||
* @returns {String|null} | ||
*/ | ||
getPhoneNumber(loginList) { | ||
const secondaryLogin = _.find(loginList, login => Str.isSMSLogin(login.partnerUserID)); | ||
return secondaryLogin ? Str.removeSMSDomain(secondaryLogin.partnerUserID) : null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe something to consolidate with Joe's code ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Dal-Papa Joe's code assumes that the user is an SMS user, so it isn't entirely possible to reuse what he has. He assumes that the user's phone number is located somewhere in their personalDetails object, which isn't always the case for this particular page. |
||
} | ||
|
||
render() { | ||
const isButtonDisabled = false; | ||
return ( | ||
<ScreenWrapper> | ||
<HeaderWithCloseButton | ||
title={this.props.translate('requestCallPage.requestACall')} | ||
shouldShowBackButton | ||
onBackButtonPress={() => fetchOrCreateChatReport([ | ||
this.props.session.email, | ||
CONST.EMAIL.CONCIERGE, | ||
], true)} | ||
onCloseButtonPress={() => Navigation.dismissModal(true)} | ||
/> | ||
<View style={[styles.flex1, styles.p5]}> | ||
<Text style={[styles.mb4, styles.textP]}> | ||
{this.props.translate('requestCallPage.description')} | ||
</Text> | ||
<Text style={[styles.mt4, styles.mb4, styles.textP]}> | ||
{this.props.translate('requestCallPage.instructions')} | ||
</Text> | ||
<FullNameInputRow | ||
firstName={this.state.firstName} | ||
lastName={this.state.lastName} | ||
onChangeFirstName={firstName => this.setState({firstName})} | ||
onChangeLastName={lastName => this.setState({lastName})} | ||
style={[styles.mt4, styles.mb4]} | ||
/> | ||
<Text style={[styles.mt4, styles.formLabel]} numberOfLines={1}> | ||
{this.props.translate('common.phoneNumber')} | ||
</Text> | ||
<TextInput | ||
autoCompleteType="off" | ||
autoCorrect={false} | ||
style={[styles.textInput]} | ||
value={this.state.phoneNumber} | ||
placeholder="+14158675309" | ||
onChangeText={phoneNumber => this.setState({phoneNumber})} | ||
/> | ||
<Text style={[styles.mt4, styles.textLabel, styles.colorMuted, styles.mb6]}> | ||
{this.props.translate('requestCallPage.availabilityText')} | ||
</Text> | ||
</View> | ||
<FixedFooter> | ||
<Button | ||
success | ||
isDisabled={isButtonDisabled} | ||
onPress={this.onSubmit} | ||
style={[styles.w100]} | ||
text={this.props.translate('requestCallPage.callMe')} | ||
isLoading={this.state.isLoading} | ||
/> | ||
</FixedFooter> | ||
</ScreenWrapper> | ||
); | ||
} | ||
} | ||
|
||
RequestCallPage.displayName = 'RequestCallPage'; | ||
RequestCallPage.propTypes = propTypes; | ||
export default compose( | ||
withLocalize, | ||
withOnyx({ | ||
myPersonalDetails: { | ||
key: ONYXKEYS.MY_PERSONAL_DETAILS, | ||
}, | ||
session: { | ||
key: ONYXKEYS.SESSION, | ||
}, | ||
user: { | ||
key: ONYXKEYS.USER, | ||
}, | ||
}), | ||
)(RequestCallPage); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you use JSX then
React
must be included