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

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.
@@ -2269,7 +2269,8 @@
2269
2269
  "insufficientBalanceWarning": {
2270
2270
  "title": "You need more {{tokenSymbol}}",
2271
2271
  "description": "Insufficient {{tokenSymbol}} balance. Try a smaller amount or choose a different asset."
2272
- }
2272
+ },
2273
+ "miniPayFilterChip": "MiniPay supported"
2273
2274
  },
2274
2275
  "jumpstartIntro": {
2275
2276
  "title": "Share crypto like a text",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wallet-stack",
3
- "version": "1.0.0-alpha.130",
3
+ "version": "1.0.0-alpha.131",
4
4
  "author": "Valora Inc",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -258,7 +258,7 @@ describe('SendEnterAmount', () => {
258
258
  </Provider>
259
259
  )
260
260
 
261
- expect(getByText('MiniPay')).toBeTruthy()
261
+ expect(getByText('sendEnterAmountScreen.miniPayFilterChip')).toBeTruthy()
262
262
 
263
263
  const tokenBottomSheet = getAllByTestId('TokenBottomSheet')[0]
264
264
  const tokens = within(tokenBottomSheet).getAllByTestId('TokenBalanceItem')
@@ -291,7 +291,7 @@ describe('SendEnterAmount', () => {
291
291
  </Provider>
292
292
  )
293
293
 
294
- fireEvent.press(getByText('MiniPay'))
294
+ fireEvent.press(getByText('sendEnterAmountScreen.miniPayFilterChip'))
295
295
 
296
296
  const tokenBottomSheet = getAllByTestId('TokenBottomSheet')[0]
297
297
  const tokens = within(tokenBottomSheet).getAllByTestId('TokenBalanceItem')
@@ -305,7 +305,7 @@ describe('SendEnterAmount', () => {
305
305
  </Provider>
306
306
  )
307
307
 
308
- expect(queryByText('MiniPay')).toBeFalsy()
308
+ expect(queryByText('sendEnterAmountScreen.miniPayFilterChip')).toBeFalsy()
309
309
  })
310
310
 
311
311
  it('should include isMiniPayRecipient in send_amount_continue analytics', async () => {
@@ -2,6 +2,7 @@ import Clipboard from '@react-native-clipboard/clipboard'
2
2
  import { act, fireEvent, render, waitFor } from '@testing-library/react-native'
3
3
  import * as React from 'react'
4
4
  import { Provider } from 'react-redux'
5
+ import Share from 'react-native-share'
5
6
  import AppAnalytics from 'src/analytics/AppAnalytics'
6
7
  import { SendEvents } from 'src/analytics/Events'
7
8
  import { SendOrigin } from 'src/analytics/types'
@@ -351,6 +352,77 @@ describe('SendSelectRecipient', () => {
351
352
  expect(getByTestId('SendOrInviteButton')).toBeTruthy()
352
353
  })
353
354
 
355
+ it('opens the platform share sheet and tracks the press analytic before awaiting when inviting an unverified phone number', async () => {
356
+ const shareUrl = 'https://example.test/invite'
357
+ jest.mocked(getAppConfig).mockReturnValue({
358
+ displayName: 'Test App',
359
+ deepLinkUrlScheme: 'testapp',
360
+ registryName: 'test',
361
+ experimental: {
362
+ phoneNumberVerification: true,
363
+ inviteFriends: { shareUrl },
364
+ },
365
+ })
366
+ jest
367
+ .mocked(getRecipientVerificationStatus)
368
+ .mockReturnValue(RecipientVerificationStatus.UNVERIFIED)
369
+
370
+ let resolveShare!: (value: {
371
+ success: boolean
372
+ dismissedAction: boolean
373
+ message: string
374
+ }) => void
375
+ const sharePromise = new Promise<{
376
+ success: boolean
377
+ dismissedAction: boolean
378
+ message: string
379
+ }>((resolve) => {
380
+ resolveShare = resolve
381
+ })
382
+ jest.mocked(Share.open).mockReturnValueOnce(sharePromise)
383
+
384
+ const store = createMockStore(storeWithPhoneVerified)
385
+
386
+ const { getByTestId } = render(
387
+ <Provider store={store}>
388
+ <SendSelectRecipient {...mockScreenProps({})} />
389
+ </Provider>
390
+ )
391
+
392
+ const searchInput = getByTestId('SendSelectRecipientSearchInput')
393
+ await act(() => {
394
+ fireEvent.changeText(searchInput, mockE164Number2Invite)
395
+ })
396
+ await act(() => {
397
+ fireEvent.press(getByTestId('RecipientItem'))
398
+ })
399
+
400
+ const button = getByTestId('SendOrInviteButton')
401
+ expect(button).toHaveTextContent('sendSelectRecipient.buttons.invite', { exact: false })
402
+
403
+ await act(() => {
404
+ fireEvent.press(button)
405
+ })
406
+
407
+ // Analytics fires synchronously on tap, before the share sheet resolves
408
+ expect(AppAnalytics.track).toHaveBeenCalledWith(SendEvents.send_select_recipient_invite_press, {
409
+ recipientType: RecipientType.PhoneNumber,
410
+ })
411
+ expect(Share.open).toHaveBeenCalledWith(
412
+ expect.objectContaining({
413
+ message: expect.stringContaining(shareUrl),
414
+ url: shareUrl,
415
+ failOnCancel: false,
416
+ })
417
+ )
418
+ expect(navigate).not.toHaveBeenCalled()
419
+
420
+ await act(async () => {
421
+ resolveShare({ success: true, dismissedAction: false, message: '' })
422
+ await sharePromise
423
+ })
424
+ })
425
+
354
426
  it('shows unknown address info text when searching for unknown address after making address verification request', async () => {
355
427
  jest
356
428
  .mocked(getRecipientVerificationStatus)
@@ -4,7 +4,7 @@ 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, { ShareSingleOptions, Social } from 'react-native-share'
7
+ import Share from 'react-native-share'
8
8
  import { isAddressFormat } from 'src/account/utils'
9
9
  import AppAnalytics from 'src/analytics/AppAnalytics'
10
10
  import { SendEvents } from 'src/analytics/Events'
@@ -326,22 +326,24 @@ function SendSelectRecipient({ route }: Props) {
326
326
  // Invites
327
327
  if (shouldInviteRecipient) {
328
328
  if (!shareUrl) {
329
- Logger.warn('SendSelectRecipient', 'No share URL found for invite with SMS')
329
+ Logger.warn('SendSelectRecipient', 'No share URL found for invite')
330
330
  return
331
331
  }
332
332
 
333
- const shareOptions: ShareSingleOptions = {
334
- social: Social.Sms,
335
- recipient: recipient.e164PhoneNumber,
336
- message: t('inviteWithSmsMessage.shareMessage', { shareUrl }),
337
- }
338
-
339
- await Share.shareSingle(shareOptions)
340
-
341
333
  AppAnalytics.track(SendEvents.send_select_recipient_invite_press, {
342
334
  recipientType: recipient.recipientType,
343
335
  })
344
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
+
345
347
  return
346
348
  }
347
349
 
@@ -1,3 +1,4 @@
1
+ import { useTranslation } from 'react-i18next'
1
2
  import { BooleanFilterChip } from 'src/components/FilterChipsCarousel'
2
3
  import { getDynamicConfigParams } from 'src/statsig'
3
4
  import { DynamicConfigs } from 'src/statsig/constants'
@@ -18,6 +19,8 @@ export default function useSendFilterChips({
18
19
  filterChips: BooleanFilterChip<TokenBalance>[]
19
20
  defaultToken: TokenBalance | undefined
20
21
  } {
22
+ const { t } = useTranslation()
23
+
21
24
  const { miniPayTokenIds: configTokenIds } = getDynamicConfigParams(
22
25
  DynamicConfigs[StatsigDynamicConfigs.SEND_CONFIG]
23
26
  )
@@ -27,7 +30,7 @@ export default function useSendFilterChips({
27
30
  ? [
28
31
  {
29
32
  id: 'minipay',
30
- name: 'MiniPay',
33
+ name: t('sendEnterAmountScreen.miniPayFilterChip'),
31
34
  filterFn: (token: TokenBalance) => miniPayTokenIds.includes(token.tokenId),
32
35
  isSelected: true,
33
36
  },