wallet-stack 1.0.0-alpha.132 → 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 (50) hide show
  1. package/locales/base/translation.json +5 -0
  2. package/package.json +1 -2
  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 +0 -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 +2 -0
  14. package/src/images/assets/minipay.png +0 -0
  15. package/src/images/assets/minipay@1.5x.png +0 -0
  16. package/src/images/assets/minipay@2x.png +0 -0
  17. package/src/images/assets/minipay@3x.png +0 -0
  18. package/src/images/assets/minipay@4x.png +0 -0
  19. package/src/images/assets/valora.png +0 -0
  20. package/src/images/assets/valora@1.5x.png +0 -0
  21. package/src/images/assets/valora@2x.png +0 -0
  22. package/src/images/assets/valora@3x.png +0 -0
  23. package/src/images/assets/valora@4x.png +0 -0
  24. package/src/index.d.ts +0 -1
  25. package/src/navigator/Navigator.tsx +4 -14
  26. package/src/navigator/Screens.tsx +1 -2
  27. package/src/navigator/types.tsx +4 -6
  28. package/src/qrcode/utils.test.tsx +4 -96
  29. package/src/qrcode/utils.ts +5 -114
  30. package/src/redux/migrations.test.ts +13 -0
  31. package/src/redux/migrations.ts +4 -0
  32. package/src/redux/store.test.ts +1 -2
  33. package/src/redux/store.ts +1 -1
  34. package/src/send/SelectRecipientAddress.test.tsx +146 -0
  35. package/src/send/SelectRecipientAddress.tsx +166 -0
  36. package/src/send/SendSelectRecipient.test.tsx +16 -87
  37. package/src/send/SendSelectRecipient.tsx +12 -27
  38. package/src/send/actions.ts +0 -26
  39. package/src/send/saga.ts +1 -6
  40. package/src/components/AccountNumberCard.tsx +0 -23
  41. package/src/components/ErrorMessageInline.tsx +0 -78
  42. package/src/components/SingleDigitInput.tsx +0 -53
  43. package/src/icons/HamburgerCard.tsx +0 -55
  44. package/src/identity/saga.test.ts +0 -103
  45. package/src/identity/secureSend.ts +0 -171
  46. package/src/send/ValidateRecipientAccount.test.tsx +0 -182
  47. package/src/send/ValidateRecipientAccount.tsx +0 -392
  48. package/src/send/ValidateRecipientIntro.test.tsx +0 -61
  49. package/src/send/ValidateRecipientIntro.tsx +0 -136
  50. package/src/send/__snapshots__/ValidateRecipientAccount.test.tsx.snap +0 -777
