Skip to content

Commit

Permalink
Merge pull request #1446 from getAlby/task-success-screen
Browse files Browse the repository at this point in the history
feat: success screen component (LNURLPay)
  • Loading branch information
escapedcat authored Sep 27, 2022
2 parents 691aa25 + 8fcaa77 commit 73bd1e5
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 53 deletions.
39 changes: 39 additions & 0 deletions src/app/components/ResultCard/index.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { render, screen } from "@testing-library/react";
import { MemoryRouter } from "react-router-dom";

import type { Props } from "./index";
import ResultCard from "./index";

const successProps: Props = {
isSuccess: true,
message: "Test Successful!",
};

const failureProps: Props = {
isSuccess: false,
message: "Test Successful!",
};

describe("ResultCard", () => {
test("success render", async () => {
render(
<MemoryRouter>
<ResultCard {...successProps} />
</MemoryRouter>
);

expect(screen.getByText("Test Successful!")).toBeInTheDocument();
expect(screen.getByAltText("success")).toBeInTheDocument();
});

test("failure render", async () => {
render(
<MemoryRouter>
<ResultCard {...failureProps} />
</MemoryRouter>
);

expect(screen.getByText("Test Successful!")).toBeInTheDocument();
expect(screen.getByAltText("failure")).toBeInTheDocument();
});
});
17 changes: 17 additions & 0 deletions src/app/components/ResultCard/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export type Props = {
isSuccess: boolean;
message: string;
};

