new wallet history and view refactoring
This commit is contained in:
parent
b015f76e72
commit
97a05ff659
@ -15,7 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Fragment } from "preact"
|
import { Fragment } from "preact"
|
||||||
import { NavBar } from '../src/popup/popup'
|
import { NavBar } from '../src/NavigationBar'
|
||||||
|
import { LogoHeader } from '../src/components/LogoHeader'
|
||||||
import { TranslationProvider } from '../src/context/translation'
|
import { TranslationProvider } from '../src/context/translation'
|
||||||
|
|
||||||
export const parameters = {
|
export const parameters = {
|
||||||
@ -51,16 +52,16 @@ export const decorators = [
|
|||||||
return <div style={{ width: 400, height: 320 }}>
|
return <div style={{ width: 400, height: 320 }}>
|
||||||
<Story />
|
<Story />
|
||||||
</div>
|
</div>
|
||||||
} else {
|
|
||||||
const path = !isTestingHeader ? /popup(\/.*).*/.exec(kind)[1] : ''
|
|
||||||
// add a fake header so it looks similar
|
|
||||||
return <Fragment>
|
|
||||||
<NavBar path={path} devMode={path === '/dev'} />
|
|
||||||
<div style={{ width: 400, height: 290 }}>
|
|
||||||
<Story />
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const path = /popup(\/.*).*/.exec(kind)[1];
|
||||||
|
// add a fake header so it looks similar
|
||||||
|
return <Fragment>
|
||||||
|
<NavBar path={path} devMode={path === '/dev'} />
|
||||||
|
<div style={{ width: 400, height: 290 }}>
|
||||||
|
<Story />
|
||||||
|
</div>
|
||||||
|
</Fragment>
|
||||||
}
|
}
|
||||||
|
|
||||||
return <div class="popup-container">
|
return <div class="popup-container">
|
||||||
@ -95,13 +96,73 @@ export const decorators = [
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
if (kind.startsWith('wallet')) {
|
if (kind.startsWith('cta')) {
|
||||||
return <div class="wallet-container">
|
return <div>
|
||||||
|
<style>{`
|
||||||
|
html {
|
||||||
|
font-family: sans-serif; /* 1 */
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}`}
|
||||||
|
</style>
|
||||||
|
<style>{`
|
||||||
|
html {
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #f8faf7;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
}`}
|
||||||
|
</style>
|
||||||
<link key="1" rel="stylesheet" type="text/css" href="/style/pure.css" />
|
<link key="1" rel="stylesheet" type="text/css" href="/style/pure.css" />
|
||||||
<link key="2" rel="stylesheet" type="text/css" href="/style/wallet.css" />
|
<link key="2" rel="stylesheet" type="text/css" href="/style/wallet.css" />
|
||||||
<Story />
|
<Story />
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
if (kind.startsWith('wallet')) {
|
||||||
|
const path = /wallet(\/.*).*/.exec(kind)[1];
|
||||||
|
return <div class="wallet-container">
|
||||||
|
<style>{`
|
||||||
|
html {
|
||||||
|
font-family: sans-serif; /* 1 */
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
}`}
|
||||||
|
</style>
|
||||||
|
<style>{`
|
||||||
|
html {
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
font-size: 2em;
|
||||||
|
}
|
||||||
|
input {
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
padding: 0;
|
||||||
|
background-color: #f8faf7;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
|
}`}
|
||||||
|
</style>
|
||||||
|
<LogoHeader />
|
||||||
|
<NavBar path={path} devMode={path === '/dev'} />
|
||||||
|
{/* <link key="1" rel="stylesheet" type="text/css" href="/style/pure.css" />
|
||||||
|
<link key="2" rel="stylesheet" type="text/css" href="/style/wallet.css" /> */}
|
||||||
|
<Story />
|
||||||
|
</div>
|
||||||
|
}
|
||||||
return <div>
|
return <div>
|
||||||
<h1>this story is not under wallet or popup, check title property</h1>
|
<h1>this story is not under wallet or popup, check title property</h1>
|
||||||
<Story />
|
<Story />
|
||||||
|
@ -12,7 +12,7 @@
|
|||||||
"test": "jest ./tests",
|
"test": "jest ./tests",
|
||||||
"compile": "tsc && rollup -c",
|
"compile": "tsc && rollup -c",
|
||||||
"build-storybook": "build-storybook",
|
"build-storybook": "build-storybook",
|
||||||
"storybook": "start-storybook -s static -p 6006",
|
"storybook": "start-storybook -s . -p 6006",
|
||||||
"watch": "tsc --watch & rollup -w -c"
|
"watch": "tsc --watch & rollup -w -c"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
@ -1,14 +1,13 @@
|
|||||||
// rollup.config.js
|
// rollup.config.js
|
||||||
import commonjs from "@rollup/plugin-commonjs";
|
|
||||||
import nodeResolve from "@rollup/plugin-node-resolve";
|
|
||||||
import json from "@rollup/plugin-json";
|
|
||||||
import builtins from "builtin-modules";
|
|
||||||
import replace from "@rollup/plugin-replace";
|
|
||||||
import ignore from "rollup-plugin-ignore"
|
|
||||||
import image from '@rollup/plugin-image';
|
|
||||||
import linaria from '@linaria/rollup';
|
import linaria from '@linaria/rollup';
|
||||||
import css from 'rollup-plugin-css-only';
|
|
||||||
import alias from '@rollup/plugin-alias';
|
import alias from '@rollup/plugin-alias';
|
||||||
|
import commonjs from "@rollup/plugin-commonjs";
|
||||||
|
import image from '@rollup/plugin-image';
|
||||||
|
import json from "@rollup/plugin-json";
|
||||||
|
import nodeResolve from "@rollup/plugin-node-resolve";
|
||||||
|
import replace from "@rollup/plugin-replace";
|
||||||
|
import css from 'rollup-plugin-css-only';
|
||||||
|
import ignore from "rollup-plugin-ignore";
|
||||||
|
|
||||||
const makePlugins = () => [
|
const makePlugins = () => [
|
||||||
alias({
|
alias({
|
||||||
|
@ -27,10 +27,11 @@
|
|||||||
import { i18n } from "@gnu-taler/taler-util";
|
import { i18n } from "@gnu-taler/taler-util";
|
||||||
import { ComponentChildren, JSX } from "preact";
|
import { ComponentChildren, JSX } from "preact";
|
||||||
import Match from "preact-router/match";
|
import Match from "preact-router/match";
|
||||||
import { useDevContext } from "../context/devContext";
|
import { useDevContext } from "./context/devContext";
|
||||||
import { PopupNavigation } from '../components/styled'
|
import { PopupNavigation } from './components/styled'
|
||||||
|
|
||||||
export enum Pages {
|
export enum Pages {
|
||||||
|
welcome = '/welcome',
|
||||||
balance = '/balance',
|
balance = '/balance',
|
||||||
settings = '/settings',
|
settings = '/settings',
|
||||||
dev = '/dev',
|
dev = '/dev',
|
||||||
@ -39,6 +40,15 @@ export enum Pages {
|
|||||||
transaction = '/transaction/:tid',
|
transaction = '/transaction/:tid',
|
||||||
provider_detail = '/provider/:pid',
|
provider_detail = '/provider/:pid',
|
||||||
provider_add = '/provider/add',
|
provider_add = '/provider/add',
|
||||||
|
|
||||||
|
reset_required = '/reset-required',
|
||||||
|
payback = '/payback',
|
||||||
|
return_coins = '/return-coins',
|
||||||
|
|
||||||
|
pay = '/pay',
|
||||||
|
refund = '/refund',
|
||||||
|
tips = '/tips',
|
||||||
|
withdraw = '/withdraw',
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TabProps {
|
interface TabProps {
|
||||||
@ -59,18 +69,23 @@ function Tab(props: TabProps): JSX.Element {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function NavBar({devMode, path}:{path:string, devMode:boolean}) {
|
export function NavBar({ devMode, path }: { path: string, devMode: boolean }) {
|
||||||
return <PopupNavigation devMode={devMode}>
|
return <PopupNavigation devMode={devMode}>
|
||||||
<Tab target="/balance" current={path}>{i18n.str`Balance`}</Tab>
|
<div>
|
||||||
<Tab target="/history" current={path}>{i18n.str`History`}</Tab>
|
<Tab target="/balance" current={path}>{i18n.str`Balance`}</Tab>
|
||||||
<Tab target="/backup" current={path}>{i18n.str`Backup`}</Tab>
|
<Tab target="/history" current={path}>{i18n.str`History`}</Tab>
|
||||||
<Tab target="/settings" current={path}>{i18n.str`Settings`}</Tab>
|
<Tab target="/backup" current={path}>{i18n.str`Backup`}</Tab>
|
||||||
{devMode && <Tab target="/dev" current={path}>{i18n.str`Dev`}</Tab>}
|
<Tab target="/settings" current={path}>{i18n.str`Settings`}</Tab>
|
||||||
|
{devMode && <Tab target="/dev" current={path}>{i18n.str`Dev`}</Tab>}
|
||||||
|
</div>
|
||||||
</PopupNavigation>
|
</PopupNavigation>
|
||||||
}
|
}
|
||||||
|
|
||||||
export function WalletNavBar() {
|
export function WalletNavBar() {
|
||||||
const { devMode } = useDevContext()
|
const { devMode } = useDevContext()
|
||||||
return <Match>{({ path }: any) => <NavBar devMode={devMode} path={path} />}</Match>
|
return <Match>{({ path }: any) => {
|
||||||
|
console.log("path", path)
|
||||||
|
return <NavBar devMode={devMode} path={path} />
|
||||||
|
}}</Match>
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
|||||||
|
export function LogoHeader() {
|
||||||
|
return <div style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-around',
|
||||||
|
margin: '2em',
|
||||||
|
}}>
|
||||||
|
<img style={{
|
||||||
|
width: 150,
|
||||||
|
height: 70,
|
||||||
|
}} src="/static/img/logo-2021.svg" width="150" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
}
|
@ -11,7 +11,7 @@ export const PaymentStatus = styled.div<{ color: string }>`
|
|||||||
background-color: ${p => p.color};
|
background-color: ${p => p.color};
|
||||||
`
|
`
|
||||||
|
|
||||||
export const WalletPage = styled.section`
|
export const WalletAction = styled.section`
|
||||||
border: solid 5px black;
|
border: solid 5px black;
|
||||||
border-radius: 10px;
|
border-radius: 10px;
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
@ -28,8 +28,73 @@ export const WalletPage = styled.section`
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
|
export const DateSeparator = styled.div`
|
||||||
|
color: gray;
|
||||||
|
margin: .2em;
|
||||||
|
margin-top: 1em;
|
||||||
|
`
|
||||||
|
export const WalletBox = styled.div<{ noPadding?: boolean }>`
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
& > * {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
& > section {
|
||||||
|
padding-left: ${({ noPadding }) => noPadding ? '0px' : '8px'};
|
||||||
|
padding-right: ${({ noPadding }) => noPadding ? '0px' : '8px'};
|
||||||
|
// this margin will send the section up when used with a header
|
||||||
|
margin-bottom: auto;
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
table td {
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
table tr {
|
||||||
|
border-bottom: 1px solid black;
|
||||||
|
border-top: 1px solid black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > header {
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
display: flex;
|
||||||
|
padding: 8px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
|
||||||
|
& > div {
|
||||||
|
align-self: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > h3 {
|
||||||
|
margin: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > .title {
|
||||||
|
/* margin: 1em; */
|
||||||
|
font-size: large;
|
||||||
|
color: #3c4e92;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
& > footer {
|
||||||
|
padding-top: 8px;
|
||||||
|
padding-bottom: 8px;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-between;
|
||||||
|
display: flex;
|
||||||
|
& button {
|
||||||
|
margin-right: 8px;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`
|
||||||
|
|
||||||
export const PopupBox = styled.div<{ noPadding?: boolean }>`
|
export const PopupBox = styled.div<{ noPadding?: boolean }>`
|
||||||
height: 290px;
|
height: 290px;
|
||||||
|
width: 400px;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
@ -194,10 +259,32 @@ export const RowBorderGray = styled(Row)`
|
|||||||
|
|
||||||
export const RowLightBorderGray = styled(Row2)`
|
export const RowLightBorderGray = styled(Row2)`
|
||||||
border: 1px solid lightgray;
|
border: 1px solid lightgray;
|
||||||
/* border-radius: 0.5em; */
|
border-top: 0px;
|
||||||
|
|
||||||
|
${DateSeparator} + & {
|
||||||
|
border: 1px solid lightgray;
|
||||||
|
background-color: red;
|
||||||
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
export const HistoryRow = styled(RowLightBorderGray)`
|
export const HistoryRow = styled.a`
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0.5em;
|
||||||
|
|
||||||
|
border: 1px solid lightgray;
|
||||||
|
border-top: 0px;
|
||||||
|
|
||||||
|
${DateSeparator} + & {
|
||||||
|
border: 1px solid lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
|
:hover {
|
||||||
|
background-color: lightgray;
|
||||||
|
}
|
||||||
|
|
||||||
& > ${Column}:last-of-type {
|
& > ${Column}:last-of-type {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
align-self: center;
|
align-self: center;
|
||||||
@ -284,11 +371,17 @@ export const ErrorBox = styled.div`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
`
|
`
|
||||||
export const PopupNavigation = styled.div<{devMode?:boolean}>`
|
export const PopupNavigation = styled.div<{ devMode?: boolean }>`
|
||||||
background-color:#0042b2;
|
background-color:#0042b2;
|
||||||
height: 35px;
|
height: 35px;
|
||||||
|
justify-content: space-around;
|
||||||
|
display: flex;
|
||||||
|
|
||||||
& > a {
|
& > div {
|
||||||
|
width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
& > div > a {
|
||||||
color: #f8faf7;
|
color: #f8faf7;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: calc(400px / ${({ devMode }) => !devMode ? 4 : 5});
|
width: calc(400px / ${({ devMode }) => !devMode ? 4 : 5});
|
||||||
@ -298,7 +391,7 @@ export const PopupNavigation = styled.div<{devMode?:boolean}>`
|
|||||||
line-height: 35px;
|
line-height: 35px;
|
||||||
}
|
}
|
||||||
|
|
||||||
& > a.active {
|
& > div > a.active {
|
||||||
background-color: #f8faf7;
|
background-color: #f8faf7;
|
||||||
color: #0042b2;
|
color: #0042b2;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
@ -20,23 +20,16 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ContractTerms, PreparePayResultType } from '@gnu-taler/taler-util';
|
import { ContractTerms, PreparePayResultType } from '@gnu-taler/taler-util';
|
||||||
import { FunctionalComponent, h } from 'preact';
|
import { createExample } from '../test-utils';
|
||||||
import { PaymentRequestView as TestedComponent } from './Pay';
|
import { PaymentRequestView as TestedComponent } from './Pay';
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'wallet/pay',
|
title: 'cta/pay',
|
||||||
component: TestedComponent,
|
component: TestedComponent,
|
||||||
argTypes: {
|
argTypes: {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
|
|
||||||
const r = (args: any) => <Component {...args} />
|
|
||||||
r.args = props
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
export const InsufficientBalance = createExample(TestedComponent, {
|
export const InsufficientBalance = createExample(TestedComponent, {
|
||||||
payStatus: {
|
payStatus: {
|
||||||
status: PreparePayResultType.InsufficientBalance,
|
status: PreparePayResultType.InsufficientBalance,
|
@ -19,24 +19,18 @@
|
|||||||
* @author Sebastian Javier Marchano (sebasjm)
|
* @author Sebastian Javier Marchano (sebasjm)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ContractTerms, OrderShortInfo, PreparePayResultType } from '@gnu-taler/taler-util';
|
import { OrderShortInfo } from '@gnu-taler/taler-util';
|
||||||
import { FunctionalComponent, h } from 'preact';
|
import { createExample } from '../test-utils';
|
||||||
import { View as TestedComponent } from './Refund';
|
import { View as TestedComponent } from './Refund';
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'wallet/refund',
|
title: 'cta/refund',
|
||||||
component: TestedComponent,
|
component: TestedComponent,
|
||||||
argTypes: {
|
argTypes: {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
|
|
||||||
const r = (args: any) => <Component {...args} />
|
|
||||||
r.args = props
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Complete = createExample(TestedComponent, {
|
export const Complete = createExample(TestedComponent, {
|
||||||
applyResult: {
|
applyResult: {
|
||||||
amountEffectivePaid: 'USD:10',
|
amountEffectivePaid: 'USD:10',
|
@ -19,24 +19,17 @@
|
|||||||
* @author Sebastian Javier Marchano (sebasjm)
|
* @author Sebastian Javier Marchano (sebasjm)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { ContractTerms, PreparePayResultType } from '@gnu-taler/taler-util';
|
import { createExample } from '../test-utils';
|
||||||
import { FunctionalComponent, h } from 'preact';
|
|
||||||
import { View as TestedComponent } from './Tip';
|
import { View as TestedComponent } from './Tip';
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'wallet/tip',
|
title: 'cta/tip',
|
||||||
component: TestedComponent,
|
component: TestedComponent,
|
||||||
argTypes: {
|
argTypes: {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
|
|
||||||
const r = (args: any) => <Component {...args} />
|
|
||||||
r.args = props
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Accepted = createExample(TestedComponent, {
|
export const Accepted = createExample(TestedComponent, {
|
||||||
prepareTipResult: {
|
prepareTipResult: {
|
||||||
accepted: true,
|
accepted: true,
|
@ -19,32 +19,27 @@
|
|||||||
* @author Sebastian Javier Marchano (sebasjm)
|
* @author Sebastian Javier Marchano (sebasjm)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { h } from 'preact';
|
import { createExample } from '../test-utils';
|
||||||
import { View, ViewProps } from './Withdraw';
|
import { View as TestedComponent } from './Withdraw';
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'wallet/withdraw',
|
title: 'cta/withdraw',
|
||||||
component: View,
|
component: TestedComponent,
|
||||||
argTypes: {
|
argTypes: {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const WithoutDetails = (a: any) => <View {...a} />;
|
export const CompleteWithExchange = createExample(TestedComponent, {
|
||||||
WithoutDetails.args = {
|
|
||||||
} as ViewProps
|
|
||||||
|
|
||||||
export const CompleteWithExchange = (a: any) => <View {...a} />;
|
|
||||||
CompleteWithExchange.args = {
|
|
||||||
details: {
|
details: {
|
||||||
amount: 'USD:2',
|
amount: 'USD:2',
|
||||||
|
possibleExchanges: [],
|
||||||
},
|
},
|
||||||
selectedExchange: 'Some exchange'
|
selectedExchange: 'Some exchange'
|
||||||
} as ViewProps
|
})
|
||||||
|
export const CompleteWithoutExchange = createExample(TestedComponent, {
|
||||||
export const CompleteWithoutExchange = (a: any) => <View {...a} />;
|
|
||||||
CompleteWithoutExchange.args = {
|
|
||||||
details: {
|
details: {
|
||||||
amount: 'USD:2',
|
amount: 'USD:2',
|
||||||
|
possibleExchanges: [],
|
||||||
},
|
},
|
||||||
} as ViewProps
|
})
|
@ -32,14 +32,13 @@ import {
|
|||||||
} from "../wxApi";
|
} from "../wxApi";
|
||||||
import { WithdrawUriInfoResponse } from "@gnu-taler/taler-util";
|
import { WithdrawUriInfoResponse } from "@gnu-taler/taler-util";
|
||||||
import { JSX } from "preact/jsx-runtime";
|
import { JSX } from "preact/jsx-runtime";
|
||||||
import { WalletPage } from '../components/styled';
|
import { WalletAction } from '../components/styled';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
talerWithdrawUri?: string;
|
talerWithdrawUri?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ViewProps {
|
export interface ViewProps {
|
||||||
talerWithdrawUri?: string;
|
|
||||||
details: WithdrawUriInfoResponse;
|
details: WithdrawUriInfoResponse;
|
||||||
selectedExchange?: string;
|
selectedExchange?: string;
|
||||||
accept: () => Promise<void>;
|
accept: () => Promise<void>;
|
||||||
@ -50,7 +49,7 @@ export interface ViewProps {
|
|||||||
export function View({ details, selectedExchange, accept, setCancelled, setSelecting }: ViewProps) {
|
export function View({ details, selectedExchange, accept, setCancelled, setSelecting }: ViewProps) {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<WalletPage>
|
<WalletAction>
|
||||||
<div style="border-bottom: 3px dashed #aa3939; margin-bottom: 2em;">
|
<div style="border-bottom: 3px dashed #aa3939; margin-bottom: 2em;">
|
||||||
<h1 style="font-family: monospace; font-size: 250%;">
|
<h1 style="font-family: monospace; font-size: 250%;">
|
||||||
<span style="color: #aa3939;">❰</span>Taler Wallet<span style="color: #aa3939;">❱</span>
|
<span style="color: #aa3939;">❰</span>Taler Wallet<span style="color: #aa3939;">❱</span>
|
||||||
@ -101,26 +100,19 @@ export function View({ details, selectedExchange, accept, setCancelled, setSelec
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</WalletPage>
|
</WalletAction>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element {
|
export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element {
|
||||||
const [details, setDetails] = useState<WithdrawUriInfoResponse | undefined>(undefined);
|
const [details, setDetails] = useState<WithdrawUriInfoResponse | undefined>(undefined);
|
||||||
const [selectedExchange, setSelectedExchange] = useState<
|
const [selectedExchange, setSelectedExchange] = useState<string | undefined>(undefined);
|
||||||
string | undefined
|
|
||||||
>(undefined);
|
|
||||||
const [cancelled, setCancelled] = useState(false);
|
const [cancelled, setCancelled] = useState(false);
|
||||||
const [selecting, setSelecting] = useState(false);
|
const [selecting, setSelecting] = useState(false);
|
||||||
const [errMsg, setErrMsg] = useState<string | undefined>("");
|
const [error, setError] = useState<boolean>(false);
|
||||||
const [updateCounter, setUpdateCounter] = useState(1);
|
const [updateCounter, setUpdateCounter] = useState(1);
|
||||||
const [state, setState] = useState(1)
|
const [state, setState] = useState(1)
|
||||||
|
|
||||||
// setTimeout(() => {
|
|
||||||
// console.log('tick...')
|
|
||||||
// setState(s => s + 1)
|
|
||||||
// }, 1000);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return onUpdateNotification(() => {
|
return onUpdateNotification(() => {
|
||||||
console.log('updating...')
|
console.log('updating...')
|
||||||
@ -132,20 +124,19 @@ export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element
|
|||||||
console.log('on effect yes', talerWithdrawUri)
|
console.log('on effect yes', talerWithdrawUri)
|
||||||
if (!talerWithdrawUri) return
|
if (!talerWithdrawUri) return
|
||||||
const fetchData = async (): Promise<void> => {
|
const fetchData = async (): Promise<void> => {
|
||||||
console.log('que pasa')
|
|
||||||
try {
|
try {
|
||||||
const res = await getWithdrawalDetailsForUri({ talerWithdrawUri });
|
const res = await getWithdrawalDetailsForUri({ talerWithdrawUri });
|
||||||
console.log('res', res)
|
|
||||||
setDetails(res);
|
setDetails(res);
|
||||||
if (res.defaultExchangeBaseUrl) {
|
if (res.defaultExchangeBaseUrl) {
|
||||||
setSelectedExchange(res.defaultExchangeBaseUrl);
|
setSelectedExchange(res.defaultExchangeBaseUrl);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error('error',JSON.stringify(e,undefined,2))
|
||||||
|
setError(true)
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [selectedExchange, errMsg, selecting, talerWithdrawUri, updateCounter, state]);
|
}, [selectedExchange, selecting, talerWithdrawUri, updateCounter, state]);
|
||||||
|
|
||||||
if (!talerWithdrawUri) {
|
if (!talerWithdrawUri) {
|
||||||
return <span><i18n.Translate>missing withdraw uri</i18n.Translate></span>;
|
return <span><i18n.Translate>missing withdraw uri</i18n.Translate></span>;
|
||||||
@ -169,6 +160,9 @@ export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element
|
|||||||
if (cancelled) {
|
if (cancelled) {
|
||||||
return <span><i18n.Translate>Withdraw operation has been cancelled.</i18n.Translate></span>;
|
return <span><i18n.Translate>Withdraw operation has been cancelled.</i18n.Translate></span>;
|
||||||
}
|
}
|
||||||
|
if (error) {
|
||||||
|
return <span><i18n.Translate>This URI is not valid anymore.</i18n.Translate></span>;
|
||||||
|
}
|
||||||
|
|
||||||
return <View accept={accept}
|
return <View accept={accept}
|
||||||
setCancelled={setCancelled} setSelecting={setSelecting}
|
setCancelled={setCancelled} setSelecting={setSelecting}
|
@ -25,7 +25,7 @@ import {
|
|||||||
SmallText, SmallTextLight
|
SmallText, SmallTextLight
|
||||||
} from "../components/styled";
|
} from "../components/styled";
|
||||||
import { useBackupStatus } from "../hooks/useBackupStatus";
|
import { useBackupStatus } from "../hooks/useBackupStatus";
|
||||||
import { Pages } from "./popup";
|
import { Pages } from "../NavigationBar";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onAddProvider: () => void;
|
onAddProvider: () => void;
|
||||||
|
@ -14,11 +14,19 @@
|
|||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { AmountJson, Amounts, AmountString, Balance, Timestamp, Transaction, TransactionsResponse, TransactionType } from "@gnu-taler/taler-util";
|
import { AmountString, Balance, Timestamp, Transaction, TransactionsResponse, TransactionType } from "@gnu-taler/taler-util";
|
||||||
|
import { formatDistance } from "date-fns";
|
||||||
import { JSX } from "preact";
|
import { JSX } from "preact";
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
import imageBank from '../../static/img/ri-bank-line.svg';
|
||||||
|
import imageHandHeart from '../../static/img/ri-hand-heart-line.svg';
|
||||||
|
import imageRefresh from '../../static/img/ri-refresh-line.svg';
|
||||||
|
import imageRefund from '../../static/img/ri-refund-2-line.svg';
|
||||||
|
import imageShoppingCart from '../../static/img/ri-shopping-cart-line.svg';
|
||||||
|
import { Column, ExtraLargeText, HistoryRow, PopupBox, SmallTextLight } from "../components/styled";
|
||||||
|
import { useBalances } from "../hooks/useBalances";
|
||||||
import * as wxApi from "../wxApi";
|
import * as wxApi from "../wxApi";
|
||||||
import { Pages } from "./popup";
|
import { Pages } from "../NavigationBar";
|
||||||
|
|
||||||
|
|
||||||
export function HistoryPage(props: any): JSX.Element {
|
export function HistoryPage(props: any): JSX.Element {
|
||||||
@ -45,7 +53,7 @@ export function HistoryPage(props: any): JSX.Element {
|
|||||||
|
|
||||||
function amountToString(c: AmountString) {
|
function amountToString(c: AmountString) {
|
||||||
const idx = c.indexOf(':')
|
const idx = c.indexOf(':')
|
||||||
return `${c.substring(idx+1)} ${c.substring(0,idx)}`
|
return `${c.substring(idx + 1)} ${c.substring(0, idx)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -68,20 +76,14 @@ export function HistoryView({ list, balances }: { list: Transaction[], balances:
|
|||||||
))}
|
))}
|
||||||
</section>
|
</section>
|
||||||
<footer style={{ justifyContent: 'space-around' }}>
|
<footer style={{ justifyContent: 'space-around' }}>
|
||||||
<a style={{ color: 'darkgreen', textDecoration:'none' }} href={Pages.transaction.replace(':tid', 'asd')}>VIEW MORE TRANSACTIONS</a>
|
<a target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
style={{ color: 'darkgreen', textDecoration: 'none' }}
|
||||||
|
href={chrome.extension ? chrome.extension.getURL(`/static/wallet.html#/history`) : '#'}>VIEW MORE TRANSACTIONS</a>
|
||||||
</footer>
|
</footer>
|
||||||
</PopupBox>
|
</PopupBox>
|
||||||
}
|
}
|
||||||
|
|
||||||
import imageBank from '../../static/img/ri-bank-line.svg';
|
|
||||||
import imageShoppingCart from '../../static/img/ri-shopping-cart-line.svg';
|
|
||||||
import imageRefund from '../../static/img/ri-refund-2-line.svg';
|
|
||||||
import imageHandHeart from '../../static/img/ri-hand-heart-line.svg';
|
|
||||||
import imageRefresh from '../../static/img/ri-refresh-line.svg';
|
|
||||||
import { Column, ExtraLargeText, HistoryRow, PopupBox, Row, RowBorderGray, SmallTextLight } from "../components/styled";
|
|
||||||
import { useBalances } from "../hooks/useBalances";
|
|
||||||
import { formatDistance } from "date-fns";
|
|
||||||
|
|
||||||
function TransactionItem(props: { tx: Transaction }): JSX.Element {
|
function TransactionItem(props: { tx: Transaction }): JSX.Element {
|
||||||
const tx = props.tx;
|
const tx = props.tx;
|
||||||
switch (tx.type) {
|
switch (tx.type) {
|
||||||
@ -171,18 +173,16 @@ function TransactionLayout(props: TransactionLayoutProps): JSX.Element {
|
|||||||
const now = new Date();
|
const now = new Date();
|
||||||
const dateStr = formatDistance(date, now, { addSuffix: true })
|
const dateStr = formatDistance(date, now, { addSuffix: true })
|
||||||
return (
|
return (
|
||||||
<HistoryRow>
|
<HistoryRow href={Pages.transaction.replace(':tid', props.id)}>
|
||||||
<img src={props.iconPath} />
|
<img src={props.iconPath} />
|
||||||
<Column>
|
<Column>
|
||||||
<ExtraLargeText>
|
<ExtraLargeText>
|
||||||
<a href={Pages.transaction.replace(':tid', props.id)}><span>{props.title}</span></a>
|
<span>{props.title}</span>
|
||||||
{props.pending ? (
|
{props.pending ? (
|
||||||
<span style={{ color: "darkblue" }}> (Pending)</span>
|
<span style={{ color: "darkblue" }}> (Pending)</span>
|
||||||
) : null}
|
) : null}
|
||||||
</ExtraLargeText>
|
</ExtraLargeText>
|
||||||
<SmallTextLight>{dateStr}</SmallTextLight>
|
<SmallTextLight>{dateStr}</SmallTextLight>
|
||||||
|
|
||||||
{/* <div>{props.subtitle}</div> */}
|
|
||||||
</Column>
|
</Column>
|
||||||
<TransactionAmount
|
<TransactionAmount
|
||||||
pending={props.pending}
|
pending={props.pending}
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Fragment, FunctionalComponent } from 'preact';
|
import { Fragment, FunctionalComponent } from 'preact';
|
||||||
import { NavBar as TestedComponent } from './popup';
|
import { NavBar as TestedComponent } from '../NavigationBar';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
title: 'popup/header',
|
title: 'popup/header',
|
||||||
|
@ -20,7 +20,7 @@ import { Fragment, JSX, VNode } from "preact";
|
|||||||
import { route } from 'preact-router';
|
import { route } from 'preact-router';
|
||||||
import { useEffect, useState } from "preact/hooks";
|
import { useEffect, useState } from "preact/hooks";
|
||||||
import * as wxApi from "../wxApi";
|
import * as wxApi from "../wxApi";
|
||||||
import { Pages } from "./popup";
|
import { Pages } from "../NavigationBar";
|
||||||
import emptyImg from "../../static/img/empty.png"
|
import emptyImg from "../../static/img/empty.png"
|
||||||
import { Button, ButtonDestructive, ButtonPrimary, ListOfProducts, PopupBox, Row, RowBorderGray, SmallTextLight } from "../components/styled";
|
import { Button, ButtonDestructive, ButtonPrimary, ListOfProducts, PopupBox, Row, RowBorderGray, SmallTextLight } from "../components/styled";
|
||||||
import { ErrorMessage } from "../components/ErrorMessage";
|
import { ErrorMessage } from "../components/ErrorMessage";
|
||||||
|
@ -34,7 +34,7 @@ import { DeveloperPage as DeveloperPage } from "./popup/Debug";
|
|||||||
import { HistoryPage } from "./popup/History";
|
import { HistoryPage } from "./popup/History";
|
||||||
import {
|
import {
|
||||||
Pages, WalletNavBar
|
Pages, WalletNavBar
|
||||||
} from "./popup/popup";
|
} from "./NavigationBar";
|
||||||
import { ProviderAddPage } from "./popup/ProviderAddPage";
|
import { ProviderAddPage } from "./popup/ProviderAddPage";
|
||||||
import { ProviderDetailPage } from "./popup/ProviderDetailPage";
|
import { ProviderDetailPage } from "./popup/ProviderDetailPage";
|
||||||
import { SettingsPage } from "./popup/Settings";
|
import { SettingsPage } from "./popup/Settings";
|
||||||
|
8
packages/taler-wallet-webextension/src/test-utils.ts
Normal file
8
packages/taler-wallet-webextension/src/test-utils.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { FunctionalComponent, h as render } from 'preact';
|
||||||
|
|
||||||
|
export function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
|
||||||
|
const r = (args: any) => render(Component, args)
|
||||||
|
r.args = props
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,294 @@
|
|||||||
|
/*
|
||||||
|
This file is part of GNU Taler
|
||||||
|
(C) 2021 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/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Sebastian Javier Marchano (sebasjm)
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
PaymentStatus,
|
||||||
|
TransactionCommon, TransactionDeposit, TransactionPayment,
|
||||||
|
TransactionRefresh, TransactionRefund, TransactionTip, TransactionType,
|
||||||
|
TransactionWithdrawal,
|
||||||
|
WithdrawalType
|
||||||
|
} from '@gnu-taler/taler-util';
|
||||||
|
import { FunctionalComponent } from 'preact';
|
||||||
|
import { HistoryView as TestedComponent } from './History';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'wallet/history/list',
|
||||||
|
component: TestedComponent,
|
||||||
|
};
|
||||||
|
|
||||||
|
let count = 0
|
||||||
|
const commonTransaction = () => ({
|
||||||
|
amountRaw: 'USD:10',
|
||||||
|
amountEffective: 'USD:9',
|
||||||
|
pending: false,
|
||||||
|
timestamp: {
|
||||||
|
t_ms: new Date().getTime() - (count++ * 1000*60*60*7)
|
||||||
|
},
|
||||||
|
transactionId: '12',
|
||||||
|
} as TransactionCommon)
|
||||||
|
|
||||||
|
const exampleData = {
|
||||||
|
withdraw: {
|
||||||
|
...commonTransaction(),
|
||||||
|
type: TransactionType.Withdrawal,
|
||||||
|
exchangeBaseUrl: 'http://exchange.taler',
|
||||||
|
withdrawalDetails: {
|
||||||
|
confirmed: false,
|
||||||
|
exchangePaytoUris: ['payto://x-taler-bank/bank/account'],
|
||||||
|
type: WithdrawalType.ManualTransfer,
|
||||||
|
}
|
||||||
|
} as TransactionWithdrawal,
|
||||||
|
payment: {
|
||||||
|
...commonTransaction(),
|
||||||
|
amountEffective: 'USD:11',
|
||||||
|
type: TransactionType.Payment,
|
||||||
|
info: {
|
||||||
|
contractTermsHash: 'ASDZXCASD',
|
||||||
|
merchant: {
|
||||||
|
name: 'the merchant',
|
||||||
|
},
|
||||||
|
orderId: '2021.167-03NPY6MCYMVGT',
|
||||||
|
products: [],
|
||||||
|
summary: 'the summary',
|
||||||
|
fulfillmentMessage: '',
|
||||||
|
},
|
||||||
|
proposalId: '1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0',
|
||||||
|
status: PaymentStatus.Accepted,
|
||||||
|
} as TransactionPayment,
|
||||||
|
deposit: {
|
||||||
|
...commonTransaction(),
|
||||||
|
type: TransactionType.Deposit,
|
||||||
|
depositGroupId: '#groupId',
|
||||||
|
targetPaytoUri: 'payto://x-taler-bank/bank/account',
|
||||||
|
} as TransactionDeposit,
|
||||||
|
refresh: {
|
||||||
|
...commonTransaction(),
|
||||||
|
type: TransactionType.Refresh,
|
||||||
|
exchangeBaseUrl: 'http://exchange.taler',
|
||||||
|
} as TransactionRefresh,
|
||||||
|
tip: {
|
||||||
|
...commonTransaction(),
|
||||||
|
type: TransactionType.Tip,
|
||||||
|
merchantBaseUrl: 'http://merchant.taler',
|
||||||
|
} as TransactionTip,
|
||||||
|
refund: {
|
||||||
|
...commonTransaction(),
|
||||||
|
type: TransactionType.Refund,
|
||||||
|
refundedTransactionId: 'payment:1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0',
|
||||||
|
info: {
|
||||||
|
contractTermsHash: 'ASDZXCASD',
|
||||||
|
merchant: {
|
||||||
|
name: 'the merchant',
|
||||||
|
},
|
||||||
|
orderId: '2021.167-03NPY6MCYMVGT',
|
||||||
|
products: [],
|
||||||
|
summary: 'the summary',
|
||||||
|
fulfillmentMessage: '',
|
||||||
|
},
|
||||||
|
} as TransactionRefund,
|
||||||
|
}
|
||||||
|
|
||||||
|
function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
|
||||||
|
const r = (args: any) => <Component {...args} />
|
||||||
|
r.args = props
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Empty = createExample(TestedComponent, {
|
||||||
|
list: [],
|
||||||
|
balances: [{
|
||||||
|
available: 'TESTKUDOS:10',
|
||||||
|
pendingIncoming: 'TESTKUDOS:0',
|
||||||
|
pendingOutgoing: 'TESTKUDOS:0',
|
||||||
|
hasPendingTransactions: false,
|
||||||
|
requiresUserInput: false,
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
export const One = createExample(TestedComponent, {
|
||||||
|
list: [exampleData.withdraw],
|
||||||
|
balances: [{
|
||||||
|
available: 'USD:10',
|
||||||
|
pendingIncoming: 'USD:0',
|
||||||
|
pendingOutgoing: 'USD:0',
|
||||||
|
hasPendingTransactions: false,
|
||||||
|
requiresUserInput: false,
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Several = createExample(TestedComponent, {
|
||||||
|
list: [
|
||||||
|
exampleData.withdraw,
|
||||||
|
exampleData.payment,
|
||||||
|
exampleData.withdraw,
|
||||||
|
exampleData.payment,
|
||||||
|
exampleData.refresh,
|
||||||
|
exampleData.refund,
|
||||||
|
exampleData.tip,
|
||||||
|
exampleData.deposit,
|
||||||
|
],
|
||||||
|
balances: [{
|
||||||
|
available: 'TESTKUDOS:10',
|
||||||
|
pendingIncoming: 'TESTKUDOS:0',
|
||||||
|
pendingOutgoing: 'TESTKUDOS:0',
|
||||||
|
hasPendingTransactions: false,
|
||||||
|
requiresUserInput: false,
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
export const SeveralWithTwoCurrencies = createExample(TestedComponent, {
|
||||||
|
list: [
|
||||||
|
exampleData.withdraw,
|
||||||
|
exampleData.payment,
|
||||||
|
exampleData.withdraw,
|
||||||
|
exampleData.payment,
|
||||||
|
exampleData.refresh,
|
||||||
|
exampleData.refund,
|
||||||
|
exampleData.tip,
|
||||||
|
exampleData.deposit,
|
||||||
|
],
|
||||||
|
balances: [{
|
||||||
|
available: 'TESTKUDOS:10',
|
||||||
|
pendingIncoming: 'TESTKUDOS:0',
|
||||||
|
pendingOutgoing: 'TESTKUDOS:0',
|
||||||
|
hasPendingTransactions: false,
|
||||||
|
requiresUserInput: false,
|
||||||
|
},{
|
||||||
|
available: 'USD:10',
|
||||||
|
pendingIncoming: 'USD:0',
|
||||||
|
pendingOutgoing: 'USD:0',
|
||||||
|
hasPendingTransactions: false,
|
||||||
|
requiresUserInput: false,
|
||||||
|
}]
|
||||||
|
});
|
||||||
|
|
||||||
|
// export const WithdrawPending = createExample(TestedComponent, {
|
||||||
|
// transaction: { ...exampleData.withdraw, pending: true },
|
||||||
|
// });
|
||||||
|
|
||||||
|
|
||||||
|
// export const Payment = createExample(TestedComponent, {
|
||||||
|
// transaction: exampleData.payment
|
||||||
|
// });
|
||||||
|
|
||||||
|
// export const PaymentWithoutFee = createExample(TestedComponent, {
|
||||||
|
// transaction: {
|
||||||
|
// ...exampleData.payment,
|
||||||
|
// amountRaw: 'USD:11',
|
||||||
|
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// export const PaymentPending = createExample(TestedComponent, {
|
||||||
|
// transaction: { ...exampleData.payment, pending: true },
|
||||||
|
// });
|
||||||
|
|
||||||
|
// export const PaymentWithProducts = createExample(TestedComponent, {
|
||||||
|
// transaction: {
|
||||||
|
// ...exampleData.payment,
|
||||||
|
// info: {
|
||||||
|
// ...exampleData.payment.info,
|
||||||
|
// summary: 'this order has 5 products',
|
||||||
|
// products: [{
|
||||||
|
// description: 't-shirt',
|
||||||
|
// unit: 'shirts',
|
||||||
|
// quantity: 1,
|
||||||
|
// }, {
|
||||||
|
// description: 't-shirt',
|
||||||
|
// unit: 'shirts',
|
||||||
|
// quantity: 1,
|
||||||
|
// }, {
|
||||||
|
// description: 'e-book',
|
||||||
|
// }, {
|
||||||
|
// description: 'beer',
|
||||||
|
// unit: 'pint',
|
||||||
|
// quantity: 15,
|
||||||
|
// }, {
|
||||||
|
// description: 'beer',
|
||||||
|
// unit: 'pint',
|
||||||
|
// quantity: 15,
|
||||||
|
// }]
|
||||||
|
// }
|
||||||
|
// } as TransactionPayment,
|
||||||
|
// });
|
||||||
|
|
||||||
|
// export const PaymentWithLongSummary = createExample(TestedComponent, {
|
||||||
|
// transaction: {
|
||||||
|
// ...exampleData.payment,
|
||||||
|
// info: {
|
||||||
|
// ...exampleData.payment.info,
|
||||||
|
// summary: 'this is a very long summary that will occupy severals lines, this is a very long summary that will occupy severals lines, this is a very long summary that will occupy severals lines, this is a very long summary that will occupy severals lines, ',
|
||||||
|
// products: [{
|
||||||
|
// description: 'an xl sized t-shirt with some drawings on it, color pink',
|
||||||
|
// unit: 'shirts',
|
||||||
|
// quantity: 1,
|
||||||
|
// }, {
|
||||||
|
// description: 'beer',
|
||||||
|
// unit: 'pint',
|
||||||
|
// quantity: 15,
|
||||||
|
// }]
|
||||||
|
// }
|
||||||
|
// } as TransactionPayment,
|
||||||
|
// });
|
||||||
|
|
||||||
|
|
||||||
|
// export const Deposit = createExample(TestedComponent, {
|
||||||
|
// transaction: exampleData.deposit
|
||||||
|
// });
|
||||||
|
|
||||||
|
// export const DepositPending = createExample(TestedComponent, {
|
||||||
|
// transaction: { ...exampleData.deposit, pending: true }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// export const Refresh = createExample(TestedComponent, {
|
||||||
|
// transaction: exampleData.refresh
|
||||||
|
// });
|
||||||
|
|
||||||
|
// export const Tip = createExample(TestedComponent, {
|
||||||
|
// transaction: exampleData.tip
|
||||||
|
// });
|
||||||
|
|
||||||
|
// export const TipPending = createExample(TestedComponent, {
|
||||||
|
// transaction: { ...exampleData.tip, pending: true }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// export const Refund = createExample(TestedComponent, {
|
||||||
|
// transaction: exampleData.refund
|
||||||
|
// });
|
||||||
|
|
||||||
|
// export const RefundPending = createExample(TestedComponent, {
|
||||||
|
// transaction: { ...exampleData.refund, pending: true }
|
||||||
|
// });
|
||||||
|
|
||||||
|
// export const RefundWithProducts = createExample(TestedComponent, {
|
||||||
|
// transaction: {
|
||||||
|
// ...exampleData.refund,
|
||||||
|
// info: {
|
||||||
|
// ...exampleData.refund.info,
|
||||||
|
// products: [{
|
||||||
|
// description: 't-shirt',
|
||||||
|
// }, {
|
||||||
|
// description: 'beer',
|
||||||
|
// }]
|
||||||
|
// }
|
||||||
|
// } as TransactionRefund,
|
||||||
|
// });
|
248
packages/taler-wallet-webextension/src/wallet/History.tsx
Normal file
248
packages/taler-wallet-webextension/src/wallet/History.tsx
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
/*
|
||||||
|
This file is part of TALER
|
||||||
|
(C) 2016 GNUnet e.V.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
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
|
||||||
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { AmountString, Balance, Timestamp, Transaction, TransactionsResponse, TransactionType } from "@gnu-taler/taler-util";
|
||||||
|
import { format } from "date-fns";
|
||||||
|
import { Fragment, JSX } from "preact";
|
||||||
|
import { useEffect, useState } from "preact/hooks";
|
||||||
|
import imageBank from '../../static/img/ri-bank-line.svg';
|
||||||
|
import imageHandHeart from '../../static/img/ri-hand-heart-line.svg';
|
||||||
|
import imageRefresh from '../../static/img/ri-refresh-line.svg';
|
||||||
|
import imageRefund from '../../static/img/ri-refund-2-line.svg';
|
||||||
|
import imageShoppingCart from '../../static/img/ri-shopping-cart-line.svg';
|
||||||
|
import { Column, ExtraLargeText, HistoryRow, WalletBox, DateSeparator, SmallTextLight } from "../components/styled";
|
||||||
|
import { useBalances } from "../hooks/useBalances";
|
||||||
|
import * as wxApi from "../wxApi";
|
||||||
|
import { Pages } from "../NavigationBar";
|
||||||
|
|
||||||
|
|
||||||
|
export function HistoryPage(props: any): JSX.Element {
|
||||||
|
const [transactions, setTransactions] = useState<
|
||||||
|
TransactionsResponse | undefined
|
||||||
|
>(undefined);
|
||||||
|
const balance = useBalances()
|
||||||
|
const balanceWithoutError = balance?.error ? [] : (balance?.response.balances || [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchData = async (): Promise<void> => {
|
||||||
|
const res = await wxApi.getTransactions();
|
||||||
|
setTransactions(res);
|
||||||
|
};
|
||||||
|
fetchData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!transactions) {
|
||||||
|
return <div>Loading ...</div>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <HistoryView balances={balanceWithoutError} list={[...transactions.transactions].reverse()} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
function amountToString(c: AmountString) {
|
||||||
|
const idx = c.indexOf(':')
|
||||||
|
return `${c.substring(idx + 1)} ${c.substring(0, idx)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function HistoryView({ list, balances }: { list: Transaction[], balances: Balance[] }) {
|
||||||
|
const byDate = list.reduce(function (rv, x) {
|
||||||
|
const theDate = x.timestamp.t_ms === "never" ? "never" : format(x.timestamp.t_ms, 'dd MMMM yyyy');
|
||||||
|
(rv[theDate] = rv[theDate] || []).push(x);
|
||||||
|
return rv;
|
||||||
|
}, {} as { [x: string]: Transaction[] });
|
||||||
|
|
||||||
|
return <WalletBox noPadding>
|
||||||
|
{balances.length > 0 && <header>
|
||||||
|
{balances.length === 1 && <div class="title">
|
||||||
|
Balance: <span>{amountToString(balances[0].available)}</span>
|
||||||
|
</div>}
|
||||||
|
{balances.length > 1 && <div class="title">
|
||||||
|
Balance: <ul style={{ margin: 0 }}>
|
||||||
|
{balances.map(b => <li>{b.available}</li>)}
|
||||||
|
</ul>
|
||||||
|
</div>}
|
||||||
|
</header>}
|
||||||
|
<section>
|
||||||
|
{Object.keys(byDate).map(d => {
|
||||||
|
return <Fragment>
|
||||||
|
<DateSeparator>{d}</DateSeparator>
|
||||||
|
{byDate[d].map((tx, i) => (
|
||||||
|
<TransactionItem key={i} tx={tx} />
|
||||||
|
))}
|
||||||
|
</Fragment>
|
||||||
|
})}
|
||||||
|
</section>
|
||||||
|
</WalletBox>
|
||||||
|
}
|
||||||
|
|
||||||
|
function TransactionItem(props: { tx: Transaction }): JSX.Element {
|
||||||
|
const tx = props.tx;
|
||||||
|
switch (tx.type) {
|
||||||
|
case TransactionType.Withdrawal:
|
||||||
|
return (
|
||||||
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
|
amount={tx.amountEffective}
|
||||||
|
debitCreditIndicator={"credit"}
|
||||||
|
title="Withdrawal"
|
||||||
|
subtitle={`via ${tx.exchangeBaseUrl}`}
|
||||||
|
timestamp={tx.timestamp}
|
||||||
|
iconPath={imageBank}
|
||||||
|
pending={tx.pending}
|
||||||
|
></TransactionLayout>
|
||||||
|
);
|
||||||
|
case TransactionType.Payment:
|
||||||
|
return (
|
||||||
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
|
amount={tx.amountEffective}
|
||||||
|
debitCreditIndicator={"debit"}
|
||||||
|
title="Payment"
|
||||||
|
subtitle={tx.info.summary}
|
||||||
|
timestamp={tx.timestamp}
|
||||||
|
iconPath={imageShoppingCart}
|
||||||
|
pending={tx.pending}
|
||||||
|
></TransactionLayout>
|
||||||
|
);
|
||||||
|
case TransactionType.Refund:
|
||||||
|
return (
|
||||||
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
|
amount={tx.amountEffective}
|
||||||
|
debitCreditIndicator={"credit"}
|
||||||
|
title="Refund"
|
||||||
|
subtitle={tx.info.summary}
|
||||||
|
timestamp={tx.timestamp}
|
||||||
|
iconPath={imageRefund}
|
||||||
|
pending={tx.pending}
|
||||||
|
></TransactionLayout>
|
||||||
|
);
|
||||||
|
case TransactionType.Tip:
|
||||||
|
return (
|
||||||
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
|
amount={tx.amountEffective}
|
||||||
|
debitCreditIndicator={"credit"}
|
||||||
|
title="Tip"
|
||||||
|
subtitle={`from ${new URL(tx.merchantBaseUrl).hostname}`}
|
||||||
|
timestamp={tx.timestamp}
|
||||||
|
iconPath={imageHandHeart}
|
||||||
|
pending={tx.pending}
|
||||||
|
></TransactionLayout>
|
||||||
|
);
|
||||||
|
case TransactionType.Refresh:
|
||||||
|
return (
|
||||||
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
|
amount={tx.amountEffective}
|
||||||
|
debitCreditIndicator={"credit"}
|
||||||
|
title="Refresh"
|
||||||
|
subtitle={`via exchange ${tx.exchangeBaseUrl}`}
|
||||||
|
timestamp={tx.timestamp}
|
||||||
|
iconPath={imageRefresh}
|
||||||
|
pending={tx.pending}
|
||||||
|
></TransactionLayout>
|
||||||
|
);
|
||||||
|
case TransactionType.Deposit:
|
||||||
|
return (
|
||||||
|
<TransactionLayout
|
||||||
|
id={tx.transactionId}
|
||||||
|
amount={tx.amountEffective}
|
||||||
|
debitCreditIndicator={"debit"}
|
||||||
|
title="Refresh"
|
||||||
|
subtitle={`to ${tx.targetPaytoUri}`}
|
||||||
|
timestamp={tx.timestamp}
|
||||||
|
iconPath={imageRefresh}
|
||||||
|
pending={tx.pending}
|
||||||
|
></TransactionLayout>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function TransactionLayout(props: TransactionLayoutProps): JSX.Element {
|
||||||
|
const date = new Date(props.timestamp.t_ms);
|
||||||
|
const dateStr = format(date, 'HH:mm:ss')
|
||||||
|
return (
|
||||||
|
// <a href={Pages.transaction.replace(':tid', props.id)}>
|
||||||
|
<HistoryRow href={Pages.transaction.replace(':tid', props.id)}>
|
||||||
|
<img src={props.iconPath} />
|
||||||
|
<Column>
|
||||||
|
<ExtraLargeText>
|
||||||
|
<span>{props.title}</span>
|
||||||
|
{props.pending ? (
|
||||||
|
<span style={{ color: "darkblue" }}> (Pending)</span>
|
||||||
|
) : null}
|
||||||
|
</ExtraLargeText>
|
||||||
|
<SmallTextLight>{dateStr}</SmallTextLight>
|
||||||
|
</Column>
|
||||||
|
<TransactionAmount
|
||||||
|
pending={props.pending}
|
||||||
|
amount={props.amount}
|
||||||
|
debitCreditIndicator={props.debitCreditIndicator}
|
||||||
|
/>
|
||||||
|
</HistoryRow>
|
||||||
|
// </a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TransactionLayoutProps {
|
||||||
|
debitCreditIndicator: "debit" | "credit" | "unknown";
|
||||||
|
amount: AmountString | "unknown";
|
||||||
|
timestamp: Timestamp;
|
||||||
|
title: string;
|
||||||
|
id: string;
|
||||||
|
subtitle: string;
|
||||||
|
iconPath: string;
|
||||||
|
pending: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TransactionAmountProps {
|
||||||
|
debitCreditIndicator: "debit" | "credit" | "unknown";
|
||||||
|
amount: AmountString | "unknown";
|
||||||
|
pending: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TransactionAmount(props: TransactionAmountProps): JSX.Element {
|
||||||
|
const [currency, amount] = props.amount.split(":");
|
||||||
|
let sign: string;
|
||||||
|
switch (props.debitCreditIndicator) {
|
||||||
|
case "credit":
|
||||||
|
sign = "+";
|
||||||
|
break;
|
||||||
|
case "debit":
|
||||||
|
sign = "-";
|
||||||
|
break;
|
||||||
|
case "unknown":
|
||||||
|
sign = "";
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<Column style={{
|
||||||
|
color:
|
||||||
|
props.pending ? "gray" :
|
||||||
|
(sign === '+' ? 'darkgreen' :
|
||||||
|
(sign === '-' ? 'darkred' :
|
||||||
|
undefined))
|
||||||
|
}}>
|
||||||
|
<ExtraLargeText>
|
||||||
|
{sign}
|
||||||
|
{amount}
|
||||||
|
</ExtraLargeText>
|
||||||
|
<div>{currency}</div>
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -19,7 +19,7 @@
|
|||||||
* @author Sebastian Javier Marchano (sebasjm)
|
* @author Sebastian Javier Marchano (sebasjm)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { FunctionalComponent, h } from 'preact';
|
import { createExample } from '../test-utils';
|
||||||
import { View as TestedComponent } from './Welcome';
|
import { View as TestedComponent } from './Welcome';
|
||||||
|
|
||||||
|
|
||||||
@ -28,12 +28,6 @@ export default {
|
|||||||
component: TestedComponent,
|
component: TestedComponent,
|
||||||
};
|
};
|
||||||
|
|
||||||
function createExample<Props>(Component: FunctionalComponent<Props>, props: Partial<Props>) {
|
|
||||||
const r = (args: any) => <Component {...args} />
|
|
||||||
r.args = props
|
|
||||||
return r
|
|
||||||
}
|
|
||||||
|
|
||||||
export const Normal = createExample(TestedComponent, {
|
export const Normal = createExample(TestedComponent, {
|
||||||
permissionsEnabled: true,
|
permissionsEnabled: true,
|
||||||
diagnostics: {
|
diagnostics: {
|
||||||
|
@ -24,7 +24,7 @@ import { JSX } from "preact/jsx-runtime";
|
|||||||
import { Checkbox } from "../components/Checkbox";
|
import { Checkbox } from "../components/Checkbox";
|
||||||
import { useExtendedPermissions } from "../hooks/useExtendedPermissions";
|
import { useExtendedPermissions } from "../hooks/useExtendedPermissions";
|
||||||
import { Diagnostics } from "../components/Diagnostics";
|
import { Diagnostics } from "../components/Diagnostics";
|
||||||
import { WalletPage } from "../components/styled";
|
import { WalletBox } from "../components/styled";
|
||||||
import { useDiagnostics } from "../hooks/useDiagnostics";
|
import { useDiagnostics } from "../hooks/useDiagnostics";
|
||||||
import { WalletDiagnostics } from "@gnu-taler/taler-util";
|
import { WalletDiagnostics } from "@gnu-taler/taler-util";
|
||||||
|
|
||||||
@ -44,12 +44,7 @@ export interface ViewProps {
|
|||||||
timedOut: boolean,
|
timedOut: boolean,
|
||||||
}
|
}
|
||||||
export function View({ permissionsEnabled, togglePermissions, diagnostics, timedOut }: ViewProps): JSX.Element {
|
export function View({ permissionsEnabled, togglePermissions, diagnostics, timedOut }: ViewProps): JSX.Element {
|
||||||
return (<WalletPage>
|
return (<WalletBox>
|
||||||
<div style="border-bottom: 3px dashed #aa3939; margin-bottom: 2em;">
|
|
||||||
<h1 style="font-family: monospace; font-size: 250%;">
|
|
||||||
<span style="color: #aa3939;">❰</span>Taler Wallet<span style="color: #aa3939;">❱</span>
|
|
||||||
</h1>
|
|
||||||
</div>
|
|
||||||
<h1>Browser Extension Installed!</h1>
|
<h1>Browser Extension Installed!</h1>
|
||||||
<div>
|
<div>
|
||||||
<p>Thank you for installing the wallet.</p>
|
<p>Thank you for installing the wallet.</p>
|
||||||
@ -68,6 +63,6 @@ export function View({ permissionsEnabled, togglePermissions, diagnostics, timed
|
|||||||
Learn how to top up your wallet balance »
|
Learn how to top up your wallet balance »
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</WalletPage>
|
</WalletBox>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -20,17 +20,24 @@
|
|||||||
* @author Florian Dold <dold@taler.net>
|
* @author Florian Dold <dold@taler.net>
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { render } from "preact";
|
import { Fragment, render } from "preact";
|
||||||
import { setupI18n } from "@gnu-taler/taler-util";
|
import { setupI18n } from "@gnu-taler/taler-util";
|
||||||
import { strings } from "./i18n/strings";
|
import { strings } from "./i18n/strings";
|
||||||
import { createHashHistory } from 'history';
|
import { createHashHistory } from 'history';
|
||||||
|
|
||||||
import { WithdrawPage } from "./wallet/Withdraw";
|
|
||||||
import { WelcomePage } from "./wallet/Welcome";
|
import { WelcomePage } from "./wallet/Welcome";
|
||||||
import { PayPage } from "./wallet/Pay";
|
import { HistoryPage } from "./wallet/History";
|
||||||
import { RefundPage } from "./wallet/Refund";
|
import { WithdrawPage } from "./cta/Withdraw";
|
||||||
import { TipPage } from './wallet/Tip';
|
import { PayPage } from "./cta/Pay";
|
||||||
|
import { RefundPage } from "./cta/Refund";
|
||||||
|
import { TipPage } from './cta/Tip';
|
||||||
import Router, { route, Route } from "preact-router";
|
import Router, { route, Route } from "preact-router";
|
||||||
|
import { DevContextProvider } from "./context/devContext";
|
||||||
|
import { LogoHeader } from "./components/LogoHeader";
|
||||||
|
import { useEffect } from "preact/hooks";
|
||||||
|
import {
|
||||||
|
Pages, WalletNavBar
|
||||||
|
} from "./NavigationBar";
|
||||||
|
|
||||||
function main(): void {
|
function main(): void {
|
||||||
try {
|
try {
|
||||||
@ -53,32 +60,43 @@ if (document.readyState === "loading") {
|
|||||||
main();
|
main();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function withLogoAndNavBar(Component: any) {
|
||||||
enum Pages {
|
return () => <Fragment>
|
||||||
welcome = '/welcome',
|
<LogoHeader />
|
||||||
pay = '/pay',
|
<WalletNavBar />
|
||||||
payback = '/payback',
|
<Component />
|
||||||
refund = '/refund',
|
</Fragment>
|
||||||
reset_required = '/reset-required',
|
|
||||||
return_coins = '/return-coins',
|
|
||||||
tips = '/tips',
|
|
||||||
withdraw = '/withdraw',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function Application() {
|
function Application() {
|
||||||
const h = createHashHistory();
|
return <div>
|
||||||
return <Router history={h} >
|
<DevContextProvider>
|
||||||
|
<Router history={createHashHistory()} >
|
||||||
|
|
||||||
<Route path={Pages.welcome} component={WelcomePage} />
|
<Route path={Pages.welcome} component={withLogoAndNavBar(WelcomePage)} />
|
||||||
<Route path={Pages.pay} component={PayPage} />
|
|
||||||
<Route path={Pages.refund} component={RefundPage} />
|
|
||||||
|
|
||||||
<Route path={Pages.tips} component={TipPage} />
|
<Route path={Pages.history} component={withLogoAndNavBar(HistoryPage)} />
|
||||||
<Route path={Pages.withdraw} component={WithdrawPage} />
|
<Route path={Pages.transaction} component={withLogoAndNavBar(HistoryPage)} />
|
||||||
|
|
||||||
<Route path={Pages.reset_required} component={() => <div>no yet implemented</div>} />
|
<Route path={Pages.reset_required} component={() => <div>no yet implemented</div>} />
|
||||||
<Route path={Pages.payback} component={() => <div>no yet implemented</div>} />
|
<Route path={Pages.payback} component={() => <div>no yet implemented</div>} />
|
||||||
<Route path={Pages.return_coins} component={() => <div>no yet implemented</div>} />
|
<Route path={Pages.return_coins} component={() => <div>no yet implemented</div>} />
|
||||||
|
|
||||||
</Router>
|
{/** call to action */}
|
||||||
|
<Route path={Pages.pay} component={PayPage} />
|
||||||
|
<Route path={Pages.refund} component={RefundPage} />
|
||||||
|
<Route path={Pages.tips} component={TipPage} />
|
||||||
|
<Route path={Pages.withdraw} component={WithdrawPage} />
|
||||||
|
|
||||||
|
<Route default component={Redirect} to={Pages.history} />
|
||||||
|
</Router>
|
||||||
|
</DevContextProvider>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
function Redirect({ to }: { to: string }): null {
|
||||||
|
useEffect(() => {
|
||||||
|
route(to, true)
|
||||||
|
})
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" width="670" height="300" viewBox="0 0 201 90"><g fill="#0042b3" fill-rule="evenodd" stroke-width=".3"><path d="M86.7 1.1c15.6 0 29 9.4 36 23.2h-5.9A35.1 35.1 0 0086.7 6.5C67 6.5 51 23.6 51 44.7c0 10.4 3.8 19.7 10 26.6a31.4 31.4 0 01-4.2 3A45.2 45.2 0 0146 44.7c0-24 18.2-43.6 40.7-43.6zm35.8 64.3a40.4 40.4 0 01-39 22.8c3-1.5 6-3.5 8.6-5.7a35.6 35.6 0 0024.6-17.1z"/><path d="M64.2 1.1l3.1.1c-3 1.6-5.9 3.5-8.5 5.8a37.5 37.5 0 00-30.2 37.7c0 14.3 7.3 26.7 18 33.3a29.6 29.6 0 01-8.5.2c-9-8-14.6-20-14.6-33.5 0-24 18.2-43.6 40.7-43.6zm5.4 81.4a35.6 35.6 0 0024.6-17.1h5.9a40.4 40.4 0 01-39 22.8c3-1.5 5.9-3.5 8.5-5.7zm24.8-58.2a37 37 0 00-12.6-12.8 29.6 29.6 0 018.5-.2c4 3.6 7.4 8 9.9 13z"/><path d="M41.8 1.1c1 0 2 0 3.1.2-3 1.5-5.9 3.4-8.5 5.6A37.5 37.5 0 006.1 44.7c0 21.1 16 38.3 35.7 38.3 12.6 0 23.6-7 30-17.6h5.8a40.4 40.4 0 01-35.8 23C19.3 88.4 1 68.8 1 44.7c0-24 18.2-43.6 40.7-43.6zm30.1 23.2a38.1 38.1 0 00-4.5-6.1c1.3-1.2 2.7-2.2 4.3-3 2.3 2.7 4.4 5.8 6 9.1z"/></g><path d="M76.1 34.4h9.2v-5H61.9v5H71v26h5.1zM92.6 52.9h13.7l3 7.4h5.3l-12.7-31.2h-4.7L84.5 60.3h5.2zm11.8-4.9h-9.9l5-12.4zM123.8 29.4h-4.6v31h20.6v-5h-16zM166.5 29.4H145v31h21.6v-5H150v-8.3h14.5v-4.9h-14.5v-8h16.4zM191.2 39.5c0 1.6-.5 2.8-1.6 3.8s-2.6 1.4-4.4 1.4h-7.4V34.3h7.4c1.9 0 3.4.4 4.4 1.3 1 .9 1.6 2.2 1.6 3.9zm6 20.8l-7.7-11.7c1-.3 1.9-.7 2.7-1.3a8.8 8.8 0 003.6-4.6c.4-1 .5-2.2.5-3.5 0-1.5-.2-2.9-.7-4.1a8.4 8.4 0 00-2.1-3.1c-1-.8-2-1.5-3.4-2-1.3-.4-2.8-.6-4.5-.6h-12.9v31h5V49.4h6.5l7 10.8z"/></svg>
|
After Width: | Height: | Size: 1.5 KiB |
@ -2,6 +2,7 @@ body {
|
|||||||
font-size: 100%;
|
font-size: 100%;
|
||||||
overflow-y: scroll;
|
overflow-y: scroll;
|
||||||
margin-top: 2em;
|
margin-top: 2em;
|
||||||
|
font-family: Arial, Helvetica, sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
.wallet-container {
|
.wallet-container {
|
||||||
|
Loading…
Reference in New Issue
Block a user