@@ -0,0 +1,146 @@
1
+ import { fireEvent, render } from '@testing-library/react-native'
2
+ import * as React from 'react'
3
+ import { Provider } from 'react-redux'
4
+ import AppAnalytics from 'src/analytics/AppAnalytics'
5
+ import { SendEvents } from 'src/analytics/Events'
6
+ import { SendOrigin } from 'src/analytics/types'
7
+ import { navigate } from 'src/navigator/NavigationService'
8
+ import { Screens } from 'src/navigator/Screens'
9
+ import { RecipientType } from 'src/recipients/recipient'
10
+ import SelectRecipientAddress from 'src/send/SelectRecipientAddress'
11
+ import { createMockStore, getMockStackScreenProps } from 'test/utils'
12
+ import { mockAccount2, mockAccount3, mockE164Number3 } from 'test/values'
13
+
14
+ const mockRecipient = {
15
+ e164PhoneNumber: mockE164Number3,
16
+ displayNumber: '(415) 555-0123',
17
+ recipientType: RecipientType.PhoneNumber,
18
+ } as const
19
+
20
+ function renderScreen(storeOverrides: any) {
21
+ const store = createMockStore({
22
+ identity: {
23
+ e164NumberToAddress: {
24
+ [mockE164Number3]: [mockAccount2.toLowerCase(), mockAccount3.toLowerCase()],
25
+ },
26
+ addressToVerifiedBy: {
27
+ [mockAccount2.toLowerCase()]: 'valora',
28
+ [mockAccount3.toLowerCase()]: 'minipay',
29
+ },
30
+ ...storeOverrides?.identity,
31
+ },
32
+ })
33
+
34
+ const props = getMockStackScreenProps(Screens.SelectRecipientAddress, {
35
+ recipient: mockRecipient,
36
+ origin: SendOrigin.AppSendFlow,
37
+ })
38
+
39
+ return render(
40
+ <Provider store={store}>
41
+ <SelectRecipientAddress {...props} />
42
+ </Provider>
43
+ )
44
+ }
45
+
46
+ describe('SelectRecipientAddress', () => {
47
+ beforeEach(() => {
48
+ jest.clearAllMocks()
49
+ })
50
+
51
+ it('renders a row per verified address with its verifier label', () => {
52
+ const { getByText, getByTestId } = renderScreen({})
53
+
54
+ expect(getByTestId(`SelectRecipientAddress/Row/${mockAccount2.toLowerCase()}`)).toBeTruthy()
55
+ expect(getByTestId(`SelectRecipientAddress/Row/${mockAccount3.toLowerCase()}`)).toBeTruthy()
56
+ expect(getByText('Valora')).toBeTruthy()
57
+ expect(getByText('MiniPay')).toBeTruthy()
58
+ expect(
59
+ getByText(`selectRecipientAddress.explanation, {"name":"${mockRecipient.displayNumber}"}`)
60
+ ).toBeTruthy()
61
+ })
62
+
63
+ it('tracks the screen-open event with the number of verified addresses', () => {
64
+ renderScreen({})
65
+
66
+ expect(AppAnalytics.track).toHaveBeenCalledWith(SendEvents.send_select_recipient_address_open, {
67
+ addressCount: 2,
68
+ })
69
+ })
70
+
71
+ it('filters out addresses without a verifier entry', () => {
72
+ const { queryByTestId } = renderScreen({
73
+ identity: {
74
+ e164NumberToAddress: {
75
+ [mockE164Number3]: [mockAccount2.toLowerCase(), mockAccount3.toLowerCase()],
76
+ },
77
+ addressToVerifiedBy: {
78
+ [mockAccount2.toLowerCase()]: 'valora',
79
+ // mockAccount3 intentionally omitted
80
+ },
81
+ },
82
+ })
83
+
84
+ expect(queryByTestId(`SelectRecipientAddress/Row/${mockAccount2.toLowerCase()}`)).toBeTruthy()
85
+ expect(queryByTestId(`SelectRecipientAddress/Row/${mockAccount3.toLowerCase()}`)).toBeNull()
86
+ })
87
+
88
+ it('filters out addresses with an unknown verifier', () => {
89
+ const { queryByTestId } = renderScreen({
90
+ identity: {
91
+ e164NumberToAddress: {
92
+ [mockE164Number3]: [mockAccount2.toLowerCase(), mockAccount3.toLowerCase()],
93
+ },
94
+ addressToVerifiedBy: {
95
+ [mockAccount2.toLowerCase()]: 'valora',
96
+ [mockAccount3.toLowerCase()]: 'somethingNew',
97
+ },
98
+ },
99
+ })
100
+
101
+ expect(queryByTestId(`SelectRecipientAddress/Row/${mockAccount2.toLowerCase()}`)).toBeTruthy()
102
+ expect(queryByTestId(`SelectRecipientAddress/Row/${mockAccount3.toLowerCase()}`)).toBeNull()
103
+ })
104
+
105
+ it('navigates to SendEnterAmount on Valora row tap with isMiniPayRecipient=false', () => {
106
+ const { getByTestId } = renderScreen({})
107
+ fireEvent.press(getByTestId(`SelectRecipientAddress/Row/${mockAccount2.toLowerCase()}`))
108
+
109
+ expect(navigate).toHaveBeenCalledWith(Screens.SendEnterAmount, {
110
+ isFromScan: false,
111
+ defaultTokenIdOverride: undefined,
112
+ forceTokenId: undefined,
113
+ recipient: {
114
+ ...mockRecipient,
115
+ address: mockAccount2.toLowerCase(),
116
+ },
117
+ origin: SendOrigin.AppSendFlow,
118
+ isMiniPayRecipient: false,
119
+ })
120
+ expect(AppAnalytics.track).toHaveBeenCalledWith(
121
+ SendEvents.send_select_recipient_address_select,
122
+ { verifier: 'valora' }
123
+ )
124
+ })
125
+
126
+ it('navigates to SendEnterAmount on MiniPay row tap with isMiniPayRecipient=true', () => {
127
+ const { getByTestId } = renderScreen({})
128
+ fireEvent.press(getByTestId(`SelectRecipientAddress/Row/${mockAccount3.toLowerCase()}`))
129
+
130
+ expect(navigate).toHaveBeenCalledWith(Screens.SendEnterAmount, {
131
+ isFromScan: false,
132
+ defaultTokenIdOverride: undefined,
133
+ forceTokenId: undefined,
134
+ recipient: {
135
+ ...mockRecipient,
136
+ address: mockAccount3.toLowerCase(),
137
+ },
138
+ origin: SendOrigin.AppSendFlow,
139
+ isMiniPayRecipient: true,
140
+ })
141
+ expect(AppAnalytics.track).toHaveBeenCalledWith(
142
+ SendEvents.send_select_recipient_address_select,
143
+ { verifier: 'minipay' }
144
+ )
145
+ })
146
+ })
@@ -0,0 +1,166 @@
1
+ import { NativeStackScreenProps } from '@react-navigation/native-stack'
2
+ import React, { useEffect } from 'react'
3
+ import { Trans, useTranslation } from 'react-i18next'
4
+ import { Image, ScrollView, StyleSheet, Text, View } from 'react-native'
5
+ import { SafeAreaView } from 'react-native-safe-area-context'
6
+ import { formatShortenedAddress } from 'src/account/utils'
7
+ import AppAnalytics from 'src/analytics/AppAnalytics'
8
+ import { SendEvents } from 'src/analytics/Events'
9
+ import BackButton from 'src/components/BackButton'
10
+ import Touchable from 'src/components/Touchable'
11
+ import CustomHeader from 'src/components/header/CustomHeader'
12
+ import { addressToVerifiedBySelector, e164NumberToAddressSelector } from 'src/identity/selectors'
13
+ import { miniPay, valora } from 'src/images/Images'
14
+ import { noHeader } from 'src/navigator/Headers'
15
+ import { navigate } from 'src/navigator/NavigationService'
16
+ import { Screens } from 'src/navigator/Screens'
17
+ import { StackParamList } from 'src/navigator/types'
18
+ import { getDisplayName } from 'src/recipients/recipient'
19
+ import { useSelector } from 'src/redux/hooks'
20
+ import Colors from 'src/styles/colors'
21
+ import { typeScale } from 'src/styles/fonts'
22
+ import { Spacing } from 'src/styles/styles'
23
+
24
+ type Props = NativeStackScreenProps<StackParamList, Screens.SelectRecipientAddress>
25
+
26
+ const VERIFIERS = {
27
+ valora: { name: 'Valora', icon: valora },
28
+ minipay: { name: 'MiniPay', icon: miniPay },
29
+ } as const
30
+
31
+ export type Verifier = keyof typeof VERIFIERS
32
+
33
+ const ICON_SIZE = 40
34
+
35
+ function isKnownVerifier(verifier: string | undefined): verifier is Verifier {
36
+ return !!verifier && Object.hasOwn(VERIFIERS, verifier)
37
+ }
38
+
39
+ function VerifierIcon({ verifier }: { verifier: Verifier }) {
40
+ return <Image source={VERIFIERS[verifier].icon} style={styles.icon} resizeMode="contain" />
41
+ }
42
+
43
+ function SelectRecipientAddress({ route }: Props) {
44
+ const { t } = useTranslation()
45
+ const { recipient, origin, forceTokenId, defaultTokenIdOverride } = route.params
46
+
47
+ const e164NumberToAddress = useSelector(e164NumberToAddressSelector)
48
+ const addressToVerifiedBy = useSelector(addressToVerifiedBySelector)
49
+
50
+ const addresses = e164NumberToAddress[recipient.e164PhoneNumber] || []
51
+ const verifiedEntries = addresses
52
+ .map((address) => ({ address, verifier: addressToVerifiedBy[address] }))
53
+ .filter((entry): entry is { address: string; verifier: Verifier } =>
54
+ isKnownVerifier(entry.verifier)
55
+ )
56
+
57
+ useEffect(() => {
58
+ AppAnalytics.track(SendEvents.send_select_recipient_address_open, {
59
+ addressCount: verifiedEntries.length,
60
+ })
61
+ }, [])
62
+
63
+ const onSelectAddress = (address: string, verifier: Verifier) => {
64
+ AppAnalytics.track(SendEvents.send_select_recipient_address_select, {
65
+ verifier,
66
+ })
67
+ navigate(Screens.SendEnterAmount, {
68
+ isFromScan: false,
69
+ defaultTokenIdOverride,
70
+ forceTokenId,
71
+ recipient: {
72
+ ...recipient,
73
+ address,
74
+ },
75
+ origin,
76
+ isMiniPayRecipient: verifier === 'minipay',
77
+ })
78
+ }
79
+
80
+ return (
81
+ <SafeAreaView style={styles.container} edges={['top']}>
82
+ <CustomHeader
83
+ style={styles.customHeader}
84
+ left={<BackButton eventName={SendEvents.send_select_recipient_address_back} />}
85
+ />
86
+ <ScrollView contentContainerStyle={styles.scrollContent}>
87
+ <Text style={styles.title}>{t('selectRecipientAddress.header')}</Text>
88
+ <Text style={styles.explanation}>
89
+ <Trans
90
+ i18nKey="selectRecipientAddress.explanation"
91
+ tOptions={{ name: getDisplayName(recipient, t) }}
92
+ >
93
+ <Text style={styles.explanationName} />
94
+ </Trans>
95
+ </Text>
96
+ {verifiedEntries.map(({ address, verifier }) => (
97
+ <Touchable
98
+ key={address}
99
+ onPress={() => onSelectAddress(address, verifier)}
100
+ testID={`SelectRecipientAddress/Row/${address}`}
101
+ >
102
+ <View style={styles.row}>
103
+ <VerifierIcon verifier={verifier} />
104
+ <View style={styles.rowContent}>
105
+ <Text style={styles.address}>{formatShortenedAddress(address)}</Text>
106
+ <Text style={styles.verifier}>{VERIFIERS[verifier].name}</Text>
107
+ </View>
108
+ </View>
109
+ </Touchable>
110
+ ))}
111
+ </ScrollView>
112
+ </SafeAreaView>
113
+ )
114
+ }
115
+
116
+ const styles = StyleSheet.create({
117
+ container: {
118
+ flex: 1,
119
+ },
120
+ customHeader: {
121
+ paddingHorizontal: Spacing.Thick24,
122
+ },
123
+ scrollContent: {
124
+ paddingBottom: Spacing.Smallest8,
125
+ },
126
+ title: {
127
+ ...typeScale.titleMedium,
128
+ paddingHorizontal: Spacing.Regular16,
129
+ paddingTop: Spacing.Thick24,
130
+ marginBottom: Spacing.Smallest8,
131
+ },
132
+ explanation: {
133
+ ...typeScale.bodySmall,
134
+ color: Colors.contentSecondary,
135
+ paddingHorizontal: Spacing.Regular16,
136
+ paddingBottom: Spacing.Regular16,
137
+ },
138
+ explanationName: {
139
+ fontWeight: 'bold',
140
+ },
141
+ icon: {
142
+ width: ICON_SIZE,
143
+ height: ICON_SIZE,
144
+ },
145
+ row: {
146
+ flexDirection: 'row',
147
+ alignItems: 'center',
148
+ paddingHorizontal: Spacing.Regular16,
149
+ paddingVertical: Spacing.Regular16,
150
+ },
151
+ rowContent: {
152
+ flex: 1,
153
+ marginLeft: Spacing.Small12,
154
+ },
155
+ address: {
156
+ ...typeScale.labelMedium,
157
+ },
158
+ verifier: {
159
+ ...typeScale.bodySmall,
160
+ color: Colors.contentSecondary,
161
+ },
162
+ })
163
+
164
+ SelectRecipientAddress.navigationOptions = noHeader
165
+
166
+ export default SelectRecipientAddress
@@ -7,7 +7,6 @@ import { SendEvents } from 'src/analytics/Events'
7
7
  import { SendOrigin } from 'src/analytics/types'
