Register.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772
  1. /**
  2. * 注册页面
  3. * @邠心vbe on 2021/03/23
  4. */
  5. import React from 'react';
  6. import { View, Text, ScrollView, StyleSheet, TextInput, Pressable, Image } from 'react-native';
  7. import { BackIcon } from '../../components/Toolbar';
  8. import apiUser from '../../api/apiUser';
  9. import Button from '../../components/Button';
  10. import { PageList } from '../Router';
  11. import Dialog from '../../components/Dialog';
  12. import Modal from 'react-native-modal';
  13. import { RegisterDialog } from '../charge/InfoDialog';
  14. import Dropdown from '../../components/Dropdown';
  15. import ImagePicker from 'react-native-image-crop-picker';
  16. import apiUpload from '../../api/apiUpload';
  17. import { host } from '../../api/http';
  18. import CheckBox from '@react-native-community/checkbox';
  19. import { ModalProps } from '../../components/BottomModal';
  20. import { CountryDropNum, GetCountryList } from '../../components/CountryIcon';
  21. const options = {
  22. width: 300,
  23. height: 200,
  24. cropping: true,
  25. multiple: false,
  26. mediaType: 'photo',
  27. writeTempFile: false,
  28. compressImageQuality: 0.8,
  29. compressImageMaxWidth: 720,
  30. compressImageMaxHeight: 1280,
  31. cropperStatusBarColor: colorAccent,
  32. cropperToolbarColor: colorAccent,
  33. cropperActiveWidgetColor: colorAccent
  34. }
  35. export const StrengthView = ({strength}) => {
  36. const getStyle = (num) => {
  37. if (strength >= num) {
  38. return {...styles.strengthItem, backgroundColor: colorAccent};
  39. } else {
  40. return styles.strengthItem;
  41. }
  42. }
  43. return (
  44. <>
  45. <Text style={getStyle(1)}></Text>
  46. <Text style={getStyle(2)}></Text>
  47. <Text style={getStyle(3)}></Text>
  48. {/* <Text style={getStyle(4)}></Text> */}
  49. {/* <Text style={getStyle(5)}></Text> */}
  50. </>
  51. );
  52. };
  53. export default class Register extends React.Component {
  54. constructor(props) {
  55. super(props);
  56. this.state = {
  57. agree: true,
  58. strength: 0,
  59. countryNum: '65',
  60. countryShow: false,
  61. userInfo: {},
  62. countryNums: [],
  63. wrongCount: 0,
  64. params: {...this.props.route.params},
  65. email: '',
  66. password: '',
  67. fleetCompanyId: '',
  68. visible: false,
  69. isFleetDriver: false,
  70. pdvImages: ['', ''],
  71. companyList: []
  72. };
  73. }
  74. componentDidMount() {
  75. //console.log(this.state.params);
  76. this.getCountryList();
  77. this.getCompanyList();
  78. }
  79. applyStrength(text) {
  80. var strength = 0;
  81. if (text.length >= 8) {
  82. strength += 1;
  83. }
  84. if (/\d{1,}/.test(text)) {
  85. strength += 1;
  86. }
  87. if (/[A-z]{1,}/.test(text)) {
  88. strength += 1;
  89. }
  90. /*if (/[A-Z]{1,}/.test(text)) {
  91. strength += 1;
  92. }
  93. /*if (/\W{1,}/.test(text)) {
  94. strength += 1;
  95. }*/
  96. if (this.state.strength != strength) {
  97. this.setState({
  98. password: text,
  99. strength: strength
  100. });
  101. } else {
  102. this.setState({
  103. password: text
  104. });
  105. }
  106. }
  107. changeInfo(key, value) {
  108. var info = this.state.userInfo;
  109. info[key] = value;
  110. this.setState({
  111. 'userInfo': info
  112. });
  113. }
  114. changeTab(type) {
  115. this.setState({
  116. isFleetDriver: type
  117. })
  118. }
  119. getCountryList() {
  120. GetCountryList(list => {
  121. this.setState({
  122. countryNums: list
  123. })
  124. })
  125. }
  126. getCompanyList() {
  127. apiUser.getConmpany().then(res => {
  128. if (res.data) {
  129. this.setState({
  130. companyList: res.data
  131. })
  132. }
  133. }).catch(err => [
  134. toastShort(err)
  135. ])
  136. }
  137. onRegister() {
  138. //console.log('sign up', this.state);
  139. var info = this.state.userInfo;
  140. if (!info.nickName) {
  141. toastShort('Please enter display name');
  142. return;
  143. }
  144. if (!info.email) {
  145. toastShort('Please enter email address');
  146. return;
  147. }
  148. if (!/^[a-zA-Z0-9]+[\S]+@[a-zA-Z0-9_-]+[\.][\Sa-zA-Z]+$/.test(info.email)) {
  149. toastShort('Email is incorrect format');
  150. return;
  151. }
  152. if (!info.phone) {
  153. toastShort('Please enter contact number');
  154. return;
  155. }
  156. if (!/^\d{6,15}$/.test(info.phone)) {
  157. toastShort('Phone Number is incorrect format');
  158. return;
  159. }
  160. if (!this.state.password) {
  161. toastShort('Please enter password');
  162. return;
  163. }
  164. if (this.state.strength < 3) {
  165. toastShort('Password is not strong');
  166. return;
  167. }
  168. if (!info.password) {
  169. toastShort('Please enter confirm password');
  170. return;
  171. }
  172. if (info.password != this.state.password) {
  173. toastShort('The twice passwords are inconsistent');
  174. if (this.state.wrongCount < 3) {
  175. this.setState({
  176. wrongCount: this.state.wrongCount + 1
  177. })
  178. }
  179. return;
  180. }
  181. if (this.state.isFleetDriver) {
  182. if (!info.pdvLicence) {
  183. toastShort('Please enter PDV Licence');
  184. return;
  185. }
  186. if (this.state.pdvImages[0] == '' || this.state.pdvImages[1] == '') {
  187. toastShort('Please upload PDV Licence Photos');
  188. return;
  189. }
  190. }
  191. let param = Object.assign({}, info);
  192. //param.phone = this.state.countryNum + info.phone
  193. param.callingCode = this.state.countryNum;
  194. if (this.state.isFleetDriver) {
  195. param.userType = 'Driver';
  196. param.pdvLicencePictures = this.state.pdvImages;
  197. param.fleetCompanyId = this.state.fleetCompanyId;
  198. } else {
  199. param.userType = 'Public';
  200. }
  201. console.log('params', param);
  202. Dialog.showProgressDialog();
  203. apiUser.register(param).then(res => {
  204. Dialog.dismissLoading();
  205. //toastShort('Sign up successfully!');
  206. this.setState({
  207. email: param.email,
  208. visible: true
  209. });
  210. //this.backToLogin();
  211. }).catch(err => {
  212. toastShort(err);
  213. Dialog.dismissLoading();
  214. });
  215. }
  216. getBackTopPosition() {
  217. return isIOS ? statusHeight - 16 : 8;
  218. }
  219. backToLogin() {
  220. if (this.state.params.actionLogin) {
  221. goBack()
  222. startPage(PageList.login);
  223. } else {
  224. goBack();
  225. }
  226. }
  227. uploadImage(index) {
  228. ImagePicker.openPicker(options).then(image => {
  229. if (image.path) {
  230. apiUpload.uploadImage(image.path, image.mime, 'PDVL').then(res => {
  231. if (res.success && res.data.picturePath) {
  232. let imageUrl = this.state.pdvImages;
  233. imageUrl[index] = res.data.picturePath;
  234. this.setState({
  235. pdvImages: imageUrl
  236. });
  237. } else {
  238. toastShort('Upload failed, please retry');
  239. }
  240. }).catch(err => {
  241. toastShort(err);
  242. });
  243. }
  244. }).catch(err => {
  245. //console.log(err);
  246. });
  247. }
  248. changeAgree(ag) {
  249. this.setState({
  250. agree: ag
  251. })
  252. }
  253. hideDialog() {
  254. this.setState({
  255. visible: false
  256. });
  257. this.backToLogin();
  258. }
  259. render() {
  260. return (
  261. <View style={StyleSheet.absoluteFillObject}>
  262. <ScrollView
  263. style={styles.scollView}
  264. keyboardShouldPersistTaps={'handled'}>
  265. <View style={styles.header}>
  266. <View style={styles.logoView}>
  267. <Image
  268. style={styles.logoImg}
  269. source={require('../../images/tool-logo.png')}/>
  270. </View>
  271. </View>
  272. <View style={{...styles.backView, top: this.getBackTopPosition()}}>
  273. <Button
  274. style={styles.backButton}
  275. viewStyle={styles.backButtonView}
  276. onClick={() => goBack()}>
  277. <BackIcon/>
  278. <Text style={{color: '#333',fontSize: 16,paddingLeft: 8}}>Back to Login</Text>
  279. </Button>
  280. </View>
  281. <View style={styles.signView}>
  282. <View style={styles.tabView}>
  283. <Text
  284. style={[
  285. styles.tabText,
  286. this.state.isFleetDriver ? {} : styles.tabActive
  287. ]}
  288. onPress={() => this.changeTab(false)}
  289. >Public Users</Text>
  290. <Text
  291. style={[
  292. styles.tabText,
  293. this.state.isFleetDriver ? styles.tabActive: {}
  294. ]}
  295. onPress={() => this.changeTab(true)}
  296. >Fleet/PH Drivers</Text>
  297. </View>
  298. <Text style={styles.title}>Registration</Text>
  299. <View style={styles.signInput}>
  300. <Text style={styles.inputLabel}>Display Name</Text>
  301. <TextInput
  302. style={styles.inputView}
  303. placeholder='Display Name'
  304. maxLength={50}
  305. onChangeText={v => this.changeInfo('nickName', v)}
  306. />
  307. </View>
  308. <View style={styles.signInput}>
  309. <Text style={styles.inputLabel}>Email Address</Text>
  310. <TextInput
  311. style={styles.inputView}
  312. placeholder='Email Address'
  313. maxLength={50}
  314. onChangeText={v => this.changeInfo('email', v)}
  315. />
  316. </View>
  317. <View style={styles.signInput}>
  318. <Text style={styles.inputLabel}>Phone Number</Text>
  319. <View style={styles.mobileView}>
  320. <View style={styles.dropView}>
  321. <TextInput style={styles.dropInput} editable={false}/>
  322. <Text style={styles.countryText}>{"+" + this.state.countryNum}</Text>
  323. <MaterialIcons name={'arrow-drop-down'} size={24} color={'#333'}/>
  324. <Dropdown
  325. style={styles.dropLayer}
  326. title='Country'
  327. prefixText="+"
  328. list={this.state.countryNums}
  329. value={this.state.countryNum}
  330. nameKey='countryNum'
  331. valueKey='countryNum'
  332. onChange={(value, index)=> {
  333. this.setState({
  334. countryNum: value
  335. })
  336. }}
  337. customerItemView={
  338. (item, index, onClick) =>
  339. <CountryDropNum
  340. key={index}
  341. country={item}
  342. value={this.state.countryNum}
  343. onClick={onClick}/>
  344. }/>
  345. </View>
  346. <TextInput
  347. style={styles.contactView}
  348. placeholder='Mobile Number'
  349. keyboardType='phone-pad'
  350. maxLength={15}
  351. onChangeText={v => this.changeInfo('phone', v)}
  352. />
  353. </View>
  354. </View>
  355. <View style={styles.signInput}>
  356. <Text style={styles.inputLabel}>Create Password</Text>
  357. <TextInput
  358. secureTextEntry={this.state.wrongCount < 3}
  359. style={styles.inputView}
  360. placeholder='Password'
  361. maxLength={20}
  362. onChangeText={(value) => {
  363. this.applyStrength(value);
  364. }}
  365. />
  366. </View>
  367. <View style={styles.signInput}>
  368. <Text style={styles.inputLabel}></Text>
  369. <View style={styles.passwordView}>
  370. <View style={styles.strengthView}>
  371. <Text style={{fontSize:12, paddingRight: 4, color: '#333'}}>Password Strength</Text>
  372. <StrengthView {...this.state}/>
  373. </View>
  374. <View>
  375. <Text style={styles.passwordRole}>Your Password Must Have:</Text>
  376. <Text style={styles.passwordRole}>- 8 or more characters</Text>
  377. {/* <Text style={styles.passwordRole}>- upper and lower case letters</Text> */}
  378. <Text style={styles.passwordRole}>- at least one number</Text>
  379. </View>
  380. </View>
  381. </View>
  382. <View style={styles.signInput}>
  383. <Text style={styles.inputLabel}>Confirm Password</Text>
  384. <TextInput
  385. secureTextEntry={this.state.wrongCount < 3}
  386. style={styles.inputView}
  387. placeholder='Password'
  388. maxLength={20}
  389. onChangeText={v => this.changeInfo('password', v)}
  390. />
  391. </View>
  392. { this.state.isFleetDriver &&
  393. <>
  394. <View style={styles.signInput}>
  395. <Text style={styles.inputLabel}>Your Company</Text>
  396. <Dropdown
  397. style={[styles.inputView, ui.flexc]}
  398. title='Company'
  399. list={this.state.companyList}
  400. value={this.state.fleetCompanyId}
  401. valueKey='fleetCompanyId'
  402. nameKey='fleetCompanyName'
  403. onChange={(value, index)=> {
  404. this.setState({
  405. fleetCompanyId: value
  406. })
  407. }}/>
  408. </View>
  409. <View style={styles.signInput}>
  410. <Text style={styles.inputLabel}>{'PDV Licence'}</Text>
  411. <TextInput
  412. style={styles.inputView}
  413. placeholder='PH Driver Vocational Licence'
  414. maxLength={20}
  415. onChangeText={v => this.changeInfo('pdvLicence', v)}
  416. />
  417. </View>
  418. <View style={styles.signInput}>
  419. <Text style={styles.inputLabel}>{' PDV Photos\n(Front & Back)'}</Text>
  420. <View style={styles.uploadGroup}>
  421. { this.state.pdvImages.map((item, index) => (
  422. <UploadView
  423. key={index}
  424. onPress={() => this.uploadImage(index)}
  425. url={item}/>
  426. ))
  427. }
  428. </View>
  429. </View>
  430. </>
  431. }
  432. <View style={styles.referView}>
  433. <Text style={styles.referTitle}>Have a referral code?</Text>
  434. <View style={styles.referText}>
  435. <Text>You'll get</Text>
  436. <Text style={styles.weight}>$5</Text>
  437. <Text>Credit as registration bonus!</Text>
  438. </View>
  439. <View style={styles.codeView}>
  440. <Text style={{ color: '#333', fontSize: 16 }}>Referral Code</Text>
  441. <TextInput
  442. style={styles.codeText}
  443. maxLength={6}
  444. placeholder='Referral Code'
  445. onChangeText={v => this.changeInfo('referralCode', v)}
  446. />
  447. </View>
  448. </View>
  449. <View style={styles.agreeView}>
  450. <CheckBox
  451. value={this.state.agree}
  452. onTintColor={colorAccent}
  453. onCheckColor={colorAccent}
  454. onValueChange={v => this.changeAgree(v)}
  455. />
  456. <View style={styles.agreeTextRow}>
  457. <Text style={styles.agreeText} onPress={() => this.changeAgree(!this.state.agree)}>
  458. {'I have read and I agree with the '}
  459. </Text>
  460. <Text style={styles.agreeLink} onPress={() => startPage(PageList.condition)}>Terms of Use</Text>
  461. <Text style={styles.agreeText}>{' '}</Text>
  462. <Text style={styles.agreeText}>{'and '}</Text>
  463. <Text style={styles.agreeLink} onPress={() => startPage(PageList.privacy)}>Privacy Policy</Text>
  464. <Text style={styles.agreeText}>.</Text>
  465. </View>
  466. </View>
  467. <Button
  468. style={styles.signButton}
  469. elevation={1.5}
  470. disabled={!this.state.agree}
  471. text='Sign up'
  472. fontSize={14}
  473. onClick={() => {
  474. this.onRegister();
  475. }}
  476. />
  477. </View>
  478. </ScrollView>
  479. <Modal
  480. isVisible={this.state.visible}
  481. onBackButtonPress={() => this.hideDialog()}
  482. {...ModalProps}>
  483. <RegisterDialog
  484. address={this.state.email}
  485. onClose={() => this.hideDialog()}
  486. />
  487. </Modal>
  488. </View>
  489. );
  490. }
  491. }
  492. const UploadView = ({url, onPress}) => (
  493. <Pressable
  494. style={styles.uploadView}
  495. onPress={onPress}>
  496. { url == ''
  497. ? <Image
  498. style={styles.uploadIcon}
  499. source={require('../../images/icon/ic-add-photo.png')}/>
  500. : <Image
  501. style={styles.uploadIcon}
  502. defaultSource={require('../../images/icon/icon-upload-default.png')}
  503. source={{uri: host + url}}/>
  504. }
  505. </Pressable>
  506. )
  507. const styles = StyleSheet.create({
  508. header: {
  509. paddingTop: 56,
  510. backgroundColor: colorAccent
  511. },
  512. backView: {
  513. top: 12,
  514. zIndex: 5,
  515. padding: 16,
  516. position: 'absolute',
  517. flexDirection: 'row'
  518. },
  519. backButton: {
  520. borderRadius: 60,
  521. backgroundColor: 'white'
  522. },
  523. backButtonView: {
  524. height: 43,
  525. paddingLeft: 16,
  526. paddingRight: 16,
  527. alignItems: 'center',
  528. flexDirection: 'row'
  529. },
  530. scollView: {
  531. flex: 1
  532. },
  533. logoView: {
  534. paddingTop: 32,
  535. paddingBottom: 56,
  536. alignItems: 'center'
  537. },
  538. logoImg: {
  539. width:165.09,
  540. height: 51.94,
  541. },
  542. signView: {
  543. flex: 1,
  544. padding: 16,
  545. marginTop: -30,
  546. borderTopLeftRadius: 20,
  547. borderTopRightRadius: 20,
  548. backgroundColor: 'white'
  549. },
  550. tabView: {
  551. marginBottom: 4,
  552. borderWidth: 1,
  553. borderRadius: 6,
  554. overflow: 'hidden',
  555. alignItems: 'center',
  556. flexDirection: 'row',
  557. borderColor: colorAccent,
  558. },
  559. tabText: {
  560. flex: 1,
  561. color: '#333',
  562. fontSize: 15,
  563. textAlign: 'center',
  564. ...$padding(10, 0),
  565. },
  566. tabActive: {
  567. fontWeight: 'bold',
  568. backgroundColor: colorAccent
  569. },
  570. title: {
  571. color: '#333',
  572. fontSize: 18,
  573. fontWeight: '700',
  574. paddingTop: 4,
  575. paddingBottom: 6,
  576. },
  577. signInput: {
  578. marginTop: 16,
  579. alignItems: 'center',
  580. flexDirection: 'row'
  581. },
  582. inputLabel: {
  583. flex: 1,
  584. color: '#333',
  585. fontSize: 14,
  586. marginRight: 4
  587. },
  588. inputView: {
  589. flex: 2,
  590. color: '#333',
  591. fontSize: 14,
  592. borderRadius: 5,
  593. minHeight: 40,
  594. paddingTop: 6,
  595. paddingLeft: 12,
  596. paddingRight: 12,
  597. paddingBottom: 6,
  598. backgroundColor: '#F5F5F5'
  599. },
  600. mobileView: {
  601. flex: 2,
  602. marginLeft: -12,
  603. alignItems: 'center',
  604. flexDirection: 'row'
  605. },
  606. dropView: {
  607. fontSize: 16,
  608. borderRadius: 4,
  609. paddingRight: 4,
  610. flexDirection: 'row',
  611. alignItems: 'center',
  612. backgroundColor: '#F5F5F5'
  613. },
  614. dropInput: {
  615. width: 12,
  616. padding: 6,
  617. color: '#333',
  618. minHeight: 40,
  619. },
  620. countryText: {
  621. color: '#333',
  622. fontSize: 14,
  623. paddingRight: 4
  624. },
  625. dropLayer: {
  626. left: 0,
  627. right: 0,
  628. opacity: 0,
  629. position: 'absolute'
  630. },
  631. contactView: {
  632. flex: 1,
  633. color: '#333',
  634. fontSize: 15,
  635. borderRadius: 4,
  636. minHeight: 40,
  637. paddingTop: 6,
  638. paddingLeft: 12,
  639. paddingRight: 12,
  640. paddingBottom: 6,
  641. marginLeft: 10,
  642. backgroundColor: '#F5F5F5'
  643. },
  644. passwordView: {
  645. flex: 2,
  646. marginTop: -8,
  647. },
  648. strengthView: {
  649. marginTop: -4,
  650. alignItems: 'center',
  651. paddingBottom: 4,
  652. flexDirection: 'row'
  653. },
  654. passwordRole: {
  655. color: '#999',
  656. fontSize: 10,
  657. lineHeight: 13
  658. },
  659. strengthItem: {
  660. width: 14,
  661. height: 2.5,
  662. marginLeft: 8,
  663. borderRadius: 3,
  664. backgroundColor: '#CCC'
  665. },
  666. referView: {
  667. padding: 16,
  668. marginTop: 24,
  669. borderRadius: 6,
  670. alignItems: 'center',
  671. backgroundColor: '#F5F5F5'
  672. },
  673. referTitle: {
  674. color: '#333',
  675. fontSize: 18,
  676. fontWeight: 'bold'
  677. },
  678. referText: {
  679. color: '#333',
  680. paddingTop: 4,
  681. alignItems: 'flex-end',
  682. flexDirection: 'row'
  683. },
  684. weight: {
  685. fontSize: 18,
  686. paddingLeft: 4,
  687. paddingRight: 4,
  688. color: colorAccent,
  689. fontWeight: 'bold'
  690. },
  691. codeView: {
  692. paddingTop: 16,
  693. alignItems: 'center',
  694. flexDirection: 'row'
  695. },
  696. codeText: {
  697. color: '#333',
  698. fontSize: 16,
  699. paddingTop: 6,
  700. paddingLeft: 12,
  701. paddingRight: 12,
  702. paddingBottom: 6,
  703. minHeight: 40,
  704. marginLeft: 16,
  705. borderRadius: 6,
  706. textAlign: 'center',
  707. backgroundColor: 'white'
  708. },
  709. signButton: {
  710. marginTop: 24,
  711. marginBottom: 8,
  712. },
  713. uploadGroup: {
  714. flex: 2,
  715. alignItems: 'center',
  716. flexDirection: 'row',
  717. justifyContent: 'center'
  718. },
  719. uploadView: {
  720. flex: 1,
  721. alignItems: 'center'
  722. },
  723. uploadIcon: {
  724. width: $vw(28),
  725. height: $vw(28),
  726. borderRadius: 6
  727. },
  728. agreeView: {
  729. marginTop: 24,
  730. marginBottom: 16,
  731. flexDirection: 'row',
  732. alignItems: 'flex-start',
  733. },
  734. agreeTextRow: {
  735. flex: 1,
  736. paddingTop: 4,
  737. paddingLeft: 8,
  738. flexWrap: 'wrap',
  739. flexDirection: 'row'
  740. },
  741. agreeText: {
  742. color: '#333',
  743. fontSize: 14,
  744. paddingTop: 2,
  745. paddingBottom: 2
  746. },
  747. agreeLink: {
  748. ...ui.link,
  749. fontSize: 14,
  750. paddingTop: 2,
  751. paddingBottom: 2,
  752. textDecorationLine: 'underline'
  753. }
  754. });