Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: handle lnurl success action #3362

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
788c041
feat(lnurl): implement successAction to display message on send-bitco…
esaugomez31 Feb 14, 2025
9f9479f
revert(config): restore default endOfLine setting to 'lf'
esaugomez31 Feb 14, 2025
1a77f0f
fix(lnurl): ensure successAction updates persist in confirmation screen
esaugomez31 Feb 14, 2025
cba0563
Merge branch 'main' into feat--handle-lnurl-success-action
esaugomez31 Feb 17, 2025
9046c4f
chore: improve code structure and optimization
esaugomez31 Feb 21, 2025
da0627a
feat: url success action - LUD-09
esaugomez31 Feb 21, 2025
e655e1c
revert: restore previous duration for Send Bitcoin Completed screen
esaugomez31 Feb 21, 2025
8645fd8
feat: LUD-10 AES implemented using decipherAES
esaugomez31 Feb 24, 2025
8633633
test: send bitcoin completed screen
esaugomez31 Feb 24, 2025
7c06888
fix(success-action): open link button UI
esaugomez31 Feb 26, 2025
a4fe8b0
test(send bitcoin screen): LUD-09 tests (message|url|url + message)
esaugomez31 Feb 26, 2025
d6626cb
fix: graphql lnInvoicePaymentSend transaction object added
esaugomez31 Feb 26, 2025
ad8a973
fix(tsc): preimage validated to SettlementViaLn type
esaugomez31 Feb 27, 2025
6b7c942
chore: code refatoring in the send bitcoin completed and send bitcoin…
esaugomez31 Feb 28, 2025
728fecd
test: LUD-10 (AES) to send bitcoin completed screen
esaugomez31 Feb 28, 2025
a10bb25
feat: description support added to LUD-10 AES
esaugomez31 Feb 28, 2025
7dee4af
feat(send-bitcoin-completed): disable auto-redirect after successful …
esaugomez31 Feb 28, 2025
ff70e8d
test: send bitcoin confirmation screen test
esaugomez31 Feb 28, 2025
d1600df
chore: updating language labels to send bitcoin completed screen and …
esaugomez31 Feb 28, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 119 additions & 0 deletions __tests__/screens/send-bitcoin-completed-screen.spec.tsx
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are missing aes test... you can use this data

    successAction: {
      tag: "aes",
      description: "Here is your redeem code",
      ciphertext: "564u3BEMRefWUV1098gJ5w==",
      iv: "IhkC5ktKB9LG91FvlbN2kg==",
    },
    preimage: "25004cd52960a3bac983e3f95c432341a7052cef37b9253b0b0b1256d754559b",

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great, thanks!

Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import React from "react"
import { fireEvent, render, screen, waitFor } from "@testing-library/react-native"
import { loadLocale } from "@app/i18n/i18n-util.sync"
import { i18nObject } from "@app/i18n/i18n-util"
import {
Success,
Queued,
Pending,
SuccessLUD09Message,
SuccessLUD09URL,
SuccessLUD09URLWithDesc,
} from "../../app/screens/send-bitcoin-screen/send-bitcoin-completed-screen.stories"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use @app/screens/...

import { ContextForScreen } from "./helper"
import { Linking } from "react-native"

jest.mock("react-native-in-app-review", () => ({
isAvailable: () => true,
RequestInAppReview: jest.fn(),
}))