8
8
  import { getAppConfig } from 'src/appConfig'
9
9
  import { fetchAddressVerification, fetchAddressesAndValidate } from 'src/identity/actions'
10
- import { AddressValidationType } from 'src/identity/reducer'
11
10
  import { RecipientVerificationStatus } from 'src/identity/types'
12
11
  import { navigate } from 'src/navigator/NavigationService'
13
12
  import { Screens } from 'src/navigator/Screens'
@@ -476,9 +475,6 @@ describe('SendSelectRecipient', () => {
476
475
  const store = createMockStore({
477
476
  ...storeWithPhoneVerified,
478
477
  identity: {
479
- secureSendPhoneNumberMapping: {
480
- [mockE164Number3]: { addressValidationType: AddressValidationType.NONE },
481
- },
482
478
  e164NumberToAddress: { [mockE164Number3]: [mockAccount3] },
483
479
  },
484
480
  })
@@ -522,9 +518,6 @@ describe('SendSelectRecipient', () => {
522
518
  const store = createMockStore({
523
519
  ...storeWithPhoneVerified,
524
520
  identity: {
525
- secureSendPhoneNumberMapping: {
526
- [mockE164Number3]: { addressValidationType: AddressValidationType.NONE },
527
- },
528
521
  e164NumberToAddress: { [mockE164Number3]: [mockAccount3] },
529
522
  addressToVerifiedBy: { [mockAccount3]: 'minipay' },
530
523
  },
@@ -558,7 +551,7 @@ describe('SendSelectRecipient', () => {
558
551
  isMiniPayRecipient: true,
559
552
  })
560
553
  })
561
- it('navigates to secure send flow when phone recipient has multiple addresses, first time seeing it', async () => {
554
+ it('navigates to address picker when phone number recipient has multiple verified addresses', async () => {
562
555
  jest
563
556
  .mocked(getRecipientVerificationStatus)
564
557
  .mockReturnValue(RecipientVerificationStatus.VERIFIED)
@@ -566,9 +559,6 @@ describe('SendSelectRecipient', () => {
566
559
  const store = createMockStore({
567
560
  ...storeWithPhoneVerified,
568
561
  identity: {
569
- secureSendPhoneNumberMapping: {
570
- [mockE164Number3]: { addressValidationType: AddressValidationType.PARTIAL },
571
- },
572
562
  e164NumberToAddress: {
573
563
  [mockE164Number3]: [mockAccount2.toLowerCase(), mockAccount3.toLowerCase()],
574
564
  },
@@ -576,6 +566,10 @@ describe('SendSelectRecipient', () => {
576
566
  [mockAccount2.toLowerCase()]: mockE164Number3,
577
567
  [mockAccount3.toLowerCase()]: mockE164Number3,
578
568
  },
569
+ addressToVerifiedBy: {
570
+ [mockAccount2.toLowerCase()]: 'valora',
571
+ [mockAccount3.toLowerCase()]: 'minipay',
572
+ },
579
573
  },
580
574
  })
581
575
 
@@ -596,14 +590,14 @@ describe('SendSelectRecipient', () => {
596
590
  expect(AppAnalytics.track).toHaveBeenCalledWith(SendEvents.send_select_recipient_send_press, {
597
591
  recipientType: RecipientType.PhoneNumber,
598
592
  })
599
- expect(navigate).toHaveBeenCalledWith(Screens.ValidateRecipientIntro, {
593
+ expect(navigate).toHaveBeenCalledWith(Screens.SelectRecipientAddress, {
600
594
  defaultTokenIdOverride: undefined,
601
595
  forceTokenId: undefined,
602
596
  recipient: expect.any(Object),
603
597
  origin: SendOrigin.AppSendFlow,
604
598
  })
605
599
  })
606
- it('navigates to send amount when phone recipient has multiple addresses and secure send was already done', async () => {
600
+ it('navigates to send enter amount when phone number has multiple raw addresses but only one with a verifier', async () => {
607
601
  jest
608
602
  .mocked(getRecipientVerificationStatus)
609
603
  .mockReturnValue(RecipientVerificationStatus.VERIFIED)
@@ -611,12 +605,6 @@ describe('SendSelectRecipient', () => {
611
605
  const store = createMockStore({
612
606
  ...storeWithPhoneVerified,
613
607
  identity: {
614
- secureSendPhoneNumberMapping: {
615
- [mockE164Number3]: {
616
- addressValidationType: AddressValidationType.NONE,
617
- address: mockAccount3,
618
- },
619
- },
620
608
  e164NumberToAddress: {
621
609
  [mockE164Number3]: [mockAccount2.toLowerCase(), mockAccount3.toLowerCase()],
622
610
  },
@@ -624,6 +612,9 @@ describe('SendSelectRecipient', () => {
624
612
  [mockAccount2.toLowerCase()]: mockE164Number3,
625
613
  [mockAccount3.toLowerCase()]: mockE164Number3,
626
614
  },
615
+ addressToVerifiedBy: {
616
+ [mockAccount3.toLowerCase()]: 'valora',
617
+ },
627
618
  },
628
619
  })
629
620
 
@@ -649,7 +640,7 @@ describe('SendSelectRecipient', () => {
649
640
  defaultTokenIdOverride: undefined,
650
641
  forceTokenId: undefined,
651
642
  recipient: {
652
- address: mockAccount3,
643
+ address: mockAccount3.toLowerCase(),
653
644
  displayNumber: '(415) 555-0123',
654
645
  e164PhoneNumber: mockE164Number3,
655
646
  recipientType: 'PhoneNumber',
@@ -659,7 +650,7 @@ describe('SendSelectRecipient', () => {
659
650
  })
660
651
  })
661
652
  it.each([{ searchAddress: mockAccount2 }, { searchAddress: mockAccount3 }])(
662
- 'navigates to send enter amount with correct address if a an address is entered which also has a phone number with secure send not done',
653
+ 'navigates to send enter amount with correct address if an address is entered which also maps to a phone number with multiple addresses',
663
654
  async ({ searchAddress }) => {
664
655
  jest
665
656
  .mocked(getRecipientVerificationStatus)
@@ -668,11 +659,6 @@ describe('SendSelectRecipient', () => {
668
659
  const store = createMockStore({
669
660
  ...storeWithPhoneVerified,
670
661
  identity: {
671
- secureSendPhoneNumberMapping: {
672
- [mockE164Number3]: {
673
- addressValidationType: AddressValidationType.PARTIAL,
674
- },
675
- },
676
662
  e164NumberToAddress: {
677
663
  [mockE164Number3]: [mockAccount2.toLowerCase(), mockAccount3.toLowerCase()],
678
664
  },
@@ -680,66 +666,9 @@ describe('SendSelectRecipient', () => {
680
666
  [mockAccount2.toLowerCase()]: mockE164Number3,
681
667
  [mockAccount3.toLowerCase()]: mockE164Number3,
682
668
  },
683
- },
684
- })
685
-
686
- const { getByTestId } = render(
687
- <Provider store={store}>
688
- <SendSelectRecipient {...mockScreenProps({})} />
689
- </Provider>
690
- )
691
- const searchInput = getByTestId('SendSelectRecipientSearchInput')
692
-
693
- await act(() => {
694
- fireEvent.changeText(searchInput, searchAddress)
695
- })
696
- await act(() => {
697
- fireEvent.press(getByTestId('RecipientItem'))
698
- })
699
-
700
- expect(AppAnalytics.track).toHaveBeenCalledWith(SendEvents.send_select_recipient_send_press, {
701
- recipientType: RecipientType.Address,
702
- })
703
- expect(navigate).toHaveBeenCalledWith(Screens.SendEnterAmount, {
704
- isFromScan: false,
705
- defaultTokenIdOverride: undefined,
706
- forceTokenId: undefined,
707
- recipient: {
708
- address: searchAddress.toLowerCase(),
709
- e164PhoneNumber: mockE164Number3,
710
- recipientType: RecipientType.Address,
711
- contactId: undefined,
712
- displayNumber: undefined,
713
- name: undefined,
714
- thumbnailPath: undefined,
715
- },
716
- origin: SendOrigin.AppSendFlow,
717
- isMiniPayRecipient: false,
718
- })
719
- }
720
- )
721
- it.each([{ searchAddress: mockAccount2 }, { searchAddress: mockAccount3 }])(
722
- 'navigates to send enter amount with correct address if a an address is entered which also has a phone number with secure send done with different address',
723
- async ({ searchAddress }) => {
724
- jest
725
- .mocked(getRecipientVerificationStatus)
726
- .mockReturnValue(RecipientVerificationStatus.VERIFIED)
727
-
728
- const store = createMockStore({
729
- ...storeWithPhoneVerified,
730
- identity: {
731
- secureSendPhoneNumberMapping: {
732
- [mockE164Number3]: {
733
- addressValidationType: AddressValidationType.NONE,
734
- address: mockAccount3,
735
- },
736
- },
737
- e164NumberToAddress: {
738
- [mockE164Number3]: [mockAccount2.toLowerCase(), mockAccount3.toLowerCase()],
739
- },
740
- addressToE164Number: {
741
- [mockAccount2.toLowerCase()]: mockE164Number3,
742
- [mockAccount3.toLowerCase()]: mockE164Number3,
669
+ addressToVerifiedBy: {
670
+ [mockAccount2.toLowerCase()]: 'valora',
671
+ [mockAccount3.toLowerCase()]: 'minipay',
743
672
  },
744
673
  },
745
674
  })
@@ -775,7 +704,7 @@ describe('SendSelectRecipient', () => {
775
704
  thumbnailPath: undefined,
776
705
  },
777
706
  origin: SendOrigin.AppSendFlow,
778
- isMiniPayRecipient: false,
707
+ isMiniPayRecipient: searchAddress.toLowerCase() === mockAccount3.toLowerCase(),
779
708
  })
780
709
  }
781
710
  )
@@ -14,14 +14,7 @@ import KeyboardAwareScrollView from 'src/components/KeyboardAwareScrollView'
14
14
  import CustomHeader from 'src/components/header/CustomHeader'
15
15
  import CircledIcon from 'src/icons/CircledIcon'
16
16
  import { importContacts } from 'src/identity/actions'
17
- import { getAddressFromPhoneNumber } from 'src/identity/contactMapping'
18
- import { AddressValidationType } from 'src/identity/reducer'
19
- import { getAddressValidationType } from 'src/identity/secureSend'
20
- import {
21
- addressToVerifiedBySelector,
22
- e164NumberToAddressSelector,
23
- secureSendPhoneNumberMappingSelector,
24
- } from 'src/identity/selectors'
17
+ import { addressToVerifiedBySelector, e164NumberToAddressSelector } from 'src/identity/selectors'
25
18
  import { RecipientVerificationStatus } from 'src/identity/types'
26
19
  import { noHeader } from 'src/navigator/Headers'
27
20
  import { navigate } from 'src/navigator/NavigationService'
@@ -171,7 +164,6 @@ enum SelectRecipientView {
171
164
  function SendSelectRecipient({ route }: Props) {
172
165
  const { t } = useTranslation()
173
166
  const dispatch = useDispatch()
174
- const secureSendPhoneNumberMapping = useSelector(secureSendPhoneNumberMappingSelector)
175
167
  const e164NumberToAddress = useSelector(e164NumberToAddressSelector)
176
168
  const addressToVerifiedBy = useSelector(addressToVerifiedBySelector)
177
169
  const shareUrl = getAppConfig().experimental?.inviteFriends?.shareUrl ?? null
@@ -241,35 +233,28 @@ function SendSelectRecipient({ route }: Props) {
241
233
  // use the address from the recipient object
242
234
  let address: string | null | undefined = selectedRecipient.address
243
235
 
244
- // if not present there must be a phone number, route through secure send or get
245
- // 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
246
238
  if (!address && recipientHasNumber(selectedRecipient)) {
247
- const addressValidationType: AddressValidationType = getAddressValidationType(
248
- selectedRecipient,
249
- secureSendPhoneNumberMapping
250
- )
251
- if (addressValidationType !== AddressValidationType.NONE) {
252
- navigate(Screens.ValidateRecipientIntro, {
253
- 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, {
254
244
  forceTokenId,
245
+ defaultTokenIdOverride,
255
246
  recipient: selectedRecipient,
256
247
  origin: SendOrigin.AppSendFlow,
257
248
  })
258
249
  return
259
250
  }
260
- address = getAddressFromPhoneNumber(
261
- selectedRecipient.e164PhoneNumber,
262
- e164NumberToAddress,
263
- secureSendPhoneNumberMapping,
264
- undefined
265
- )
251
+
252
+ address = verifiedAddresses[0] ?? phoneAddresses[0]
266
253
  }
267
254
 
268
255
  if (!address) {
269
256
  // this should never happen
270
- throw new Error(
271
- 'No address found, this should never happen. Should have routed to invite or secure send.'
272
- )
257
+ throw new Error('No address found, this should never happen. Should have routed to invite.')
273
258
  }
274
259
 
275
260
  navigate(Screens.SendEnterAmount, {
@@ -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
- })