Skip to content

Commit

Permalink
feat(wallet): add quickpay (#2357)
Browse files Browse the repository at this point in the history
  • Loading branch information
pwltr authored Nov 25, 2024
1 parent b46b108 commit 8dcc70b
Show file tree
Hide file tree
Showing 43 changed files with 1,532 additions and 857 deletions.
5 changes: 3 additions & 2 deletions __tests__/todos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
transferClosingChannelTodo,
supportTodo,
inviteTodo,
// fastpayTodo,
quickpayTodo,
// discountTodo,
} from '../src/store/shapes/todos';
import { createNewWallet } from '../src/utils/startup';
Expand Down Expand Up @@ -50,7 +50,7 @@ describe('Todos selector', () => {
buyBitcoinTodo,
supportTodo,
inviteTodo,
// fastpayTodo,
quickpayTodo,
slashtagsProfileTodo,
// discountTodo,
]);
Expand Down Expand Up @@ -104,6 +104,7 @@ describe('Todos selector', () => {
invite: +new Date(),
lightning: +new Date(),
pin: +new Date(),
quickpay: +new Date(),
slashtagsProfile: +new Date(),
support: +new Date(),
};
Expand Down
76 changes: 61 additions & 15 deletions e2e/send.e2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ d('Send', () => {
// - send to onchain address
// - send to lightning invoice
// - send to unified invoice
// - quickpay to lightning invoice
// - quickpay to unified invoice

if (checkComplete('send-2')) {
return;
Expand Down Expand Up @@ -243,9 +245,9 @@ d('Send', () => {

// receive lightning funds
await element(by.id('Receive')).tap();
let { label: invoice1 } = await element(by.id('QRCode')).getAttributes();
invoice1 = invoice1.replaceAll(/bitcoin.*=/gi, '').toLowerCase();
await lnd.sendPaymentSync({ paymentRequest: invoice1, amt: 50000 });
let { label: receive } = await element(by.id('QRCode')).getAttributes();
receive = receive.replaceAll(/bitcoin.*=/gi, '').toLowerCase();
await lnd.sendPaymentSync({ paymentRequest: receive, amt: 50000 });
await waitFor(element(by.id('NewTxPrompt')))
.toBeVisible()
.withTimeout(10000);
Expand Down Expand Up @@ -278,8 +280,8 @@ d('Send', () => {
.withTimeout(10000);

// send to lightning invoice
const { paymentRequest: lnInvoice1 } = await lnd.addInvoice();
await enterAddress(lnInvoice1);
const { paymentRequest: invoice1 } = await lnd.addInvoice();
await enterAddress(invoice1);
await expect(element(by.id('AssetButton-spending'))).toBeVisible();
await element(by.id('N1').withAncestor(by.id('SendAmountNumberPad'))).tap();
await element(
Expand All @@ -298,11 +300,9 @@ d('Send', () => {
.withTimeout(10000);

// send to unified invoice w/ amount
const { paymentRequest: lnInvoice2 } = await lnd.addInvoice({
value: 10000,
});
const { paymentRequest: invoice2 } = await lnd.addInvoice({ value: 10000 });
const unified1 = encode(onchainAddress, {
lightning: lnInvoice2,
lightning: invoice2,
amount: 10000,
});

Expand All @@ -319,11 +319,11 @@ d('Send', () => {
.withTimeout(10000);

// send to unified invoice w/ amount exceeding balance(s)
const { paymentRequest: lnInvoice3 } = await lnd.addInvoice({
const { paymentRequest: invoice3 } = await lnd.addInvoice({
value: 200000,
});
const unified2 = encode(onchainAddress, {
lightning: lnInvoice3,
lightning: invoice3,
amount: 200000,
});

Expand Down Expand Up @@ -369,8 +369,8 @@ d('Send', () => {
.withTimeout(10000);

// send to unified invoice w/o amount (lightning)
const { paymentRequest: lnInvoice4 } = await lnd.addInvoice();
const unified4 = encode(onchainAddress, { lightning: lnInvoice4 });
const { paymentRequest: invoice4 } = await lnd.addInvoice();
const unified4 = encode(onchainAddress, { lightning: invoice4 });

await enterAddress(unified4);
// max amount (lightning)
Expand All @@ -396,8 +396,8 @@ d('Send', () => {
.withTimeout(10000);

// send to unified invoice w/o amount (switch to onchain)
const { paymentRequest: lnInvoice5 } = await lnd.addInvoice();
const unified5 = encode(onchainAddress, { lightning: lnInvoice5 });
const { paymentRequest: invoice5 } = await lnd.addInvoice();
const unified5 = encode(onchainAddress, { lightning: invoice5 });

await enterAddress(unified5);

Expand Down Expand Up @@ -433,6 +433,52 @@ d('Send', () => {
.toHaveText('78 506')
.withTimeout(10000);

// send to lightning invoice w/ amount (quickpay)
const { paymentRequest: invoice6 } = await lnd.addInvoice({ value: 1000 });

// enable quickpay
await element(by.id('Settings')).tap();
await element(by.id('GeneralSettings')).tap();
await element(by.id('QuickpaySettings')).tap();
await element(by.id('QuickpayIntro-button')).tap();
await element(by.id('QuickpayToggle')).tap();
await element(by.id('NavigationClose')).tap();

await enterAddress(invoice6);
await waitFor(element(by.id('SendSuccess')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('Close')).tap();
await waitFor(
element(by.id('MoneyText').withAncestor(by.id('TotalBalance'))),
)
.toHaveText('77 506')
.withTimeout(10000);

// send to unified invoice w/ amount (quickpay)
const { paymentRequest: invoice7 } = await lnd.addInvoice({ value: 1000 });
const unified7 = encode(onchainAddress, {
lightning: invoice7,
amount: 1000,
});

await enterAddress(unified7);
await waitFor(element(by.id('SendSuccess')))
.toBeVisible()
.withTimeout(10000);
await element(by.id('Close')).tap();
await waitFor(
element(by.id('MoneyText').withAncestor(by.id('TotalBalance'))),
)
.toHaveText('76 506')
.withTimeout(10000);

// send to lightning invoice w/ amount (skip quickpay for large amounts)
const { paymentRequest: invoice8 } = await lnd.addInvoice({ value: 10000 });
await enterAddress(invoice8);
await expect(element(by.id('ReviewAmount'))).toBeVisible();
await element(by.id('SendSheet')).swipe('down');

markComplete('send-2');
});
});
Binary file added src/assets/illustrations/coin-stack-4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/illustrations/fast-forward.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
13 changes: 11 additions & 2 deletions src/components/HourglassSpinner.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import React, { ReactElement } from 'react';
import { View, StyleSheet, StyleProp, ViewStyle, Image } from 'react-native';
import {
View,
StyleSheet,
StyleProp,
ViewStyle,
Image,
ImageSourcePropType,
} from 'react-native';
import { Easing, withRepeat, withTiming } from 'react-native-reanimated';

import { AnimatedView } from '../styles/components';
Expand All @@ -8,9 +15,11 @@ import { __E2E__ } from '../constants/env';
const imageSrc = require('../assets/illustrations/hourglass.png');

const HourglassSpinner = ({
image = imageSrc,
imageSize = 256,
style,
}: {
image?: ImageSourcePropType;
imageSize?: number;
style?: StyleProp<ViewStyle>;
}): ReactElement => {
Expand Down Expand Up @@ -47,7 +56,7 @@ const HourglassSpinner = ({
color="transparent"
testID="HourglassSpinner">
<View style={[styles.imageContainer, { width: imageSize }]}>
<Image style={styles.image} source={imageSrc} />
<Image style={styles.image} source={image} />
</View>
</AnimatedView>
);
Expand Down
98 changes: 3 additions & 95 deletions src/components/LightningSyncing.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import React, { ReactElement, useEffect, useState } from 'react';
import { Image, StyleProp, StyleSheet, View, ViewStyle } from 'react-native';
import Animated, {
import {
Easing,
cancelAnimation,
runOnJS,
useSharedValue,
withDelay,
withRepeat,
withSequence,
withTiming,
} from 'react-native-reanimated';
import { useTranslation } from 'react-i18next';
Expand All @@ -17,13 +15,12 @@ import { AnimatedView } from '../styles/components';
import SafeAreaInset from './SafeAreaInset';
import BottomSheetNavigationHeader from './BottomSheetNavigationHeader';
import GradientView from './GradientView';
import SyncSpinner from './SyncSpinner';
import { useAppSelector } from '../hooks/redux';
import { isLDKReadySelector } from '../store/reselect/ui';
import { __E2E__ } from '../constants/env';

const imageSrc = require('../assets/illustrations/lightning.png');
const imageSyncSmall = require('../assets/illustrations/ln-sync-small.png');
const imageSyncLarge = require('../assets/illustrations/ln-sync-large.png');

const LightningSyncing = ({
title,
Expand Down Expand Up @@ -69,62 +66,6 @@ const LightningSyncing = ({
return <></>;
}

const animationLarge = (): { initialValues: {}; animations: {} } => {
'worklet';
const initialValues = { transform: [{ rotate: '0deg' }] };
const animations = {
transform: [
{
rotate: withRepeat(
withSequence(
withTiming('-180deg', {
duration: 1500,
easing: Easing.inOut(Easing.ease),
}),
withDelay(
100,
withTiming('-360deg', {
duration: 1500,
easing: Easing.inOut(Easing.ease),
}),
),
),
-1,
),
},
],
};
return { initialValues, animations };
};

const animationSmall = (): { initialValues: {}; animations: {} } => {
'worklet';
const initialValues = { transform: [{ rotate: '0deg' }] };
const animations = {
transform: [
{
rotate: withRepeat(
withSequence(
withTiming('180deg', {
duration: 1500,
easing: Easing.inOut(Easing.ease),
}),
withDelay(
100,
withTiming('360deg', {
duration: 1500,
easing: Easing.inOut(Easing.ease),
}),
),
),
-1,
),
},
],
};
return { initialValues, animations };
};

return (
<AnimatedView
style={[style, { opacity: rootOpacity }]}
Expand All @@ -135,18 +76,7 @@ const LightningSyncing = ({
<BodyM color="secondary">{t('wait_text_top')}</BodyM>

<View style={styles.imageContainer}>
<View style={styles.animation}>
<Animated.Image
style={styles.circleSmall}
source={imageSyncSmall}
entering={__E2E__ ? undefined : animationSmall}
/>
<Animated.Image
style={styles.circleLarge}
source={imageSyncLarge}
entering={__E2E__ ? undefined : animationLarge}
/>
</View>
<SyncSpinner />
<Image style={styles.image} source={imageSrc} />
</View>

Expand Down Expand Up @@ -182,28 +112,6 @@ const styles = StyleSheet.create({
flex: 1,
resizeMode: 'contain',
},
animation: {
justifyContent: 'center',
alignItems: 'center',
alignSelf: 'center',
position: 'absolute',
width: 311,
aspectRatio: 1,
},
circleSmall: {
flex: 1,
position: 'absolute',
resizeMode: 'contain',
width: 207,
aspectRatio: 1,
},
circleLarge: {
flex: 1,
position: 'absolute',
resizeMode: 'contain',
width: 311,
aspectRatio: 1,
},
bottom: {
textAlign: 'center',
marginTop: 'auto',
Expand Down
Loading

0 comments on commit 8dcc70b

Please sign in to comment.