describe("SendBitcoinCompletedScreen", () => {
let LL: ReturnType<typeof i18nObject>

beforeEach(() => {
loadLocale("en")
LL = i18nObject("en")
})

it("renders the Success state correctly", async () => {
render(
<ContextForScreen>
<Success />
</ContextForScreen>,
)

const successTextElement = await waitFor(() => screen.findByTestId("Success Text"))
expect(successTextElement.props.children).toContain(
"Payment has been sent successfully",
)
})

it("renders the Queued state correctly", async () => {
render(
<ContextForScreen>
<Queued />
</ContextForScreen>,
)

const queuedTextElement = await waitFor(() => screen.findByTestId("Success Text"))
expect(queuedTextElement.props.children).toEqual(
expect.stringContaining("Your transaction is queued"),
)
})

it("renders the Pending state correctly", async () => {
render(
<ContextForScreen>
<Pending />
</ContextForScreen>,
)

const pendingTextElement = await waitFor(() => screen.findByTestId("Success Text"))
expect(pendingTextElement.props.children).toEqual(
expect.stringContaining("The payment has been sent"),
)
})

it("render successAction - LUD 09 - message", async () => {
const message = "Thanks for your support."

render(
<ContextForScreen>
<SuccessLUD09Message />
</ContextForScreen>,
)

expect(screen.getByText(message)).toBeTruthy()
expect(screen.getByText(LL.SendBitcoinScreen.note())).toBeTruthy()
})

it("render successAction - LUD 09 - URL", async () => {
const url = "https://es.blink.sv"

render(
<ContextForScreen>
<SuccessLUD09URL />
</ContextForScreen>,
)

const button = screen.getByText(LL.ScanningQRCodeScreen.openLinkTitle())

expect(button).toBeTruthy()

fireEvent.press(button)

expect(Linking.openURL).toHaveBeenCalledWith(url)
})

it("render successAction - LUD 09 - URL with description", async () => {
const url = "https://es.blink.sv"
const description = "Example URL + description"

render(
<ContextForScreen>
<SuccessLUD09URLWithDesc />
</ContextForScreen>,
)

expect(screen.getByText(description)).toBeTruthy()

const button = screen.getByText(LL.ScanningQRCodeScreen.openLinkTitle())

expect(button).toBeTruthy()

fireEvent.press(button)

expect(Linking.openURL).toHaveBeenCalledWith(url)
})
})
10 changes: 10 additions & 0 deletions app/graphql/generated.gql
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,16 @@ mutation lnInvoicePaymentSend($input: LnInvoicePaymentInput!) {
__typename
}
status
transaction {
settlementVia {
... on SettlementViaLn {
preImage
__typename
}
__typename
}
__typename
}
__typename
}
}
Expand Down
9 changes: 8 additions & 1 deletion app/graphql/generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2954,7 +2954,7 @@ export type LnInvoicePaymentSendMutationVariables = Exact<{
}>;


export type LnInvoicePaymentSendMutation = { readonly __typename: 'Mutation', readonly lnInvoicePaymentSend: { readonly __typename: 'PaymentSendPayload', readonly status?: PaymentSendResult | null, readonly errors: ReadonlyArray<{ readonly __typename: 'GraphQLApplicationError', readonly message: string }> } };
export type LnInvoicePaymentSendMutation = { readonly __typename: 'Mutation', readonly lnInvoicePaymentSend: { readonly __typename: 'PaymentSendPayload', readonly status?: PaymentSendResult | null, readonly errors: ReadonlyArray<{ readonly __typename: 'GraphQLApplicationError', readonly message: string }>, readonly transaction?: { readonly __typename: 'Transaction', readonly settlementVia: { readonly __typename: 'SettlementViaIntraLedger' } | { readonly __typename: 'SettlementViaLn', readonly preImage?: string | null } | { readonly __typename: 'SettlementViaOnChain' } } | null } };

