wallet-stack 1.0.0-alpha.131 → 1.0.0-alpha.133

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/locales/base/translation.json +11 -0
  2. package/package.json +2 -3
  3. package/src/analytics/Events.tsx +3 -10
  4. package/src/analytics/Properties.tsx +9 -25
  5. package/src/analytics/docs.ts +11 -8
  6. package/src/app/ErrorMessages.ts +1 -7
  7. package/src/identity/actions.ts +1 -97
  8. package/src/identity/contactMapping.test.ts +3 -28
  9. package/src/identity/contactMapping.ts +2 -88
  10. package/src/identity/reducer.ts +0 -77
  11. package/src/identity/saga.ts +2 -85
  12. package/src/identity/selectors.ts +0 -2
  13. package/src/images/Images.ts +3 -0
  14. package/src/images/assets/invite-modal.png +0 -0
  15. package/src/images/assets/invite-modal@1.5x.png +0 -0
  16. package/src/images/assets/invite-modal@2x.png +0 -0
  17. package/src/images/assets/invite-modal@3x.png +0 -0
  18. package/src/images/assets/invite-modal@4x.png +0 -0
  19. package/src/images/assets/minipay.png +0 -0
  20. package/src/images/assets/minipay@1.5x.png +0 -0
  21. package/src/images/assets/minipay@2x.png +0 -0
  22. package/src/images/assets/minipay@3x.png +0 -0
  23. package/src/images/assets/minipay@4x.png +0 -0
  24. package/src/images/assets/valora.png +0 -0
  25. package/src/images/assets/valora@1.5x.png +0 -0
  26. package/src/images/assets/valora@2x.png +0 -0
  27. package/src/images/assets/valora@3x.png +0 -0
  28. package/src/images/assets/valora@4x.png +0 -0
  29. package/src/index.d.ts +0 -1
  30. package/src/navigator/Navigator.tsx +10 -14
  31. package/src/navigator/Screens.tsx +2 -2
  32. package/src/navigator/types.tsx +5 -6
  33. package/src/qrcode/utils.test.tsx +4 -96
  34. package/src/qrcode/utils.ts +5 -114
  35. package/src/redux/migrations.test.ts +13 -0
  36. package/src/redux/migrations.ts +4 -0
  37. package/src/redux/store.test.ts +1 -2
  38. package/src/redux/store.ts +1 -1
  39. package/src/send/SelectRecipientAddress.test.tsx +146 -0
  40. package/src/send/SelectRecipientAddress.tsx +166 -0
  41. package/src/send/SendConfirmation.test.tsx +28 -0
  42. package/src/send/SendConfirmation.tsx +18 -1
  43. package/src/send/SendInvite.test.tsx +107 -0
  44. package/src/send/SendInvite.tsx +99 -0
  45. package/src/send/SendSelectRecipient.test.tsx +44 -223
  46. package/src/send/SendSelectRecipient.tsx +41 -149
  47. package/src/send/actions.ts +0 -26
  48. package/src/send/saga.ts +1 -6
  49. package/src/components/AccountNumberCard.tsx +0 -23
  50. package/src/components/ErrorMessageInline.tsx +0 -78
  51. package/src/components/SingleDigitInput.tsx +0 -53
  52. package/src/icons/HamburgerCard.tsx +0 -55
  53. package/src/identity/saga.test.ts +0 -103
  54. package/src/identity/secureSend.ts +0 -171
  55. package/src/send/ValidateRecipientAccount.test.tsx +0 -182
  56. package/src/send/ValidateRecipientAccount.tsx +0 -392
  57. package/src/send/ValidateRecipientIntro.test.tsx +0 -61
  58. package/src/send/ValidateRecipientIntro.tsx +0 -136
  59. package/src/send/__snapshots__/ValidateRecipientAccount.test.tsx.snap +0 -777
@@ -1,30 +1,20 @@
1
1
  import { NativeStackScreenProps } from '@react-navigation/native-stack'
2
- import React, { useState } from 'react'
2
+ import React, { useEffect, useState } from 'react'
3
3
  import { useTranslation } from 'react-i18next'
4
4
  import { StyleSheet, Text, View } from 'react-native'
5
5
  import { getFontScaleSync } from 'react-native-device-info'
