diff options
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx')
-rw-r--r-- | packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx | 393 |
1 files changed, 376 insertions, 17 deletions
diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx b/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx index 187a2412f..5f9c5065c 100644 --- a/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx +++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection.tsx @@ -14,17 +14,22 @@ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ +import { Amounts } from "@gnu-taler/taler-util"; import { styled } from "@linaria/react"; import { Fragment, h, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { + InputWithLabel, + LightText, + SvgIcon, +} from "../components/styled/index.js"; +import { useTranslationContext } from "../context/translation.js"; +import { Button } from "../mui/Button.js"; +import { Grid } from "../mui/Grid.js"; import { Paper } from "../mui/Paper.js"; - -const QrVideo = styled.video` - width: 80%; - margin-left: auto; - margin-right: auto; - padding: 8px; - background-color: black; -`; +import { TextField } from "../mui/TextField.js"; +import arrowIcon from "../svg/chevron-down.svg"; +import bankIcon from "../svg/ri-bank-line.svg"; const Container = styled.div` display: flex; @@ -36,25 +41,379 @@ const Container = styled.div` interface Props { action: "send" | "get"; - currency?: string; + amount?: string; + goToWalletManualWithdraw: (amount: string) => void; } -export function DestinationSelectionGetCash({ currency }: Props): VNode { +type Contact = { + icon: string; + name: string; + description: string; +}; + +const ContactTable = styled.table` + width: 100%; + & > tr > td { + padding: 8px; + & > div:not([data-disabled]):hover { + background-color: lightblue; + } + color: black; + div[data-disabled] > * { + color: gray; + } + } + + & > tr:nth-child(2n) { + background: #ebebeb; + } +`; + +const MediaExample = styled.div` + text-size-adjust: 100%; + color: inherit; + font-family: inherit; + font-size: inherit; + line-height: inherit; + text-transform: none; + text-align: left; + box-sizing: border-box; + align-items: center; + display: flex; + padding: 8px 8px; + + &[data-disabled]:hover { + cursor: inherit; + } + cursor: pointer; +`; + +const MediaLeft = styled.div` + text-size-adjust: 100%; + + color: inherit; + font-family: inherit; + font-size: inherit; + line-height: inherit; + text-transform: none; + text-align: left; + box-sizing: border-box; + padding-right: 8px; + display: block; +`; + +const MediaBody = styled.div` + text-size-adjust: 100%; + + font-family: inherit; + text-transform: none; + text-align: left; + box-sizing: border-box; + flex: 1 1; + font-size: 14px; + font-weight: 500; + line-height: 1.42857; +`; +const MediaRight = styled.div` + text-size-adjust: 100%; + + color: inherit; + font-family: inherit; + font-size: inherit; + line-height: inherit; + text-transform: none; + text-align: left; + box-sizing: border-box; + padding-left: 8px; +`; + +const CircleDiv = styled.div` + box-sizing: border-box; + align-items: center; + background-position: 50%; + background-repeat: no-repeat; + background-size: cover; + border-radius: 50%; + display: flex; + justify-content: center; + margin-left: auto; + margin-right: auto; + overflow: hidden; + text-align: center; + text-decoration: none; + text-transform: uppercase; + transition: background-color 0.15s ease, border-color 0.15s ease, + color 0.15s ease; + font-size: 16px; + background-color: #86a7bd1a; + height: 40px; + line-height: 40px; + width: 40px; + border: none; +`; + +function RowExample({ + info, + disabled, +}: { + info: Contact; + disabled?: boolean; +}): VNode { + return ( + <MediaExample data-disabled={disabled}> + <MediaLeft> + <CircleDiv> + <SvgIcon + title={info.name} + dangerouslySetInnerHTML={{ __html: info.icon }} + color="currentColor" + /> + </CircleDiv> + </MediaLeft> + <MediaBody> + <span>{info.name}</span> + <LightText>{info.description}</LightText> + </MediaBody> + <MediaRight> + <SvgIcon + title="Select this contact" + dangerouslySetInnerHTML={{ __html: arrowIcon }} + color="currentColor" + transform="rotate(-90deg)" + /> + </MediaRight> + </MediaExample> + ); +} + +export function DestinationSelectionGetCash({ + amount: initialAmount, + goToWalletManualWithdraw, +}: Props): VNode { + const parsedInitialAmount = !initialAmount + ? undefined + : Amounts.parse(initialAmount); + const parsedInitialAmountValue = !parsedInitialAmount + ? "" + : Amounts.stringifyValue(parsedInitialAmount); + const currency = parsedInitialAmount?.currency; + + const [amount, setAmount] = useState(parsedInitialAmountValue); + const { i18n } = useTranslationContext(); + const previous1: Contact[] = []; + const previous2: Contact[] = [ + { + name: "International Bank", + icon: bankIcon, + description: "account ending with 3454", + }, + { + name: "Max", + icon: bankIcon, + description: "account ending with 3454", + }, + { + name: "Alex", + icon: bankIcon, + description: "account ending with 3454", + }, + ]; + + if (!currency) { + return <div>currency not provided</div>; + } + const currencyAndAmount = `${currency}:${amount}`; + const parsedAmount = Amounts.parse(currencyAndAmount); + // const dirty = parsedInitialAmountValue !== amount; + const invalid = !parsedAmount || Amounts.isZero(parsedAmount); return ( <Container> - <p>Request {currency} from:</p> - <Paper style={{ padding: 8 }}>Bank account</Paper> - <Paper style={{ padding: 8 }}>Another person</Paper> + <h1> + <i18n.Translate>Specify the amount and the origin</i18n.Translate> + </h1> + <TextField + label="Amount" + type="number" + variant="filled" + error={invalid} + required + startAdornment={ + <div style={{ padding: "25px 12px 8px 12px" }}>{currency}</div> + } + value={amount} + onChange={(e) => { + setAmount(e); + }} + /> + + <Grid container spacing={1} columns={1}> + {previous2.length > 0 ? ( + <Fragment> + <p>Previous origins:</p> + <Grid item xs={1}> + <Paper style={{ padding: 8 }}> + <ContactTable> + {previous2.map((info, i) => ( + <tr key={i}> + <td> + <RowExample info={info} disabled={invalid} /> + </td> + </tr> + ))} + </ContactTable> + </Paper> + </Grid> + </Fragment> + ) : undefined} + <Grid item> + <p>Create new origin for the money</p> + </Grid> + <Grid item container columns={3} spacing={1}> + <Grid item xs={1}> + <Paper style={{ padding: 8 }}> + <p>From my bank account</p> + <Button + disabled={invalid} + onClick={async () => + goToWalletManualWithdraw(currencyAndAmount) + } + > + Withdraw + </Button> + </Paper> + </Grid> + <Grid item xs={1}> + <Paper style={{ padding: 8 }}> + <p>From someone else</p> + <Button disabled>Request</Button> + </Paper> + </Grid> + <Grid item xs={1}> + <Paper style={{ padding: 8 }}> + <p>From a business or charity</p> + <Button disabled>Invoice</Button> + </Paper> + </Grid> + </Grid> + <Grid item columns={1} spacing={1} xs={1}> + <Paper style={{ padding: 8 }}> + <p>From a exchange reserve or purse</p> + <Button disabled>Create</Button> + </Paper> + </Grid> + </Grid> </Container> ); } -export function DestinationSelectionSendCash({ currency }: Props): VNode { +export function DestinationSelectionSendCash({ + amount: initialAmount, +}: Props): VNode { + const parsedInitialAmount = !initialAmount + ? undefined + : Amounts.parse(initialAmount); + const parsedInitialAmountValue = !parsedInitialAmount + ? "" + : Amounts.stringifyValue(parsedInitialAmount); + const currency = parsedInitialAmount?.currency; + + const [amount, setAmount] = useState(parsedInitialAmountValue); + const { i18n } = useTranslationContext(); + const previous1: Contact[] = []; + const previous2: Contact[] = [ + { + name: "International Bank", + icon: bankIcon, + description: "account ending with 3454", + }, + { + name: "Max", + icon: bankIcon, + description: "account ending with 3454", + }, + { + name: "Alex", + icon: bankIcon, + description: "account ending with 3454", + }, + ]; + + if (!currency) { + return <div>currency not provided</div>; + } + const currencyAndAmount = `${currency}:${amount}`; + const parsedAmount = Amounts.parse(currencyAndAmount); + const invalid = !parsedAmount || Amounts.isZero(parsedAmount); return ( <Container> - <p>Sending {currency} to:</p> - <Paper style={{ padding: 8 }}>Bank account</Paper> - <Paper style={{ padding: 8 }}>Another person</Paper> + <h1> + <i18n.Translate>Specify the amount and the destination</i18n.Translate> + </h1> + + <TextField + label="Amount" + type="number" + variant="filled" + required + error={invalid} + startAdornment={ + <div style={{ padding: "25px 12px 8px 12px" }}>{currency}</div> + } + value={amount} + onChange={(e) => { + setAmount(e); + }} + /> + + <Grid container spacing={1} columns={1}> + {previous2.length > 0 ? ( + <Fragment> + <p>Previous destinations:</p> + <Grid item xs={1}> + <Paper style={{ padding: 8 }}> + <ContactTable> + {previous2.map((info, i) => ( + <tr key={i}> + <td> + <RowExample info={info} disabled={invalid} /> + </td> + </tr> + ))} + </ContactTable> + </Paper> + </Grid> + </Fragment> + ) : undefined} + <Grid item> + <p>Create a destination for the money</p> + </Grid> + <Grid item container columns={3} spacing={1}> + <Grid item xs={1}> + <Paper style={{ padding: 8 }}> + <p>To my bank account</p> + <Button disabled={invalid}>Deposit</Button> + </Paper> + </Grid> + <Grid item xs={1}> + <Paper style={{ padding: 8 }}> + <p>To someone else</p> + <Button disabled>Send</Button> + </Paper> + </Grid> + <Grid item xs={1}> + <Paper style={{ padding: 8 }}> + <p>To a business or charity</p> + <Button disabled>Pay</Button> + </Paper> + </Grid> + </Grid> + <Grid item columns={1} spacing={1} xs={1}> + <Paper style={{ padding: 8 }}> + <p>To an exchange reserve or purse</p> + <Button disabled>Create</Button> + </Paper> + </Grid> + </Grid> </Container> ); } |