summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx')
-rw-r--r--packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx430
1 files changed, 430 insertions, 0 deletions
diff --git a/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx
new file mode 100644
index 000000000..8a74a20f1
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/DestinationSelection/views.tsx
@@ -0,0 +1,430 @@
+/*
+ This file is part of GNU Taler
+ (C) 2022 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+import { useTranslationContext } from "@gnu-taler/web-util/browser";
+import { styled } from "@linaria/react";
+import { Fragment, h, VNode } from "preact";
+import { AmountField } from "../../components/AmountField.js";
+import { EnabledBySettings } from "../../components/EnabledBySettings.js";
+import { SelectList } from "../../components/SelectList.js";
+import {
+ Input,
+ LightText,
+ LinkPrimary,
+ SvgIcon,
+} from "../../components/styled/index.js";
+import { Button } from "../../mui/Button.js";
+import { Grid } from "../../mui/Grid.js";
+import { Paper } from "../../mui/Paper.js";
+import { Pages } from "../../NavigationBar.js";
+import arrowIcon from "../../svg/chevron-down.inline.svg";
+import bankIcon from "../../svg/ri-bank-line.inline.svg";
+import { assertUnreachable } from "../../utils/index.js";
+import { Contact, State } from "./index.js";
+
+export function SelectCurrencyView({
+ currencies,
+ onCurrencySelected,
+}: State.SelectCurrency): VNode {
+ const { i18n } = useTranslationContext();
+
+ return (
+ <Fragment>
+ <h2>
+ <i18n.Translate>
+ Choose a currency to proceed or add another exchange
+ </i18n.Translate>
+ </h2>
+
+ <p>
+ <Input>
+ <SelectList
+ label={i18n.str`Known currencies`}
+ list={currencies}
+ name="lang"
+ value={""}
+ onChange={(v) => onCurrencySelected(v)}
+ />
+ </Input>
+ </p>
+ <div style={{ display: "flex", justifyContent: "space-between" }}>
+ <div />
+ <LinkPrimary href={Pages.settingsExchangeAdd({})}>
+ <i18n.Translate>Add an exchange</i18n.Translate>
+ </LinkPrimary>
+ </div>
+ </Fragment>
+ );
+}
+
+const Container = styled.div`
+ display: flex;
+ flex-direction: column;
+ & > * {
+ margin: 8px;
+ }
+`;
+
+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;
+`;
+
+export function ReadyView(props: State.Ready): VNode {
+ switch (props.type) {
+ case "get":
+ return ReadyGetView(props);
+ case "send":
+ return ReadySendView(props);
+ default:
+ assertUnreachable(props.type);
+ }
+}
+export function ReadyGetView({
+ amountHandler,
+ goToBank,
+ goToWallet,
+ selectCurrency,
+ previous,
+}: State.Ready): VNode {
+ const { i18n } = useTranslationContext();
+
+ return (
+ <Container>
+ <h1>
+ <i18n.Translate>Specify the amount and the origin</i18n.Translate>
+ </h1>
+ <Grid container columns={2} justifyContent="space-between">
+ <AmountField
+ label={i18n.str`Amount`}
+ required
+ handler={amountHandler}
+ />
+
+ <Button onClick={selectCurrency.onClick}>
+ <i18n.Translate>Change currency</i18n.Translate>
+ </Button>
+ </Grid>
+
+ <Grid container spacing={1} columns={1}>
+ {previous.length > 0 ? (
+ <Fragment>
+ <p>
+ <i18n.Translate>Use previous origins:</i18n.Translate>
+ </p>
+ <Grid item xs={1}>
+ <Paper style={{ padding: 8 }}>
+ <ContactTable>
+ {previous.map((info, i) => (
+ <tr key={i}>
+ <td>
+ <RowExample
+ info={info}
+ disabled={!amountHandler.onInput}
+ />
+ </td>
+ </tr>
+ ))}
+ </ContactTable>
+ </Paper>
+ </Grid>
+ </Fragment>
+ ) : undefined}
+ {previous.length > 0 ? (
+ <Grid item>
+ <p>
+ <i18n.Translate>
+ Or specify the origin of the money
+ </i18n.Translate>
+ </p>
+ </Grid>
+ ) : (
+ <Grid item>
+ <p>
+ <i18n.Translate>Specify the origin of the money</i18n.Translate>
+ </p>
+ </Grid>
+ )}
+ <Grid item container columns={2} spacing={1}>
+ <Grid item xs={1}>
+ <Paper style={{ padding: 8 }}>
+ <p>
+ <i18n.Translate>From my bank account</i18n.Translate>
+ </p>
+ <Button onClick={goToBank.onClick}>
+ <i18n.Translate>Withdraw</i18n.Translate>
+ </Button>
+ </Paper>
+ </Grid>
+ <Grid item xs={1}>
+ <Paper style={{ padding: 8 }}>
+ <p>
+ <i18n.Translate>From another wallet</i18n.Translate>
+ </p>
+ <Button onClick={goToWallet.onClick}>
+ <i18n.Translate>Invoice</i18n.Translate>
+ </Button>
+ </Paper>
+ </Grid>
+ <Grid item xs={1}>
+ <Paper style={{ padding: 8 }}>
+ <p>
+ <i18n.Translate>From a <pre style={{display:"inline"}}>taler://peer-push-credit</pre> URI</i18n.Translate>
+ </p>
+ <a href={Pages.qr}>
+ <i18n.Translate>Enter URI here</i18n.Translate>
+ </a>
+ </Paper>
+ </Grid>
+ </Grid>
+ </Grid>
+ </Container>
+ );
+}
+export function ReadySendView({
+ amountHandler,
+ goToBank,
+ goToWallet,
+ previous,
+ selectMax,
+}: State.Ready): VNode {
+ const { i18n } = useTranslationContext();
+
+ return (
+ <Container>
+ <h1>
+ <i18n.Translate>Specify the amount and the destination</i18n.Translate>
+ </h1>
+
+ <Grid container columns={2} justifyContent="space-between">
+ <AmountField
+ label={i18n.str`Amount`}
+ required
+ handler={amountHandler}
+ />
+ <EnabledBySettings name="advancedMode">
+ <Button onClick={selectMax.onClick}>
+ <i18n.Translate>Send all</i18n.Translate>
+ </Button>
+ </EnabledBySettings>
+ </Grid>
+
+ <Grid container spacing={1} columns={1}>
+ {previous.length > 0 ? (
+ <Fragment>
+ <p>
+ <i18n.Translate>Use previous destinations:</i18n.Translate>
+ </p>
+ <Grid item xs={1}>
+ <Paper style={{ padding: 8 }}>
+ <ContactTable>
+ {previous.map((info, i) => (
+ <tr key={i}>
+ <td>
+ <RowExample
+ info={info}
+ disabled={!amountHandler.onInput}
+ />
+ </td>
+ </tr>
+ ))}
+ </ContactTable>
+ </Paper>
+ </Grid>
+ </Fragment>
+ ) : undefined}
+ {previous.length > 0 ? (
+ <Grid item>
+ <p>
+ <i18n.Translate>
+ Or specify the destination of the money
+ </i18n.Translate>
+ </p>
+ </Grid>
+ ) : (
+ <Grid item>
+ <p>
+ <i18n.Translate>
+ Specify the destination of the money
+ </i18n.Translate>
+ </p>
+ </Grid>
+ )}
+ <Grid item container columns={2} spacing={1}>
+ <Grid item xs={1}>
+ <Paper style={{ padding: 8 }}>
+ <p>
+ <i18n.Translate>To my bank account</i18n.Translate>
+ </p>
+ <Button onClick={goToBank.onClick}>
+ <i18n.Translate>Deposit</i18n.Translate>
+ </Button>
+ </Paper>
+ </Grid>
+ <Grid item xs={1}>
+ <Paper style={{ padding: 8 }}>
+ <p>
+ <i18n.Translate>To another wallet</i18n.Translate>
+ </p>
+ <Button onClick={goToWallet.onClick}>
+ <i18n.Translate>Send</i18n.Translate>
+ </Button>
+ </Paper>
+ </Grid>
+ </Grid>
+ </Grid>
+ </Container>
+ );
+}
+
+function RowExample({
+ info,
+ disabled,
+}: {
+ info: Contact;
+ disabled?: boolean;
+}): VNode {
+ const icon = info.icon_type === "bank" ? bankIcon : undefined;
+ return (
+ <MediaExample data-disabled={disabled}>
+ <MediaLeft>
+ <CircleDiv>
+ {icon !== undefined ? (
+ <SvgIcon
+ title={info.name}
+ dangerouslySetInnerHTML={{
+ __html: icon,
+ }}
+ color="currentColor"
+ />
+ ) : (
+ <span>A</span>
+ )}
+ </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>
+ );
+}