export type LnNoAmountUsdInvoicePaymentSendMutationVariables = Exact<{
input: LnNoAmountUsdInvoicePaymentInput;
Expand Down Expand Up @@ -6533,6 +6533,13 @@ export const LnInvoicePaymentSendDocument = gql`
message
}
status
transaction {
settlementVia {
... on SettlementViaLn {
preImage
}
}
}
}
}
`;
Expand Down
3 changes: 3 additions & 0 deletions app/navigation/stack-param-lists.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { LNURLPaySuccessAction } from "lnurl-pay/dist/types/types"
import { PhoneCodeChannelType, UserContact, WalletCurrency } from "@app/graphql/generated"
import { EarnSectionType } from "@app/screens/earns-screen/sections"
import { PhoneLoginInitiateType } from "@app/screens/phone-auth-screen"
Expand Down Expand Up @@ -52,6 +53,8 @@ export type RootStackParamList = {
sendBitcoinCompleted: {
arrivalAtMempoolEstimate?: number
status: PaymentSendCompletedStatus
successAction?: LNURLPaySuccessAction
preimage?: string
}
language: undefined
currency: undefined
Expand Down
20 changes: 17 additions & 3 deletions app/screens/send-bitcoin-screen/payment-details/index.types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { LnUrlPayServiceResponse } from "lnurl-pay/dist/types/types"

import {
LnUrlPayServiceResponse,
LNURLPaySuccessAction,
} from "lnurl-pay/dist/types/types"
import {
GraphQlApplicationError,
IntraLedgerPaymentSendMutationHookResult,
Expand Down Expand Up @@ -87,6 +89,7 @@ export type SendPaymentMutation = (

export type PaymentSendExtraInfo = {
arrivalAtMempoolEstimate?: number
preimage?: string | null
}

export type SetAmount<T extends WalletCurrency> = (
Expand All @@ -101,6 +104,10 @@ export type SetInvoice<T extends WalletCurrency> = (params: {
paymentRequestAmount: BtcMoneyAmount
}) => PaymentDetail<T>

export type SetSuccessAction<T extends WalletCurrency> = (
successAction: LNURLPaySuccessAction | undefined,
) => PaymentDetail<T>

type BasePaymentDetail<T extends WalletCurrency> = {
memo?: string
paymentType:
Expand Down Expand Up @@ -152,6 +159,11 @@ export type PaymentDetailSetAmount<T extends WalletCurrency> =
destinationSpecifiedAmount: BtcMoneyAmount // the amount that comes from the destination
}

export type PaymentDetailSetSuccessAction<T extends WalletCurrency> = {
setSuccessAction?: SetSuccessAction<T>
successAction?: LNURLPaySuccessAction
}

// sendPayment and getFee are defined together
export type PaymentDetailSendPaymentGetFee<T extends WalletCurrency> =
| {
Expand Down Expand Up @@ -179,14 +191,16 @@ type LnurlSpecificProperties<T extends WalletCurrency> =
paymentType: typeof PaymentType.Lnurl
lnurlParams: LnUrlPayServiceResponse
setInvoice: SetInvoice<T>
setSuccessAction: SetSuccessAction<T>
}

// combine all rules together with base type
export type PaymentDetail<T extends WalletCurrency> = BasePaymentDetail<T> &
LnurlSpecificProperties<T> &
PaymentDetailSetMemo<T> &
PaymentDetailSetAmount<T> &
PaymentDetailSendPaymentGetFee<T>
PaymentDetailSendPaymentGetFee<T> &
PaymentDetailSetSuccessAction<T>

export const AmountInvalidReason = {
InsufficientBalance: "InsufficientBalance",
Expand Down
24 changes: 23 additions & 1 deletion app/screens/send-bitcoin-screen/payment-details/lightning.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { LnUrlPayServiceResponse } from "lnurl-pay/dist/types/types"
import {
LnUrlPayServiceResponse,
LNURLPaySuccessAction,
} from "lnurl-pay/dist/types/types"

import { WalletCurrency } from "@app/graphql/generated"
import {
Expand All @@ -13,6 +16,7 @@ import { PaymentType } from "@galoymoney/client"

import {
ConvertMoneyAmount,
SetSuccessAction,
SetInvoice,
GetFee,
PaymentDetail,
Expand Down Expand Up @@ -250,10 +254,17 @@ export const createAmountLightningPaymentDetails = <T extends WalletCurrency>(
},
},
})
const { settlementVia } = data?.lnInvoicePaymentSend.transaction || {}

return {
status: data?.lnInvoicePaymentSend.status,
errors: data?.lnInvoicePaymentSend.errors,
extraInfo: {
preimage:
settlementVia?.__typename === "SettlementViaLn"
? settlementVia.preImage
: undefined,
},
}
}

Expand Down Expand Up @@ -368,6 +379,7 @@ export type CreateLnurlPaymentDetailsParams<T extends WalletCurrency> = {
paymentRequest?: string
paymentRequestAmount?: BtcMoneyAmount
unitOfAccountAmount: MoneyAmount<WalletOrDisplayCurrency>
successAction?: LNURLPaySuccessAction
} & BaseCreatePaymentDetailsParams<T>

export const createLnurlPaymentDetails = <T extends WalletCurrency>(
Expand All @@ -383,6 +395,7 @@ export const createLnurlPaymentDetails = <T extends WalletCurrency>(
sendingWalletDescriptor,
destinationSpecifiedMemo,
senderSpecifiedMemo,
successAction,
} = params

const destinationSpecifiedAmount =
Expand Down Expand Up @@ -465,6 +478,13 @@ export const createLnurlPaymentDetails = <T extends WalletCurrency>(
})
}

const setSuccessAction: SetSuccessAction<T> = (newSuccessAction) => {
return createLnurlPaymentDetails({
...params,
successAction: newSuccessAction,
})
}

const setSendingWalletDescriptor: SetSendingWalletDescriptor<T> = (
newSendingWalletDescriptor,
) => {
Expand All @@ -488,6 +508,8 @@ export const createLnurlPaymentDetails = <T extends WalletCurrency>(
setInvoice,
convertMoneyAmount,
setConvertMoneyAmount,
successAction,
setSuccessAction,
...setAmount,
...setMemo,
...sendPaymentAndGetFee,
Expand Down
Loading
Loading