export default function ResultCard({ message, isSuccess }: Props) {
return (
<div className="p-12 font-medium drop-shadow rounded-lg mt-4 flex flex-col items-center bg-white dark:bg-surface-02dp">
<img
src={isSuccess ? "assets/icons/tick.svg" : "assets/icons/cross.svg"}
alt={isSuccess ? "success" : "failure"}
className="mb-8"
/>
<p className="text-center">{message}</p>
</div>
);
}
129 changes: 81 additions & 48 deletions src/app/screens/LNURLPay/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import Button from "@components/Button";
import ConfirmOrCancel from "@components/ConfirmOrCancel";
import Container from "@components/Container";
import PublisherCard from "@components/PublisherCard";
import ResultCard from "@components/ResultCard";
import SatButtons from "@components/SatButtons";
import DualCurrencyField from "@components/form/DualCurrencyField";
import TextField from "@components/form/TextField";
Expand Down Expand Up @@ -61,6 +62,8 @@ function LNURLPay() {
const [successAction, setSuccessAction] = useState<
LNURLPaymentSuccessAction | undefined
>();
const [result, setResult] = useState("");
const [failureMessage, setFailureMessage] = useState("");

useEffect(() => {
if (showFiat) {
Expand Down Expand Up @@ -130,7 +133,10 @@ function LNURLPay() {
}
} catch (e) {
const message = e instanceof Error ? `(${e.message})` : "";
toast.error(`Payment aborted: Could not fetch invoice. \n${message}`);
setFailureMessage(
`Payment aborted: Could not fetch invoice. ${message}`
);
setResult("fail");
return;
}

Expand Down Expand Up @@ -180,6 +186,7 @@ function LNURLPay() {
setSuccessAction({ tag: "message", message: t("success") });
}

setResult("success");
auth.fetchAccountInfo(); // Update balance.

// ATTENTION: if this LNURL is called through `webln.lnurl` then we immediately return and return the payment response. This closes the window which means the user will NOT see the above successAction.
Expand All @@ -190,7 +197,10 @@ function LNURLPay() {
} catch (e) {
console.error(e);
if (e instanceof Error) {
toast.error(`Error: ${e.message}`);
setFailureMessage(
`There was an error in processing your transaction. ${e.message}`
);
setResult("fail");
}
} finally {
setLoadingConfirm(false);
Expand Down Expand Up @@ -269,53 +279,76 @@ function LNURLPay() {
return [];
}

function renderSuccessAction() {
if (!successAction) return;
function header() {
switch (result) {
case "success":
return tCommon("success");
case "fail":
return tCommon("errors.payment_failed");
default:
return tCommon("actions.send");
}
}

function renderResultAction() {
if (!result) return;
const isSuccess = result === "success";
const isMessage =
successAction?.tag === "url" || successAction?.tag === "message";
let descriptionList: [string, string | React.ReactNode][] = [];
if (successAction.tag === "url") {
descriptionList = [
[`${tCommon("description")}`, successAction.description],
[
"URL",
<>
{successAction.url}
<div className="mt-4">
<Button
onClick={() => {
if (successAction.url) utils.openUrl(successAction.url);
}}
label={tCommon("actions.open")}
primary
/>
</div>
</>,
],
];
} else if (successAction.tag === "message") {
descriptionList = [[`${tCommon("message")}`, successAction.message]];
if (successAction) {
if (successAction.tag === "url") {
descriptionList = [
[`${tCommon("description")}`, successAction.description],
[
"URL",
<>
{successAction.url}
<div className="mt-4">
<Button
onClick={() => {
if (successAction.url) utils.openUrl(successAction.url);
}}
label={tCommon("actions.open")}
primary
/>
</div>
</>,
],
];
} else if (successAction.tag === "message") {
descriptionList = [[`${tCommon("message")}`, successAction.message]];
}
}

return (
<Container maxWidth="sm">
<PublisherCard
title={navState.origin?.name}
description={getRecipient()}
image={navState.origin?.icon || getImage()}
/>

<dl className="shadow bg-white dark:bg-surface-02dp mt-4 pt-4 px-4 rounded-lg mb-6 overflow-hidden">
{descriptionList.map(([dt, dd]) => (
<>
<Dt>{dt}</Dt>
<Dd>{dd}</Dd>
</>
))}
</dl>

<div className="text-center">
<button className="underline text-sm text-gray-500" onClick={close}>
{tCommon("actions.close")}
</button>
<Container justifyBetween maxWidth="sm">
<div>
<ResultCard
isSuccess={isSuccess}
message={
isSuccess
? `${valueSat} (SATS) ${
showFiat ? "(" + fiatValue + ")" : ""
} ${t("were_sent_to")} ${
navState.origin?.name || getRecipient()
}`
: failureMessage
}
/>
{isMessage && (
<dl className="shadow bg-white dark:bg-surface-02dp mt-4 pt-4 px-4 rounded-lg mb-6 overflow-hidden">
{descriptionList.map(([dt, dd]) => (
<>
<Dt>{dt}</Dt>
<Dd>{dd}</Dd>
</>
))}
</dl>
)}
</div>
<div className="mb-4">
<Button onClick={close} label={tCommon("actions.close")} fullWidth />
</div>
</Container>
);
Expand All @@ -324,8 +357,8 @@ function LNURLPay() {
return (
<>
<div className="flex flex-col grow overflow-hidden">
<ScreenHeader title={tCommon("actions.send")} />
{!successAction ? (
<ScreenHeader title={header()} />
{!result ? (
<>
<div className="grow overflow-y-auto no-scrollbar">
<Container maxWidth="sm">
Expand Down Expand Up @@ -424,7 +457,7 @@ function LNURLPay() {
</div>
</>
) : (
renderSuccessAction()
renderResultAction()
)}
</div>
</>
Expand Down
6 changes: 4 additions & 2 deletions src/i18n/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@
}
},
"lnurlpay": {
"were_sent_to": "were sent to",
"amount": {
"label": "Amount"
},
Expand Down Expand Up @@ -472,7 +473,7 @@
},
"common": {
"password": "Password",
"success": "Success!",
"success": "Success",
"error": "Error",
"settings": "Settings",
"websites": "Websites",
Expand Down Expand Up @@ -506,7 +507,8 @@
"copy": "Copy"
},
"errors": {
"connection_failed": "Connection failed"
"connection_failed": "Connection failed",
"payment_failed": "Payment Failed"
}
},
"components": {
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/locales/es/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -463,7 +463,7 @@
},
"common": {
"password": "Contraseña",
"success": "¡Éxito!",
"success": "Éxito",
"error": "Error",
"settings": "Ajustes",
"websites": "Sitios web",
Expand Down
2 changes: 1 addition & 1 deletion src/i18n/locales/pt_BR/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@
},
"common": {
"password": "Senha",
"success": "Sucesso!",
"success": "Sucesso",
"error": "Erro",
"settings": "Configurações",
"websites": "Sites",
Expand Down
4 changes: 4 additions & 0 deletions static/assets/icons/cross.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions static/assets/icons/tick.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion tests/e2e/001-createWallets.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ const commonCreateWalletSuccessCheck = async ({ page, $document }) => {
page.waitForNavigation(), // The promise resolves after navigation has finished
]);

await findByText($document, "Success!", undefined, { timeout: 15000 });
await findByText($document, "Success", undefined, { timeout: 15000 });
};

test.describe("Create or connect wallets", () => {
Expand Down

0 comments on commit 73bd1e5

Please sign in to comment.