mui button impl

This commit is contained in:
Sebastian 2022-06-01 15:47:47 -03:00
parent 2aade8e7ae
commit af7b107f45
No known key found for this signature in database
GPG Key ID: BE4FF68352439FC1
37 changed files with 476 additions and 367 deletions

View File

@ -77,8 +77,8 @@ export const BasicExample = (): VNode => (
]} ]}
confirm={{ confirm={{
label: "turn on wifi", label: "turn on wifi",
action: () => { action: async () => {
return null; return;
}, },
}} }}
/> />

View File

@ -15,7 +15,7 @@ interface Props extends JSX.HTMLAttributes<HTMLDivElement> {
}[]; }[];
confirm?: { confirm?: {
label: string; label: string;
action: () => void; action: () => Promise<void>;
}; };
} }

View File

@ -18,7 +18,7 @@ import { h, VNode } from "preact";
interface Props { interface Props {
enabled?: boolean; enabled?: boolean;
onToggle?: () => void; onToggle?: () => Promise<void>;
label: VNode; label: VNode;
name: string; name: string;
description?: VNode; description?: VNode;

View File

@ -19,7 +19,7 @@ import { h, VNode } from "preact";
interface Props { interface Props {
enabled: boolean; enabled: boolean;
onToggle: () => void; onToggle: () => Promise<void>;
label: VNode; label: VNode;
name: string; name: string;
} }

View File

@ -1,16 +1,14 @@
import { getUnpackedSettings } from "http2";
import { h, VNode } from "preact"; import { h, VNode } from "preact";
import arrowDown from "../svg/chevron-down.svg";
import {
ButtonBoxPrimary,
ButtonPrimary,
ParagraphClickable,
} from "./styled/index.js";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { Button } from "../mui/Button.js";
import arrowDown from "../svg/chevron-down.svg";
import { ParagraphClickable } from "./styled/index.js";
export interface Props { export interface Props {
label: (s: string) => VNode; label: (s: string) => VNode;
actions: string[]; actions: string[];
onClick: (s: string) => void; onClick: (s: string) => Promise<void>;
} }
/** /**
@ -43,9 +41,9 @@ export function MultiActionButton({
if (!canChange) { if (!canChange) {
return ( return (
<ButtonPrimary onClick={() => doClick(selected)}> <Button variant="contained" onClick={() => doClick(selected)}>
{label(selected)} {label(selected)}
</ButtonPrimary> </Button>
); );
} }
@ -73,40 +71,44 @@ export function MultiActionButton({
))} ))}
</div> </div>
)} )}
<ButtonBoxPrimary <Button
variant="contained"
onClick={() => doClick(selected)} onClick={() => doClick(selected)}
style={{ style={{
borderTopRightRadius: 0, borderTopRightRadius: 0,
borderBottomRightRadius: 0, borderBottomRightRadius: 0,
marginRight: 0, marginRight: 0,
maxWidth: 170, // maxWidth: 170,
overflowX: "hidden", overflowX: "hidden",
textOverflow: "ellipsis", textOverflow: "ellipsis",
}} }}
> >
{label(selected)} {label(selected)}
</ButtonBoxPrimary> </Button>
<ButtonPrimary <Button
onClick={() => setOpened((s) => !s)} variant="outlined"
onClick={async () => setOpened((s) => !s)}
style={{ style={{
marginLeft: 0, marginLeft: 0,
borderTopLeftRadius: 0, borderTopLeftRadius: 0,
borderBottomLeftRadius: 0, borderBottomLeftRadius: 0,
width: 36, paddingLeft: 4,
padding: 4, paddingRight: 4,
height: 36, minWidth: "unset",
fill: "white",
}} }}
> >
<div <div
style={{ style={{
height: 24, height: 24,
width: 24, width: 24,
marginLeft: 4,
marginRight: 4,
// fill: "white",
}} }}
dangerouslySetInnerHTML={{ __html: arrowDown }} dangerouslySetInnerHTML={{ __html: arrowDown }}
/> />
</ButtonPrimary> </Button>
</div> </div>
); );
} }

View File

@ -25,11 +25,11 @@ import { useLocalStorage } from "../hooks/useLocalStorage.js";
interface Type { interface Type {
devMode: boolean; devMode: boolean;
toggleDevMode: () => void; toggleDevMode: () => Promise<void>;
} }
const Context = createContext<Type>({ const Context = createContext<Type>({
devMode: false, devMode: false,
toggleDevMode: () => null, toggleDevMode: async () => { return; },
}); });
export const useDevContext = (): Type => useContext(Context); export const useDevContext = (): Type => useContext(Context);
@ -44,8 +44,8 @@ export const DevContextProviderForTesting = ({
return h(Context.Provider, { return h(Context.Provider, {
value: { value: {
devMode: value, devMode: value,
toggleDevMode: () => { toggleDevMode: async () => {
null; return;
}, },
}, },
children, children,
@ -55,7 +55,7 @@ export const DevContextProviderForTesting = ({
export const DevContextProvider = ({ children }: { children: any }): VNode => { export const DevContextProvider = ({ children }: { children: any }): VNode => {
const [value, setter] = useLocalStorage("devMode"); const [value, setter] = useLocalStorage("devMode");
const devMode = value === "true"; const devMode = value === "true";
const toggleDevMode = (): void => setter((v) => (!v ? "true" : undefined)); const toggleDevMode = async (): Promise<void> => setter((v) => (!v ? "true" : undefined));
children = children =
children.length === 1 && typeof children === "function" children.length === 1 && typeof children === "function"
? children({ devMode }) ? children({ devMode })

View File

@ -44,13 +44,14 @@ import {
} from "../components/styled/index.js"; } from "../components/styled/index.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
import { Button } from "../mui/Button.js";
import { ButtonHandler } from "../mui/handlers.js"; import { ButtonHandler } from "../mui/handlers.js";
import * as wxApi from "../wxApi.js"; import * as wxApi from "../wxApi.js";
interface Props { interface Props {
talerDepositUri?: string; talerDepositUri?: string;
amount: AmountString; amount: AmountString;
goBack: () => void; goBack: () => Promise<void>;
} }
type State = Loading | Ready | Completed; type State = Loading | Ready | Completed;
@ -206,11 +207,15 @@ export function View({ state }: ViewProps): VNode {
/> />
</section> </section>
<section> <section>
<ButtonSuccess upperCased onClick={state.confirm.onClick}> <Button
variant="contained"
color="success"
onClick={state.confirm.onClick}
>
<i18n.Translate> <i18n.Translate>
Deposit {<Amount value={state.effective} />} Deposit {<Amount value={state.effective} />}
</i18n.Translate> </i18n.Translate>
</ButtonSuccess> </Button>
</section> </section>
</WalletAction> </WalletAction>
); );

View File

@ -33,6 +33,10 @@ export default {
argTypes: {}, argTypes: {},
}; };
const noop = async (): Promise<void> => {
return;
};
export const NoBalance = createExample(TestedComponent, { export const NoBalance = createExample(TestedComponent, {
state: { state: {
status: "ready", status: "ready",
@ -61,8 +65,8 @@ export const NoBalance = createExample(TestedComponent, {
amountRaw: "USD:10", amountRaw: "USD:10",
}, },
}, },
goBack: () => null, goBack: noop,
goToWalletManualWithdraw: () => null, goToWalletManualWithdraw: noop,
}); });
export const NoEnoughBalance = createExample(TestedComponent, { export const NoEnoughBalance = createExample(TestedComponent, {
@ -97,8 +101,8 @@ export const NoEnoughBalance = createExample(TestedComponent, {
amountRaw: "USD:10", amountRaw: "USD:10",
}, },
}, },
goBack: () => null, goBack: noop,
goToWalletManualWithdraw: () => null, goToWalletManualWithdraw: noop,
}); });
export const EnoughBalanceButRestricted = createExample(TestedComponent, { export const EnoughBalanceButRestricted = createExample(TestedComponent, {
@ -133,8 +137,8 @@ export const EnoughBalanceButRestricted = createExample(TestedComponent, {
amountRaw: "USD:10", amountRaw: "USD:10",
}, },
}, },
goBack: () => null, goBack: noop,
goToWalletManualWithdraw: () => null, goToWalletManualWithdraw: noop,
}); });
export const PaymentPossible = createExample(TestedComponent, { export const PaymentPossible = createExample(TestedComponent, {
@ -172,8 +176,8 @@ export const PaymentPossible = createExample(TestedComponent, {
proposalId: "proposal1234", proposalId: "proposal1234",
}, },
}, },
goBack: () => null, goBack: noop,
goToWalletManualWithdraw: () => null, goToWalletManualWithdraw: noop,
}); });
export const PaymentPossibleWithFee = createExample(TestedComponent, { export const PaymentPossibleWithFee = createExample(TestedComponent, {
@ -211,8 +215,8 @@ export const PaymentPossibleWithFee = createExample(TestedComponent, {
proposalId: "proposal1234", proposalId: "proposal1234",
}, },
}, },
goBack: () => null, goBack: noop,
goToWalletManualWithdraw: () => null, goToWalletManualWithdraw: noop,
}); });
import beer from "../../static-dev/beer.png"; import beer from "../../static-dev/beer.png";
@ -271,8 +275,8 @@ export const TicketWithAProductList = createExample(TestedComponent, {
proposalId: "proposal1234", proposalId: "proposal1234",
}, },
}, },
goBack: () => null, goBack: noop,
goToWalletManualWithdraw: () => null, goToWalletManualWithdraw: noop,
}); });
export const AlreadyConfirmedByOther = createExample(TestedComponent, { export const AlreadyConfirmedByOther = createExample(TestedComponent, {
@ -309,8 +313,8 @@ export const AlreadyConfirmedByOther = createExample(TestedComponent, {
paid: false, paid: false,
}, },
}, },
goBack: () => null, goBack: noop,
goToWalletManualWithdraw: () => null, goToWalletManualWithdraw: noop,
}); });
export const AlreadyPaidWithoutFulfillment = createExample(TestedComponent, { export const AlreadyPaidWithoutFulfillment = createExample(TestedComponent, {
@ -347,8 +351,8 @@ export const AlreadyPaidWithoutFulfillment = createExample(TestedComponent, {
paid: true, paid: true,
}, },
}, },
goBack: () => null, goBack: noop,
goToWalletManualWithdraw: () => null, goToWalletManualWithdraw: noop,
}); });
export const AlreadyPaidWithFulfillment = createExample(TestedComponent, { export const AlreadyPaidWithFulfillment = createExample(TestedComponent, {
@ -387,6 +391,6 @@ export const AlreadyPaidWithFulfillment = createExample(TestedComponent, {
paid: true, paid: true,
}, },
}, },
goBack: () => null, goBack: noop,
goToWalletManualWithdraw: () => null, goToWalletManualWithdraw: noop,
}); });

View File

@ -60,13 +60,14 @@ import {
} from "../components/styled/index.js"; } from "../components/styled/index.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
import { Button } from "../mui/Button.js";
import { ButtonHandler } from "../mui/handlers.js"; import { ButtonHandler } from "../mui/handlers.js";
import * as wxApi from "../wxApi.js"; import * as wxApi from "../wxApi.js";
interface Props { interface Props {
talerPayUri?: string; talerPayUri?: string;
goToWalletManualWithdraw: (currency?: string) => void; goToWalletManualWithdraw: (currency?: string) => Promise<void>;
goBack: () => void; goBack: () => Promise<void>;
} }
type State = Loading | Ready | Confirmed; type State = Loading | Ready | Confirmed;
@ -265,8 +266,8 @@ export function View({
goToWalletManualWithdraw, goToWalletManualWithdraw,
}: { }: {
state: Ready | Confirmed; state: Ready | Confirmed;
goToWalletManualWithdraw: (currency?: string) => void; goToWalletManualWithdraw: (currency?: string) => Promise<void>;
goBack: () => void; goBack: () => Promise<void>;
}): VNode { }): VNode {
const { i18n } = useTranslationContext(); const { i18n } = useTranslationContext();
const contractTerms: ContractTerms = state.payStatus.contractTerms; const contractTerms: ContractTerms = state.payStatus.contractTerms;
@ -522,7 +523,7 @@ function ButtonsSection({
goToWalletManualWithdraw, goToWalletManualWithdraw,
}: { }: {
state: Ready | Confirmed; state: Ready | Confirmed;
goToWalletManualWithdraw: (currency: string) => void; goToWalletManualWithdraw: (currency: string) => Promise<void>;
}): VNode { }): VNode {
const { i18n } = useTranslationContext(); const { i18n } = useTranslationContext();
if (state.status === "ready") { if (state.status === "ready") {
@ -531,11 +532,15 @@ function ButtonsSection({
return ( return (
<Fragment> <Fragment>
<section> <section>
<ButtonSuccess upperCased onClick={state.payHandler.onClick}> <Button
variant="contained"
color="success"
onClick={state.payHandler.onClick}
>
<i18n.Translate> <i18n.Translate>
Pay {<Amount value={payStatus.amountEffective} />} Pay {<Amount value={payStatus.amountEffective} />}
</i18n.Translate> </i18n.Translate>
</ButtonSuccess> </Button>
</section> </section>
<PayWithMobile state={state} /> <PayWithMobile state={state} />
</Fragment> </Fragment>
@ -560,12 +565,13 @@ function ButtonsSection({
<WarningBox>{BalanceMessage}</WarningBox> <WarningBox>{BalanceMessage}</WarningBox>
</section> </section>
<section> <section>
<ButtonSuccess <Button
upperCased variant="contained"
color="success"
onClick={() => goToWalletManualWithdraw(state.amount.currency)} onClick={() => goToWalletManualWithdraw(state.amount.currency)}
> >
<i18n.Translate>Withdraw digital cash</i18n.Translate> <i18n.Translate>Withdraw digital cash</i18n.Translate>
</ButtonSuccess> </Button>
</section> </section>
<PayWithMobile state={state} /> <PayWithMobile state={state} />
</Fragment> </Fragment>

View File

@ -40,6 +40,7 @@ import {
} from "../components/styled/index.js"; } from "../components/styled/index.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
import { Button } from "../mui/Button.js";
import { ButtonHandler } from "../mui/handlers.js"; import { ButtonHandler } from "../mui/handlers.js";
import * as wxApi from "../wxApi.js"; import * as wxApi from "../wxApi.js";
import { ProductList } from "./Pay.js"; import { ProductList } from "./Pay.js";
@ -188,9 +189,9 @@ export function View({ state }: ViewProps): VNode {
</section> </section>
) : undefined} ) : undefined}
<section> <section>
<ButtonSuccess onClick={state.accept.onClick}> <Button variant="contained" onClick={state.accept.onClick}>
<i18n.Translate>Confirm refund</i18n.Translate> <i18n.Translate>Confirm refund</i18n.Translate>
</ButtonSuccess> </Button>
</section> </section>
</WalletAction> </WalletAction>
); );

View File

@ -2,14 +2,13 @@ import { Fragment, h, VNode } from "preact";
import { CheckboxOutlined } from "../components/CheckboxOutlined.js"; import { CheckboxOutlined } from "../components/CheckboxOutlined.js";
import { ExchangeXmlTos } from "../components/ExchangeToS.js"; import { ExchangeXmlTos } from "../components/ExchangeToS.js";
import { import {
ButtonSuccess,
ButtonWarning,
LinkSuccess, LinkSuccess,
TermsOfService, TermsOfService,
WarningBox, WarningBox,
WarningText, WarningText,
} from "../components/styled/index.js"; } from "../components/styled/index.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { Button } from "../mui/Button.js";
import { TermsState } from "../utils/index.js"; import { TermsState } from "../utils/index.js";
export interface Props { export interface Props {
@ -58,20 +57,28 @@ export function TermsOfServiceSection({
)} )}
{terms.status === "new" && ( {terms.status === "new" && (
<section> <section>
<ButtonSuccess upperCased onClick={() => onReview(true)}> <Button
variant="contained"
color="success"
onClick={async () => onReview(true)}
>
<i18n.Translate> <i18n.Translate>
Review exchange terms of service Review exchange terms of service
</i18n.Translate> </i18n.Translate>
</ButtonSuccess> </Button>
</section> </section>
)} )}
{terms.status === "changed" && ( {terms.status === "changed" && (
<section> <section>
<ButtonWarning upperCased onClick={() => onReview(true)}> <Button
variant="contained"
color="success"
onClick={async () => onReview(true)}
>
<i18n.Translate> <i18n.Translate>
Review new version of terms of service Review new version of terms of service
</i18n.Translate> </i18n.Translate>
</ButtonWarning> </Button>
</section> </section>
)} )}
</Fragment> </Fragment>
@ -95,7 +102,7 @@ export function TermsOfServiceSection({
I accept the exchange terms of service I accept the exchange terms of service
</i18n.Translate> </i18n.Translate>
} }
onToggle={() => { onToggle={async () => {
onAccept(!reviewed); onAccept(!reviewed);
if (ableToReviewTermsOfService) onReview(false); if (ableToReviewTermsOfService) onReview(false);
}} }}
@ -154,7 +161,7 @@ export function TermsOfServiceSection({
I accept the exchange terms of service I accept the exchange terms of service
</i18n.Translate> </i18n.Translate>
} }
onToggle={() => { onToggle={async () => {
onAccept(!reviewed); onAccept(!reviewed);
if (ableToReviewTermsOfService) onReview(false); if (ableToReviewTermsOfService) onReview(false);
}} }}

View File

@ -210,9 +210,13 @@ export function View({ state }: { state: State }): VNode {
/> />
</section> </section>
<section> <section>
<ButtonSuccess onClick={state.accept.onClick}> <Button
variant="contained"
color="success"
onClick={state.accept.onClick}
>
<i18n.Translate>Accept tip</i18n.Translate> <i18n.Translate>Accept tip</i18n.Translate>
</ButtonSuccess> </Button>
<Button onClick={state.ignore.onClick}> <Button onClick={state.ignore.onClick}>
<i18n.Translate>Ignore</i18n.Translate> <i18n.Translate>Ignore</i18n.Translate>
</Button> </Button>

View File

@ -33,8 +33,6 @@ import { LogoHeader } from "../components/LogoHeader.js";
import { Part } from "../components/Part.js"; import { Part } from "../components/Part.js";
import { SelectList } from "../components/SelectList.js"; import { SelectList } from "../components/SelectList.js";
import { import {
ButtonSuccess,
ButtonWarning,
Input, Input,
LinkSuccess, LinkSuccess,
SubTitle, SubTitle,
@ -43,19 +41,14 @@ import {
} from "../components/styled/index.js"; } from "../components/styled/index.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
import { Button } from "../mui/Button.js";
import { ButtonHandler, SelectFieldHandler } from "../mui/handlers.js";
import { buildTermsOfServiceState } from "../utils/index.js"; import { buildTermsOfServiceState } from "../utils/index.js";
import {
ButtonHandler,
SelectFieldHandler,
ToggleHandler,
} from "../mui/handlers.js";
import * as wxApi from "../wxApi.js"; import * as wxApi from "../wxApi.js";
import { import {
Props as TermsOfServiceSectionProps, Props as TermsOfServiceSectionProps,
TermsOfServiceSection, TermsOfServiceSection,
} from "./TermsOfServiceSection.js"; } from "./TermsOfServiceSection.js";
import { startOfWeekYear } from "date-fns/esm";
import { Checkbox } from "../components/Checkbox.js";
interface Props { interface Props {
talerWithdrawUri?: string; talerWithdrawUri?: string;
@ -527,22 +520,24 @@ export function View({ state }: { state: State }): VNode {
<section> <section>
{(state.tosProps.terms.status === "accepted" || {(state.tosProps.terms.status === "accepted" ||
(state.mustAcceptFirst && state.tosProps.reviewed)) && ( (state.mustAcceptFirst && state.tosProps.reviewed)) && (
<ButtonSuccess <Button
upperCased variant="contained"
color="success"
disabled={!state.doWithdrawal.onClick} disabled={!state.doWithdrawal.onClick}
onClick={state.doWithdrawal.onClick} onClick={state.doWithdrawal.onClick}
> >
<i18n.Translate>Confirm withdrawal</i18n.Translate> <i18n.Translate>Confirm withdrawal</i18n.Translate>
</ButtonSuccess> </Button>
)} )}
{state.tosProps.terms.status === "notfound" && ( {state.tosProps.terms.status === "notfound" && (
<ButtonWarning <Button
upperCased variant="contained"
color="warning"
disabled={!state.doWithdrawal.onClick} disabled={!state.doWithdrawal.onClick}
onClick={state.doWithdrawal.onClick} onClick={state.doWithdrawal.onClick}
> >
<i18n.Translate>Withdraw anyway</i18n.Translate> <i18n.Translate>Withdraw anyway</i18n.Translate>
</ButtonWarning> </Button>
)} )}
</section> </section>
) : ( ) : (

View File

@ -68,18 +68,22 @@ export const WithTitle = (): VNode => (
</Wrapper> </Wrapper>
); );
const showSomething = async function (): Promise<void> {
alert("closed");
};
export const WithAction = (): VNode => ( export const WithAction = (): VNode => (
<Wrapper> <Wrapper>
<Alert title="Warning" severity="warning" onClose={() => alert("closed")}> <Alert title="Warning" severity="warning" onClose={showSomething}>
this is an warning this is an warning
</Alert> </Alert>
<Alert title="Error" severity="error" onClose={() => alert("closed")}> <Alert title="Error" severity="error" onClose={showSomething}>
this is an error this is an error
</Alert> </Alert>
<Alert title="Success" severity="success" onClose={() => alert("closed")}> <Alert title="Success" severity="success" onClose={showSomething}>
this is an success this is an success
</Alert> </Alert>
<Alert title="Info" severity="info" onClose={() => alert("closed")}> <Alert title="Info" severity="info" onClose={showSomething}>
this is an info this is an info
</Alert> </Alert>
</Wrapper> </Wrapper>

View File

@ -49,7 +49,7 @@ interface Props {
title?: string; title?: string;
variant?: "filled" | "outlined" | "standard"; variant?: "filled" | "outlined" | "standard";
role?: string; role?: string;
onClose?: () => void; onClose?: () => Promise<void>;
// icon: VNode; // icon: VNode;
severity?: "info" | "warning" | "success" | "error"; severity?: "info" | "warning" | "success" | "error";
children: ComponentChildren; children: ComponentChildren;

View File

@ -1,7 +1,7 @@
import { ComponentChildren, h, VNode, JSX } from "preact"; import { ComponentChildren, h, VNode, JSX } from "preact";
import { css } from "@linaria/core"; import { css } from "@linaria/core";
// eslint-disable-next-line import/extensions // eslint-disable-next-line import/extensions
import { theme, ripple, Colors } from "./style"; import { theme, ripple, Colors, rippleOutlined } from "./style";
// eslint-disable-next-line import/extensions // eslint-disable-next-line import/extensions
import { alpha } from "./colors/manipulation"; import { alpha } from "./colors/manipulation";
@ -31,12 +31,13 @@ interface Props {
disableFocusRipple?: boolean; disableFocusRipple?: boolean;
endIcon?: string | VNode; endIcon?: string | VNode;
fullWidth?: boolean; fullWidth?: boolean;
style?: h.JSX.CSSProperties;
href?: string; href?: string;
size?: "small" | "medium" | "large"; size?: "small" | "medium" | "large";
startIcon?: VNode | string; startIcon?: VNode | string;
variant?: "contained" | "outlined" | "text"; variant?: "contained" | "outlined" | "text";
color?: Colors; color?: Colors;
onClick?: () => void; onClick?: () => Promise<void>;
} }
const button = css` const button = css`
@ -199,6 +200,7 @@ export function Button({
fullWidth, fullWidth,
variant = "text", variant = "text",
size = "medium", size = "medium",
style: parentStyle,
color = "primary", color = "primary",
onClick, onClick,
}: Props): VNode { }: Props): VNode {
@ -267,12 +269,15 @@ export function Button({
colorVariant[variant], colorVariant[variant],
sizeVariant[variant][size], sizeVariant[variant][size],
].join(" ")} ].join(" ")}
containedRipple={variant === "contained"}
onClick={onClick} onClick={onClick}
style={{ style={{
...parentStyle,
"--color-main": theme.palette[color].main, "--color-main": theme.palette[color].main,
"--color-contrastText": theme.palette[color].contrastText, "--color-contrastText": theme.palette[color].contrastText,
"--color-main-alpha-half": alpha(theme.palette[color].main, 0.5), "--color-main-alpha-half": alpha(theme.palette[color].main, 0.5),
"--color-dark": theme.palette[color].dark, "--color-dark": theme.palette[color].dark,
"--color-light": theme.palette[color].light,
"--color-main-alpha-opacity": alpha( "--color-main-alpha-opacity": alpha(
theme.palette[color].main, theme.palette[color].main,
theme.palette.action.hoverOpacity, theme.palette.action.hoverOpacity,
@ -295,13 +300,15 @@ export function Button({
interface BaseProps extends JSX.HTMLAttributes<HTMLButtonElement> { interface BaseProps extends JSX.HTMLAttributes<HTMLButtonElement> {
class: string; class: string;
onClick?: () => void; onClick?: () => Promise<void>;
containedRipple?: boolean;
children?: ComponentChildren; children?: ComponentChildren;
} }
function ButtonBase({ function ButtonBase({
class: _class, class: _class,
children, children,
containedRipple,
onClick, onClick,
dangerouslySetInnerHTML, dangerouslySetInnerHTML,
...rest ...rest
@ -309,7 +316,11 @@ function ButtonBase({
function doClick(): void { function doClick(): void {
if (onClick) onClick(); if (onClick) onClick();
} }
const classNames = [buttonBaseStyle, _class, ripple].join(" "); const classNames = [
buttonBaseStyle,
_class,
containedRipple ? ripple : rippleOutlined,
].join(" ");
if (dangerouslySetInnerHTML) { if (dangerouslySetInnerHTML) {
return ( return (
<button <button
@ -332,7 +343,7 @@ export function IconButton({
onClick, onClick,
}: { }: {
svg: any; svg: any;
onClick?: () => void; onClick?: () => Promise<void>;
}): VNode { }): VNode {
return ( return (
<ButtonBase <ButtonBase

View File

@ -46,13 +46,33 @@ export const theme = createTheme();
export const ripple = css` export const ripple = css`
background-position: center; background-position: center;
transition: background 0.5s; transition: background 0.2s;
&:hover {
background: #eeeeee radial-gradient(circle, transparent 1%, #eeeeee 1%) &:hover:enabled {
background: var(--color-main)
radial-gradient(circle, transparent 1%, var(--color-dark) 1%)
center/15000%; center/15000%;
} }
&:active { &:active:enabled {
background-color: currentColor; background-color: var(--color-main);
background-size: 100%;
transition: background 0s;
}
`;
export const rippleOutlined = css`
background-position: center;
transition: background 0.2s;
&:hover:enabled {
background: var(--color-contrastText)
radial-gradient(circle, transparent 1%, var(--color-light) 1%)
center/15000%;
}
&:active:enabled {
background-color: var(--color-contrastText);
background-size: 100%; background-size: 100%;
transition: background 0s; transition: background 0s;
} }
@ -680,15 +700,15 @@ function createTheme() {
function getDefaultSecondary(mode = "light") { function getDefaultSecondary(mode = "light") {
if (mode === "dark") { if (mode === "dark") {
return { return {
main: purple[200], main: grey[200],
light: purple[50], light: grey[50],
dark: purple[400], dark: grey[400],
}; };
} }
return { return {
main: purple[500], main: grey[300],
light: purple[300], light: grey[100],
dark: purple[700], dark: grey[600],
}; };
} }

View File

@ -61,7 +61,7 @@ export function Application(): VNode {
<IoCProviderForRuntime> <IoCProviderForRuntime>
<PendingTransactions <PendingTransactions
goToTransaction={(txId: string) => goToTransaction={(txId: string) =>
route(Pages.balance_transaction.replace(":tid", txId)) redirectTo(Pages.balance_transaction.replace(":tid", txId))
} }
/> />
<Match> <Match>
@ -74,15 +74,19 @@ export function Application(): VNode {
path={Pages.balance} path={Pages.balance}
component={BalancePage} component={BalancePage}
goToWalletManualWithdraw={() => goToWalletManualWithdraw={() =>
route( redirectTo(
Pages.balance_manual_withdraw.replace(":currency?", ""), Pages.balance_manual_withdraw.replace(":currency?", ""),
) )
} }
goToWalletDeposit={(currency: string) => goToWalletDeposit={(currency: string) =>
route(Pages.balance_deposit.replace(":currency", currency)) redirectTo(
Pages.balance_deposit.replace(":currency", currency),
)
} }
goToWalletHistory={(currency: string) => goToWalletHistory={(currency: string) =>
route(Pages.balance_history.replace(":currency?", currency)) redirectTo(
Pages.balance_history.replace(":currency?", currency),
)
} }
/> />
@ -96,7 +100,7 @@ export function Application(): VNode {
url={decodeURIComponent(action)} url={decodeURIComponent(action)}
onDismiss={() => { onDismiss={() => {
setDismissed(true); setDismissed(true);
route(Pages.balance); return redirectTo(Pages.balance);
}} }}
/> />
); );
@ -106,16 +110,12 @@ export function Application(): VNode {
<Route <Route
path={Pages.backup} path={Pages.backup}
component={BackupPage} component={BackupPage}
onAddProvider={() => { onAddProvider={() => redirectTo(Pages.backup_provider_add)}
route(Pages.backup_provider_add);
}}
/> />
<Route <Route
path={Pages.backup_provider_detail} path={Pages.backup_provider_detail}
component={ProviderDetailPage} component={ProviderDetailPage}
onBack={() => { onBack={() => redirectTo(Pages.backup)}
route(Pages.backup);
}}
/> />
<Route <Route
@ -175,6 +175,10 @@ function RedirectToWalletPage(): VNode {
); );
} }
async function redirectTo(location: string): Promise<void> {
route(location);
}
function Redirect({ to }: { to: string }): null { function Redirect({ to }: { to: string }): null {
useEffect(() => { useEffect(() => {
route(to, true); route(to, true);

View File

@ -22,17 +22,17 @@ import { JustInDevMode } from "../components/JustInDevMode.js";
import { Loading } from "../components/Loading.js"; import { Loading } from "../components/Loading.js";
import { LoadingError } from "../components/LoadingError.js"; import { LoadingError } from "../components/LoadingError.js";
import { MultiActionButton } from "../components/MultiActionButton.js"; import { MultiActionButton } from "../components/MultiActionButton.js";
import { ButtonBoxPrimary, ButtonPrimary } from "../components/styled/index.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
import { Button } from "../mui/Button.js";
import { AddNewActionView } from "../wallet/AddNewActionView.js"; import { AddNewActionView } from "../wallet/AddNewActionView.js";
import * as wxApi from "../wxApi.js"; import * as wxApi from "../wxApi.js";
import { NoBalanceHelp } from "./NoBalanceHelp.js"; import { NoBalanceHelp } from "./NoBalanceHelp.js";
export interface Props { export interface Props {
goToWalletDeposit: (currency: string) => void; goToWalletDeposit: (currency: string) => Promise<void>;
goToWalletHistory: (currency: string) => void; goToWalletHistory: (currency: string) => Promise<void>;
goToWalletManualWithdraw: () => void; goToWalletManualWithdraw: () => Promise<void>;
} }
export function BalancePage({ export function BalancePage({
goToWalletManualWithdraw, goToWalletManualWithdraw,
@ -65,7 +65,7 @@ export function BalancePage({
} }
if (addingAction) { if (addingAction) {
return <AddNewActionView onCancel={() => setAddingAction(false)} />; return <AddNewActionView onCancel={async () => setAddingAction(false)} />;
} }
return ( return (
@ -74,16 +74,16 @@ export function BalancePage({
goToWalletManualWithdraw={goToWalletManualWithdraw} goToWalletManualWithdraw={goToWalletManualWithdraw}
goToWalletDeposit={goToWalletDeposit} goToWalletDeposit={goToWalletDeposit}
goToWalletHistory={goToWalletHistory} goToWalletHistory={goToWalletHistory}
goToAddAction={() => setAddingAction(true)} goToAddAction={async () => setAddingAction(true)}
/> />
); );
} }
export interface BalanceViewProps { export interface BalanceViewProps {
balances: Balance[]; balances: Balance[];
goToWalletManualWithdraw: () => void; goToWalletManualWithdraw: () => Promise<void>;
goToAddAction: () => void; goToAddAction: () => Promise<void>;
goToWalletDeposit: (currency: string) => void; goToWalletDeposit: (currency: string) => Promise<void>;
goToWalletHistory: (currency: string) => void; goToWalletHistory: (currency: string) => Promise<void>;
} }
export function BalanceView({ export function BalanceView({
@ -113,22 +113,20 @@ export function BalanceView({
/> />
</section> </section>
<footer style={{ justifyContent: "space-between" }}> <footer style={{ justifyContent: "space-between" }}>
<ButtonPrimary onClick={goToWalletManualWithdraw}> <Button variant="contained" onClick={goToWalletManualWithdraw}>
<i18n.Translate>Withdraw</i18n.Translate> <i18n.Translate>Withdraw</i18n.Translate>
</ButtonPrimary> </Button>
{currencyWithNonZeroAmount.length > 0 && ( {currencyWithNonZeroAmount.length > 0 && (
<MultiActionButton <MultiActionButton
label={(s) => ( label={(s) => <i18n.Translate>Deposit {s}</i18n.Translate>}
<i18n.Translate>Deposit {<span>{s}</span>}</i18n.Translate>
)}
actions={currencyWithNonZeroAmount} actions={currencyWithNonZeroAmount}
onClick={(c) => goToWalletDeposit(c)} onClick={(c) => goToWalletDeposit(c)}
/> />
)} )}
<JustInDevMode> <JustInDevMode>
<ButtonBoxPrimary onClick={goToAddAction}> <Button onClick={goToAddAction}>
<i18n.Translate>Enter URI</i18n.Translate> <i18n.Translate>Enter URI</i18n.Translate>
</ButtonBoxPrimary> </Button>
</JustInDevMode> </JustInDevMode>
</footer> </footer>
</Fragment> </Fragment>

View File

@ -8,7 +8,7 @@ import { Typography } from "../mui/Typography.js";
export function NoBalanceHelp({ export function NoBalanceHelp({
goToWalletManualWithdraw, goToWalletManualWithdraw,
}: { }: {
goToWalletManualWithdraw: () => void; goToWalletManualWithdraw: () => Promise<void>;
}): VNode { }): VNode {
return ( return (
<Paper <Paper

View File

@ -28,16 +28,17 @@ import {
Title, Title,
} from "../components/styled/index.js"; } from "../components/styled/index.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { Button } from "../mui/Button.js";
export interface Props { export interface Props {
url: string; url: string;
onDismiss: () => void; onDismiss: () => Promise<void>;
} }
export function TalerActionFound({ url, onDismiss }: Props): VNode { export function TalerActionFound({ url, onDismiss }: Props): VNode {
const uriType = classifyTalerUri(url); const uriType = classifyTalerUri(url);
const { i18n } = useTranslationContext(); const { i18n } = useTranslationContext();
function redirectToWallet(): void { async function redirectToWallet(): Promise<void> {
platform.openWalletURIFromPopup(url); platform.openWalletURIFromPopup(url);
} }
return ( return (
@ -51,9 +52,13 @@ export function TalerActionFound({ url, onDismiss }: Props): VNode {
<p> <p>
<i18n.Translate>This page has pay action.</i18n.Translate> <i18n.Translate>This page has pay action.</i18n.Translate>
</p> </p>
<ButtonSuccess onClick={redirectToWallet}> <Button
variant="contained"
color="success"
onClick={redirectToWallet}
>
<i18n.Translate>Open pay page</i18n.Translate> <i18n.Translate>Open pay page</i18n.Translate>
</ButtonSuccess> </Button>
</div> </div>
)} )}
{uriType === TalerUriType.TalerWithdraw && ( {uriType === TalerUriType.TalerWithdraw && (
@ -63,9 +68,13 @@ export function TalerActionFound({ url, onDismiss }: Props): VNode {
This page has a withdrawal action. This page has a withdrawal action.
</i18n.Translate> </i18n.Translate>
</p> </p>
<ButtonSuccess onClick={redirectToWallet}> <Button
variant="contained"
color="success"
onClick={redirectToWallet}
>
<i18n.Translate>Open withdraw page</i18n.Translate> <i18n.Translate>Open withdraw page</i18n.Translate>
</ButtonSuccess> </Button>
</div> </div>
)} )}
{uriType === TalerUriType.TalerTip && ( {uriType === TalerUriType.TalerTip && (
@ -73,9 +82,13 @@ export function TalerActionFound({ url, onDismiss }: Props): VNode {
<p> <p>
<i18n.Translate>This page has a tip action.</i18n.Translate> <i18n.Translate>This page has a tip action.</i18n.Translate>
</p> </p>
<ButtonSuccess onClick={redirectToWallet}> <Button
variant="contained"
color="success"
onClick={redirectToWallet}
>
<i18n.Translate>Open tip page</i18n.Translate> <i18n.Translate>Open tip page</i18n.Translate>
</ButtonSuccess> </Button>
</div> </div>
)} )}
{uriType === TalerUriType.TalerNotifyReserve && ( {uriType === TalerUriType.TalerNotifyReserve && (
@ -85,9 +98,13 @@ export function TalerActionFound({ url, onDismiss }: Props): VNode {
This page has a notify reserve action. This page has a notify reserve action.
</i18n.Translate> </i18n.Translate>
</p> </p>
<ButtonSuccess onClick={redirectToWallet}> <Button
variant="contained"
color="success"
onClick={redirectToWallet}
>
<i18n.Translate>Notify</i18n.Translate> <i18n.Translate>Notify</i18n.Translate>
</ButtonSuccess> </Button>
</div> </div>
)} )}
{uriType === TalerUriType.TalerRefund && ( {uriType === TalerUriType.TalerRefund && (
@ -95,9 +112,13 @@ export function TalerActionFound({ url, onDismiss }: Props): VNode {
<p> <p>
<i18n.Translate>This page has a refund action.</i18n.Translate> <i18n.Translate>This page has a refund action.</i18n.Translate>
</p> </p>
<ButtonSuccess onClick={redirectToWallet}> <Button
variant="contained"
color="success"
onClick={redirectToWallet}
>
<i18n.Translate>Open refund page</i18n.Translate> <i18n.Translate>Open refund page</i18n.Translate>
</ButtonSuccess> </Button>
</div> </div>
)} )}
{uriType === TalerUriType.Unknown && ( {uriType === TalerUriType.Unknown && (
@ -113,10 +134,9 @@ export function TalerActionFound({ url, onDismiss }: Props): VNode {
</section> </section>
<footer> <footer>
<div /> <div />
<ButtonPrimary onClick={() => onDismiss()}> <Button variant="contained" onClick={onDismiss}>
{" "} <i18n.Translate>Dismiss</i18n.Translate>
<i18n.Translate>Dismiss</i18n.Translate>{" "} </Button>
</ButtonPrimary>
</footer> </footer>
</Fragment> </Fragment>
); );

View File

@ -2,15 +2,12 @@ import { classifyTalerUri, TalerUriType } from "@gnu-taler/taler-util";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { platform } from "../platform/api.js"; import { platform } from "../platform/api.js";
import { import { InputWithLabel } from "../components/styled/index.js";
Button,
ButtonSuccess,
InputWithLabel,
} from "../components/styled/index.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { Button } from "../mui/Button.js";
export interface Props { export interface Props {
onCancel: () => void; onCancel: () => Promise<void>;
} }
export function AddNewActionView({ onCancel }: Props): VNode { export function AddNewActionView({ onCancel }: Props): VNode {
@ -18,7 +15,7 @@ export function AddNewActionView({ onCancel }: Props): VNode {
const uriType = classifyTalerUri(url); const uriType = classifyTalerUri(url);
const { i18n } = useTranslationContext(); const { i18n } = useTranslationContext();
function redirectToWallet(): void { async function redirectToWallet(): Promise<void> {
platform.openWalletURIFromPopup(url); platform.openWalletURIFromPopup(url);
} }
@ -41,11 +38,15 @@ export function AddNewActionView({ onCancel }: Props): VNode {
</InputWithLabel> </InputWithLabel>
</section> </section>
<footer> <footer>
<Button onClick={onCancel}> <Button variant="contained" color="secondary" onClick={onCancel}>
<i18n.Translate>Cancel</i18n.Translate> <i18n.Translate>Cancel</i18n.Translate>
</Button> </Button>
{uriType !== TalerUriType.Unknown && ( {uriType !== TalerUriType.Unknown && (
<ButtonSuccess onClick={redirectToWallet}> <Button
variant="contained"
color="success"
onClick={redirectToWallet}
>
{(() => { {(() => {
switch (uriType) { switch (uriType) {
case TalerUriType.TalerNotifyReserve: case TalerUriType.TalerNotifyReserve:
@ -61,7 +62,7 @@ export function AddNewActionView({ onCancel }: Props): VNode {
} }
return <Fragment />; return <Fragment />;
})()} })()}
</ButtonSuccess> </Button>
)} )}
</footer> </footer>
</Fragment> </Fragment>

View File

@ -94,7 +94,9 @@ export function Application(): VNode {
> >
<PendingTransactions <PendingTransactions
goToTransaction={(txId: string) => goToTransaction={(txId: string) =>
route(Pages.balance_transaction.replace(":tid", txId)) redirectTo(
Pages.balance_transaction.replace(":tid", txId),
)
} }
/> />
</div> </div>
@ -123,10 +125,12 @@ export function Application(): VNode {
path={Pages.balance_history} path={Pages.balance_history}
component={HistoryPage} component={HistoryPage}
goToWalletDeposit={(currency: string) => goToWalletDeposit={(currency: string) =>
route(Pages.balance_deposit.replace(":currency", currency)) redirectTo(
Pages.balance_deposit.replace(":currency", currency),
)
} }
goToWalletManualWithdraw={(currency?: string) => goToWalletManualWithdraw={(currency?: string) =>
route( redirectTo(
Pages.balance_manual_withdraw.replace( Pages.balance_manual_withdraw.replace(
":currency?", ":currency?",
currency || "", currency || "",
@ -137,29 +141,31 @@ export function Application(): VNode {
<Route <Route
path={Pages.balance_transaction} path={Pages.balance_transaction}
component={TransactionPage} component={TransactionPage}
goToWalletHistory={(currency?: string) => { goToWalletHistory={(currency?: string) =>
route( redirectTo(
Pages.balance_history.replace(":currency?", currency || ""), Pages.balance_history.replace(":currency?", currency || ""),
); )
}} }
/> />
<Route <Route
path={Pages.balance_manual_withdraw} path={Pages.balance_manual_withdraw}
component={ManualWithdrawPage} component={ManualWithdrawPage}
onCancel={() => { onCancel={() => redirectTo(Pages.balance)}
route(Pages.balance);
}}
/> />
<Route <Route
path={Pages.balance_deposit} path={Pages.balance_deposit}
component={DepositPage} component={DepositPage}
onCancel={(currency: string) => { onCancel={(currency: string) => {
route(Pages.balance_history.replace(":currency?", currency)); redirectTo(
Pages.balance_history.replace(":currency?", currency),
);
}} }}
onSuccess={(currency: string) => { onSuccess={(currency: string) => {
route(Pages.balance_history.replace(":currency?", currency)); redirectTo(
Pages.balance_history.replace(":currency?", currency),
);
setGlobalNotification( setGlobalNotification(
<i18n.Translate> <i18n.Translate>
All done, your transaction is in progress All done, your transaction is in progress
@ -178,23 +184,17 @@ export function Application(): VNode {
<Route <Route
path={Pages.backup} path={Pages.backup}
component={BackupPage} component={BackupPage}
onAddProvider={() => { onAddProvider={() => redirectTo(Pages.backup_provider_add)}
route(Pages.backup_provider_add);
}}
/> />
<Route <Route
path={Pages.backup_provider_detail} path={Pages.backup_provider_detail}
component={ProviderDetailPage} component={ProviderDetailPage}
onBack={() => { onBack={() => redirectTo(Pages.backup)}
route(Pages.backup);
}}
/> />
<Route <Route
path={Pages.backup_provider_add} path={Pages.backup_provider_add}
component={ProviderAddPage} component={ProviderAddPage}
onBack={() => { onBack={() => redirectTo(Pages.backup)}
route(Pages.backup);
}}
/> />
{/** {/**
@ -203,9 +203,7 @@ export function Application(): VNode {
<Route <Route
path={Pages.settings_exchange_add} path={Pages.settings_exchange_add}
component={ExchangeAddPage} component={ExchangeAddPage}
onBack={() => { onBack={() => redirectTo(Pages.balance)}
route(Pages.balance);
}}
/> />
{/** {/**
@ -221,14 +219,14 @@ export function Application(): VNode {
path={Pages.cta_pay} path={Pages.cta_pay}
component={PayPage} component={PayPage}
goToWalletManualWithdraw={(currency?: string) => goToWalletManualWithdraw={(currency?: string) =>
route( redirectTo(
Pages.balance_manual_withdraw.replace( Pages.balance_manual_withdraw.replace(
":currency?", ":currency?",
currency || "", currency || "",
), ),
) )
} }
goBack={() => route(Pages.balance)} goBack={() => redirectTo(Pages.balance)}
/> />
<Route path={Pages.cta_refund} component={RefundPage} /> <Route path={Pages.cta_refund} component={RefundPage} />
<Route path={Pages.cta_tips} component={TipPage} /> <Route path={Pages.cta_tips} component={TipPage} />
@ -258,9 +256,12 @@ export function Application(): VNode {
); );
} }
async function redirectTo(location: string): Promise<void> {
route(location);
}
function Redirect({ to }: { to: string }): null { function Redirect({ to }: { to: string }): null {
useEffect(() => { useEffect(() => {
console.log("got some wrong route", to);
route(to, true); route(to, true);
}); });
return null; return null;

View File

@ -31,8 +31,6 @@ import { Loading } from "../components/Loading.js";
import { LoadingError } from "../components/LoadingError.js"; import { LoadingError } from "../components/LoadingError.js";
import { import {
BoldLight, BoldLight,
ButtonPrimary,
ButtonSuccess,
Centered, Centered,
CenteredBoldText, CenteredBoldText,
CenteredText, CenteredText,
@ -42,11 +40,12 @@ import {
} from "../components/styled/index.js"; } from "../components/styled/index.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
import { Button } from "../mui/Button.js";
import { Pages } from "../NavigationBar.js"; import { Pages } from "../NavigationBar.js";
import * as wxApi from "../wxApi.js"; import * as wxApi from "../wxApi.js";
interface Props { interface Props {
onAddProvider: () => void; onAddProvider: () => Promise<void>;
} }
export function BackupPage({ onAddProvider }: Props): VNode { export function BackupPage({ onAddProvider }: Props): VNode {
@ -87,7 +86,7 @@ export function BackupPage({ onAddProvider }: Props): VNode {
export interface ViewProps { export interface ViewProps {
providers: ProviderInfo[]; providers: ProviderInfo[];
onAddProvider: () => void; onAddProvider: () => Promise<void>;
onSyncAll: () => Promise<void>; onSyncAll: () => Promise<void>;
} }
@ -121,9 +120,9 @@ export function BackupView({
<BoldLight> <BoldLight>
<i18n.Translate>No backup providers configured</i18n.Translate> <i18n.Translate>No backup providers configured</i18n.Translate>
</BoldLight> </BoldLight>
<ButtonSuccess onClick={onAddProvider}> <Button variant="contained" color="success" onClick={onAddProvider}>
<i18n.Translate>Add provider</i18n.Translate> <i18n.Translate>Add provider</i18n.Translate>
</ButtonSuccess> </Button>
</Centered> </Centered>
)} )}
</section> </section>
@ -131,16 +130,16 @@ export function BackupView({
<footer> <footer>
<div /> <div />
<div> <div>
<ButtonPrimary onClick={onSyncAll}> <Button variant="contained" onClick={onSyncAll}>
{providers.length > 1 ? ( {providers.length > 1 ? (
<i18n.Translate>Sync all backups</i18n.Translate> <i18n.Translate>Sync all backups</i18n.Translate>
) : ( ) : (
<i18n.Translate>Sync now</i18n.Translate> <i18n.Translate>Sync now</i18n.Translate>
)} )}
</ButtonPrimary> </Button>
<ButtonSuccess onClick={onAddProvider}> <Button variant="contained" color="success" onClick={onAddProvider}>
<i18n.Translate>Add provider</i18n.Translate> <i18n.Translate>Add provider</i18n.Translate>
</ButtonSuccess> </Button>
</div> </div>
</footer> </footer>
)} )}

View File

@ -21,14 +21,12 @@
*/ */
import { AmountJson, Amounts } from "@gnu-taler/taler-util"; import { AmountJson, Amounts } from "@gnu-taler/taler-util";
import { TalerError } from "@gnu-taler/taler-wallet-core";
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { ErrorMessage } from "../components/ErrorMessage.js"; import { ErrorMessage } from "../components/ErrorMessage.js";
import { SelectList } from "../components/SelectList.js"; import { SelectList } from "../components/SelectList.js";
import { import {
BoldLight, BoldLight,
ButtonPrimary,
Centered, Centered,
Input, Input,
InputWithLabel, InputWithLabel,
@ -37,6 +35,7 @@ import {
SubTitle, SubTitle,
} from "../components/styled/index.js"; } from "../components/styled/index.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { Button } from "../mui/Button.js";
import { SelectFieldHandler, TextFieldHandler } from "../mui/handlers.js"; import { SelectFieldHandler, TextFieldHandler } from "../mui/handlers.js";
import { Pages } from "../NavigationBar.js"; import { Pages } from "../NavigationBar.js";
@ -270,12 +269,13 @@ export function CreateManualWithdraw({
</section> </section>
<footer> <footer>
<div /> <div />
<ButtonPrimary <Button
variant="contained"
disabled={!state.parsedAmount || !state.exchange.value} disabled={!state.parsedAmount || !state.exchange.value}
onClick={() => onCreate(state.exchange.value, state.parsedAmount!)} onClick={() => onCreate(state.exchange.value, state.parsedAmount!)}
> >
<i18n.Translate>Start withdrawal</i18n.Translate> <i18n.Translate>Start withdrawal</i18n.Translate>
</ButtonPrimary> </Button>
</footer> </footer>
</Fragment> </Fragment>
); );

View File

@ -23,8 +23,6 @@ import { Loading } from "../components/Loading.js";
import { LoadingError } from "../components/LoadingError.js"; import { LoadingError } from "../components/LoadingError.js";
import { SelectList } from "../components/SelectList.js"; import { SelectList } from "../components/SelectList.js";
import { import {
Button,
ButtonPrimary,
ErrorText, ErrorText,
Input, Input,
InputWithLabel, InputWithLabel,
@ -33,6 +31,7 @@ import {
} from "../components/styled/index.js"; } from "../components/styled/index.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { HookError, useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
import { Button } from "../mui/Button.js";
import { import {
ButtonHandler, ButtonHandler,
SelectFieldHandler, SelectFieldHandler,
@ -275,7 +274,11 @@ export function View({ state }: ViewProps): VNode {
</p> </p>
</WarningBox> </WarningBox>
<footer> <footer>
<Button onClick={state.cancelHandler.onClick}> <Button
variant="contained"
color="secondary"
onClick={state.cancelHandler.onClick}
>
<i18n.Translate>Cancel</i18n.Translate> <i18n.Translate>Cancel</i18n.Translate>
</Button> </Button>
</footer> </footer>
@ -345,20 +348,24 @@ export function View({ state }: ViewProps): VNode {
} }
</section> </section>
<footer> <footer>
<Button onClick={state.cancelHandler.onClick}> <Button
variant="contained"
color="secondary"
onClick={state.cancelHandler.onClick}
>
<i18n.Translate>Cancel</i18n.Translate> <i18n.Translate>Cancel</i18n.Translate>
</Button> </Button>
{!state.depositHandler.onClick ? ( {!state.depositHandler.onClick ? (
<ButtonPrimary disabled> <Button variant="contained" disabled>
<i18n.Translate>Deposit</i18n.Translate> <i18n.Translate>Deposit</i18n.Translate>
</ButtonPrimary> </Button>
) : ( ) : (
<ButtonPrimary onClick={state.depositHandler.onClick}> <Button variant="contained" onClick={state.depositHandler.onClick}>
<i18n.Translate> <i18n.Translate>
Deposit {Amounts.stringifyValue(state.totalToDeposit)}{" "} Deposit {Amounts.stringifyValue(state.totalToDeposit)}{" "}
{state.currency} {state.currency}
</i18n.Translate> </i18n.Translate>
</ButtonPrimary> </Button>
)} )}
</footer> </footer>
</Fragment> </Fragment>

View File

@ -30,6 +30,9 @@ import { Time } from "../components/Time.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
import { useDiagnostics } from "../hooks/useDiagnostics.js"; import { useDiagnostics } from "../hooks/useDiagnostics.js";
import { Button } from "../mui/Button.js";
import { Grid } from "../mui/Grid.js";
import { Paper } from "../mui/Paper.js";
import * as wxApi from "../wxApi.js"; import * as wxApi from "../wxApi.js";
export function DeveloperPage(): VNode { export function DeveloperPage(): VNode {
@ -133,7 +136,6 @@ export function View({
const money_by_exchange = coins.reduce( const money_by_exchange = coins.reduce(
(prev, cur) => { (prev, cur) => {
const denom = Amounts.parseOrThrow(cur.denom_value); const denom = Amounts.parseOrThrow(cur.denom_value);
console.log(cur);
if (!prev[cur.exchange_base_url]) { if (!prev[cur.exchange_base_url]) {
prev[cur.exchange_base_url] = []; prev[cur.exchange_base_url] = [];
currencies[cur.exchange_base_url] = denom.currency; currencies[cur.exchange_base_url] = denom.currency;
@ -154,57 +156,72 @@ export function View({
[exchange_name: string]: CalculatedCoinfInfo[]; [exchange_name: string]: CalculatedCoinfInfo[];
}, },
); );
function Item({ children }: any) {
return <div>{children}</div>;
}
return ( return (
<div> <div>
<p> <p>
<i18n.Translate>Debug tools</i18n.Translate>: <i18n.Translate>Debug tools</i18n.Translate>:
</p> </p>
<button <Grid container justifyContent="space-between" spacing={1}>
onClick={() => <Grid item>
confirmReset( <Button
i18n.str`Do you want to IRREVOCABLY DESTROY everything inside your wallet and LOSE ALL YOUR COINS?`, variant="contained"
wxApi.resetDb, onClick={() =>
) confirmReset(
} i18n.str`Do you want to IRREVOCABLY DESTROY everything inside your wallet and LOSE ALL YOUR COINS?`,
> wxApi.resetDb,
<i18n.Translate>reset</i18n.Translate> )
</button> }
<button >
onClick={() => <i18n.Translate>reset</i18n.Translate>
confirmReset( </Button>
i18n.str`TESTING: This may delete all your coin, proceed with caution`, </Grid>
wxApi.runGarbageCollector, <Grid item>
) <Button
} variant="contained"
> onClick={() =>
<i18n.Translate>run gc</i18n.Translate> confirmReset(
</button> i18n.str`TESTING: This may delete all your coin, proceed with caution`,
<br /> wxApi.runGarbageCollector,
<button onClick={() => fileRef?.current?.click()}> )
<i18n.Translate>import database</i18n.Translate> }
</button> >
<input <i18n.Translate>run gc</i18n.Translate>
ref={fileRef} </Button>
style={{ display: "none" }} </Grid>
type="file" <Grid item>
onChange={async (e) => { <Button
const f: FileList | null = e.currentTarget.files; variant="contained"
if (!f || f.length != 1) { onClick={async () => fileRef?.current?.click()}
return Promise.reject(); >
} <i18n.Translate>import database</i18n.Translate>
const buf = await f[0].arrayBuffer(); </Button>
const str = new Uint8Array(buf).reduce( </Grid>
(data, byte) => data + String.fromCharCode(byte), <Grid item>
"", <input
); ref={fileRef}
return onImportDatabase(str); style={{ display: "none" }}
}} type="file"
/> onChange={async (e) => {
<br /> const f: FileList | null = e.currentTarget.files;
<button onClick={onExportDatabase}> if (!f || f.length != 1) {
<i18n.Translate>export database</i18n.Translate> return Promise.reject();
</button> }
const buf = await f[0].arrayBuffer();
const str = new Uint8Array(buf).reduce(
(data, byte) => data + String.fromCharCode(byte),
"",
);
return onImportDatabase(str);
}}
/>
<Button variant="contained" onClick={onExportDatabase}>
<i18n.Translate>export database</i18n.Translate>
</Button>
</Grid>
</Grid>
{downloadedDatabase && ( {downloadedDatabase && (
<div> <div>
<i18n.Translate> <i18n.Translate>

View File

@ -1,21 +1,17 @@
import { Fragment, h, VNode } from "preact"; import { Fragment, h, VNode } from "preact";
import { useState } from "preact/hooks"; import { useState } from "preact/hooks";
import { import { Title } from "../components/styled/index.js";
Button,
ButtonSuccess,
ButtonWarning,
Title,
} from "../components/styled/index.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { TermsOfServiceSection } from "../cta/TermsOfServiceSection.js"; import { TermsOfServiceSection } from "../cta/TermsOfServiceSection.js";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
import { Button } from "../mui/Button.js";
import { buildTermsOfServiceState, TermsState } from "../utils/index.js"; import { buildTermsOfServiceState, TermsState } from "../utils/index.js";
import * as wxApi from "../wxApi.js"; import * as wxApi from "../wxApi.js";
export interface Props { export interface Props {
url: string; url: string;
onCancel: () => void; onCancel: () => Promise<void>;
onConfirm: () => void; onConfirm: () => Promise<void>;
} }
export function ExchangeAddConfirmPage({ export function ExchangeAddConfirmPage({
@ -71,8 +67,8 @@ export interface ViewProps {
url: string; url: string;
terms: TermsState | undefined; terms: TermsState | undefined;
onAccept: (b: boolean) => Promise<void>; onAccept: (b: boolean) => Promise<void>;
onCancel: () => void; onCancel: () => Promise<void>;
onConfirm: () => void; onConfirm: () => Promise<void>;
} }
export function View({ export function View({
@ -114,30 +110,35 @@ export function View({
)} )}
<footer> <footer>
<Button onClick={onCancel}> <Button variant="contained" color="secondary" onClick={onCancel}>
<i18n.Translate>Cancel</i18n.Translate> <i18n.Translate>Cancel</i18n.Translate>
</Button> </Button>
{!terms && ( {!terms && (
<Button disabled> <Button variant="contained" disabled>
<i18n.Translate>Loading terms..</i18n.Translate> <i18n.Translate>Loading terms..</i18n.Translate>
</Button> </Button>
)} )}
{terms && ( {terms && (
<Fragment> <Fragment>
{needsReview && !reviewed && ( {needsReview && !reviewed && (
<ButtonSuccess disabled upperCased onClick={onConfirm}> <Button
variant="contained"
color="success"
disabled
onClick={onConfirm}
>
<i18n.Translate>Add exchange</i18n.Translate> <i18n.Translate>Add exchange</i18n.Translate>
</ButtonSuccess> </Button>
)} )}
{(terms.status === "accepted" || (needsReview && reviewed)) && ( {(terms.status === "accepted" || (needsReview && reviewed)) && (
<ButtonSuccess upperCased onClick={onConfirm}> <Button variant="contained" color="success" onClick={onConfirm}>
<i18n.Translate>Add exchange</i18n.Translate> <i18n.Translate>Add exchange</i18n.Translate>
</ButtonSuccess> </Button>
)} )}
{terms.status === "notfound" && ( {terms.status === "notfound" && (
<ButtonWarning upperCased onClick={onConfirm}> <Button variant="contained" color="warning" onClick={onConfirm}>
<i18n.Translate>Add exchange anyway</i18n.Translate> <i18n.Translate>Add exchange anyway</i18n.Translate>
</ButtonWarning> </Button>
)} )}
</Fragment> </Fragment>
)} )}

View File

@ -28,7 +28,7 @@ import { ExchangeSetUrlPage } from "./ExchangeSetUrl.js";
interface Props { interface Props {
currency?: string; currency?: string;
onBack: () => void; onBack: () => Promise<void>;
} }
export function ExchangeAddPage({ currency, onBack }: Props): VNode { export function ExchangeAddPage({ currency, onBack }: Props): VNode {

View File

@ -6,8 +6,6 @@ import { Fragment, h, VNode } from "preact";
import { useEffect, useState } from "preact/hooks"; import { useEffect, useState } from "preact/hooks";
import { ErrorMessage } from "../components/ErrorMessage.js"; import { ErrorMessage } from "../components/ErrorMessage.js";
import { import {
Button,
ButtonPrimary,
Input, Input,
LightText, LightText,
SubTitle, SubTitle,
@ -15,11 +13,12 @@ import {
WarningBox, WarningBox,
} from "../components/styled/index.js"; } from "../components/styled/index.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { Button } from "../mui/Button.js";
export interface Props { export interface Props {
initialValue?: string; initialValue?: string;
expectedCurrency?: string; expectedCurrency?: string;
onCancel: () => void; onCancel: () => Promise<void>;
onVerify: (s: string) => Promise<TalerConfigResponse | undefined>; onVerify: (s: string) => Promise<TalerConfigResponse | undefined>;
onConfirm: (url: string) => Promise<string | undefined>; onConfirm: (url: string) => Promise<string | undefined>;
withError?: string; withError?: string;
@ -64,7 +63,7 @@ function useEndpointStatus<T>(
} }
}, 500); }, 500);
setHandler(h); setHandler(h);
}, [value, setHandler, handler, onVerify]); }, [value, setHandler, onVerify]);
return { return {
error: dirty ? error : undefined, error: dirty ? error : undefined,
@ -172,10 +171,11 @@ export function ExchangeSetUrlPage({
</p> </p>
</section> </section>
<footer> <footer>
<Button onClick={onCancel}> <Button variant="contained" color="secondary" onClick={onCancel}>
<i18n.Translate>Cancel</i18n.Translate> <i18n.Translate>Cancel</i18n.Translate>
</Button> </Button>
<ButtonPrimary <Button
variant="contained"
disabled={ disabled={
!result || !result ||
!!error || !!error ||
@ -189,7 +189,7 @@ export function ExchangeSetUrlPage({
}} }}
> >
<i18n.Translate>Next</i18n.Translate> <i18n.Translate>Next</i18n.Translate>
</ButtonPrimary> </Button>
</footer> </footer>
</Fragment> </Fragment>
); );

View File

@ -26,7 +26,6 @@ import { Loading } from "../components/Loading.js";
import { LoadingError } from "../components/LoadingError.js"; import { LoadingError } from "../components/LoadingError.js";
import { import {
ButtonBoxPrimary, ButtonBoxPrimary,
ButtonPrimary,
CenteredBoldText, CenteredBoldText,
CenteredText, CenteredText,
DateSeparator, DateSeparator,
@ -36,13 +35,14 @@ import { Time } from "../components/Time.js";
import { TransactionItem } from "../components/TransactionItem.js"; import { TransactionItem } from "../components/TransactionItem.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
import { Button } from "../mui/Button.js";
import { NoBalanceHelp } from "../popup/NoBalanceHelp.js"; import { NoBalanceHelp } from "../popup/NoBalanceHelp.js";
import * as wxApi from "../wxApi.js"; import * as wxApi from "../wxApi.js";
interface Props { interface Props {
currency?: string; currency?: string;
goToWalletDeposit: (currency: string) => void; goToWalletDeposit: (currency: string) => Promise<void>;
goToWalletManualWithdraw: (currency?: string) => void; goToWalletManualWithdraw: (currency?: string) => Promise<void>;
} }
export function HistoryPage({ export function HistoryPage({
currency, currency,
@ -101,8 +101,8 @@ export function HistoryView({
goToWalletManualWithdraw, goToWalletManualWithdraw,
goToWalletDeposit, goToWalletDeposit,
}: { }: {
goToWalletDeposit: (currency: string) => void; goToWalletDeposit: (currency: string) => Promise<void>;
goToWalletManualWithdraw: (currency?: string) => void; goToWalletManualWithdraw: (currency?: string) => Promise<void>;
defaultCurrency?: string; defaultCurrency?: string;
transactions: Transaction[]; transactions: Transaction[];
balances: Balance[]; balances: Balance[];
@ -198,19 +198,22 @@ export function HistoryView({
)} )}
</div> </div>
<div> <div>
<ButtonPrimary <Button
style={{ marginLeft: 0, marginTop: 8 }} variant="contained"
// style={{ marginLeft: 0, marginTop: 8 }}
onClick={() => goToWalletManualWithdraw(selectedCurrency)} onClick={() => goToWalletManualWithdraw(selectedCurrency)}
> >
<i18n.Translate>Withdraw</i18n.Translate> <i18n.Translate>Withdraw</i18n.Translate>
</ButtonPrimary> </Button>
{currencyAmount && Amounts.isNonZero(currencyAmount) && ( {currencyAmount && Amounts.isNonZero(currencyAmount) && (
<ButtonBoxPrimary <Button
style={{ marginLeft: 0, marginTop: 8 }} variant="outlined"
color="primary"
// style={{ marginLeft: 0, marginTop: 8 }}
onClick={() => goToWalletDeposit(selectedCurrency)} onClick={() => goToWalletDeposit(selectedCurrency)}
> >
<i18n.Translate>Deposit</i18n.Translate> <i18n.Translate>Deposit</i18n.Translate>
</ButtonBoxPrimary> </Button>
)} )}
</div> </div>
</div> </div>

View File

@ -34,7 +34,7 @@ import { ReserveCreated } from "./ReserveCreated.js";
interface Props { interface Props {
currency?: string; currency?: string;
onCancel: () => void; onCancel: () => Promise<void>;
} }
export function ManualWithdrawPage({ currency, onCancel }: Props): VNode { export function ManualWithdrawPage({ currency, onCancel }: Props): VNode {

View File

@ -25,8 +25,6 @@ import { useEffect, useState } from "preact/hooks";
import { Checkbox } from "../components/Checkbox.js"; import { Checkbox } from "../components/Checkbox.js";
import { ErrorMessage } from "../components/ErrorMessage.js"; import { ErrorMessage } from "../components/ErrorMessage.js";
import { import {
Button,
ButtonPrimary,
Input, Input,
LightText, LightText,
SmallLightText, SmallLightText,
@ -34,12 +32,13 @@ import {
Title, Title,
} from "../components/styled/index.js"; } from "../components/styled/index.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { Button } from "../mui/Button.js";
import { queryToSlashConfig } from "../utils/index.js"; import { queryToSlashConfig } from "../utils/index.js";
import * as wxApi from "../wxApi.js"; import * as wxApi from "../wxApi.js";
interface Props { interface Props {
currency: string; currency: string;
onBack: () => void; onBack: () => Promise<void>;
} }
export function ProviderAddPage({ onBack }: Props): VNode { export function ProviderAddPage({ onBack }: Props): VNode {
@ -67,11 +66,13 @@ export function ProviderAddPage({ onBack }: Props): VNode {
<ConfirmProviderView <ConfirmProviderView
provider={verifying.provider} provider={verifying.provider}
url={verifying.url} url={verifying.url}
onCancel={() => { onCancel={async () => {
setVerifying(undefined); setVerifying(undefined);
}} }}
onConfirm={() => { onConfirm={() => {
wxApi.addBackupProvider(verifying.url, verifying.name).then(onBack); return wxApi
.addBackupProvider(verifying.url, verifying.name)
.then(onBack);
}} }}
/> />
); );
@ -79,7 +80,7 @@ export function ProviderAddPage({ onBack }: Props): VNode {
export interface SetUrlViewProps { export interface SetUrlViewProps {
initialValue?: string; initialValue?: string;
onCancel: () => void; onCancel: () => Promise<void>;
onVerify: (s: string) => Promise<BackupBackupProviderTerms | undefined>; onVerify: (s: string) => Promise<BackupBackupProviderTerms | undefined>;
onConfirm: (url: string, name: string) => Promise<string | undefined>; onConfirm: (url: string, name: string) => Promise<string | undefined>;
withError?: string; withError?: string;
@ -161,10 +162,11 @@ export function SetUrlView({
</p> </p>
</section> </section>
<footer> <footer>
<Button onClick={onCancel}> <Button variant="contained" color="secondary" onClick={onCancel}>
<i18n.Translate>Cancel</i18n.Translate> <i18n.Translate>Cancel</i18n.Translate>
</Button> </Button>
<ButtonPrimary <Button
variant="contained"
disabled={!value && !urlError} disabled={!value && !urlError}
onClick={() => { onClick={() => {
const url = canonicalizeBaseUrl(value); const url = canonicalizeBaseUrl(value);
@ -174,7 +176,7 @@ export function SetUrlView({
}} }}
> >
<i18n.Translate>Next</i18n.Translate> <i18n.Translate>Next</i18n.Translate>
</ButtonPrimary> </Button>
</footer> </footer>
</Fragment> </Fragment>
); );
@ -183,8 +185,8 @@ export function SetUrlView({
export interface ConfirmProviderViewProps { export interface ConfirmProviderViewProps {
provider: BackupBackupProviderTerms; provider: BackupBackupProviderTerms;
url: string; url: string;
onCancel: () => void; onCancel: () => Promise<void>;
onConfirm: () => void; onConfirm: () => Promise<void>;
} }
export function ConfirmProviderView({ export function ConfirmProviderView({
url, url,
@ -236,17 +238,17 @@ export function ConfirmProviderView({
<Checkbox <Checkbox
label={<i18n.Translate>Accept terms of service</i18n.Translate>} label={<i18n.Translate>Accept terms of service</i18n.Translate>}
name="terms" name="terms"
onToggle={() => setAccepted((old) => !old)} onToggle={async () => setAccepted((old) => !old)}
enabled={accepted} enabled={accepted}
/> />
</section> </section>
<footer> <footer>
<Button onClick={onCancel}> <Button variant="contained" color="secondary" onClick={onCancel}>
<i18n.Translate>Cancel</i18n.Translate> <i18n.Translate>Cancel</i18n.Translate>
</Button> </Button>
<ButtonPrimary disabled={!accepted} onClick={onConfirm}> <Button variant="contained" disabled={!accepted} onClick={onConfirm}>
<i18n.Translate>Add provider</i18n.Translate> <i18n.Translate>Add provider</i18n.Translate>
</ButtonPrimary> </Button>
</footer> </footer>
</Fragment> </Fragment>
); );

View File

@ -25,21 +25,16 @@ import { Fragment, h, VNode } from "preact";
import { ErrorMessage } from "../components/ErrorMessage.js"; import { ErrorMessage } from "../components/ErrorMessage.js";
import { Loading } from "../components/Loading.js"; import { Loading } from "../components/Loading.js";
import { LoadingError } from "../components/LoadingError.js"; import { LoadingError } from "../components/LoadingError.js";
import { import { PaymentStatus, SmallLightText } from "../components/styled/index.js";
Button,
ButtonDestructive,
ButtonPrimary,
PaymentStatus,
SmallLightText,
} from "../components/styled/index.js";
import { Time } from "../components/Time.js"; import { Time } from "../components/Time.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
import { Button } from "../mui/Button.js";
import * as wxApi from "../wxApi.js"; import * as wxApi from "../wxApi.js";
interface Props { interface Props {
pid: string; pid: string;
onBack: () => void; onBack: () => Promise<void>;
} }
export function ProviderDetailPage({ pid: providerURL, onBack }: Props): VNode { export function ProviderDetailPage({ pid: providerURL, onBack }: Props): VNode {
@ -77,10 +72,10 @@ export function ProviderDetailPage({ pid: providerURL, onBack }: Props): VNode {
<ProviderView <ProviderView
url={providerURL} url={providerURL}
info={state.response} info={state.response}
onSync={async () => wxApi.syncOneProvider(providerURL)} onSync={() => wxApi.syncOneProvider(providerURL)}
onDelete={async () => wxApi.removeProvider(providerURL).then(onBack)} onDelete={() => wxApi.removeProvider(providerURL).then(onBack)}
onBack={onBack} onBack={onBack}
onExtend={() => { onExtend={async () => {
null; null;
}} }}
/> />
@ -90,10 +85,10 @@ export function ProviderDetailPage({ pid: providerURL, onBack }: Props): VNode {
export interface ViewProps { export interface ViewProps {
url: string; url: string;
info: ProviderInfo | null; info: ProviderInfo | null;
onDelete: () => void; onDelete: () => Promise<void>;
onSync: () => void; onSync: () => Promise<void>;
onBack: () => void; onBack: () => Promise<void>;
onExtend: () => void; onExtend: () => Promise<void>;
} }
export function ProviderView({ export function ProviderView({
@ -116,7 +111,7 @@ export function ProviderView({
</p> </p>
</section> </section>
<footer> <footer>
<Button onClick={onBack}> <Button variant="contained" color="secondary" onClick={onBack}>
<i18n.Translate>See providers</i18n.Translate> <i18n.Translate>See providers</i18n.Translate>
</Button> </Button>
<div /> <div />
@ -149,9 +144,9 @@ export function ProviderView({
</b>{" "} </b>{" "}
<Time timestamp={lb} format="dd MMMM yyyy" /> <Time timestamp={lb} format="dd MMMM yyyy" />
</p> </p>
<ButtonPrimary onClick={onSync}> <Button variant="contained" onClick={onSync}>
<i18n.Translate>Back up</i18n.Translate> <i18n.Translate>Back up</i18n.Translate>
</ButtonPrimary> </Button>
{info.terms && ( {info.terms && (
<Fragment> <Fragment>
<p> <p>
@ -164,9 +159,9 @@ export function ProviderView({
</Fragment> </Fragment>
)} )}
<p>{descriptionByStatus(info.paymentStatus, i18n)}</p> <p>{descriptionByStatus(info.paymentStatus, i18n)}</p>
<ButtonPrimary disabled onClick={onExtend}> <Button variant="contained" disabled onClick={onExtend}>
<i18n.Translate>Extend</i18n.Translate> <i18n.Translate>Extend</i18n.Translate>
</ButtonPrimary> </Button>
{info.paymentStatus.type === ProviderPaymentType.TermsChanged && ( {info.paymentStatus.type === ProviderPaymentType.TermsChanged && (
<div> <div>
@ -212,13 +207,13 @@ export function ProviderView({
)} )}
</section> </section>
<footer> <footer>
<Button onClick={onBack}> <Button variant="contained" color="secondary" onClick={onBack}>
<i18n.Translate>See providers</i18n.Translate> <i18n.Translate>See providers</i18n.Translate>
</Button> </Button>
<div> <div>
<ButtonDestructive onClick={onDelete}> <Button variant="contained" color="error" onClick={onDelete}>
<i18n.Translate>Remove provider</i18n.Translate> <i18n.Translate>Remove provider</i18n.Translate>
</ButtonDestructive> </Button>
</div> </div>
</footer> </footer>
</Fragment> </Fragment>

View File

@ -4,18 +4,15 @@ import { Amount } from "../components/Amount.js";
import { BankDetailsByPaytoType } from "../components/BankDetailsByPaytoType.js"; import { BankDetailsByPaytoType } from "../components/BankDetailsByPaytoType.js";
import { ErrorMessage } from "../components/ErrorMessage.js"; import { ErrorMessage } from "../components/ErrorMessage.js";
import { QR } from "../components/QR.js"; import { QR } from "../components/QR.js";
import { import { Title, WarningBox } from "../components/styled/index.js";
ButtonDestructive,
Title,
WarningBox,
} from "../components/styled/index.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { Button } from "../mui/Button.js";
export interface Props { export interface Props {
reservePub: string; reservePub: string;
paytoURI: PaytoUri | undefined; paytoURI: PaytoUri | undefined;
exchangeBaseUrl: string; exchangeBaseUrl: string;
amount: AmountJson; amount: AmountJson;
onCancel: () => void; onCancel: () => Promise<void>;
} }
export function ReserveCreated({ export function ReserveCreated({
@ -82,9 +79,9 @@ export function ReserveCreated({
</section> </section>
<footer> <footer>
<div /> <div />
<ButtonDestructive onClick={onCancel}> <Button variant="contained" color="error" onClick={onCancel}>
<i18n.Translate>Cancel withdrawal</i18n.Translate> <i18n.Translate>Cancel withdrawal</i18n.Translate>
</ButtonDestructive> </Button>
</footer> </footer>
</Fragment> </Fragment>
); );

View File

@ -66,7 +66,7 @@ export interface ViewProps {
setDeviceName: (s: string) => Promise<void>; setDeviceName: (s: string) => Promise<void>;
permissionToggle: ToggleHandler; permissionToggle: ToggleHandler;
developerMode: boolean; developerMode: boolean;
toggleDeveloperMode: () => void; toggleDeveloperMode: () => Promise<void>;
knownExchanges: Array<ExchangeListItem>; knownExchanges: Array<ExchangeListItem>;
} }

View File

@ -21,7 +21,6 @@ import {
Location, Location,
NotificationType, NotificationType,
parsePaytoUri, parsePaytoUri,
parsePayUri,
PaytoUri, PaytoUri,
stringifyPaytoUri, stringifyPaytoUri,
TalerProtocolTimestamp, TalerProtocolTimestamp,
@ -47,17 +46,11 @@ import { Loading } from "../components/Loading.js";
import { LoadingError } from "../components/LoadingError.js"; import { LoadingError } from "../components/LoadingError.js";
import { Kind, Part, PartCollapsible, PartPayto } from "../components/Part.js"; import { Kind, Part, PartCollapsible, PartPayto } from "../components/Part.js";
import { import {
Button,
ButtonBox,
ButtonDestructive,
ButtonPrimary,
CenteredDialog, CenteredDialog,
HistoryRow,
InfoBox, InfoBox,
ListOfProducts, ListOfProducts,
Overlay, Overlay,
Row, Row,
RowBorderGray,
SmallLightText, SmallLightText,
SubTitle, SubTitle,
WarningBox, WarningBox,
@ -65,12 +58,13 @@ import {
import { Time } from "../components/Time.js"; import { Time } from "../components/Time.js";
import { useTranslationContext } from "../context/translation.js"; import { useTranslationContext } from "../context/translation.js";
import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js";
import { Button } from "../mui/Button.js";
import { Pages } from "../NavigationBar.js"; import { Pages } from "../NavigationBar.js";
import * as wxApi from "../wxApi.js"; import * as wxApi from "../wxApi.js";
interface Props { interface Props {
tid: string; tid: string;
goToWalletHistory: (currency?: string) => void; goToWalletHistory: (currency?: string) => Promise<void>;
} }
async function getTransaction(tid: string): Promise<Transaction> { async function getTransaction(tid: string): Promise<Transaction> {
@ -122,7 +116,7 @@ export function TransactionPage({ tid, goToWalletHistory }: Props): VNode {
onRetry={() => onRetry={() =>
wxApi.retryTransaction(tid).then(() => goToWalletHistory(currency)) wxApi.retryTransaction(tid).then(() => goToWalletHistory(currency))
} }
onRefund={(id) => wxApi.applyRefundFromPurchaseId(id)} onRefund={(id) => wxApi.applyRefundFromPurchaseId(id).then()}
onBack={() => goToWalletHistory(currency)} onBack={() => goToWalletHistory(currency)}
/> />
); );
@ -130,10 +124,10 @@ export function TransactionPage({ tid, goToWalletHistory }: Props): VNode {
export interface WalletTransactionProps { export interface WalletTransactionProps {
transaction: Transaction; transaction: Transaction;
onDelete: () => void; onDelete: () => Promise<void>;
onRetry: () => void; onRetry: () => Promise<void>;
onRefund: (id: string) => void; onRefund: (id: string) => Promise<void>;
onBack: () => void; onBack: () => Promise<void>;
} }
const PurchaseDetailsTable = styled.table` const PurchaseDetailsTable = styled.table`
@ -152,7 +146,7 @@ export function TransactionView({
}: WalletTransactionProps): VNode { }: WalletTransactionProps): VNode {
const [confirmBeforeForget, setConfirmBeforeForget] = useState(false); const [confirmBeforeForget, setConfirmBeforeForget] = useState(false);
function doCheckBeforeForget(): void { async function doCheckBeforeForget(): Promise<void> {
if ( if (
transaction.pending && transaction.pending &&
transaction.type === TransactionType.Withdrawal transaction.type === TransactionType.Withdrawal
@ -198,13 +192,17 @@ export function TransactionView({
<div /> <div />
<div> <div>
{showRetry ? ( {showRetry ? (
<ButtonPrimary onClick={onRetry}> <Button variant="contained" onClick={onRetry}>
<i18n.Translate>Retry</i18n.Translate> <i18n.Translate>Retry</i18n.Translate>
</ButtonPrimary> </Button>
) : null} ) : null}
<ButtonDestructive onClick={doCheckBeforeForget}> <Button
variant="contained"
color="error"
onClick={doCheckBeforeForget}
>
<i18n.Translate>Forget</i18n.Translate> <i18n.Translate>Forget</i18n.Translate>
</ButtonDestructive> </Button>
</div> </div>
</footer> </footer>
</Fragment> </Fragment>
@ -229,13 +227,17 @@ export function TransactionView({
</i18n.Translate> </i18n.Translate>
</section> </section>
<footer> <footer>
<Button onClick={() => setConfirmBeforeForget(false)}> <Button
variant="contained"
color="secondary"
onClick={async () => setConfirmBeforeForget(false)}
>
<i18n.Translate>Cancel</i18n.Translate> <i18n.Translate>Cancel</i18n.Translate>
</Button> </Button>
<ButtonDestructive onClick={onDelete}> <Button variant="contained" color="error" onClick={onDelete}>
<i18n.Translate>Confirm</i18n.Translate> <i18n.Translate>Confirm</i18n.Translate>
</ButtonDestructive> </Button>
</footer> </footer>
</CenteredDialog> </CenteredDialog>
</Overlay> </Overlay>
@ -387,9 +389,12 @@ export function TransactionView({
<div> <div>
<div /> <div />
<div> <div>
<ButtonPrimary onClick={() => onRefund(transaction.proposalId)}> <Button
variant="contained"
onClick={() => onRefund(transaction.proposalId)}
>
<i18n.Translate>Accept</i18n.Translate> <i18n.Translate>Accept</i18n.Translate>
</ButtonPrimary> </Button>
</div> </div>
</div> </div>
</InfoBox> </InfoBox>