1import * as React from 'react'
2import * as Kb from '../../common-adapters'
3import * as Constants from '../../constants/settings'
4import * as Container from '../../util/container'
5import * as Platform from '../../constants/platform'
6import * as RouteTreeGen from '../../actions/route-tree-gen'
7import * as Styles from '../../styles'
8import * as SettingsGen from '../../actions/settings-gen'
9import {EnterEmailBody} from '../../signup/email'
10import {EnterPhoneNumberBody} from '../../signup/phone-number'
11import {VerifyBody} from '../../signup/phone-number/verify'
12import {e164ToDisplay} from '../../util/phone-numbers'
13
14export const Email = () => {
15  const dispatch = Container.useDispatch()
16  const nav = Container.useSafeNavigation()
17
18  const [email, onChangeEmail] = React.useState('')
19  const [searchable, onChangeSearchable] = React.useState(true)
20  const [addEmailInProgress, onAddEmailInProgress] = React.useState('')
21  const emailTrimmed = email.trim()
22  const disabled = !emailTrimmed
23
24  const addedEmail = Container.useSelector(state => state.settings.email.addedEmail)
25  const emailError = Container.useSelector(state => state.settings.email.error)
26  const waiting = Container.useAnyWaiting(Constants.addEmailWaitingKey)
27
28  // clean on unmount
29  React.useEffect(() => () => dispatch(SettingsGen.createClearAddingEmail()), [dispatch])
30  // watch for + nav away on success
31  React.useEffect(() => {
32    if (addedEmail === addEmailInProgress) {
33      // success
34      dispatch(RouteTreeGen.createClearModals())
35    }
36  }, [addEmailInProgress, addedEmail, dispatch])
37  // clean on edit
38  React.useEffect(() => {
39    if (emailTrimmed !== addEmailInProgress && emailError) {
40      dispatch(SettingsGen.createClearAddingEmail())
41    }
42  }, [addEmailInProgress, dispatch, emailError, emailTrimmed])
43
44  const onClose = React.useCallback(() => dispatch(nav.safeNavigateUpPayload()), [dispatch, nav])
45  const onContinue = React.useCallback(() => {
46    if (disabled || waiting) {
47      return
48    }
49    onAddEmailInProgress(emailTrimmed)
50    dispatch(SettingsGen.createAddEmail({email: emailTrimmed, searchable: searchable}))
51  }, [disabled, waiting, emailTrimmed, dispatch, searchable])
52  return (
53    <Kb.Modal
54      onClose={onClose}
55      header={{
56        leftButton: Styles.isMobile ? (
57          <Kb.Text type="BodySemiboldLink" onClick={onClose}>
58            Close
59          </Kb.Text>
60        ) : null,
61        title: Styles.isMobile ? 'Add email address' : 'Add an email address',
62      }}
63      footer={{
64        content: (
65          <Kb.ButtonBar style={styles.buttonBar} fullWidth={true}>
66            {!Styles.isMobile && (
67              <Kb.Button type="Dim" label="Cancel" fullWidth={true} onClick={onClose} disabled={waiting} />
68            )}
69            <Kb.Button
70              label="Continue"
71              fullWidth={true}
72              onClick={onContinue}
73              disabled={disabled}
74              waiting={waiting}
75            />
76          </Kb.ButtonBar>
77        ),
78        style: styles.footer,
79      }}
80      mode="Wide"
81    >
82      <Kb.Box2
83        direction="vertical"
84        centerChildren={true}
85        fullWidth={true}
86        fullHeight={true}
87        style={styles.body}
88      >
89        <EnterEmailBody
90          email={email}
91          onChangeEmail={onChangeEmail}
92          showSearchable={true}
93          searchable={searchable}
94          onChangeSearchable={onChangeSearchable}
95          onContinue={onContinue}
96          iconType={
97            Styles.isMobile
98              ? Platform.isLargeScreen
99                ? 'icon-email-add-96'
100                : 'icon-email-add-64'
101              : 'icon-email-add-64'
102          }
103        />
104      </Kb.Box2>
105      {!!emailError && (
106        <Kb.Banner color="red" style={styles.banner}>
107          <Kb.BannerParagraph bannerColor="red" content={emailError} />
108        </Kb.Banner>
109      )}
110    </Kb.Modal>
111  )
112}
113export const Phone = () => {
114  const dispatch = Container.useDispatch()
115  const nav = Container.useSafeNavigation()
116
117  const [phoneNumber, onChangeNumber] = React.useState('')
118  const [valid, onChangeValidity] = React.useState(false)
119  const [searchable, onChangeSearchable] = React.useState(true)
120  const disabled = !valid
121
122  const defaultCountry = Container.useSelector(state => state.settings.phoneNumbers.defaultCountry)
123
124  const error = Container.useSelector(state => state.settings.phoneNumbers.error)
125  const pendingVerification = Container.useSelector(state => state.settings.phoneNumbers.pendingVerification)
126  const waiting = Container.useAnyWaiting(Constants.addPhoneNumberWaitingKey)
127
128  // clean only errors on unmount so verify screen still has info
129  React.useEffect(() => () => dispatch(SettingsGen.createClearPhoneNumberErrors()), [dispatch])
130  // watch for go to verify
131  React.useEffect(() => {
132    if (!error && !!pendingVerification) {
133      dispatch(nav.safeNavigateAppendPayload({path: ['settingsVerifyPhone']}))
134    }
135  }, [dispatch, error, nav, pendingVerification])
136  // trigger a default phone number country rpc if it's not already loaded
137  React.useEffect(() => {
138    !defaultCountry && dispatch(SettingsGen.createLoadDefaultPhoneNumberCountry())
139  }, [defaultCountry, dispatch])
140
141  const onClose = React.useCallback(() => {
142    dispatch(SettingsGen.createClearPhoneNumberAdd())
143    dispatch(nav.safeNavigateUpPayload())
144  }, [dispatch, nav])
145
146  const onContinue = React.useCallback(
147    () =>
148      disabled || waiting ? null : dispatch(SettingsGen.createAddPhoneNumber({phoneNumber, searchable})),
149    [dispatch, disabled, waiting, searchable, phoneNumber]
150  )
151
152  const onChangeNumberCb = React.useCallback((phoneNumber: string, validity: boolean) => {
153    onChangeNumber(phoneNumber)
154    onChangeValidity(validity)
155  }, [])
156
157  return (
158    <Kb.Modal
159      onClose={onClose}
160      header={{
161        leftButton: Styles.isMobile ? (
162          <Kb.Text type="BodySemiboldLink" onClick={onClose}>
163            Close
164          </Kb.Text>
165        ) : null,
166        title: Styles.isMobile ? 'Add phone number' : 'Add a phone number',
167      }}
168      footer={{
169        content: (
170          <Kb.ButtonBar style={styles.buttonBar} fullWidth={true}>
171            {!Styles.isMobile && (
172              <Kb.Button type="Dim" label="Cancel" fullWidth={true} onClick={onClose} disabled={waiting} />
173            )}
174            <Kb.Button
175              label="Continue"
176              fullWidth={true}
177              onClick={onContinue}
178              disabled={disabled}
179              waiting={waiting}
180            />
181          </Kb.ButtonBar>
182        ),
183        style: styles.footer,
184      }}
185      mode="Wide"
186    >
187      <Kb.Box2
188        direction="vertical"
189        centerChildren={true}
190        fullWidth={true}
191        fullHeight={true}
192        style={styles.body}
193      >
194        <EnterPhoneNumberBody
195          defaultCountry={defaultCountry}
196          onChangeNumber={onChangeNumberCb}
197          onContinue={onContinue}
198          searchable={searchable}
199          onChangeSearchable={onChangeSearchable}
200          iconType={
201            Styles.isMobile
202              ? Platform.isLargeScreen
203                ? 'icon-phone-number-add-96'
204                : 'icon-phone-number-add-64'
205              : 'icon-phone-number-add-64'
206          }
207        />
208      </Kb.Box2>
209      {!!error && (
210        <Kb.Banner color="red" style={styles.banner}>
211          <Kb.BannerParagraph bannerColor="red" content={error} />
212        </Kb.Banner>
213      )}
214    </Kb.Modal>
215  )
216}
217export const VerifyPhone = () => {
218  const dispatch = Container.useDispatch()
219
220  const [code, onChangeCode] = React.useState('')
221
222  const pendingVerification = Container.useSelector(state => state.settings.phoneNumbers.pendingVerification)
223  const error = Container.useSelector(state => state.settings.phoneNumbers.error)
224  const verificationState = Container.useSelector(state => state.settings.phoneNumbers.verificationState)
225  const resendWaiting = Container.useAnyWaiting(
226    Constants.addPhoneNumberWaitingKey,
227    Constants.resendVerificationForPhoneWaitingKey
228  )
229  const verifyWaiting = Container.useAnyWaiting(Constants.verifyPhoneNumberWaitingKey)
230
231  // clean everything on unmount
232  React.useEffect(() => () => dispatch(SettingsGen.createClearPhoneNumberAdd()), [dispatch])
233  // Clear on success
234  React.useEffect(() => {
235    if (verificationState === 'success' && !error) {
236      dispatch(RouteTreeGen.createClearModals())
237    }
238  }, [verificationState, error, dispatch])
239
240  const onResend = React.useCallback(
241    () => dispatch(SettingsGen.createResendVerificationForPhoneNumber({phoneNumber: pendingVerification})),
242    [dispatch, pendingVerification]
243  )
244  const onClose = React.useCallback(() => {
245    dispatch(SettingsGen.createClearPhoneNumberAdd())
246    dispatch(RouteTreeGen.createClearModals())
247  }, [dispatch])
248  const onContinue = React.useCallback(
249    () => dispatch(SettingsGen.createVerifyPhoneNumber({code, phoneNumber: pendingVerification})),
250    [dispatch, code, pendingVerification]
251  )
252  const disabled = !code
253
254  const displayPhone = e164ToDisplay(pendingVerification)
255  return (
256    <Kb.Modal
257      onClose={onClose}
258      header={{
259        hideBorder: true,
260        leftButton: Styles.isMobile ? (
261          <Kb.BackButton onClick={onClose} iconColor={Styles.globalColors.white} />
262        ) : null,
263        style: styles.blueBackground,
264        title: (
265          <Kb.Text type="BodySmall" negative={true} center={true}>
266            {displayPhone || 'Unknown number'}
267          </Kb.Text>
268        ),
269      }}
270      footer={{
271        content: (
272          <Kb.ButtonBar style={styles.buttonBar} fullWidth={true}>
273            <Kb.Button
274              disabled={disabled}
275              type="Success"
276              label="Continue"
277              onClick={onContinue}
278              waiting={verifyWaiting}
279              fullWidth={true}
280            />
281          </Kb.ButtonBar>
282        ),
283        hideBorder: true,
284        style: styles.blueBackground,
285      }}
286      mode="Wide"
287    >
288      <Kb.Box2
289        direction="vertical"
290        style={Styles.collapseStyles([
291          styles.blueBackground,
292          styles.verifyContainer,
293          Styles.globalStyles.flexOne,
294        ])}
295        fullWidth={true}
296        fullHeight={true}
297        centerChildren={true}
298      >
299        <VerifyBody
300          onResend={onResend}
301          resendWaiting={resendWaiting}
302          code={code}
303          onChangeCode={onChangeCode}
304        />
305      </Kb.Box2>
306      {!!error && (
307        <Kb.Banner color="red" style={styles.banner}>
308          <Kb.BannerParagraph bannerColor="red" content={error} />
309        </Kb.Banner>
310      )}
311    </Kb.Modal>
312  )
313}
314
315const styles = Styles.styleSheetCreate(
316  () =>
317    ({
318      banner: {
319        left: 0,
320        position: 'absolute',
321        right: 0,
322        top: 0,
323      },
324      blueBackground: {
325        backgroundColor: Styles.globalColors.blue,
326      },
327      body: {
328        ...Styles.padding(
329          Styles.isMobile ? Styles.globalMargins.tiny : Styles.globalMargins.xlarge,
330          Styles.globalMargins.small,
331          0
332        ),
333        backgroundColor: Styles.globalColors.blueGrey,
334        flexGrow: 1,
335        position: 'relative',
336      },
337      buttonBar: {
338        minHeight: undefined,
339      },
340      footer: {
341        ...Styles.padding(Styles.globalMargins.small),
342      },
343      verifyContainer: {
344        ...Styles.padding(0, Styles.globalMargins.small),
345      },
346    } as const)
347)
348