6
6
  import { SafeAreaView } from 'react-native-safe-area-context'
7
- import Share from 'react-native-share'
8
7
  import { isAddressFormat } from 'src/account/utils'
9
8
  import AppAnalytics from 'src/analytics/AppAnalytics'
10
9
  import { SendEvents } from 'src/analytics/Events'
11
10
  import { SendOrigin } from 'src/analytics/types'
12
11
  import { getAppConfig } from 'src/appConfig'
13
12
  import BackButton from 'src/components/BackButton'
14
- import Button, { BtnSizes } from 'src/components/Button'
15
- import InLineNotification, { NotificationVariant } from 'src/components/InLineNotification'
16
13
  import KeyboardAwareScrollView from 'src/components/KeyboardAwareScrollView'
17
14
  import CustomHeader from 'src/components/header/CustomHeader'
18
15
  import CircledIcon from 'src/icons/CircledIcon'
19
16
  import { importContacts } from 'src/identity/actions'
20
- import { getAddressFromPhoneNumber } from 'src/identity/contactMapping'
21
- import { AddressValidationType } from 'src/identity/reducer'
22
- import { getAddressValidationType } from 'src/identity/secureSend'
23
- import {
24
- addressToVerifiedBySelector,
25
- e164NumberToAddressSelector,
26
- secureSendPhoneNumberMappingSelector,
27
- } from 'src/identity/selectors'
17
+ import { addressToVerifiedBySelector, e164NumberToAddressSelector } from 'src/identity/selectors'
28
18
  import { RecipientVerificationStatus } from 'src/identity/types'
29
19
  import { noHeader } from 'src/navigator/Headers'
30
20
  import { navigate } from 'src/navigator/NavigationService'
@@ -42,7 +32,6 @@ import colors from 'src/styles/colors'
42
32
  import { typeScale } from 'src/styles/fonts'
43
33
  import { Spacing } from 'src/styles/styles'
44
34
  import variables from 'src/styles/variables'
45
- import Logger from 'src/utils/Logger'
46
35
 
47
36
  type Props = NativeStackScreenProps<StackParamList, Screens.SendSelectRecipient>
48
37
 
@@ -167,45 +156,6 @@ const getStartedStyles = StyleSheet.create({
167
156
  },
168
157
  })
169
158
 
