Skip to content
This repository has been archived by the owner on Mar 23, 2023. It is now read-only.

feat: add second signature detail #2871

Merged
merged 8 commits into from
Sep 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { boolean } from "@storybook/addon-knobs";
import React from "react";
import { TransactionFixture } from "tests/fixtures/transactions";

import { SecondSignatureDetail } from "./SecondSignatureDetail";

export default { title: "Domains / Transaction / Components / SecondSignatureDetail" };

export const Default = () => (
<SecondSignatureDetail
isOpen={boolean("Is Open", true)}
transaction={{ ...TransactionFixture, blockId: () => "adsad12312xsd1w312e1s13203e12" }}
onClose={() => alert("closed")}
/>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import React from "react";
import { render } from "testing-library";
import { TransactionFixture } from "tests/fixtures/transactions";

import { translations } from "../../i18n";
import { SecondSignatureDetail } from "./SecondSignatureDetail";

describe("SecondSignatureDetail", () => {
const extraProps = {
ticker: "BTC",
walletAlias: "Wallet 1",
};

it("should not render if not open", () => {
const { asFragment, getByTestId } = render(
<SecondSignatureDetail isOpen={false} transaction={TransactionFixture} {...extraProps} />,
);

expect(() => getByTestId("modal__inner")).toThrow(/Unable to find an element by/);
expect(asFragment()).toMatchSnapshot();
});

it("should render a modal", () => {
const { asFragment, getByTestId } = render(
<SecondSignatureDetail isOpen={true} transaction={TransactionFixture} {...extraProps} />,
);

expect(getByTestId("modal__inner")).toHaveTextContent(translations.MODAL_SECOND_SIGNATURE_DETAIL.TITLE);
expect(asFragment()).toMatchSnapshot();
});

it("should render a modal without a wallet alias", () => {
const { asFragment, getByTestId } = render(
<SecondSignatureDetail isOpen={true} transaction={TransactionFixture} ticker="BTC" />,
);

expect(getByTestId("modal__inner")).toHaveTextContent(translations.MODAL_SECOND_SIGNATURE_DETAIL.TITLE);
expect(asFragment()).toMatchSnapshot();
});

it("should render as confirmed", () => {
const { asFragment, getByText, getByTestId } = render(
<SecondSignatureDetail
isOpen={true}
transaction={{
...TransactionFixture,
isConfirmed: () => true,
}}
{...extraProps}
/>,
);

expect(getByTestId("modal__inner")).toHaveTextContent(translations.MODAL_SECOND_SIGNATURE_DETAIL.TITLE);
expect(getByText("Well confirmed")).toBeTruthy();
expect(asFragment()).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import Tippy from "@tippyjs/react";
import { Amount } from "app/components/Amount";
import { Avatar } from "app/components/Avatar";
import { Circle } from "app/components/Circle";
import { Clipboard } from "app/components/Clipboard";
import { Icon } from "app/components/Icon";
import { Link } from "app/components/Link";
import { Modal } from "app/components/Modal";
import { TransactionDetail } from "app/components/TransactionDetail";
import { TruncateMiddle } from "app/components/TruncateMiddle";
import React, { useMemo } from "react";
import { useTranslation } from "react-i18next";

type SecondSignatureDetailProps = {
isOpen: boolean;
ticker?: string;
transaction: any;
walletAlias?: string;
onClose?: any;
};

export const SecondSignatureDetail = ({
isOpen,
ticker,
transaction,
walletAlias,
onClose,
}: SecondSignatureDetailProps) => {
const { t } = useTranslation();

const renderConfirmationStatus = (isConfirmed: boolean, confirmations: number) => {
const confirmationStatusStyle = isConfirmed
? "bg-theme-success-200 text-theme-success-500"
: "bg-theme-danger-200 text-theme-danger-500";

if (isConfirmed) {
return (
<div className="flex">
<span>{t("TRANSACTION.WELL_CONFIRMED")}</span>
<Tippy content={t("TRANSACTION.CONFIRMATIONS_COUNT", { count: confirmations })}>
<div className={`flex w-6 h-6 ml-2 rounded-full ${confirmationStatusStyle}`}>
<div className="m-auto">
<Icon name="Checkmark" width={15} height={15} />
</div>
</div>
</Tippy>
</div>
);
}

return (
<div className="flex">
<span>{t("TRANSACTION.NOT_CONFIRMED")}</span>
<div className={`flex w-6 h-6 ml-2 rounded-full ${confirmationStatusStyle}`}>
<div className="m-auto">
<Icon name="CrossSlim" width={12} height={12} />
</div>
</div>
</div>
);
};

const renderSender = () => {
if (walletAlias) {
return (
<TransactionDetail
label={t("TRANSACTION.SENDER")}
extra={
<div className="flex items-center">
<Circle className="-mr-2 border-black">
<Icon name="Key" width={20} height={20} />
</Circle>
<Avatar address={transaction.sender()} />
</div>
}
border={false}
>
{walletAlias}
<TruncateMiddle text={transaction.sender()} className="ml-2 text-theme-neutral" />
</TransactionDetail>
);
}

return (
<TransactionDetail
label={t("TRANSACTION.SENDER")}
extra={
<div className="flex items-center">
<Circle className="-mr-2 border-black">
<Icon name="Key" width={20} height={20} />
</Circle>
<Avatar address={transaction.sender()} />
</div>
}
border={false}
>
<TruncateMiddle text={transaction.sender()} className="text-theme-neutral" />
</TransactionDetail>
);
};

const currency = useMemo(() => transaction.wallet()?.currency() || "", [transaction]);

return (
<Modal title={t("TRANSACTION.MODAL_SECOND_SIGNATURE_DETAIL.TITLE")} isOpen={isOpen} onClose={onClose}>
{renderSender()}

<TransactionDetail label={t("TRANSACTION.TRANSACTION_FEE")}>
<Amount ticker={currency} value={transaction.fee()} />
</TransactionDetail>

<TransactionDetail label={t("TRANSACTION.TIMESTAMP")}>
{transaction.timestamp()!.format("DD.MM.YYYY HH:mm:ss")}
</TransactionDetail>

<TransactionDetail label={t("TRANSACTION.CONFIRMATIONS")}>
{renderConfirmationStatus(transaction.isConfirmed(), transaction.confirmations().toNumber())}
</TransactionDetail>

<TransactionDetail label={t("TRANSACTION.ID")}>
<Link to={transaction.explorerLink()} isExternal showExternalIcon={false}>
<TruncateMiddle text={transaction.id()} maxChars={30} className="text-theme-primary-dark" />
</Link>

<span className="inline-block ml-4 text-theme-primary-300">
<Clipboard data={transaction.id()}>
<Icon name="Copy" />
</Clipboard>
</span>
</TransactionDetail>

{!!transaction.blockId() && (
<TransactionDetail label={t("TRANSACTION.BLOCK_ID")}>
<Link
to={transaction.coin()?.link().block(transaction.blockId())}
isExternal
showExternalIcon={false}
>
<TruncateMiddle
text={transaction.blockId()}
maxChars={30}
className="text-theme-primary-dark"
/>
</Link>

<span className="inline-block ml-4 text-theme-primary-300">
<Clipboard data={transaction.blockId()}>
<Icon name="Copy" />
</Clipboard>
</span>
</TransactionDetail>
)}
</Modal>
);
};

SecondSignatureDetail.defaultProps = {
isOpen: false,
};
Loading