wallet-stack 1.0.0-alpha.132 → 1.0.0-alpha.134
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.
- package/locales/base/translation.json +5 -0
- package/package.json +1 -2
- package/src/analytics/Events.tsx +3 -10
- package/src/analytics/Properties.tsx +9 -25
- package/src/analytics/docs.ts +11 -8
- package/src/app/ErrorMessages.ts +0 -7
- package/src/components/ReviewTransaction.test.tsx +49 -8
- package/src/components/ReviewTransaction.tsx +58 -12
- package/src/icons/VerifiedBadge.tsx +27 -0
- package/src/identity/actions.ts +1 -114
- package/src/identity/contactMapping.test.ts +80 -46
- package/src/identity/contactMapping.ts +46 -102
- package/src/identity/reducer.ts +7 -98
- package/src/identity/saga.ts +2 -85
- package/src/identity/selectors.ts +0 -4
- package/src/images/Images.ts +2 -0
- package/src/images/assets/minipay.png +0 -0
- package/src/images/assets/minipay@1.5x.png +0 -0
- package/src/images/assets/minipay@2x.png +0 -0
- package/src/images/assets/minipay@3x.png +0 -0
- package/src/images/assets/minipay@4x.png +0 -0
- package/src/images/assets/valora.png +0 -0
- package/src/images/assets/valora@1.5x.png +0 -0
- package/src/images/assets/valora@2x.png +0 -0
- package/src/images/assets/valora@3x.png +0 -0
- package/src/images/assets/valora@4x.png +0 -0
- package/src/index.d.ts +0 -1
- package/src/navigator/Navigator.tsx +4 -14
- package/src/navigator/Screens.tsx +1 -2
- package/src/navigator/types.tsx +4 -6
- package/src/qrcode/utils.test.tsx +4 -96
- package/src/qrcode/utils.ts +5 -114
- package/src/recipients/RecipientItemV2.test.tsx +11 -72
- package/src/recipients/RecipientItemV2.tsx +1 -34
- package/src/recipients/recipient.test.ts +6 -5
- package/src/recipients/recipient.ts +7 -12
- package/src/recipients/verifier.ts +20 -0
- package/src/redux/migrations.test.ts +38 -0
- package/src/redux/migrations.ts +25 -0
- package/src/redux/store.test.ts +1 -3
- package/src/redux/store.ts +1 -1
- package/src/send/SelectRecipientAddress.test.tsx +146 -0
- package/src/send/SelectRecipientAddress.tsx +164 -0
- package/src/send/SendConfirmation.test.tsx +3 -3
- package/src/send/SendConfirmation.tsx +3 -3
- package/src/send/SendSelectRecipient.test.tsx +53 -138
- package/src/send/SendSelectRecipient.tsx +12 -27
- package/src/send/actions.ts +0 -26
- package/src/send/saga.ts +1 -6
- package/src/send/useFetchRecipientVerificationStatus.ts +6 -11
- package/src/transactions/UserSection.tsx +37 -11
- package/src/transactions/feed/TransactionDetailsScreen.test.tsx +6 -6
- package/src/utils/phoneNumbers.ts +0 -11
- package/src/components/AccountNumberCard.tsx +0 -23
- package/src/components/ErrorMessageInline.tsx +0 -78
- package/src/components/SingleDigitInput.tsx +0 -53
- package/src/icons/HamburgerCard.tsx +0 -55
- package/src/identity/saga.test.ts +0 -103
- package/src/identity/secureSend.ts +0 -171
- package/src/send/ValidateRecipientAccount.test.tsx +0 -182
- package/src/send/ValidateRecipientAccount.tsx +0 -392
- package/src/send/ValidateRecipientIntro.test.tsx +0 -61
- package/src/send/ValidateRecipientIntro.tsx +0 -136
- package/src/send/__snapshots__/ValidateRecipientAccount.test.tsx.snap +0 -777
|
@@ -2169,6 +2169,11 @@
|
|
|
2169
2169
|
"importSuccess": "Successfully imported {{tokenSymbol}} token",
|
|
2170
2170
|
"importButton": "Import"
|
|
2171
2171
|
},
|
|
2172
|
+
"selectRecipientAddress": {
|
|
2173
|
+
"header": "Select wallet",
|
|
2174
|
+
"recipient": "Recipient",
|
|
2175
|
+
"explanation": "<0>{{name}}</0> has more than one wallet linked to the phone number. Choose which one to send to."
|
|
2176
|
+
},
|
|
2172
2177
|
"sendSelectRecipient": {
|
|
2173
2178
|
"searchText": "Search by name, phone, wallet...",
|
|
2174
2179
|
"searchInputLabel": "To",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "wallet-stack",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.134",
|
|
4
4
|
"author": "Valora Inc",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -148,7 +148,6 @@
|
|
|
148
148
|
"bignumber.js": "^9.1.2",
|
|
149
149
|
"country-data": "^0.0.31",
|
|
150
150
|
"date-fns": "^4.1.0",
|
|
151
|
-
"dot-prop-immutable": "^2.1.1",
|
|
152
151
|
"expo": "^53.0.22",
|
|
153
152
|
"expo-image": "~2.4.0",
|
|
154
153
|
"fast-levenshtein": "^3.0.0",
|
package/src/analytics/Events.tsx
CHANGED
|
@@ -269,16 +269,6 @@ export enum SendEvents {
|
|
|
269
269
|
send_confirm_back = 'send_confirm_back',
|
|
270
270
|
send_confirm_send = 'send_confirm_send',
|
|
271
271
|
|
|
272
|
-
send_secure_start = 'send_secure_start',
|
|
273
|
-
send_secure_back = 'send_secure_back',
|
|
274
|
-
send_secure_cancel = 'send_secure_cancel',
|
|
275
|
-
|
|
276
|
-
send_secure_info = 'send_secure_info',
|
|
277
|
-
send_secure_info_dismissed = 'send_secure_info_dismissed',
|
|
278
|
-
send_secure_submit = 'send_secure_submit',
|
|
279
|
-
send_secure_incorrect = 'send_secure_incorrect',
|
|
280
|
-
send_secure_complete = 'send_secure_complete',
|
|
281
|
-
|
|
282
272
|
send_tx_start = 'send_tx_start',
|
|
283
273
|
send_tx_complete = 'send_tx_complete',
|
|
284
274
|
send_tx_error = 'send_tx_error',
|
|
@@ -305,6 +295,9 @@ export enum SendEvents {
|
|
|
305
295
|
send_select_recipient_invite_press = 'send_select_recipient_invite_press',
|
|
306
296
|
send_select_recipient_send_press = 'send_select_recipient_send_press',
|
|
307
297
|
send_select_recipient_recent_press = 'send_select_recipient_recent_press',
|
|
298
|
+
send_select_recipient_address_open = 'send_select_recipient_address_open',
|
|
299
|
+
send_select_recipient_address_select = 'send_select_recipient_address_select',
|
|
300
|
+
send_select_recipient_address_back = 'send_select_recipient_address_back',
|
|
308
301
|
}
|
|
309
302
|
|
|
310
303
|
export enum QrScreenEvents {
|
|
@@ -71,6 +71,7 @@ import { NftOrigin } from 'src/nfts/types'
|
|
|
71
71
|
import { NotificationReceiveState } from 'src/notifications/types'
|
|
72
72
|
import { PointsActivityId } from 'src/points/types'
|
|
73
73
|
import { RecipientType } from 'src/recipients/recipient'
|
|
74
|
+
import { Verifier } from 'src/recipients/verifier'
|
|
74
75
|
import { AmountEnteredIn, QrCode } from 'src/send/types'
|
|
75
76
|
import { Field, SwapType } from 'src/swap/types'
|
|
76
77
|
import { TokenActionName } from 'src/tokens/types'
|
|
@@ -521,31 +522,6 @@ interface SendEventsProperties {
|
|
|
521
522
|
isTokenManuallyImported: boolean
|
|
522
523
|
}
|
|
523
524
|
|
|
524
|
-
[SendEvents.send_secure_start]: {
|
|
525
|
-
confirmByScan: boolean
|
|
526
|
-
}
|
|
527
|
-
[SendEvents.send_secure_back]: undefined
|
|
528
|
-
[SendEvents.send_secure_cancel]: undefined
|
|
529
|
-
[SendEvents.send_secure_submit]: {
|
|
530
|
-
partialAddressValidation: boolean
|
|
531
|
-
address: string
|
|
532
|
-
}
|
|
533
|
-
[SendEvents.send_secure_complete]: {
|
|
534
|
-
confirmByScan: boolean
|
|
535
|
-
partialAddressValidation?: boolean
|
|
536
|
-
}
|
|
537
|
-
[SendEvents.send_secure_incorrect]: {
|
|
538
|
-
confirmByScan: boolean
|
|
539
|
-
partialAddressValidation?: boolean
|
|
540
|
-
error: string
|
|
541
|
-
}
|
|
542
|
-
[SendEvents.send_secure_info]: {
|
|
543
|
-
partialAddressValidation: boolean
|
|
544
|
-
}
|
|
545
|
-
[SendEvents.send_secure_info_dismissed]: {
|
|
546
|
-
partialAddressValidation: boolean
|
|
547
|
-
}
|
|
548
|
-
|
|
549
525
|
[SendEvents.send_tx_start]: undefined
|
|
550
526
|
[SendEvents.send_tx_complete]: {
|
|
551
527
|
txId: string
|
|
@@ -605,6 +581,14 @@ interface SendEventsProperties {
|
|
|
605
581
|
[SendEvents.send_select_recipient_recent_press]: {
|
|
606
582
|
recipientType: RecipientType
|
|
607
583
|
}
|
|
584
|
+
|
|
585
|
+
[SendEvents.send_select_recipient_address_open]: {
|
|
586
|
+
addressCount: number
|
|
587
|
+
}
|
|
588
|
+
[SendEvents.send_select_recipient_address_select]: {
|
|
589
|
+
verifier: Verifier
|
|
590
|
+
}
|
|
591
|
+
[SendEvents.send_select_recipient_address_back]: undefined
|
|
608
592
|
}
|
|
609
593
|
|
|
610
594
|
interface FeeEventsProperties {
|
package/src/analytics/docs.ts
CHANGED
|
@@ -259,14 +259,6 @@ export const eventDocs: Record<AnalyticsEventType, string> = {
|
|
|
259
259
|
[SendEvents.send_amount_continue]: `when next button pressed on amount enter page`,
|
|
260
260
|
[SendEvents.send_confirm_back]: `when back button pressed on send confirmation screen`,
|
|
261
261
|
[SendEvents.send_confirm_send]: `when send button pressed on send confirmation screen`,
|
|
262
|
-
[SendEvents.send_secure_start]: `when either secure send scan or manual confirm button pressed`,
|
|
263
|
-
[SendEvents.send_secure_back]: `when back button is pressed during secure send`,
|
|
264
|
-
[SendEvents.send_secure_cancel]: `when secure send flow is canceled`,
|
|
265
|
-
[SendEvents.send_secure_info]: `when "help" button is pressed`,
|
|
266
|
-
[SendEvents.send_secure_info_dismissed]: `when "help" button is dismissed`,
|
|
267
|
-
[SendEvents.send_secure_submit]: `when an account is submitted for validation`,
|
|
268
|
-
[SendEvents.send_secure_incorrect]: `when there's been an error validating the account`,
|
|
269
|
-
[SendEvents.send_secure_complete]: `when an account has been validated`,
|
|
270
262
|
[SendEvents.send_tx_start]: `issued from the sendPayment saga, after a user confirms their intent to send and right before we build and send the transaction to the network`,
|
|
271
263
|
[SendEvents.send_tx_complete]: `when a send transaction has successfully completed`,
|
|
272
264
|
[SendEvents.send_tx_error]: `when there is an error sending a transaction`,
|
|
@@ -292,6 +284,9 @@ export const eventDocs: Record<AnalyticsEventType, string> = {
|
|
|
292
284
|
[SendEvents.send_select_recipient_send_press]: `When the send button is pressed after selecting a recipient`,
|
|
293
285
|
[SendEvents.send_select_recipient_invite_press]: `When the invite button is pressed after selecting a recipient`,
|
|
294
286
|
[SendEvents.send_select_recipient_recent_press]: `When a recent recipient is pressed`,
|
|
287
|
+
[SendEvents.send_select_recipient_address_open]: `When the address picker screen is shown (a phone recipient has multiple verified addresses)`,
|
|
288
|
+
[SendEvents.send_select_recipient_address_select]: `When a verified address is picked from the address picker screen`,
|
|
289
|
+
[SendEvents.send_select_recipient_address_back]: `When the back button is pressed on the address picker screen`,
|
|
295
290
|
[JumpstartEvents.jumpstart_reclaim_press]:
|
|
296
291
|
'When user taps on "Reclaim" button on the Jumpstart screen',
|
|
297
292
|
[JumpstartEvents.jumpstart_reclaim_start]:
|
|
@@ -578,6 +573,14 @@ export const eventDocs: Record<AnalyticsEventType, string> = {
|
|
|
578
573
|
|
|
579
574
|
// Legacy event docs
|
|
580
575
|
// The below events had docs, but are no longer produced by the latest app version.
|
|
576
|
+
// [SendEvents.send_secure_start]: `when either secure send scan or manual confirm button pressed`,
|
|
577
|
+
// [SendEvents.send_secure_back]: `when back button is pressed during secure send`,
|
|
578
|
+
// [SendEvents.send_secure_cancel]: `when secure send flow is canceled`,
|
|
579
|
+
// [SendEvents.send_secure_info]: `when "help" button is pressed`,
|
|
580
|
+
// [SendEvents.send_secure_info_dismissed]: `when "help" button is dismissed`,
|
|
581
|
+
// [SendEvents.send_secure_submit]: `when an account is submitted for validation`,
|
|
582
|
+
// [SendEvents.send_secure_incorrect]: `when there's been an error validating the account`,
|
|
583
|
+
// [SendEvents.send_secure_complete]: `when an account has been validated`,
|
|
581
584
|
// [HomeEvents.home_send]: `when "send" button is pressed from home screen send or request bar (NOT from home screen actions)`,
|
|
582
585
|
// [HomeEvents.view_nft_home_assets]: `When "NFTs" is clicked in Home Assets Pages`,
|
|
583
586
|
// [DappExplorerEvents.dapp_open_info]: `when a user taps on the help icon`,
|
package/src/app/ErrorMessages.ts
CHANGED
|
@@ -16,13 +16,6 @@ export enum ErrorMessages {
|
|
|
16
16
|
FIREBASE_FAILED = 'firebaseFailed',
|
|
17
17
|
IMPORT_CONTACTS_FAILED = 'importContactsFailed',
|
|
18
18
|
QR_FAILED_INVALID_ADDRESS = 'qrFailedInvalidAddress',
|
|
19
|
-
QR_FAILED_INVALID_RECIPIENT = 'qrFailedInvalidRecipient',
|
|
20
|
-
ADDRESS_VALIDATION_ERROR = 'addressValidationError',
|
|
21
|
-
ADDRESS_VALIDATION_NO_MATCH = 'addressValidationNoMatch',
|
|
22
|
-
ADDRESS_VALIDATION_FULL_POORLY_FORMATTED = 'addressValidationFullPoorlyFormatted',
|
|
23
|
-
ADDRESS_VALIDATION_PARTIAL_POORLY_FORMATTED = 'addressValidationPartialPoorlyFormatted',
|
|
24
|
-
ADDRESS_VALIDATION_FULL_OWN_ADDRESS = 'addressValidationFullOwnAddress',
|
|
25
|
-
ADDRESS_VALIDATION_PARTIAL_OWN_ADDRESS = 'addressValidationPartialOwnAddress',
|
|
26
19
|
ACCOUNT_CLEAR_FAILED = 'accountClearFailed',
|
|
27
20
|
KEYCHAIN_FETCH_ACCOUNTS = 'keychainFetchAccounts',
|
|
28
21
|
KEYCHAIN_ACCOUNT_ALREADY_EXISTS = 'keychainAccountAlreadyExists',
|
|
@@ -76,18 +76,27 @@ describe('ReviewSummaryItem', () => {
|
|
|
76
76
|
})
|
|
77
77
|
|
|
78
78
|
describe('ReviewSummaryItemContact', () => {
|
|
79
|
-
|
|
79
|
+
const renderContact = (recipient: Recipient, storeOverrides: Record<string, unknown> = {}) =>
|
|
80
|
+
render(
|
|
81
|
+
<Provider store={createMockStore(storeOverrides)}>
|
|
82
|
+
<ReviewSummaryItemContact recipient={recipient} testID="ContactItem" />
|
|
83
|
+
</Provider>
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
it('shows name as primary value and resolved short address as subtitle for phone recipients', () => {
|
|
80
87
|
const recipient = {
|
|
81
88
|
name: 'John Doe',
|
|
82
89
|
displayNumber: '+111111111',
|
|
83
90
|
e164PhoneNumber: '+222222222',
|
|
91
|
+
address: '0x0123456789012345678901234567890123456789',
|
|
84
92
|
} as Recipient
|
|
85
|
-
const tree =
|
|
93
|
+
const tree = renderContact(recipient)
|
|
86
94
|
|
|
87
95
|
expect(tree.getByTestId('ContactItem/PrimaryValue')).toHaveTextContent('John Doe', {
|
|
88
96
|
exact: false,
|
|
89
97
|
})
|
|
90
|
-
|
|
98
|
+
// Phone recipients always surface the on-chain destination on the review screen.
|
|
99
|
+
expect(tree.getByTestId('ContactItem/SecondaryValue')).toHaveTextContent('0x0123...6789', {
|
|
91
100
|
exact: false,
|
|
92
101
|
})
|
|
93
102
|
})
|
|
@@ -109,14 +118,17 @@ describe('ReviewSummaryItemContact', () => {
|
|
|
109
118
|
])(
|
|
110
119
|
'displays only $phoneNumberType phone if name is not available',
|
|
111
120
|
({ displayNumber, e164PhoneNumber, expectedDisplayedValue }) => {
|
|
112
|
-
const
|
|
113
|
-
const
|
|
121
|
+
const address = '0x0123456789012345678901234567890123456789'
|
|
122
|
+
const recipient = { displayNumber, e164PhoneNumber, address } as Recipient
|
|
123
|
+
const tree = renderContact(recipient)
|
|
114
124
|
|
|
115
125
|
expect(tree.getByTestId('ContactItem/PrimaryValue')).toHaveTextContent(
|
|
116
126
|
expectedDisplayedValue,
|
|
117
127
|
{ exact: false }
|
|
118
128
|
)
|
|
119
|
-
expect(tree.
|
|
129
|
+
expect(tree.getByTestId('ContactItem/SecondaryValue')).toHaveTextContent('0x0123...6789', {
|
|
130
|
+
exact: false,
|
|
131
|
+
})
|
|
120
132
|
}
|
|
121
133
|
)
|
|
122
134
|
|
|
@@ -124,16 +136,45 @@ describe('ReviewSummaryItemContact', () => {
|
|
|
124
136
|
const recipient = {
|
|
125
137
|
address: '0x123456789',
|
|
126
138
|
} as Recipient
|
|
127
|
-
const tree =
|
|
139
|
+
const tree = renderContact(recipient)
|
|
128
140
|
|
|
129
141
|
expect(tree.getByTestId('ContactItem/PrimaryValue')).toHaveTextContent('0x123456789', {
|
|
130
142
|
exact: false,
|
|
131
143
|
})
|
|
132
144
|
})
|
|
133
145
|
|
|
146
|
+
it('inlines the verifier with the short address for phone recipients', () => {
|
|
147
|
+
const address = '0x0123456789012345678901234567890123456789'
|
|
148
|
+
const recipient = {
|
|
149
|
+
name: 'John Doe',
|
|
150
|
+
displayNumber: '+111111111',
|
|
151
|
+
e164PhoneNumber: '+222222222',
|
|
152
|
+
address,
|
|
153
|
+
} as Recipient
|
|
154
|
+
const tree = renderContact(recipient, {
|
|
155
|
+
identity: { addressToVerifiedBy: { [address]: 'valora' } },
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
const subtitle = tree.getByTestId('ContactItem/SecondaryValue')
|
|
159
|
+
expect(subtitle).toHaveTextContent('0x0123...6789', { exact: false })
|
|
160
|
+
expect(subtitle).toHaveTextContent('Valora', { exact: false })
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
it('inlines just the verifier for address-only recipients with a known verifier', () => {
|
|
164
|
+
const address = '0x0123456789012345678901234567890123456789'
|
|
165
|
+
const recipient = { address } as Recipient
|
|
166
|
+
const tree = renderContact(recipient, {
|
|
167
|
+
identity: { addressToVerifiedBy: { [address]: 'minipay' } },
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
const subtitle = tree.getByTestId('ContactItem/SecondaryValue')
|
|
171
|
+
// No address in the subtitle — it's already in the primary slot for address-only recipients
|
|
172
|
+
expect(subtitle).toHaveTextContent('MiniPay', { exact: false })
|
|
173
|
+
})
|
|
174
|
+
|
|
134
175
|
it('logs an error if no name/phone/address exist', () => {
|
|
135
176
|
const recipient = {} as Recipient
|
|
136
|
-
const tree =
|
|
177
|
+
const tree = renderContact(recipient)
|
|
137
178
|
expect(Logger.error).toHaveBeenCalledTimes(1)
|
|
138
179
|
expect(tree.toJSON()).toBeNull()
|
|
139
180
|
})
|
|
@@ -4,6 +4,7 @@ import React, { useMemo, type ReactNode } from 'react'
|
|
|
4
4
|
import { Trans, useTranslation } from 'react-i18next'
|
|
5
5
|
import { ScrollView, StyleSheet, Text, View, type StyleProp, type TextStyle } from 'react-native'
|
|
6
6
|
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
|
|
7
|
+
import { formatShortenedAddress } from 'src/account/utils'
|
|
7
8
|
import BackButton from 'src/components/BackButton'
|
|
8
9
|
import ContactCircle from 'src/components/ContactCircle'
|
|
9
10
|
import CustomHeader from 'src/components/header/CustomHeader'
|
|
@@ -14,8 +15,10 @@ import InfoIcon from 'src/icons/InfoIcon'
|
|
|
14
15
|
import WalletIcon from 'src/icons/navigator/Wallet'
|
|
15
16
|
import PhoneIcon from 'src/icons/Phone'
|
|
16
17
|
import UserIcon from 'src/icons/User'
|
|
18
|
+
import VerifiedBadge from 'src/icons/VerifiedBadge'
|
|
17
19
|
import { LocalCurrencySymbol } from 'src/localCurrency/consts'
|
|
18
|
-
import {
|
|
20
|
+
import { type Recipient } from 'src/recipients/recipient'
|
|
21
|
+
import { useVerifierName } from 'src/recipients/verifier'
|
|
19
22
|
import colors, { type ColorValue } from 'src/styles/colors'
|
|
20
23
|
import { typeScale } from 'src/styles/fonts'
|
|
21
24
|
import { Spacing } from 'src/styles/styles'
|
|
@@ -70,7 +73,7 @@ export function ReviewSummaryItem(props: {
|
|
|
70
73
|
label: string
|
|
71
74
|
icon: ReactNode
|
|
72
75
|
primaryValue: string
|
|
73
|
-
secondaryValue?:
|
|
76
|
+
secondaryValue?: ReactNode
|
|
74
77
|
testID?: string
|
|
75
78
|
onPress?: () => void
|
|
76
79
|
}) {
|
|
@@ -96,12 +99,21 @@ export function ReviewSummaryItem(props: {
|
|
|
96
99
|
|
|
97
100
|
{!!props.secondaryValue && (
|
|
98
101
|
<View style={styles.reviewSummaryItemSecondaryValueWrapper}>
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
{typeof props.secondaryValue === 'string' ? (
|
|
103
|
+
<Text
|
|
104
|
+
style={styles.reviewSummaryItemSecondaryValue}
|
|
105
|
+
testID={`${props.testID}/SecondaryValue`}
|
|
106
|
+
>
|
|
107
|
+
{props.secondaryValue}
|
|
108
|
+
</Text>
|
|
109
|
+
) : (
|
|
110
|
+
<View
|
|
111
|
+
style={styles.reviewSummaryItemSecondaryValueContent}
|
|
112
|
+
testID={`${props.testID}/SecondaryValue`}
|
|
113
|
+
>
|
|
114
|
+
{props.secondaryValue}
|
|
115
|
+
</View>
|
|
116
|
+
)}
|
|
105
117
|
{!!props.onPress && <InfoIcon size={14} color={colors.contentSecondary} />}
|
|
106
118
|
</View>
|
|
107
119
|
)}
|
|
@@ -112,6 +124,24 @@ export function ReviewSummaryItem(props: {
|
|
|
112
124
|
)
|
|
113
125
|
}
|
|
114
126
|
|
|
127
|
+
function renderAddressAndVerifier(
|
|
128
|
+
shortAddress: string | undefined,
|
|
129
|
+
verifierName: string | undefined
|
|
130
|
+
): ReactNode {
|
|
131
|
+
if (!shortAddress && !verifierName) return undefined
|
|
132
|
+
return (
|
|
133
|
+
<>
|
|
134
|
+
{!!shortAddress && <Text style={styles.reviewSummaryItemSecondaryValue}>{shortAddress}</Text>}
|
|
135
|
+
{!!verifierName && (
|
|
136
|
+
<>
|
|
137
|
+
<VerifiedBadge color={colors.contentSecondary} />
|
|
138
|
+
<Text style={styles.reviewSummaryItemSecondaryValue}>{verifierName}</Text>
|
|
139
|
+
</>
|
|
140
|
+
)}
|
|
141
|
+
</>
|
|
142
|
+
)
|
|
143
|
+
}
|
|
144
|
+
|
|
115
145
|
export function ReviewSummaryItemContact({
|
|
116
146
|
testID,
|
|
117
147
|
recipient,
|
|
@@ -120,20 +150,30 @@ export function ReviewSummaryItemContact({
|
|
|
120
150
|
recipient: Recipient
|
|
121
151
|
}) {
|
|
122
152
|
const { t } = useTranslation()
|
|
153
|
+
const verifierName = useVerifierName(recipient.address)
|
|
123
154
|
const contact = useMemo(() => {
|
|
124
155
|
const phone = recipient.displayNumber || recipient.e164PhoneNumber
|
|
156
|
+
// For recipients with a phone mapping, surface the resolved on-chain address (and verifier,
|
|
157
|
+
// if known) as a subtitle so the user can verify the actual destination they are signing.
|
|
158
|
+
const shortAddress = recipient.address ? formatShortenedAddress(recipient.address) : undefined
|
|
159
|
+
const phoneSubtitle = renderAddressAndVerifier(shortAddress, verifierName)
|
|
160
|
+
|
|
125
161
|
if (recipient.name) {
|
|
126
|
-
return { title: recipient.name, subtitle:
|
|
162
|
+
return { title: recipient.name, subtitle: phoneSubtitle, icon: UserIcon }
|
|
127
163
|
}
|
|
128
164
|
|
|
129
165
|
if (phone) {
|
|
130
|
-
return { title: phone, icon: PhoneIcon }
|
|
166
|
+
return { title: phone, subtitle: phoneSubtitle, icon: PhoneIcon }
|
|
131
167
|
}
|
|
132
168
|
|
|
133
169
|
if (recipient.address) {
|
|
134
|
-
return {
|
|
170
|
+
return {
|
|
171
|
+
title: recipient.address,
|
|
172
|
+
subtitle: renderAddressAndVerifier(undefined, verifierName),
|
|
173
|
+
icon: WalletIcon,
|
|
174
|
+
}
|
|
135
175
|
}
|
|
136
|
-
}, [recipient])
|
|
176
|
+
}, [recipient, verifierName])
|
|
137
177
|
|
|
138
178
|
// This should never happen
|
|
139
179
|
if (!contact) {
|
|
@@ -478,6 +518,12 @@ const styles = StyleSheet.create({
|
|
|
478
518
|
gap: Spacing.Smallest8,
|
|
479
519
|
alignItems: 'center',
|
|
480
520
|
},
|
|
521
|
+
reviewSummaryItemSecondaryValueContent: {
|
|
522
|
+
flexDirection: 'row',
|
|
523
|
+
gap: Spacing.Tiny4,
|
|
524
|
+
alignItems: 'center',
|
|
525
|
+
flexShrink: 1,
|
|
526
|
+
},
|
|
481
527
|
reviewDetails: {
|
|
482
528
|
gap: Spacing.Regular16,
|
|
483
529
|
width: '100%',
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import * as React from 'react'
|
|
2
|
+
import Svg, { Path } from 'react-native-svg'
|
|
3
|
+
import colors, { ColorValue } from 'src/styles/colors'
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
size?: number
|
|
7
|
+
color?: ColorValue
|
|
8
|
+
testID?: string
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function VerifiedBadge({ size = 14, color = colors.contentSecondary, testID }: Props) {
|
|
12
|
+
return (
|
|
13
|
+
<Svg width={size} height={size} viewBox="0 0 22 22" fill="none" testID={testID}>
|
|
14
|
+
<Path
|
|
15
|
+
d="M20.396 11c-.018-.646-.215-1.275-.57-1.816-.354-.54-.852-.972-1.438-1.246.223-.607.27-1.264.14-1.897-.131-.634-.437-1.218-.882-1.687-.47-.445-1.053-.75-1.687-.882-.633-.13-1.29-.083-1.897.14-.273-.587-.704-1.086-1.245-1.44S11.647 1.62 11 1.604c-.646.017-1.273.213-1.813.568s-.969.854-1.24 1.44c-.608-.223-1.267-.272-1.902-.14-.635.13-1.22.436-1.69.882-.445.47-.749 1.055-.878 1.688-.13.633-.08 1.29.144 1.896-.587.274-1.087.705-1.443 1.245-.356.54-.555 1.17-.574 1.817.02.647.218 1.276.574 1.817.356.54.856.972 1.443 1.245-.224.606-.274 1.263-.144 1.896.13.634.433 1.218.877 1.688.47.443 1.054.747 1.687.878.633.132 1.29.084 1.897-.138.274.586.705 1.084 1.246 1.439.54.354 1.17.551 1.816.569.647-.016 1.276-.213 1.817-.567s.972-.854 1.245-1.44c.604.239 1.266.296 1.903.164.636-.132 1.22-.447 1.68-.907.46-.46.776-1.044.908-1.681s.075-1.299-.165-1.903c.586-.274 1.084-.705 1.439-1.246.354-.54.551-1.17.569-1.816Z"
|
|
16
|
+
stroke={color}
|
|
17
|
+
strokeWidth="1.2"
|
|
18
|
+
/>
|
|
19
|
+
<Path
|
|
20
|
+
d="M9.662 14.85 6.233 11.42l1.293-1.302 2.072 2.072 4.4-4.794 1.347 1.246-5.683 6.208Z"
|
|
21
|
+
fill={color}
|
|
22
|
+
/>
|
|
23
|
+
</Svg>
|
|
24
|
+
)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export default React.memo(VerifiedBadge)
|
package/src/identity/actions.ts
CHANGED
|
@@ -2,28 +2,19 @@ import {
|
|
|
2
2
|
AddressToDisplayNameType,
|
|
3
3
|
AddressToE164NumberType,
|
|
4
4
|
AddressToVerifiedByType,
|
|
5
|
-
AddressValidationType,
|
|
6
5
|
E164NumberToAddressType,
|
|
7
6
|
} from 'src/identity/reducer'
|
|
8
7
|
import { ImportContactsStatus } from 'src/identity/types'
|
|
9
|
-
import { Recipient } from 'src/recipients/recipient'
|
|
10
|
-
import { type E164Number } from 'src/utils/io'
|
|
11
8
|
|
|
12
9
|
export enum Actions {
|
|
13
10
|
UPDATE_E164_PHONE_NUMBER_ADDRESSES = 'IDENTITY/UPDATE_E164_PHONE_NUMBER_ADDRESSES',
|
|
14
11
|
UPDATE_KNOWN_ADDRESSES = 'IDENTITY/UPDATE_KNOWN_ADDRESSES',
|
|
15
12
|
FETCH_ADDRESSES_AND_VALIDATION_STATUS = 'IDENTITY/FETCH_ADDRESSES_AND_VALIDATION_STATUS',
|
|
16
|
-
END_FETCHING_ADDRESSES = 'IDENTITY/END_FETCHING_ADDRESSES',
|
|
17
13
|
IMPORT_CONTACTS = 'IDENTITY/IMPORT_CONTACTS',
|
|
18
14
|
UPDATE_IMPORT_CONTACT_PROGRESS = 'IDENTITY/UPDATE_IMPORT_CONTACT_PROGRESS',
|
|
19
15
|
CANCEL_IMPORT_CONTACTS = 'IDENTITY/CANCEL_IMPORT_CONTACTS',
|
|
20
16
|
END_IMPORT_CONTACTS = 'IDENTITY/END_IMPORT_CONTACTS',
|
|
21
|
-
VALIDATE_RECIPIENT_ADDRESS = 'IDENTITY/VALIDATE_RECIPIENT_ADDRESS',
|
|
22
|
-
VALIDATE_RECIPIENT_ADDRESS_SUCCESS = 'IDENTITY/VALIDATE_RECIPIENT_ADDRESS_SUCCESS',
|
|
23
|
-
VALIDATE_RECIPIENT_ADDRESS_RESET = 'IDENTITY/VALIDATE_RECIPIENT_ADDRESS_RESET',
|
|
24
|
-
REQUIRE_SECURE_SEND = 'IDENTITY/REQUIRE_SECURE_SEND',
|
|
25
17
|
FETCH_ADDRESS_VERIFICATION_STATUS = 'IDENTITY/FETCH_ADDRESS_VERIFICATION_STATUS',
|
|
26
|
-
ADDRESS_VERIFICATION_STATUS_RECEIVED = 'IDENTITY/ADDRESS_VERIFICATION_STATUS_RECEIVED',
|
|
27
18
|
CONTACTS_SAVED = 'IDENTITY/CONTACTS_SAVED',
|
|
28
19
|
STORED_PASSWORD_REFRESHED = 'IDENTITY/STORED_PASSWORD_REFRESHED',
|
|
29
20
|
}
|
|
@@ -43,13 +34,6 @@ export interface UpdateKnownAddressesAction {
|
|
|
43
34
|
export interface FetchAddressesAndValidateAction {
|
|
44
35
|
type: Actions.FETCH_ADDRESSES_AND_VALIDATION_STATUS
|
|
45
36
|
e164Number: string
|
|
46
|
-
requesterAddress?: string
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export interface EndFetchingAddressesAction {
|
|
50
|
-
type: Actions.END_FETCHING_ADDRESSES
|
|
51
|
-
e164Number: string
|
|
52
|
-
lastFetchSuccessful: boolean
|
|
53
37
|
}
|
|
54
38
|
|
|
55
39
|
export interface ImportContactsAction {
|
|
@@ -68,42 +52,11 @@ export interface EndImportContactsAction {
|
|
|
68
52
|
success: boolean
|
|
69
53
|
}
|
|
70
54
|
|
|
71
|
-
export interface ValidateRecipientAddressAction {
|
|
72
|
-
type: Actions.VALIDATE_RECIPIENT_ADDRESS
|
|
73
|
-
userInputOfFullAddressOrLastFourDigits: string
|
|
74
|
-
addressValidationType: AddressValidationType
|
|
75
|
-
recipient: Recipient
|
|
76
|
-
requesterAddress?: string
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export interface ValidateRecipientAddressSuccessAction {
|
|
80
|
-
type: Actions.VALIDATE_RECIPIENT_ADDRESS_SUCCESS
|
|
81
|
-
e164Number: string
|
|
82
|
-
validatedAddress: string
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export interface ValidateRecipientAddressResetAction {
|
|
86
|
-
type: Actions.VALIDATE_RECIPIENT_ADDRESS_RESET
|
|
87
|
-
e164Number: string
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export interface RequireSecureSendAction {
|
|
91
|
-
type: Actions.REQUIRE_SECURE_SEND
|
|
92
|
-
e164Number: E164Number
|
|
93
|
-
addressValidationType: AddressValidationType
|
|
94
|
-
}
|
|
95
|
-
|
|
96
55
|
export interface FetchAddressVerificationAction {
|
|
97
56
|
type: Actions.FETCH_ADDRESS_VERIFICATION_STATUS
|
|
98
57
|
address: string
|
|
99
58
|
}
|
|
100
59
|
|
|
101
|
-
export interface AddressVerificationStatusReceivedAction {
|
|
102
|
-
type: Actions.ADDRESS_VERIFICATION_STATUS_RECEIVED
|
|
103
|
-
address: string
|
|
104
|
-
addressVerified: boolean
|
|
105
|
-
}
|
|
106
|
-
|
|
107
60
|
interface ContactsSavedAction {
|
|
108
61
|
type: Actions.CONTACTS_SAVED
|
|
109
62
|
hash: string
|
|
@@ -119,33 +72,14 @@ export type ActionTypes =
|
|
|
119
72
|
| ImportContactsAction
|
|
120
73
|
| UpdateImportContactProgress
|
|
121
74
|
| EndImportContactsAction
|
|
122
|
-
| ValidateRecipientAddressAction
|
|
123
|
-
| ValidateRecipientAddressSuccessAction
|
|
124
|
-
| ValidateRecipientAddressResetAction
|
|
125
|
-
| RequireSecureSendAction
|
|
126
75
|
| FetchAddressesAndValidateAction
|
|
127
|
-
| EndFetchingAddressesAction
|
|
128
76
|
| FetchAddressVerificationAction
|
|
129
|
-
| AddressVerificationStatusReceivedAction
|
|
130
77
|
| ContactsSavedAction
|
|
131
78
|
| StoredPasswordRefreshedAction
|
|
132
79
|
|
|
133
|
-
export const fetchAddressesAndValidate = (
|
|
134
|
-
e164Number: string,
|
|
135
|
-
requesterAddress?: string
|
|
136
|
-
): FetchAddressesAndValidateAction => ({
|
|
80
|
+
export const fetchAddressesAndValidate = (e164Number: string): FetchAddressesAndValidateAction => ({
|
|
137
81
|
type: Actions.FETCH_ADDRESSES_AND_VALIDATION_STATUS,
|
|
138
82
|
e164Number,
|
|
139
|
-
requesterAddress,
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
export const addressVerificationStatusReceived = (
|
|
143
|
-
address: string,
|
|
144
|
-
addressVerified: boolean
|
|
145
|
-
): AddressVerificationStatusReceivedAction => ({
|
|
146
|
-
type: Actions.ADDRESS_VERIFICATION_STATUS_RECEIVED,
|
|
147
|
-
address,
|
|
148
|
-
addressVerified,
|
|
149
83
|
})
|
|
150
84
|
|
|
151
85
|
export const fetchAddressVerification = (address: string): FetchAddressVerificationAction => ({
|
|
@@ -153,15 +87,6 @@ export const fetchAddressVerification = (address: string): FetchAddressVerificat
|
|
|
153
87
|
address,
|
|
154
88
|
})
|
|
155
89
|
|
|
156
|
-
export const endFetchingAddresses = (
|
|
157
|
-
e164Number: string,
|
|
158
|
-
lastFetchSuccessful: boolean
|
|
159
|
-
): EndFetchingAddressesAction => ({
|
|
160
|
-
type: Actions.END_FETCHING_ADDRESSES,
|
|
161
|
-
e164Number,
|
|
162
|
-
lastFetchSuccessful,
|
|
163
|
-
})
|
|
164
|
-
|
|
165
90
|
export const updateE164PhoneNumberAddresses = (
|
|
166
91
|
e164NumberToAddress: E164NumberToAddressType,
|
|
167
92
|
addressToE164Number: AddressToE164NumberType,
|
|
@@ -200,44 +125,6 @@ export const endImportContacts = (success: boolean): EndImportContactsAction =>
|
|
|
200
125
|
success,
|
|
201
126
|
})
|
|
202
127
|
|
|
203
|
-
export const validateRecipientAddress = (
|
|
204
|
-
userInputOfFullAddressOrLastFourDigits: string,
|
|
205
|
-
addressValidationType: AddressValidationType,
|
|
206
|
-
recipient: Recipient,
|
|
207
|
-
requesterAddress?: string
|
|
208
|
-
): ValidateRecipientAddressAction => ({
|
|
209
|
-
type: Actions.VALIDATE_RECIPIENT_ADDRESS,
|
|
210
|
-
userInputOfFullAddressOrLastFourDigits,
|
|
211
|
-
addressValidationType,
|
|
212
|
-
recipient,
|
|
213
|
-
requesterAddress,
|
|
214
|
-
})
|
|
215
|
-
|
|
216
|
-
export const validateRecipientAddressSuccess = (
|
|
217
|
-
e164Number: E164Number,
|
|
218
|
-
validatedAddress: string
|
|
219
|
-
): ValidateRecipientAddressSuccessAction => ({
|
|
220
|
-
type: Actions.VALIDATE_RECIPIENT_ADDRESS_SUCCESS,
|
|
221
|
-
e164Number,
|
|
222
|
-
validatedAddress,
|
|
223
|
-
})
|
|
224
|
-
|
|
225
|
-
export const validateRecipientAddressReset = (
|
|
226
|
-
e164Number: E164Number
|
|
227
|
-
): ValidateRecipientAddressResetAction => ({
|
|
228
|
-
type: Actions.VALIDATE_RECIPIENT_ADDRESS_RESET,
|
|
229
|
-
e164Number,
|
|
230
|
-
})
|
|
231
|
-
|
|
232
|
-
export const requireSecureSend = (
|
|
233
|
-
e164Number: E164Number,
|
|
234
|
-
addressValidationType: AddressValidationType
|
|
235
|
-
): RequireSecureSendAction => ({
|
|
236
|
-
type: Actions.REQUIRE_SECURE_SEND,
|
|
237
|
-
e164Number,
|
|
238
|
-
addressValidationType,
|
|
239
|
-
})
|
|
240
|
-
|
|
241
128
|
export const contactsSaved = (hash: string): ContactsSavedAction => ({
|
|
242
129
|
type: Actions.CONTACTS_SAVED,
|
|
243
130
|
hash,
|