Skip to content

Commit

Permalink
✨(lld): rework coin control UI
Browse files Browse the repository at this point in the history
  • Loading branch information
LucasWerey committed Oct 9, 2024
1 parent b0d535b commit 3afc6e6
Show file tree
Hide file tree
Showing 17 changed files with 692 additions and 18 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React, { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useTranslation } from "react-i18next";
import { setHasProtectedOrdinalsAssets } from "~/renderer/actions/settings";
import { hasProtectedOrdinalsAssetsSelector } from "~/renderer/reducers/settings";
import { Flex, Text } from "@ledgerhq/react-ui";
import Switch from "~/renderer/components/Switch";

export const ExcludeAssetsSection: React.FC = () => {
const { t } = useTranslation();
const dispatch = useDispatch();
const hasProtectedOrdinals = useSelector(hasProtectedOrdinalsAssetsSelector);

const onSwitchChange = useCallback(() => {
dispatch(setHasProtectedOrdinalsAssets(!hasProtectedOrdinals));
}, [dispatch, hasProtectedOrdinals]);

return (
<Flex alignItems="center">
<Text variant="bodyLineHeight" color="neutral.c70" flex={1} fontSize={14}>
{t("ordinals.inscriptions.discoveryDrawer.protectDescription")}
</Text>
<Flex>
<Switch isChecked={hasProtectedOrdinals} onChange={onSwitchChange} />
</Flex>
</Flex>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import React from "react";
import { Flex, Icons, Button, Link } from "@ledgerhq/react-ui";
import { BitcoinOutput } from "@ledgerhq/coin-bitcoin/lib/types";

type Props = {
onClickLink: () => void;
onClose: () => void;
returning?: BitcoinOutput; // will be used in the future
};

export const Footer: React.FC<Props> = ({ onClickLink, onClose }) => (
<Flex justifyContent="space-between" alignItems="center" flex={1} p={10}>
<Flex alignItems="center" columnGap={1}>
<Link
textProps={{
fontSize: 4,
color: "palette.text.shade100",
}}
onClick={onClickLink}
>
{"Learn more about this setting"}
</Link>
<Icons.ExternalLink size="S" />
</Flex>
<Button borderRadius={4} variant="color" onClick={onClose}>
{"CONFIRM"}
</Button>
</Flex>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import React from "react";
import { Flex, Text } from "@ledgerhq/react-ui";
import { SplitAddress, Cell } from "~/renderer/components/OperationsList/AddressCell";

type Props = {
outputIndex: number;
hash: string;
address: string;
};

export const NoOrdinalsRow: React.FC<Props> = ({ outputIndex, hash, address }) => {
return (
<Flex
flex={1}
width="100%"
borderBottom={1}
borderBottomLeftRadius={8}
borderBottomRightRadius={8}
flexDirection="column"
p={15}
overflowX="hidden"
rowGap={18}
>
<Flex flexDirection="row" justifyContent="space-between" alignItems={"center"}>
<Text variant="subtitle" color="neutral.c70" fontSize={11}>
{"Address"}
</Text>
<Flex width={150}>
<Cell px={0} pl={2}>
<SplitAddress fontSize={13} value={address} ff="Inter|Regular" />
</Cell>
</Flex>
</Flex>
<Flex flexDirection="row" justifyContent="space-between" alignItems="center">
<Text variant="subtitle" flex={1} color="neutral.c70" fontSize={11}>
{"Transaction ID"}
</Text>
<Flex alignItems="center">
<Text variant="subtitle" color="neutral.c70" fontSize={11}>
#{outputIndex} of
</Text>
<Cell px={0} pl={2}>
<SplitAddress fontSize={13} value={hash} ff="Inter|Regular" />
</Cell>
</Flex>
</Flex>
</Flex>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import React from "react";
import {
BitcoinAccount,
BitcoinOutput,
Transaction,
TransactionStatus,
UtxoStrategy,
} from "@ledgerhq/live-common/families/bitcoin/types";
import { AccountBridge } from "@ledgerhq/types-live";
import { Unit } from "@ledgerhq/types-cryptoassets";
import { Trans } from "react-i18next";
import styled from "styled-components";
import { Box, Flex, Icons, Tooltip, Text } from "@ledgerhq/react-ui";
import { NoOrdinalsRow } from "./NoOrdinalsRow";
import Checkbox from "~/renderer/components/CheckBox";
import FormattedVal from "~/renderer/components/FormattedVal";
import { space } from "@ledgerhq/react-ui/styles/theme";
import { useOrdinalRowModel } from "./useOrdinalRowModel";

const Container = styled(Box)<{ disabled?: boolean }>`
display: flex;
flex-direction: column;
justify-content: flex-start;
border-radius: 8px;
align-items: center;
border: 1px solid ${({ theme }) => theme.colors.palette.neutral.c20};
background-color: ${({ theme }) => theme.colors.palette.opacityDefault.c05};
opacity: ${({ disabled }) => (disabled ? 0.5 : 1)};
cursor: ${({ disabled }) => (disabled ? "default" : "pointer")};
&:hover {
border-color: ${({ theme, disabled }) =>
disabled ? theme.colors.palette.text.shade20 : theme.colors.palette.primary.main};
}
`;

const UtxoContainer = styled(Flex)<{ isOpened?: boolean }>`
background-color: ${({ theme, isOpened }) =>
isOpened ? theme.colors.palette.opacityDefault.c05 : undefined};
`;

const TooltipContainer = styled(Box)`
background-color: ${({ theme }) => theme.colors.palette.neutral.c100};
padding: 8px;
border-radius: 16px;
display: flex;
gap: 2px;
`;

type ViewProps = ReturnType<typeof useOrdinalRowModel>;

function View({
utxo,
account,
unit,
isDetailsVisible,
disabled,
last,
unconfirmed,
input,
excluded,
toggleDetailsVisibility,
handleClick,
}: ViewProps) {
return (
<Container>
<UtxoContainer
flexDirection="row"
flex={1}
width="100%"
alignItems="center"
columnGap={12}
justifyContent="space-between"
py={2}
px={space[5]}
borderRadius={8}
isOpened={isDetailsVisible}
onClick={handleClick}
>
<Flex flexDirection="row" flex={1} alignItems="center" width="100%" columnGap={12}>
{unconfirmed ? (
<Tooltip
placement="top"
content={
<TooltipContainer>
<Icons.Information size="S" />
<Trans i18nKey={"bitcoin.cannotSelect.pending"} />
</TooltipContainer>
}
></Tooltip>
) : last ? (
<Tooltip
placement="top"
content={
<TooltipContainer>
<Icons.Information size="S" />
<Trans i18nKey={"bitcoin.cannotSelect.last"} />
</TooltipContainer>
}
>
<Checkbox isChecked disabled />
</Tooltip>
) : (
<Checkbox isChecked={!excluded} onChange={handleClick} />
)}
<Box>
<FormattedVal
val={utxo.value}
unit={unit}
showCode
fontSize={4}
color="palette.text.shade100"
ff="Inter|SemiBold"
/>
{utxo.blockHeight ? (
<Text ff="Inter|Medium" fontSize={3} color={"palette.text.shade50"}>
{account.blockHeight - utxo.blockHeight + " confirmations"}
</Text>
) : (
<Text ff="Inter|Medium" fontSize={3} color={"alertRed"}>
<Trans i18nKey="bitcoin.pending" />
</Text>
)}
</Box>
<Box
style={{
flexBasis: "10%",
}}
>
{input && !disabled ? (
<Text
ff="Inter|Bold"
fontSize={2}
color="wallet"
style={{
lineHeight: "10px",
}}
>
<Trans i18nKey="bitcoin.inputSelected" />
</Text>
) : null}
</Box>
</Flex>
<Box onClickCapture={toggleDetailsVisibility} display="flex" alignItems="center">
{isDetailsVisible ? <Icons.ChevronUp /> : <Icons.ChevronDown />}
</Box>
</UtxoContainer>
{isDetailsVisible && (
<NoOrdinalsRow
hash={utxo.hash}
address={String(utxo.address)}
outputIndex={utxo.outputIndex}
/>
)}
</Container>
);
}

type Props = {
utxo: BitcoinOutput;
utxoStrategy: UtxoStrategy;
status: TransactionStatus;
account: BitcoinAccount;
totalExcludedUTXOS: number;
bridge: AccountBridge<Transaction>;
unit: Unit;
updateTransaction: (updater: (t: Transaction) => Transaction) => void;
};

export const Row = (props: Props) => {
return <View {...useOrdinalRowModel(props)} />;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { useState } from "react";
import {
BitcoinAccount,
BitcoinOutput,
Transaction,
TransactionStatus,
UtxoStrategy,
} from "@ledgerhq/live-common/families/bitcoin/types";
import { Unit } from "@ledgerhq/types-cryptoassets";
import { AccountBridge } from "@ledgerhq/types-live";
import { getUTXOStatus } from "@ledgerhq/live-common/families/bitcoin/logic";

type Props = {
utxo: BitcoinOutput;
utxoStrategy: UtxoStrategy;
status: TransactionStatus;
account: BitcoinAccount;
totalExcludedUTXOS: number;
bridge: AccountBridge<Transaction>;
unit: Unit;
updateTransaction: (updater: (t: Transaction) => Transaction) => void;
};

export const useOrdinalRowModel = ({
utxo,
utxoStrategy,
status,
account,
totalExcludedUTXOS,
bridge,
unit,
updateTransaction,
}: Props) => {
const [isDetailsVisible, setIsDetailsVisible] = useState(false);
const { excluded, reason } = getUTXOStatus(utxo, utxoStrategy);
const utxoStatus = excluded ? reason || "" : "";

const input = (status.txInputs || []).find(
input => input.previousOutputIndex === utxo.outputIndex && input.previousTxHash === utxo.hash,
);
const unconfirmed = utxoStatus === "pickPendingUtxo";
const last = !excluded && totalExcludedUTXOS + 1 === account.bitcoinResources?.utxos.length;
const disabled = unconfirmed || last;

const handleClick = () => {
if (disabled) return;
const updatedStrategy = {
...utxoStrategy,
excludeUTXOs: excluded
? utxoStrategy.excludeUTXOs.filter(
e => e.hash !== utxo.hash || e.outputIndex !== utxo.outputIndex,
)
: [...utxoStrategy.excludeUTXOs, { hash: utxo.hash, outputIndex: utxo.outputIndex }],
};

updateTransaction(t => bridge.updateTransaction(t, { utxoStrategy: updatedStrategy }));
};

const toggleDetailsVisibility = (e: React.MouseEvent<HTMLDivElement>) => {
e.stopPropagation();
setIsDetailsVisible(!isDetailsVisible);
};

return {
utxo,
account,
unit,
isDetailsVisible,
disabled,
last,
unconfirmed,
input,
excluded,
toggleDetailsVisibility,
handleClick,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react";
import { Text, Flex } from "@ledgerhq/react-ui";

export const UtxoPickerHeaderSection: React.FC = () => {
return (
<Flex flexDirection="column">
<Text variant="body" fontSize={14} ff="Inter|SemiBold">
UTXOs
</Text>
<Text color="neutral.c70" fontSize={14} variant="bodyLineHeight">
Select the UTXO you want to use when you do transactions. Unchecked UTXO wont be used.
</Text>
</Flex>
);
};
Loading

0 comments on commit 3afc6e6

Please sign in to comment.