1import * as React from 'react'
2import * as Kb from '../../common-adapters'
3import * as Styles from '../../styles'
4import * as Container from '../../util/container'
5import * as SettingsGen from '../../actions/settings-gen'
6import {usePhoneNumberList} from '../../teams/common'
7import * as RPCGen from '../../constants/types/rpc-gen'
8import {pluralize} from '../../util/string'
9
10const shareURL = 'https://keybase.io/download?invite'
11const waitingKey = 'invitePeople'
12
13const InviteFriendsModal = () => {
14  const dispatch = Container.useDispatch()
15  const nav = Container.useSafeNavigation()
16  const onClose = () => dispatch(nav.safeNavigateUpPayload())
17  const defaultCountry = Container.useSelector(s => s.settings.phoneNumbers.defaultCountry)
18
19  React.useEffect(() => {
20    // TODO: phone input should do this + remove from uses
21    if (!defaultCountry) {
22      dispatch(SettingsGen.createLoadDefaultPhoneNumberCountry())
23    }
24  }, [defaultCountry, dispatch])
25
26  const [emails, setEmails] = React.useState('')
27  const {
28    phoneNumbers,
29    setPhoneNumber,
30    addPhoneNumber,
31    removePhoneNumber,
32    resetPhoneNumbers,
33  } = usePhoneNumberList()
34
35  // disabled if both are empty or if there are some invalid phone numbers
36  const disabled =
37    (!emails && phoneNumbers.every(pn => !pn.phoneNumber)) ||
38    phoneNumbers.some(pn => pn.phoneNumber && !pn.valid)
39
40  const submit = Container.useRPC(RPCGen.inviteFriendsInvitePeopleRpcPromise)
41  const [error, setError] = React.useState('')
42  const [successCount, setSuccessCount] = React.useState<number | null>(null)
43  const onSubmit = () =>
44    submit(
45      [
46        {
47          emails: {commaSeparatedEmailsFromUser: emails},
48          phones: phoneNumbers.filter(p => !!p.phoneNumber).map(p => p.phoneNumber),
49        },
50        waitingKey,
51      ],
52      r => {
53        setSuccessCount(r)
54        setError('')
55        setEmails('')
56        resetPhoneNumbers()
57      },
58      err => {
59        setSuccessCount(null)
60        if (err.code === RPCGen.StatusCode.scratelimit) {
61          setError("You've been doing that a bit too much lately. Try again later.")
62        } else {
63          setError(err.message)
64        }
65      }
66    )
67  const {popup, setShowingPopup} = Kb.usePopup(() => (
68    <ShareLinkPopup onClose={() => setShowingPopup(false)} />
69  ))
70  return (
71    <Kb.Modal
72      mode="DefaultFullHeight"
73      onClose={onClose}
74      header={{
75        leftButton: Styles.isMobile && (
76          <Kb.Text type="BodyBigLink" onClick={onClose}>
77            Cancel
78          </Kb.Text>
79        ),
80        title: Styles.isMobile ? 'Invite friends' : 'Invite your friends to Keybase',
81      }}
82      footer={{
83        content: (
84          <Kb.Box2 direction="vertical" gap="medium" fullWidth={true}>
85            <Kb.WaitingButton
86              fullWidth={true}
87              type="Success"
88              label="Send invite"
89              disabled={disabled}
90              waitingKey={waitingKey}
91              onClick={onSubmit}
92            />
93            {!Styles.isMobile && (
94              <Kb.Box2 direction="vertical" gap="tiny" fullWidth={true}>
95                <Kb.Text type="BodySmall" center={true}>
96                  or share a link:
97                </Kb.Text>
98                <Kb.CopyText text={shareURL} />
99              </Kb.Box2>
100            )}
101          </Kb.Box2>
102        ),
103      }}
104      banners={[
105        ...(error
106          ? [
107              <Kb.Banner color="red" key="error" style={styles.banner}>
108                {error}
109              </Kb.Banner>,
110            ]
111          : []),
112        ...(successCount === null
113          ? []
114          : [
115              <Kb.Banner
116                color="green"
117                key="success"
118                style={styles.banner}
119              >{`Yeehaw! You invited ${successCount} ${pluralize('friend', successCount)}.`}</Kb.Banner>,
120            ]),
121      ]}
122    >
123      <Kb.Box2 direction="vertical" gap="small" fullWidth={true} alignItems="center" style={styles.container}>
124        <Kb.Box2
125          direction="vertical"
126          fullWidth={true}
127          centerChildren={true}
128          style={styles.illustrationContainer}
129        >
130          <Kb.Icon type="icon-illustration-invite-friends-460-96" />
131        </Kb.Box2>
132        <Kb.Box2 direction="vertical" gap="small" fullWidth={true} style={styles.content}>
133          <Kb.Box2 direction="vertical" gap={Styles.isMobile ? 'xtiny' : 'tiny'} fullWidth={true}>
134            <Kb.Text type="BodySmallSemibold">By email address (separate with commas)</Kb.Text>
135            <Kb.LabeledInput
136              multiline={true}
137              hoverPlaceholder="Ex: cori@domain.com, paul@domain.com, etc."
138              placeholder="Email addresses"
139              rowsMin={3}
140              value={emails}
141              onChangeText={setEmails}
142            />
143          </Kb.Box2>
144          <Kb.Box2 direction="vertical" gap={Styles.isMobile ? 'xtiny' : 'tiny'} fullWidth={true}>
145            <Kb.Text type="BodySmallSemibold">By phone number</Kb.Text>
146            <Kb.Box2 direction="vertical" gap="tiny" fullWidth={true}>
147              {phoneNumbers.map((pn, i) => (
148                <Kb.PhoneInput
149                  small={true}
150                  key={pn.key}
151                  defaultCountry={defaultCountry}
152                  onChangeNumber={(phoneNumber, valid) => setPhoneNumber(i, phoneNumber, valid)}
153                  onClear={phoneNumbers.length === 1 ? undefined : () => removePhoneNumber(i)}
154                />
155              ))}
156              <Kb.Button
157                mode="Secondary"
158                icon="iconfont-new"
159                onClick={addPhoneNumber}
160                style={styles.alignSelfStart}
161              />
162            </Kb.Box2>
163          </Kb.Box2>
164          {Styles.isMobile && (
165            <Kb.ClickableBox style={styles.shareALink} onClick={() => setShowingPopup(true)}>
166              <Kb.Box2 direction="horizontal" gap="tiny" alignItems="center" alignSelf="flex-start">
167                <Kb.Icon type="iconfont-link" color={Styles.globalColors.blueDark} />
168                <Kb.Text type="BodyPrimaryLink">or share a link</Kb.Text>
169              </Kb.Box2>
170              {popup}
171            </Kb.ClickableBox>
172          )}
173        </Kb.Box2>
174      </Kb.Box2>
175    </Kb.Modal>
176  )
177}
178
179export const ShareLinkPopup = ({onClose}: {onClose: () => void}) => (
180  <Kb.MobilePopup>
181    <Kb.Box2 direction="vertical" style={styles.linkPopupContainer} gap="small" fullWidth={true}>
182      <Kb.Text type="Header">Share a link to Keybase</Kb.Text>
183      <Kb.CopyText text={shareURL} shareSheet={true} />
184      <Kb.Button type="Dim" label="Close" fullWidth={true} onClick={onClose} />
185    </Kb.Box2>
186  </Kb.MobilePopup>
187)
188
189const styles = Styles.styleSheetCreate(() => ({
190  alignSelfStart: {alignSelf: 'flex-start'},
191  banner: {
192    position: 'absolute',
193    top: 47,
194    zIndex: 1,
195  },
196  container: {
197    backgroundColor: Styles.globalColors.blueGrey,
198    flex: 1,
199  },
200  content: {
201    ...Styles.padding(0, Styles.globalMargins.small, Styles.globalMargins.small),
202  },
203  illustrationContainer: {
204    backgroundColor: Styles.globalColors.purpleLight,
205    overflow: 'hidden',
206  },
207  linkPopupContainer: {
208    ...Styles.padding(Styles.globalMargins.small),
209  },
210  shareALink: {
211    ...Styles.padding(Styles.globalMargins.tiny, 0),
212    width: '100%',
213  },
214}))
215
216export default InviteFriendsModal
217