170
- function SendOrInviteButton({
171
- recipient,
172
- recipientVerificationStatus,
173
- shareUrl,
174
- onPress,
175
- }: {
176
- recipient: Recipient | null
177
- recipientVerificationStatus: RecipientVerificationStatus
178
- shareUrl: string | null
179
- onPress: (shouldInviteRecipient: boolean) => void
180
- }) {
181
- const { t } = useTranslation()
182
-
183
- const sendOrInviteButtonDisabled =
184
- (!!recipient && recipientVerificationStatus === RecipientVerificationStatus.UNKNOWN) ||
185
- // If the phone number is present and unverified and no share URL is found, disable the send/invite button
186
- (recipient?.recipientType === RecipientType.PhoneNumber &&
187
- recipientVerificationStatus === RecipientVerificationStatus.UNVERIFIED &&
188
- !shareUrl)
189
-
190
- const shouldInviteRecipient =
191
- !sendOrInviteButtonDisabled &&
192
- recipient?.recipientType === RecipientType.PhoneNumber &&
193
- recipientVerificationStatus === RecipientVerificationStatus.UNVERIFIED
194
- return (
195
- <Button
196
- testID="SendOrInviteButton"
197
- style={styles.sendOrInviteButton}
198
- onPress={() => onPress(shouldInviteRecipient)}
199
- disabled={sendOrInviteButtonDisabled}
200
- text={
201
- shouldInviteRecipient
202
- ? t('sendSelectRecipient.buttons.invite')
203
- : t('sendSelectRecipient.buttons.send')
204
- }
205
- size={BtnSizes.FULL}
206
- />
207
- )
208
- }
209
159
  enum SelectRecipientView {
210
160
  Recent = 'Recent',
211
161
  Contacts = 'Contacts',
@@ -214,7 +164,6 @@ enum SelectRecipientView {
214
164
  function SendSelectRecipient({ route }: Props) {
215
165
  const { t } = useTranslation()
216
166
  const dispatch = useDispatch()
217
- const secureSendPhoneNumberMapping = useSelector(secureSendPhoneNumberMappingSelector)
218
167
  const e164NumberToAddress = useSelector(e164NumberToAddressSelector)
219
168
  const addressToVerifiedBy = useSelector(addressToVerifiedBySelector)
220
169
  const shareUrl = getAppConfig().experimental?.inviteFriends?.shareUrl ?? null
@@ -222,19 +171,14 @@ function SendSelectRecipient({ route }: Props) {
222
171
  const forceTokenId = route.params?.forceTokenId
223
172
  const defaultTokenIdOverride = route.params?.defaultTokenIdOverride
224
173
 
225
- const [showSendOrInviteButton, setShowSendOrInviteButton] = useState(false)
226
-
227
174
  const [showSearchResults, setShowSearchResults] = useState(false)
228
175
 
229
176
  const [activeView, setActiveView] = useState(SelectRecipientView.Recent)
230
177
 
231
178
  const onSearch = (searchQuery: string) => {
232
- // Always unset the selected recipient and hide the send/invite button
233
- // when the search query is changed in order to prevent edge cases
234
- // where the button appears but is bound to a recipient that is
235
- // not present on the page.
179
+ // Clear any in-flight selection so a stale recipient can't auto-navigate
180
+ // once the user starts typing a different query.
236
181
  unsetSelectedRecipient()
237
- setShowSendOrInviteButton(false)
238
182
  setShowSearchResults(!!searchQuery)
239
183
  }
240
184
  const { contactRecipients, recentRecipients } = useSendRecipients()
@@ -243,17 +187,31 @@ function SendSelectRecipient({ route }: Props) {
243
187
  const { recipientVerificationStatus, recipient, setSelectedRecipient, unsetSelectedRecipient } =
244
188
  useFetchRecipientVerificationStatus()
245
189
 
246
- const showUnknownAddressInfo =
247
- showSendOrInviteButton &&
248
- showSearchResults &&
249
- recipient &&
250
- recipient.recipientType !== RecipientType.PhoneNumber &&
251
- recipientVerificationStatus === RecipientVerificationStatus.UNVERIFIED
190
+ useEffect(() => {
191
+ // Auto-navigate once verification resolves. The picker stays mounted so the
192
+ // user's search text and selection are preserved when they come back.
193
+ if (!recipient || recipientVerificationStatus === RecipientVerificationStatus.UNKNOWN) {
194
+ return
195
+ }
252
196
 
253
- const setSelectedRecipientWrapper = (selectedRecipient: Recipient) => {
254
- setSelectedRecipient(selectedRecipient)
255
- setShowSendOrInviteButton(true)
256
- }
197
+ const isUnverifiedPhone =
198
+ recipient.recipientType === RecipientType.PhoneNumber &&
199
+ recipientVerificationStatus === RecipientVerificationStatus.UNVERIFIED
200
+
201
+ if (isUnverifiedPhone) {
202
+ if (shareUrl) {
203
+ navigate(Screens.SendInvite, { recipient, shareUrl })
204
+ }
205
+ // Without shareUrl there's no invite flow and no send flow for an
206
+ // unverified phone — stay on the picker so the user can pick someone else.
207
+ return
208
+ }
209
+
210
+ AppAnalytics.track(SendEvents.send_select_recipient_send_press, {
211
+ recipientType: recipient.recipientType,
212
+ })
213
+ nextScreen(recipient)
214
+ }, [recipient, recipientVerificationStatus, shareUrl])
257
215
 
258
216
  const onContactsPermissionGranted = () => {
259
217
  dispatch(importContacts())
@@ -268,7 +226,6 @@ function SendSelectRecipient({ route }: Props) {
268
226
  AppAnalytics.track(SendEvents.send_select_recipient_recent_press, {
269
227
  recipientType: recentRecipient.recipientType,
270
228
  })
271
- setSelectedRecipient(recentRecipient)
272
229
  nextScreen(recentRecipient)
273
230
  }
274
231
 
@@ -276,35 +233,28 @@ function SendSelectRecipient({ route }: Props) {
276
233
  // use the address from the recipient object
277
234
  let address: string | null | undefined = selectedRecipient.address
278
235
 
279
- // if not present there must be a phone number, route through secure send or get
280
- // the secure send mapped address
236
+ // if not present there must be a phone number, route through the address picker
237
+ // when multiple verified addresses exist, otherwise go directly to amount entry
281
238
  if (!address && recipientHasNumber(selectedRecipient)) {
282
- const addressValidationType: AddressValidationType = getAddressValidationType(
283
- selectedRecipient,
284
- secureSendPhoneNumberMapping
285
- )
286
- if (addressValidationType !== AddressValidationType.NONE) {
287
- navigate(Screens.ValidateRecipientIntro, {
288
- defaultTokenIdOverride,
239
+ const phoneAddresses = e164NumberToAddress[selectedRecipient.e164PhoneNumber] ?? []
240
+ const verifiedAddresses = phoneAddresses.filter((a) => !!addressToVerifiedBy[a])
241
+
242
+ if (verifiedAddresses.length > 1) {
243
+ navigate(Screens.SelectRecipientAddress, {
289
244
  forceTokenId,
245
+ defaultTokenIdOverride,
290
246
  recipient: selectedRecipient,
291
247
  origin: SendOrigin.AppSendFlow,
292
248
  })
293
249
  return
294
250
  }
295
- address = getAddressFromPhoneNumber(
296
- selectedRecipient.e164PhoneNumber,
297
- e164NumberToAddress,
298
- secureSendPhoneNumberMapping,
299
- undefined
300
- )
251
+
252
+ address = verifiedAddresses[0] ?? phoneAddresses[0]
301
253
  }
302
254
 
303
255
  if (!address) {
304
256
  // this should never happen
305
- throw new Error(
306
- 'No address found, this should never happen. Should have routed to invite or secure send.'
307
- )
257
+ throw new Error('No address found, this should never happen. Should have routed to invite.')
308
258
  }
309
259
 
310
260
  navigate(Screens.SendEnterAmount, {
@@ -320,40 +270,6 @@ function SendSelectRecipient({ route }: Props) {
320
270
  })
321
271
  }
322
272
 
323
- const onPressSendOrInvite = async (shouldInviteRecipient: boolean) => {
324
- if (!recipient) return
325
-
326
- // Invites
327
- if (shouldInviteRecipient) {
328
- if (!shareUrl) {
329
- Logger.warn('SendSelectRecipient', 'No share URL found for invite')
330
- return
331
- }
332
-
333
- AppAnalytics.track(SendEvents.send_select_recipient_invite_press, {
334
- recipientType: recipient.recipientType,
335
- })
336
-
337
- try {
338
- await Share.open({
339
- message: t('inviteWithSmsMessage.shareMessage', { shareUrl }),
340
- url: shareUrl,
341
- failOnCancel: false,
342
- })
343
- } catch (error) {
344
- Logger.warn('SendSelectRecipient', 'Share sheet failed', error)
345
- }
346
-
347
- return
348
- }
349
-
350
- // Sends
351
- AppAnalytics.track(SendEvents.send_select_recipient_send_press, {
352
- recipientType: recipient.recipientType,
353
- })
354
- nextScreen(recipient)
355
- }
356
-
357
273
  const renderSearchResults = () => {
358
274
  if (mergedRecipients.length) {
359
275
  return (
@@ -362,7 +278,7 @@ function SendSelectRecipient({ route }: Props) {
362
278
  <RecipientPicker
363
279
  testID={'SelectRecipient/AllRecipientsPicker'}
364
280
  recipients={mergedRecipients}
365
- onSelectRecipient={setSelectedRecipientWrapper}
281
+ onSelectRecipient={setSelectedRecipient}
366
282
  selectedRecipient={recipient}
367
283
  isSelectedRecipientLoading={
368
284
  !!recipient && recipientVerificationStatus === RecipientVerificationStatus.UNKNOWN
@@ -409,7 +325,7 @@ function SendSelectRecipient({ route }: Props) {
409
325
  <RecipientPicker
410
326
  testID={'SelectRecipient/ContactRecipientPicker'}
411
327
  recipients={contactRecipients}
412
- onSelectRecipient={setSelectedRecipientWrapper}
328
+ onSelectRecipient={setSelectedRecipient}
413
329
  selectedRecipient={recipient}
414
330
  isSelectedRecipientLoading={
415
331
  !!recipient && recipientVerificationStatus === RecipientVerificationStatus.UNKNOWN
@@ -439,22 +355,6 @@ function SendSelectRecipient({ route }: Props) {
439
355
  </>
440
356
  )}
441
357
  </KeyboardAwareScrollView>
442
- {showUnknownAddressInfo && (
443
- <InLineNotification
444
- variant={NotificationVariant.Info}
445
- description={t('sendSelectRecipient.unknownAddressInfo')}
446
- testID="UnknownAddressInfo"
447
- style={styles.unknownAddressInfo}
448
- />
449
- )}
450
- {showSendOrInviteButton && (
451
- <SendOrInviteButton
452
- recipient={recipient}
453
- recipientVerificationStatus={recipientVerificationStatus}
454
- onPress={onPressSendOrInvite}
455
- shareUrl={shareUrl}
456
- />
457
- )}
458
358
  </SafeAreaView>
459
359
  )
460
360
  }
@@ -496,14 +396,6 @@ const styles = StyleSheet.create({
496
396
  padding: Spacing.Regular16,
497
397
  textAlign: 'center',
498
398
  },
499
- unknownAddressInfo: {
500
- margin: Spacing.Regular16,
501
- marginBottom: variables.contentPadding,
502
- },
503
- sendOrInviteButton: {
504
- margin: Spacing.Regular16,
505
- marginTop: variables.contentPadding,
506
- },
507
399
  })
508
400
 
509
401
  export default SendSelectRecipient
@@ -9,7 +9,6 @@ export type SVG = typeof Svg
9
9
 
10
10
  export enum Actions {
11
11
  BARCODE_DETECTED = 'SEND/BARCODE_DETECTED',
12
- BARCODE_DETECTED_SECURE_SEND = 'SEND/BARCODE_DETECTED_SECURE_SEND',
13
12
  QRCODE_SHARE = 'SEND/QRCODE_SHARE',
14
13
  SEND_PAYMENT = 'SEND/SEND_PAYMENT',
15
14
  SEND_PAYMENT_SUCCESS = 'SEND/SEND_PAYMENT_SUCCESS',
@@ -23,15 +22,6 @@ export interface HandleQRCodeDetectedAction {
23
22
  defaultTokenIdOverride?: string
24
23
  }
25
24
 
26
- export interface HandleQRCodeDetectedSecureSendAction {
27
- type: Actions.BARCODE_DETECTED_SECURE_SEND
28
- qrCode: QrCode
29
- requesterAddress?: string
30
- recipient: Recipient
31
- forceTokenId?: boolean
32
- defaultTokenIdOverride?: string
33
- }
34
-
35
25
  export interface ShareQRCodeAction {
36
26
  type: Actions.QRCODE_SHARE
37
27
  qrCodeSvg: SVG
@@ -64,7 +54,6 @@ export interface UpdateLastUsedCurrencyAction {
64
54
 
65
55
  export type ActionTypes =
66
56
  | HandleQRCodeDetectedAction
67
- | HandleQRCodeDetectedSecureSendAction
68
57
  | ShareQRCodeAction
69
58
  | SendPaymentAction
70
59
  | SendPaymentSuccessAction
@@ -83,21 +72,6 @@ export const handleQRCodeDetected = ({
83
72
  defaultTokenIdOverride,
84
73
  })
85
74
 
86
- export const handleQRCodeDetectedSecureSend = (
87
- qrCode: QrCode,
88
- recipient: Recipient,
89
- requesterAddress?: string,
90
- forceTokenId?: boolean,
91
- defaultTokenIdOverride?: string
92
- ): HandleQRCodeDetectedSecureSendAction => ({
93
- type: Actions.BARCODE_DETECTED_SECURE_SEND,
94
- qrCode,
95
- requesterAddress,
96
- recipient,
97
- forceTokenId,
98
- defaultTokenIdOverride,
99
- })
100
-
101
75
  export const shareQRCode = (qrCodeSvg: SVG): ShareQRCodeAction => ({
102
76
  type: Actions.QRCODE_SHARE,
103
77
  qrCodeSvg,
package/src/send/saga.ts CHANGED
@@ -3,7 +3,7 @@ import AppAnalytics from 'src/analytics/AppAnalytics'
3
3
  import { CeloExchangeEvents, SendEvents } from 'src/analytics/Events'
4
4
  import { ErrorMessages } from 'src/app/ErrorMessages'
5
5
  import { navigateBack, navigateInitialTab } from 'src/navigator/NavigationService'
6
- import { handleQRCodeDefault, handleQRCodeSecureSend, shareSVGImage } from 'src/qrcode/utils'
6
+ import { handleQRCodeDefault, shareSVGImage } from 'src/qrcode/utils'
7
7
  import {
8
8
  Actions,
9
9
  SendPaymentAction,
@@ -164,12 +164,7 @@ function* watchQrCodeDetections() {
164
164
  yield* takeEvery(Actions.BARCODE_DETECTED, safely(handleQRCodeDefault))
165
165
  }
166
166
 
167
- function* watchQrCodeDetectionsSecureSend() {
168
- yield* takeEvery(Actions.BARCODE_DETECTED_SECURE_SEND, safely(handleQRCodeSecureSend))
169
- }
170
-
171
167
  export function* sendSaga() {
172
- yield* spawn(watchQrCodeDetectionsSecureSend)
173
168
  yield* spawn(watchQrCodeDetections)
174
169
  yield* spawn(watchQrCodeShare)
175
170
  yield* spawn(watchSendPayment)
@@ -1,23 +0,0 @@
1
- import React from 'react'
2
- import { StyleSheet, ViewStyle } from 'react-native'
3
- import AccountNumber from 'src/components/AccountNumber'
4
- import Card from 'src/components/Card'
5
-
6
- interface Props {
7
- address: string
8
- style?: ViewStyle
9
- }
10
-
11
- export default function AccountNumberCard({ address, style }: Props) {
12
- return (
13
- <Card style={[styles.container, style]} rounded={true}>
14
- <AccountNumber address={address} touchDisabled={true} />
15
- </Card>
16
- )
17
- }
18
-
19
- const styles = StyleSheet.create({
20
- container: {
21
- justifyContent: 'center',
22
- },
23
- })
@@ -1,78 +0,0 @@
1
- import React from 'react'
2
- import { WithTranslation } from 'react-i18next'
3
- import { StyleSheet, Text, View } from 'react-native'
4
- import { connect } from 'react-redux'
5
- import { hideAlert } from 'src/alert/actions'
6
- import { ErrorDisplayType } from 'src/alert/reducer'
7
- import { ErrorMessages } from 'src/app/ErrorMessages'
8
- import { withTranslation } from 'src/i18n'
9
- import { RootState } from 'src/redux/reducers'
10
- import colors from 'src/styles/colors'
11
- import { typeScale } from 'src/styles/fonts'
12
-
13
- const DISMISS_DEFAULT = 5
14
-
15
- interface StateProps {
16
- displayMethod: ErrorDisplayType | null
17
- }
18
-
19
- interface OwnProps {
20
- error?: ErrorMessages | null
21
- dismissAfter?: number
22
- }
23
-
24
- interface DispatchProps {
25
- hideAlert: typeof hideAlert
26
- }
27
-
28
- const mapStateToProps = (state: RootState): StateProps => {
29
- const displayMethod = state.alert ? state.alert.displayMethod : null
30
- return {
31
- displayMethod,
32
- }
33
- }
34
-
35
- const mapDispatchToProps = {
36
- hideAlert,
37
- }
38
-
39
- type Props = DispatchProps & OwnProps & WithTranslation & StateProps
40
-
41
- function ErrorMessageInline(props: Props) {
42
- const { error, displayMethod, dismissAfter, t } = props
43
-
44
- // Want to initiate/cleanup a timer for each new error
45
- React.useEffect(() => {
46
- const timer = window.setTimeout(props.hideAlert, (dismissAfter || DISMISS_DEFAULT) * 1000)
47
- return () => window.clearTimeout(timer)
48
- }, [error])
49
-
50
- // Keep the space empty when there isn't an inline error
51
- // displayMethod lives in redux store and we want to be able to use this component
52
- // without populating it so much check if other types are already displayed
53
- // rather than if INLINE is
54
- if (!error || displayMethod === ErrorDisplayType.BANNER) {
55
- return <View style={dismissAfter !== null && styles.errorContainer} />
56
- }
57
-
58
- return (
59
- <View style={dismissAfter !== null && styles.errorContainer}>
60
- <Text style={styles.errorMessage}>{t(error)} </Text>
61
- </View>
62
- )
63
- }
64
-
65
- const styles = StyleSheet.create({
66
- errorContainer: {
67
- height: 64,
68
- },
69
- errorMessage: {
70
- ...typeScale.bodySmall,
71
- color: colors.errorPrimary,
72
- },
73
- })
74
-
75
- export default connect<StateProps, DispatchProps, OwnProps, RootState>(
76
- (state: RootState) => mapStateToProps,
77
- mapDispatchToProps
78
- )(withTranslation<Props>()(ErrorMessageInline))
@@ -1,53 +0,0 @@
1
- import * as React from 'react'
2
- import { StyleSheet } from 'react-native'
3
- import TextInput from 'src/components/TextInput'
4
- import colors from 'src/styles/colors'
5
-
6
- export interface SingleDigitInputProps {
7
- inputValue: string
8
- inputPlaceholder: string
9
- onInputChange: (value: string) => void
10
- testID?: string
11
- forwardedRef: any
12
- }
13
-
14
- type Props = SingleDigitInputProps
15
-
16
- // Multiline enabled as to handle unexpected cursor behavior
17
- // https://github.com/facebook/react-native/issues/28794#issuecomment-877769852
18
-
19
- export function SingleDigitInput({
20
- inputValue,
21
- inputPlaceholder,
22
- onInputChange,
23
- testID,
24
- forwardedRef,
25
- }: Props) {
26
- return (
27
- <TextInput
28
- multiline={true}
29
- textAlign={'center'}
30
- value={inputValue}
31
- placeholder={inputPlaceholder}
32
- onChangeText={onInputChange}
33
- maxLength={1}
34
- showClearButton={false}
35
- style={styles.codeInput}
36
- testID={testID}
37
- ref={forwardedRef}
38
- />
39
- )
40
- }
41
-
42
- const styles = StyleSheet.create({
43
- codeInput: {
44
- borderColor: colors.borderPrimary,
45
- borderRadius: 3,
46
- borderWidth: 1,
47
- flex: 0,
48
- backgroundColor: colors.backgroundPrimary,
49
- height: 50,
50
- width: 50,
51
- marginVertical: 5,
52
- },
53
- })
@@ -1,55 +0,0 @@
1
- import * as React from 'react'
2
- import { StyleSheet, View } from 'react-native'
3
- import { Line, Svg } from 'react-native-svg'
4
- import { default as Colors, default as colors } from 'src/styles/colors'
5
- import globalStyles from 'src/styles/styles'
6
-
7
- function HamburgerCard() {
8
- return (
9
- <View style={styles.container}>
10
- <Svg testID="Hamburger" width="32" height="32" viewBox="0 0 32 32" fill="none">
11
- <Line
12
- x1="7.25"
13
- y1="9.75"
14
- x2="24.75"
15
- y2="9.75"
16
- stroke={Colors.contentPrimary}
17
- strokeWidth="2.5"
18
- strokeLinecap="round"
19
- />
20
- <Line
21
- x1="7.25"
22
- y1="15.75"
23
- x2="24.75"
24
- y2="15.75"
25
- stroke={Colors.contentPrimary}
26
- strokeWidth="2.5"
27
- strokeLinecap="round"
28
- />
29
- <Line
30
- x1="7.25"
31
- y1="21.75"
32
- x2="24.75"
33
- y2="21.75"
34
- stroke={Colors.contentPrimary}
35
- strokeWidth="2.5"
36
- strokeLinecap="round"
37
- />
38
- </Svg>
39
- </View>
40
- )
41
- }
42
-
43
- const styles = StyleSheet.create({
44
- container: {
45
- backgroundColor: colors.backgroundPrimary,
46
- ...globalStyles.softShadowLight,
47
- alignItems: 'center',
48
- justifyContent: 'center',
49
- borderRadius: 4,
50
- height: 32,
51
- width: 32,
52
- },
53
- })
54
-
55
- export default React.memo(HamburgerCard)