summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2021-08-13 18:04:05 -0300
committerSebastian <sebasjm@gmail.com>2021-08-13 18:04:30 -0300
commite9bb85a212dbd9b86875e89a0aca5d805e2ad61b (patch)
tree7fb2f9f28fc382d7348cd45cb730b302217099c7 /packages/taler-wallet-webextension
parenteb553be84163946e4ffa5b2a4dfaa2029aebc534 (diff)
downloadwallet-core-e9bb85a212dbd9b86875e89a0aca5d805e2ad61b.tar.gz
wallet-core-e9bb85a212dbd9b86875e89a0aca5d805e2ad61b.tar.bz2
wallet-core-e9bb85a212dbd9b86875e89a0aca5d805e2ad61b.zip
new wallet UI and more tests
Diffstat (limited to 'packages/taler-wallet-webextension')
-rw-r--r--packages/taler-wallet-webextension/.storybook/preview.js2
-rw-r--r--packages/taler-wallet-webextension/src/browserWorkerEntry.ts2
-rw-r--r--packages/taler-wallet-webextension/src/components/Diagnostics.tsx28
-rw-r--r--packages/taler-wallet-webextension/src/components/styled/index.tsx66
-rw-r--r--packages/taler-wallet-webextension/src/hooks/useDiagnostics.ts29
-rw-r--r--packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts8
-rw-r--r--packages/taler-wallet-webextension/src/popup/BalancePage.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/popup/Debug.tsx4
-rw-r--r--packages/taler-wallet-webextension/src/popup/History.stories.tsx55
-rw-r--r--packages/taler-wallet-webextension/src/popup/History.tsx52
-rw-r--r--packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx7
-rw-r--r--packages/taler-wallet-webextension/src/popup/Settings.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/popup/popup.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/popupEntryPoint.tsx2
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Pay.stories.tsx103
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Pay.tsx287
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Refund.stories.tsx83
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Refund.tsx48
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Tip.stories.tsx66
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Tip.tsx63
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx56
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Welcome.tsx34
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx16
-rw-r--r--packages/taler-wallet-webextension/src/wallet/Withdraw.tsx155
-rw-r--r--packages/taler-wallet-webextension/src/walletEntryPoint.tsx68
-rw-r--r--packages/taler-wallet-webextension/src/wxBackend.ts8
-rw-r--r--packages/taler-wallet-webextension/static/wallet.html1
28 files changed, 851 insertions, 400 deletions
diff --git a/packages/taler-wallet-webextension/.storybook/preview.js b/packages/taler-wallet-webextension/.storybook/preview.js
index 169b726f9..02a4e43d4 100644
--- a/packages/taler-wallet-webextension/.storybook/preview.js
+++ b/packages/taler-wallet-webextension/.storybook/preview.js
@@ -56,7 +56,7 @@ export const decorators = [
// add a fake header so it looks similar
return <Fragment>
<NavBar path={path} devMode={path === '/dev'} />
- <div style={{ padding: 8, width: 'calc(400px - 16px)', height: 'calc(320px - 34px - 16px)' }}>
+ <div style={{ width: 400, height: 290 }}>
<Story />
</div>
</Fragment>
diff --git a/packages/taler-wallet-webextension/src/browserWorkerEntry.ts b/packages/taler-wallet-webextension/src/browserWorkerEntry.ts
index d8dff72fb..b5c26a7bb 100644
--- a/packages/taler-wallet-webextension/src/browserWorkerEntry.ts
+++ b/packages/taler-wallet-webextension/src/browserWorkerEntry.ts
@@ -68,6 +68,6 @@ worker.onmessage = (msg: MessageEvent) => {
}
handleRequest(operation, id, args).catch((e) => {
- console.error("error in browsere worker", e);
+ console.error("error in browser worker", e);
});
};
diff --git a/packages/taler-wallet-webextension/src/components/Diagnostics.tsx b/packages/taler-wallet-webextension/src/components/Diagnostics.tsx
index 146b0dd3e..b36525dbf 100644
--- a/packages/taler-wallet-webextension/src/components/Diagnostics.tsx
+++ b/packages/taler-wallet-webextension/src/components/Diagnostics.tsx
@@ -4,30 +4,12 @@ import { PageLink } from "../renderHtml";
import { WalletDiagnostics } from "@gnu-taler/taler-util";
import { JSX } from "preact/jsx-runtime";
+interface Props {
+ timedOut: boolean;
+ diagnostics: WalletDiagnostics | undefined
+}
-export function Diagnostics(): JSX.Element | null {
- const [timedOut, setTimedOut] = useState(false);
- const [diagnostics, setDiagnostics] = useState<WalletDiagnostics | undefined>(
- undefined
- );
-
- useEffect(() => {
- let gotDiagnostics = false;
- setTimeout(() => {
- if (!gotDiagnostics) {
- console.error("timed out");
- setTimedOut(true);
- }
- }, 1000);
- const doFetch = async (): Promise<void> => {
- const d = await getDiagnostics();
- console.log("got diagnostics", d);
- gotDiagnostics = true;
- setDiagnostics(d);
- };
- console.log("fetching diagnostics");
- doFetch();
- }, []);
+export function Diagnostics({timedOut, diagnostics}: Props): JSX.Element | null {
if (timedOut) {
return <p>Diagnostics timed out. Could not talk to the wallet backend.</p>;
diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx b/packages/taler-wallet-webextension/src/components/styled/index.tsx
index cf7f3e06a..7f709db46 100644
--- a/packages/taler-wallet-webextension/src/components/styled/index.tsx
+++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx
@@ -11,13 +11,32 @@ export const PaymentStatus = styled.div<{ color: string }>`
background-color: ${p => p.color};
`
-export const PopupBox = styled.div`
- height: calc(320px - 34px - 16px);
+export const WalletPage = styled.section`
+ border: solid 5px black;
+ border-radius: 10px;
+ margin-left: auto;
+ margin-right: auto;
+ padding-top: 2em;
+ max-width: 50%;
+ padding: 2em;
+
+ margin: auto;
+ height: 100%;
+
+ & h1:first-child {
+ margin-top: 0;
+ }
+`
+
+export const PopupBox = styled.div<{ noPadding?: boolean }>`
+ height: 290px;
display: flex;
flex-direction: column;
justify-content: space-between;
& > 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;
@@ -35,6 +54,7 @@ export const PopupBox = styled.div`
flex-direction: row;
justify-content: space-between;
display: flex;
+ padding: 8px;
margin-bottom: 5px;
& > div {
@@ -44,15 +64,23 @@ export const PopupBox = styled.div`
& > h3 {
margin: 0px;
}
+
+ & > .title {
+ /* margin: 1em; */
+ font-size: large;
+ color: #3c4e92;
+ }
}
& > footer {
- padding-top: 5px;
+ padding-top: 8px;
+ padding-bottom: 8px;
flex-direction: row;
justify-content: space-between;
display: flex;
& button {
- margin-left: 5px;
+ margin-right: 8px;
+ margin-left: 8px;
}
}
@@ -145,6 +173,13 @@ export const Row = styled.div`
padding: 0.5em;
`
+export const Row2 = styled.div`
+ display: flex;
+ /* margin: 0.5em 0; */
+ justify-content: space-between;
+ padding: 0.5em;
+`
+
export const Column = styled.div`
display: flex;
flex-direction: column;
@@ -154,10 +189,15 @@ export const Column = styled.div`
export const RowBorderGray = styled(Row)`
border: 1px solid gray;
- border-radius: 0.5em;
+ /* border-radius: 0.5em; */
`
-export const HistoryRow = styled(RowBorderGray)`
+export const RowLightBorderGray = styled(Row2)`
+ border: 1px solid lightgray;
+ /* border-radius: 0.5em; */
+`
+
+export const HistoryRow = styled(RowLightBorderGray)`
& > ${Column}:last-of-type {
margin-left: auto;
align-self: center;
@@ -244,24 +284,24 @@ export const ErrorBox = styled.div`
}
}
`
-export const PopupNavigation = styled.div`
- background-color: #033;
+export const PopupNavigation = styled.div<{devMode?:boolean}>`
+ background-color:#0042b2;
+ height: 35px;
& > a {
color: #f8faf7;
- padding-top: 0.7em;
display: inline-block;
- width: calc(400px / 5);
- padding-bottom: 0.7em;
+ width: calc(400px / ${({ devMode }) => !devMode ? 4 : 5});
text-align: center;
text-decoration: none;
+ vertical-align: middle;
+ line-height: 35px;
}
& > a.active {
background-color: #f8faf7;
- color: #000;
+ color: #0042b2;
font-weight: bold;
-
}
`;
diff --git a/packages/taler-wallet-webextension/src/hooks/useDiagnostics.ts b/packages/taler-wallet-webextension/src/hooks/useDiagnostics.ts
new file mode 100644
index 000000000..e2c62f998
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/hooks/useDiagnostics.ts
@@ -0,0 +1,29 @@
+import { WalletDiagnostics } from "@gnu-taler/taler-util";
+import { useEffect, useState } from "preact/hooks";
+import * as wxApi from "../wxApi";
+
+export function useDiagnostics(): [WalletDiagnostics | undefined, boolean] {
+ const [timedOut, setTimedOut] = useState(false);
+ const [diagnostics, setDiagnostics] = useState<WalletDiagnostics | undefined>(
+ undefined
+ );
+
+ useEffect(() => {
+ let gotDiagnostics = false;
+ setTimeout(() => {
+ if (!gotDiagnostics) {
+ console.error("timed out");
+ setTimedOut(true);
+ }
+ }, 1000);
+ const doFetch = async (): Promise<void> => {
+ const d = await wxApi.getDiagnostics();
+ console.log("got diagnostics", d);
+ gotDiagnostics = true;
+ setDiagnostics(d);
+ };
+ console.log("fetching diagnostics");
+ doFetch();
+ }, []);
+ return [diagnostics, timedOut]
+} \ No newline at end of file
diff --git a/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts b/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts
index b884ca943..1c8504a8e 100644
--- a/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts
+++ b/packages/taler-wallet-webextension/src/hooks/useTalerActionURL.ts
@@ -57,12 +57,8 @@ function makeExtensionUrlWithParams(
): string {
const innerUrl = new URL(chrome.extension.getURL("/" + url));
if (params) {
- for (const key in params) {
- const p = params[key];
- if (p) {
- innerUrl.searchParams.set(key, p);
- }
- }
+ const hParams = Object.keys(params).map(k => `${k}=${params[k]}`).join('&')
+ innerUrl.hash = innerUrl.hash + '?' + hParams
}
return innerUrl.href;
}
diff --git a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
index cff17af1a..5a2b9f747 100644
--- a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
+++ b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx
@@ -43,7 +43,7 @@ export function BalanceView({ balance, Linker }: BalanceViewProps) {
<div>
<p>{i18n.str`Error: could not retrieve balance information.`}</p>
<p>
- Click <Linker pageName="welcome.html">here</Linker> for help and
+ Click <Linker pageName="welcome">here</Linker> for help and
diagnostics.
</p>
</div>
diff --git a/packages/taler-wallet-webextension/src/popup/Debug.tsx b/packages/taler-wallet-webextension/src/popup/Debug.tsx
index 1f6014e8e..33b82b05b 100644
--- a/packages/taler-wallet-webextension/src/popup/Debug.tsx
+++ b/packages/taler-wallet-webextension/src/popup/Debug.tsx
@@ -16,10 +16,12 @@
import { JSX } from "preact";
import { Diagnostics } from "../components/Diagnostics";
+import { useDiagnostics } from "../hooks/useDiagnostics.js";
import * as wxApi from "../wxApi";
export function DeveloperPage(props: any): JSX.Element {
+ const [status, timedOut] = useDiagnostics();
return (
<div>
<p>Debug tools:</p>
@@ -27,7 +29,7 @@ export function DeveloperPage(props: any): JSX.Element {
<br />
<button onClick={confirmReset}>reset</button>
<button onClick={reload}>reload chrome extension</button>
- <Diagnostics />
+ <Diagnostics diagnostics={status} timedOut={timedOut} />
</div>
);
}
diff --git a/packages/taler-wallet-webextension/src/popup/History.stories.tsx b/packages/taler-wallet-webextension/src/popup/History.stories.tsx
index 8eef7dc31..5337a6c1c 100644
--- a/packages/taler-wallet-webextension/src/popup/History.stories.tsx
+++ b/packages/taler-wallet-webextension/src/popup/History.stories.tsx
@@ -30,7 +30,7 @@ import { FunctionalComponent } from 'preact';
import { HistoryView as TestedComponent } from './History';
export default {
- title: 'popup/transaction/list',
+ title: 'popup/history/list',
component: TestedComponent,
};
@@ -112,12 +112,26 @@ function createExample<Props>(Component: FunctionalComponent<Props>, props: Part
}
export const Empty = createExample(TestedComponent, {
- list: []
+ list: [],
+ balances: [{
+ available: 'TESTKUDOS:10',
+ pendingIncoming: 'TESTKUDOS:0',
+ pendingOutgoing: 'TESTKUDOS:0',
+ hasPendingTransactions: false,
+ requiresUserInput: false,
+ }]
});
export const One = createExample(TestedComponent, {
- list: [exampleData.withdraw]
+ list: [exampleData.withdraw],
+ balances: [{
+ available: 'USD:10',
+ pendingIncoming: 'USD:0',
+ pendingOutgoing: 'USD:0',
+ hasPendingTransactions: false,
+ requiresUserInput: false,
+ }]
});
export const Several = createExample(TestedComponent, {
@@ -130,7 +144,40 @@ export const Several = createExample(TestedComponent, {
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, {
diff --git a/packages/taler-wallet-webextension/src/popup/History.tsx b/packages/taler-wallet-webextension/src/popup/History.tsx
index 57fc10c26..b6b65314e 100644
--- a/packages/taler-wallet-webextension/src/popup/History.tsx
+++ b/packages/taler-wallet-webextension/src/popup/History.tsx
@@ -14,7 +14,7 @@
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
-import { AmountString, Timestamp, Transaction, TransactionsResponse, TransactionType } from "@gnu-taler/taler-util";
+import { AmountJson, Amounts, AmountString, Balance, Timestamp, Transaction, TransactionsResponse, TransactionType } from "@gnu-taler/taler-util";
import { JSX } from "preact";
import { useEffect, useState } from "preact/hooks";
import * as wxApi from "../wxApi";
@@ -25,6 +25,8 @@ 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> => {
@@ -38,16 +40,36 @@ export function HistoryPage(props: any): JSX.Element {
return <div>Loading ...</div>;
}
- return <HistoryView list={[...transactions.transactions].reverse()} />;
+ return <HistoryView balances={balanceWithoutError} list={[...transactions.transactions].reverse()} />;
}
-export function HistoryView({ list }: { list: Transaction[] }) {
- return <PopupBox>
+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[] }) {
+ return <PopupBox 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>
- {list.map((tx, i) => (
+ {list.slice(0, 3).map((tx, i) => (
<TransactionItem key={i} tx={tx} />
))}
</section>
+ <footer style={{ justifyContent: 'space-around' }}>
+ <a style={{ color: 'darkgreen', textDecoration:'none' }} href={Pages.transaction.replace(':tid', 'asd')}>VIEW MORE TRANSACTIONS</a>
+ </footer>
</PopupBox>
}
@@ -57,6 +79,8 @@ 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 {
const tx = props.tx;
@@ -144,23 +168,21 @@ function TransactionItem(props: { tx: Transaction }): JSX.Element {
function TransactionLayout(props: TransactionLayoutProps): JSX.Element {
const date = new Date(props.timestamp.t_ms);
- const dateStr = date.toLocaleString([], {
- dateStyle: "medium",
- timeStyle: "short",
- } as any);
+ const now = new Date();
+ const dateStr = formatDistance(date, now, { addSuffix: true })
return (
<HistoryRow>
<img src={props.iconPath} />
<Column>
- <SmallTextLight>{dateStr}</SmallTextLight>
<ExtraLargeText>
<a href={Pages.transaction.replace(':tid', props.id)}><span>{props.title}</span></a>
{props.pending ? (
<span style={{ color: "darkblue" }}> (Pending)</span>
) : null}
</ExtraLargeText>
+ <SmallTextLight>{dateStr}</SmallTextLight>
- <div>{props.subtitle}</div>
+ {/* <div>{props.subtitle}</div> */}
</Column>
<TransactionAmount
pending={props.pending}
@@ -202,7 +224,13 @@ function TransactionAmount(props: TransactionAmountProps): JSX.Element {
sign = "";
}
return (
- <Column style={{ color: props.pending ? "gray" : undefined }}>
+ <Column style={{
+ color:
+ props.pending ? "gray" :
+ (sign === '+' ? 'darkgreen' :
+ (sign === '-' ? 'darkred' :
+ undefined))
+ }}>
<ExtraLargeText>
{sign}
{amount}
diff --git a/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx b/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx
index c92137ee3..707e6c33a 100644
--- a/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx
+++ b/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx
@@ -58,13 +58,12 @@ export function ProviderView({ info, onDelete, onSync, onBack, onExtend }: ViewP
const isPaid = info.paymentStatus.type === ProviderPaymentType.Paid || info.paymentStatus.type === ProviderPaymentType.TermsChanged
return (
<PopupBox>
- <header>
+ {info.backupProblem || info.lastError ? <header>
<Error info={info} />
-
- </header>
+ </header> : undefined }
<header>
<h3>{info.name} <SmallTextLight>{info.syncProviderBaseUrl}</SmallTextLight></h3>
- <PaymentStatus color={isPaid ? 'rgb(28, 184, 65)' : 'rgb(202, 60, 60)'}>{isPaid ? 'Paid': 'Unpaid' }</PaymentStatus>
+ <PaymentStatus color={isPaid ? 'rgb(28, 184, 65)' : 'rgb(202, 60, 60)'}>{isPaid ? 'Paid' : 'Unpaid'}</PaymentStatus>
</header>
<section>
<p><b>Last backup:</b> {lb == null || lb.t_ms == "never" ? "never" : format(lb.t_ms, 'dd MMM yyyy')} </p>
diff --git a/packages/taler-wallet-webextension/src/popup/Settings.tsx b/packages/taler-wallet-webextension/src/popup/Settings.tsx
index 18afcd100..40ab51561 100644
--- a/packages/taler-wallet-webextension/src/popup/Settings.tsx
+++ b/packages/taler-wallet-webextension/src/popup/Settings.tsx
@@ -68,7 +68,7 @@ const names: LangsNames = {
export function SettingsView({ lang, changeLang, deviceName, setDeviceName, permissionsEnabled, togglePermissions, developerMode, toggleDeveloperMode }: ViewProps): VNode {
return (
<div>
- <section style={{ height: 'calc(320px - 34px - 16px)', overflow: 'auto' }}>
+ <section style={{ height: 300, overflow: 'auto' }}>
<h2><i18n.Translate>Wallet</i18n.Translate></h2>
<SelectList
value={lang}
diff --git a/packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx b/packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx
index 3c0bed6c7..4e63b9242 100644
--- a/packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx
+++ b/packages/taler-wallet-webextension/src/popup/Transaction.stories.tsx
@@ -30,7 +30,7 @@ import { FunctionalComponent } from 'preact';
import { TransactionView as TestedComponent } from './Transaction';
export default {
- title: 'popup/transaction/details',
+ title: 'popup/history/details',
component: TestedComponent,
argTypes: {
onRetry: { action: 'onRetry' },
diff --git a/packages/taler-wallet-webextension/src/popup/popup.tsx b/packages/taler-wallet-webextension/src/popup/popup.tsx
index a6be4d192..4aee48fb7 100644
--- a/packages/taler-wallet-webextension/src/popup/popup.tsx
+++ b/packages/taler-wallet-webextension/src/popup/popup.tsx
@@ -60,7 +60,7 @@ function Tab(props: TabProps): JSX.Element {
}
export function NavBar({devMode, path}:{path:string, devMode:boolean}) {
- return <PopupNavigation>
+ return <PopupNavigation devMode={devMode}>
<Tab target="/balance" current={path}>{i18n.str`Balance`}</Tab>
<Tab target="/history" current={path}>{i18n.str`History`}</Tab>
<Tab target="/backup" current={path}>{i18n.str`Backup`}</Tab>
diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
index 39c25d508..faa5149ac 100644
--- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
+++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx
@@ -93,7 +93,7 @@ function Application() {
<div>
<DevContextProvider>
<WalletNavBar />
- <div style={{ padding: 8, width: 'calc(400px - 16px)', height: 'calc(320px - 34px - 16px)' }}>
+ <div style={{ width: 400, height: 290 }}>
<Router history={createHashHistory()}>
<Route path={Pages.balance} component={BalancePage} />
<Route path={Pages.settings} component={SettingsPage} />
diff --git a/packages/taler-wallet-webextension/src/wallet/Pay.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Pay.stories.tsx
new file mode 100644
index 000000000..0297d6264
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/Pay.stories.tsx
@@ -0,0 +1,103 @@
+/*
+ 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 { ContractTerms, PreparePayResultType } from '@gnu-taler/taler-util';
+import { FunctionalComponent, h } from 'preact';
+import { PaymentRequestView as TestedComponent } from './Pay';
+
+
+export default {
+ title: 'wallet/pay',
+ component: TestedComponent,
+ 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, {
+ payStatus: {
+ status: PreparePayResultType.InsufficientBalance,
+ proposalId: "proposal1234",
+ contractTerms: {
+ merchant: {
+ name: 'someone'
+ },
+ amount: 'USD:10',
+ } as Partial<ContractTerms> as any,
+ amountRaw: 'USD:10',
+ }
+});
+
+export const PaymentPossible = createExample(TestedComponent, {
+ payStatus: {
+ status: PreparePayResultType.PaymentPossible,
+ amountEffective: 'USD:10',
+ amountRaw: 'USD:10',
+ contractTerms: {
+ merchant: {
+ name: 'someone'
+ },
+ amount: 'USD:10',
+ } as Partial<ContractTerms> as any,
+ contractTermsHash: '123456',
+ proposalId: 'proposal1234'
+ }
+});
+
+export const AlreadyConfirmedWithFullfilment = createExample(TestedComponent, {
+ payStatus: {
+ status: PreparePayResultType.AlreadyConfirmed,
+ amountEffective: 'USD:10',
+ amountRaw: 'USD:10',
+ contractTerms: {
+ merchant: {
+ name: 'someone'
+ },
+ fulfillment_message: 'congratulations! you are looking at the fulfillment message! ',
+ amount: 'USD:10',
+ } as Partial<ContractTerms> as any,
+ contractTermsHash: '123456',
+ proposalId: 'proposal1234',
+ paid: false,
+ }
+});
+
+export const AlreadyConfirmedWithoutFullfilment = createExample(TestedComponent, {
+ payStatus: {
+ status: PreparePayResultType.AlreadyConfirmed,
+ amountEffective: 'USD:10',
+ amountRaw: 'USD:10',
+ contractTerms: {
+ merchant: {
+ name: 'someone'
+ },
+ amount: 'USD:10',
+ } as Partial<ContractTerms> as any,
+ contractTermsHash: '123456',
+ proposalId: 'proposal1234',
+ paid: false,
+ }
+});
diff --git a/packages/taler-wallet-webextension/src/wallet/Pay.tsx b/packages/taler-wallet-webextension/src/wallet/Pay.tsx
index bd06656c7..a5849bb28 100644
--- a/packages/taler-wallet-webextension/src/wallet/Pay.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Pay.tsx
@@ -29,7 +29,7 @@ import * as wxApi from "../wxApi";
import { useState, useEffect } from "preact/hooks";
-import { getJsonI18n, i18n } from "@gnu-taler/taler-util";
+import { ConfirmPayResultDone, getJsonI18n, i18n } from "@gnu-taler/taler-util";
import {
PreparePayResult,
ConfirmPayResult,
@@ -45,13 +45,54 @@ interface Props {
talerPayUri?: string
}
+export function AlreadyPaid({ payStatus }: { payStatus: PreparePayResult }) {
+ const fulfillmentUrl = payStatus.contractTerms.fulfillment_url;
+ let message;
+ if (fulfillmentUrl) {
+ message = (
+ <span>
+ You have already paid for this article. Click{" "}
+ <a href={fulfillmentUrl} target="_bank" rel="external">here</a> to view it again.
+ </span>
+ );
+ } else {
+ message = <span>
+ You have already paid for this article:{" "}
+ <em>
+ {payStatus.contractTerms.fulfillment_message ?? "no message given"}
+ </em>
+ </span>;
+ }
+ return <section class="main">
+ <h1>GNU Taler Wallet</h1>
+ <article class="fade">
+ {message}
+ </article>
+ </section>
+}
+
+const doPayment = async (payStatus: PreparePayResult): Promise<ConfirmPayResultDone> => {
+ if (payStatus.status !== "payment-possible") {
+ throw Error(`invalid state: ${payStatus.status}`);
+ }
+ const proposalId = payStatus.proposalId;
+ const res = await wxApi.confirmPay(proposalId, undefined);
+ if (res.type !== ConfirmPayResultType.Done) {
+ throw Error("payment pending");
+ }
+ const fu = res.contractTerms.fulfillment_url;
+ if (fu) {
+ document.location.href = fu;
+ }
+ return res;
+};
+
+
+
export function PayPage({ talerPayUri }: Props): JSX.Element {
const [payStatus, setPayStatus] = useState<PreparePayResult | undefined>(undefined);
const [payResult, setPayResult] = useState<ConfirmPayResult | undefined>(undefined);
const [payErrMsg, setPayErrMsg] = useState<string | undefined>("");
- const [numTries, setNumTries] = useState(0);
- const [loading, setLoading] = useState(false);
- let totalFees: AmountJson | undefined = undefined;
useEffect(() => {
if (!talerPayUri) return;
@@ -60,53 +101,67 @@ export function PayPage({ talerPayUri }: Props): JSX.Element {
setPayStatus(p);
};
doFetch();
- }, [numTries, talerPayUri]);
+ }, [talerPayUri]);
if (!talerPayUri) {
return <span>missing pay uri</span>
}
-
+
if (!payStatus) {
return <span>Loading payment information ...</span>;
}
- let insufficientBalance = false;
- if (payStatus.status == PreparePayResultType.InsufficientBalance) {
- insufficientBalance = true;
- }
-
- if (payStatus.status === PreparePayResultType.PaymentPossible) {
- const amountRaw = Amounts.parseOrThrow(payStatus.amountRaw);
- const amountEffective: AmountJson = Amounts.parseOrThrow(
- payStatus.amountEffective,
- );
- totalFees = Amounts.sub(amountEffective, amountRaw).amount;
- }
-
- if (
- payStatus.status === PreparePayResultType.AlreadyConfirmed &&
- numTries === 0
- ) {
- const fulfillmentUrl = payStatus.contractTerms.fulfillment_url;
- if (fulfillmentUrl) {
+ if (payResult && payResult.type === ConfirmPayResultType.Done) {
+ if (payResult.contractTerms.fulfillment_message) {
+ const obj = {
+ fulfillment_message: payResult.contractTerms.fulfillment_message,
+ fulfillment_message_i18n:
+ payResult.contractTerms.fulfillment_message_i18n,
+ };
+ const msg = getJsonI18n(obj, "fulfillment_message");
return (
- <span>
- You have already paid for this article. Click{" "}
- <a href={fulfillmentUrl} target="_bank" rel="external">here</a> to view it again.
- </span>
+ <div>
+ <p>Payment succeeded.</p>
+ <p>{msg}</p>
+ </div>
);
} else {
- <span>
- You have already paid for this article:{" "}
- <em>
- {payStatus.contractTerms.fulfillment_message ?? "no message given"}
- </em>
- </span>;
+ return <span>Redirecting ...</span>;
}
}
+ const onClick = async () => {
+ try {
+ const res = await doPayment(payStatus)
+ setPayResult(res);
+ } catch (e) {
+ console.error(e);
+ setPayErrMsg(e.message);
+ }
+
+ }
+
+ return <PaymentRequestView payStatus={payStatus} onClick={onClick} payErrMsg={payErrMsg} />;
+}
+
+export interface PaymentRequestViewProps {
+ payStatus: PreparePayResult;
+ onClick: () => void;
+ payErrMsg?: string;
+
+}
+export function PaymentRequestView({ payStatus, onClick, payErrMsg }: PaymentRequestViewProps) {
+ let totalFees: AmountJson | undefined = undefined;
+ let insufficientBalance = false;
+ const [loading, setLoading] = useState(false);
const contractTerms: ContractTerms = payStatus.contractTerms;
+ if (
+ payStatus.status === PreparePayResultType.AlreadyConfirmed
+ ) {
+ return <AlreadyPaid payStatus={payStatus} />
+ }
+
if (!contractTerms) {
return (
<span>
@@ -115,6 +170,18 @@ export function PayPage({ talerPayUri }: Props): JSX.Element {
);
}
+ if (payStatus.status == PreparePayResultType.InsufficientBalance) {
+ insufficientBalance = true;
+ }
+
+ if (payStatus.status === PreparePayResultType.PaymentPossible) {
+ const amountRaw = Amounts.parseOrThrow(payStatus.amountRaw);
+ const amountEffective: AmountJson = Amounts.parseOrThrow(
+ payStatus.amountEffective,
+ );
+ totalFees = Amounts.sub(amountEffective, amountRaw).amount;
+ }
+
let merchantName: VNode;
if (contractTerms.merchant && contractTerms.merchant.name) {
merchantName = <strong>{contractTerms.merchant.name}</strong>;
@@ -126,99 +193,61 @@ export function PayPage({ talerPayUri }: Props): JSX.Element {
<strong>{renderAmount(Amounts.parseOrThrow(contractTerms.amount))}</strong>
);
- const doPayment = async (): Promise<void> => {
- if (payStatus.status !== "payment-possible") {
- throw Error(`invalid state: ${payStatus.status}`);
- }
- const proposalId = payStatus.proposalId;
- setNumTries(numTries + 1);
- try {
- setLoading(true);
- const res = await wxApi.confirmPay(proposalId, undefined);
- if (res.type !== ConfirmPayResultType.Done) {
- throw Error("payment pending");
- }
- const fu = res.contractTerms.fulfillment_url;
- if (fu) {
- document.location.href = fu;
- }
- setPayResult(res);
- } catch (e) {
- console.error(e);
- setPayErrMsg(e.message);
- }
- };
-
- if (payResult && payResult.type === ConfirmPayResultType.Done) {
- if (payResult.contractTerms.fulfillment_message) {
- const obj = {
- fulfillment_message: payResult.contractTerms.fulfillment_message,
- fulfillment_message_i18n:
- payResult.contractTerms.fulfillment_message_i18n,
- };
- const msg = getJsonI18n(obj, "fulfillment_message");
- return (
- <div>
- <p>Payment succeeded.</p>
- <p>{msg}</p>
- </div>
- );
- } else {
- return <span>Redirecting ...</span>;
- }
- }
-
- return (
- <div>
- <p>
- <i18n.Translate>
- The merchant <span>{merchantName}</span> offers you to purchase:
- </i18n.Translate>
- <div style={{ textAlign: "center" }}>
- <strong>{contractTerms.summary}</strong>
- </div>
- {totalFees ? (
+ return <section class="main">
+ <h1>GNU Taler Wallet</h1>
+ <article class="fade">
+ <div>
+ <p>
<i18n.Translate>
- The total price is <span>{amount} </span>
- (plus <span>{renderAmount(totalFees)}</span> fees).
- </i18n.Translate>
+ The merchant <span>{merchantName}</span> offers you to purchase:
+ </i18n.Translate>
+ <div style={{ textAlign: "center" }}>
+ <strong>{contractTerms.summary}</strong>
+ </div>
+ {totalFees ? (
+ <i18n.Translate>
+ The total price is <span>{amount} </span>
+ (plus <span>{renderAmount(totalFees)}</span> fees).
+ </i18n.Translate>
+ ) : (
+ <i18n.Translate>
+ The total price is <span>{amount}</span>.
+ </i18n.Translate>
+ )}
+ </p>
+
+ {insufficientBalance ? (
+ <div>
+ <p style={{ color: "red", fontWeight: "bold" }}>
+ Unable to pay: Your balance is insufficient.
+ </p>
+ </div>
+ ) : null}
+
+ {payErrMsg ? (
+ <div>
+ <p>Payment failed: {payErrMsg}</p>
+ <button
+ class="pure-button button-success"
+ onClick={onClick}
+ >
+ {i18n.str`Retry`}
+ </button>
+ </div>
) : (
- <i18n.Translate>
- The total price is <span>{amount}</span>.
- </i18n.Translate>
- )}
- </p>
-
- {insufficientBalance ? (
- <div>
- <p style={{ color: "red", fontWeight: "bold" }}>
- Unable to pay: Your balance is insufficient.
- </p>
- </div>
- ) : null}
-
- {payErrMsg ? (
- <div>
- <p>Payment failed: {payErrMsg}</p>
- <button
- class="pure-button button-success"
- onClick={() => doPayment()}
- >
- {i18n.str`Retry`}
- </button>
- </div>
- ) : (
- <div>
- <ProgressButton
- isLoading={loading}
- disabled={insufficientBalance}
- onClick={() => doPayment()}
- >
- {i18n.str`Confirm payment`}
- </ProgressButton>
- </div>
- )}
- </div>
- );
-}
-
+ <div>
+ <ProgressButton
+ isLoading={loading}
+ disabled={insufficientBalance}
+ onClick={onClick}
+ >
+ {i18n.str`Confirm payment`}
+ </ProgressButton>
+ </div>
+ )}
+ </div>
+ </article>
+ </section>
+
+
+} \ No newline at end of file
diff --git a/packages/taler-wallet-webextension/src/wallet/Refund.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Refund.stories.tsx
new file mode 100644
index 000000000..044141f0c
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/Refund.stories.tsx
@@ -0,0 +1,83 @@
+/*
+ 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 { ContractTerms, OrderShortInfo, PreparePayResultType } from '@gnu-taler/taler-util';
+import { FunctionalComponent, h } from 'preact';
+import { View as TestedComponent } from './Refund';
+
+
+export default {
+ title: 'wallet/refund',
+ component: TestedComponent,
+ 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, {
+ applyResult: {
+ amountEffectivePaid: 'USD:10',
+ amountRefundGone: 'USD:0',
+ amountRefundGranted: 'USD:2',
+ contractTermsHash: 'QWEASDZXC',
+ info: {
+ summary: 'tasty cold beer',
+ contractTermsHash: 'QWEASDZXC',
+ } as Partial<OrderShortInfo> as any,
+ pendingAtExchange: false,
+ proposalId: "proposal123",
+ }
+});
+
+export const Partial = createExample(TestedComponent, {
+ applyResult: {
+ amountEffectivePaid: 'USD:10',
+ amountRefundGone: 'USD:1',
+ amountRefundGranted: 'USD:2',
+ contractTermsHash: 'QWEASDZXC',
+ info: {
+ summary: 'tasty cold beer',
+ contractTermsHash: 'QWEASDZXC',
+ } as Partial<OrderShortInfo> as any,
+ pendingAtExchange: false,
+ proposalId: "proposal123",
+ }
+});
+
+export const InProgress = createExample(TestedComponent, {
+ applyResult: {
+ amountEffectivePaid: 'USD:10',
+ amountRefundGone: 'USD:1',
+ amountRefundGranted: 'USD:2',
+ contractTermsHash: 'QWEASDZXC',
+ info: {
+ summary: 'tasty cold beer',
+ contractTermsHash: 'QWEASDZXC',
+ } as Partial<OrderShortInfo> as any,
+ pendingAtExchange: true,
+ proposalId: "proposal123",
+ }
+});
diff --git a/packages/taler-wallet-webextension/src/wallet/Refund.tsx b/packages/taler-wallet-webextension/src/wallet/Refund.tsx
index 702217415..bb26d933b 100644
--- a/packages/taler-wallet-webextension/src/wallet/Refund.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Refund.tsx
@@ -32,7 +32,32 @@ import { JSX } from "preact/jsx-runtime";
interface Props {
talerRefundUri?: string
}
-
+export interface ViewProps {
+ applyResult: ApplyRefundResponse;
+}
+export function View({ applyResult }: ViewProps) {
+ return <section class="main">
+ <h1>GNU Taler Wallet</h1>
+ <article class="fade">
+ <h2>Refund Status</h2>
+ <p>
+ The product <em>{applyResult.info.summary}</em> has received a total
+ effective refund of{" "}
+ <AmountView amount={applyResult.amountRefundGranted} />.
+ </p>
+ {applyResult.pendingAtExchange ? (
+ <p>Refund processing is still in progress.</p>
+ ) : null}
+ {!Amounts.isZero(applyResult.amountRefundGone) ? (
+ <p>
+ The refund amount of{" "}
+ <AmountView amount={applyResult.amountRefundGone} />{" "}
+ could not be applied.
+ </p>
+ ) : null}
+ </article>
+ </section>
+}
export function RefundPage({ talerRefundUri }: Props): JSX.Element {
const [applyResult, setApplyResult] = useState<ApplyRefundResponse | undefined>(undefined);
const [errMsg, setErrMsg] = useState<string | undefined>(undefined);
@@ -66,24 +91,5 @@ export function RefundPage({ talerRefundUri }: Props): JSX.Element {
return <span>Updating refund status</span>;
}
- return (
- <>
- <h2>Refund Status</h2>
- <p>
- The product <em>{applyResult.info.summary}</em> has received a total
- effective refund of{" "}
- <AmountView amount={applyResult.amountRefundGranted} />.
- </p>
- {applyResult.pendingAtExchange ? (
- <p>Refund processing is still in progress.</p>
- ) : null}
- {!Amounts.isZero(applyResult.amountRefundGone) ? (
- <p>
- The refund amount of{" "}
- <AmountView amount={applyResult.amountRefundGone} />
- could not be applied.
- </p>
- ) : null}
- </>
- );
+ return <View applyResult={applyResult} />;
}
diff --git a/packages/taler-wallet-webextension/src/wallet/Tip.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Tip.stories.tsx
new file mode 100644
index 000000000..ffd976144
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/Tip.stories.tsx
@@ -0,0 +1,66 @@
+/*
+ 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 { ContractTerms, PreparePayResultType } from '@gnu-taler/taler-util';
+import { FunctionalComponent, h } from 'preact';
+import { View as TestedComponent } from './Tip';
+
+
+export default {
+ title: 'wallet/tip',
+ component: TestedComponent,
+ 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, {
+ prepareTipResult: {
+ accepted: true,
+ merchantBaseUrl: '',
+ exchangeBaseUrl: '',
+ expirationTimestamp : {
+ t_ms: 0
+ },
+ tipAmountEffective: 'USD:10',
+ tipAmountRaw: 'USD:5',
+ walletTipId: 'id'
+ }
+});
+
+export const NotYetAccepted = createExample(TestedComponent, {
+ prepareTipResult: {
+ accepted: false,
+ merchantBaseUrl: 'http://merchant.url/',
+ exchangeBaseUrl: 'http://exchange.url/',
+ expirationTimestamp : {
+ t_ms: 0
+ },
+ tipAmountEffective: 'USD:10',
+ tipAmountRaw: 'USD:5',
+ walletTipId: 'id'
+ }
+});
diff --git a/packages/taler-wallet-webextension/src/wallet/Tip.tsx b/packages/taler-wallet-webextension/src/wallet/Tip.tsx
index 708e8940b..69886668b 100644
--- a/packages/taler-wallet-webextension/src/wallet/Tip.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Tip.tsx
@@ -26,8 +26,41 @@ import { AmountView } from "../renderHtml";
import * as wxApi from "../wxApi";
import { JSX } from "preact/jsx-runtime";
-interface Props {
- talerTipUri?: string
+interface Props {
+ talerTipUri?: string
+}
+export interface ViewProps {
+ prepareTipResult: PrepareTipResult;
+ onAccept: () => void;
+ onIgnore: () => void;
+
+}
+export function View({ prepareTipResult, onAccept, onIgnore }: ViewProps) {
+ return <section class="main">
+ <h1>GNU Taler Wallet</h1>
+ <article class="fade">
+ {prepareTipResult.accepted ? (
+ <span>
+ Tip from <code>{prepareTipResult.merchantBaseUrl}</code> accepted. Check
+ your transactions list for more details.
+ </span>
+ ) : (
+ <div>
+ <p>
+ The merchant <code>{prepareTipResult.merchantBaseUrl}</code> is
+ offering you a tip of{" "}
+ <strong>
+ <AmountView amount={prepareTipResult.tipAmountEffective} />
+ </strong>{" "}
+ via the exchange <code>{prepareTipResult.exchangeBaseUrl}</code>
+ </p>
+ <button onClick={onAccept}>Accept tip</button>
+ <button onClick={onIgnore}>Ignore</button>
+ </div>
+ )}
+ </article>
+ </section>
+
}
export function TipPage({ talerTipUri }: Props): JSX.Element {
@@ -71,27 +104,7 @@ export function TipPage({ talerTipUri }: Props): JSX.Element {
return <span>Loading ...</span>;
}
- if (prepareTipResult.accepted) {
- return (
- <span>
- Tip from <code>{prepareTipResult.merchantBaseUrl}</code> accepted. Check
- your transactions list for more details.
- </span>
- );
- } else {
- return (
- <div>
- <p>
- The merchant <code>{prepareTipResult.merchantBaseUrl}</code> is
- offering you a tip of{" "}
- <strong>
- <AmountView amount={prepareTipResult.tipAmountEffective} />
- </strong>{" "}
- via the exchange <code>{prepareTipResult.exchangeBaseUrl}</code>
- </p>
- <button onClick={doAccept}>Accept tip</button>
- <button onClick={doIgnore}>Ignore</button>
- </div>
- );
- }
+ return <View prepareTipResult={prepareTipResult}
+ onAccept={doAccept} onIgnore={doIgnore}
+ />
}
diff --git a/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx
new file mode 100644
index 000000000..4fa87a137
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/Welcome.stories.tsx
@@ -0,0 +1,56 @@
+/*
+ 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 { FunctionalComponent, h } from 'preact';
+import { View as TestedComponent } from './Welcome';
+
+
+export default {
+ title: 'wallet/welcome',
+ 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, {
+ permissionsEnabled: true,
+ diagnostics: {
+ errors: [],
+ walletManifestVersion: '1.0',
+ walletManifestDisplayVersion: '1.0',
+ firefoxIdbProblem: false,
+ dbOutdated: false,
+ }
+});
+
+export const TimedoutDiagnostics = createExample(TestedComponent, {
+ timedOut: true,
+ permissionsEnabled: false,
+});
+
+export const RunningDiagnostics = createExample(TestedComponent, {
+ permissionsEnabled: false,
+});
+
diff --git a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
index c74384596..4c33e1c72 100644
--- a/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Welcome.tsx
@@ -24,13 +24,36 @@ import { JSX } from "preact/jsx-runtime";
import { Checkbox } from "../components/Checkbox";
import { useExtendedPermissions } from "../hooks/useExtendedPermissions";
import { Diagnostics } from "../components/Diagnostics";
+import { WalletPage } from "../components/styled";
+import { useDiagnostics } from "../hooks/useDiagnostics";
+import { WalletDiagnostics } from "@gnu-taler/taler-util";
-export function WelcomePage(): JSX.Element {
+export function WelcomePage() {
const [permissionsEnabled, togglePermissions] = useExtendedPermissions()
- return (
- <>
+ const [diagnostics, timedOut] = useDiagnostics()
+ return <View
+ permissionsEnabled={permissionsEnabled} togglePermissions={togglePermissions}
+ diagnostics={diagnostics} timedOut={timedOut}
+ />
+}
+
+export interface ViewProps {
+ permissionsEnabled: boolean,
+ togglePermissions: () => void,
+ diagnostics: WalletDiagnostics | undefined,
+ timedOut: boolean,
+}
+export function View({ permissionsEnabled, togglePermissions, diagnostics, timedOut }: ViewProps): JSX.Element {
+ return (<WalletPage>
+ <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>
+ <div>
<p>Thank you for installing the wallet.</p>
- <Diagnostics />
+ <Diagnostics diagnostics={diagnostics} timedOut={timedOut} />
<h2>Permissions</h2>
<Checkbox label="Automatically open wallet based on page content"
name="perm"
@@ -44,6 +67,7 @@ export function WelcomePage(): JSX.Element {
<a href="https://demo.taler.net/" style={{ display: "block" }}>
Learn how to top up your wallet balance »
</a>
- </>
+ </div>
+ </WalletPage>
);
}
diff --git a/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx b/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx
index 24fb17dfa..fef36b820 100644
--- a/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Withdraw.stories.tsx
@@ -30,27 +30,12 @@ export default {
},
};
-export const WithoutURI = (a: any) => <View {...a} />;
-WithoutURI.args = {
-} as ViewProps
-
export const WithoutDetails = (a: any) => <View {...a} />;
WithoutDetails.args = {
- talerWithdrawUri: 'http://something'
-} as ViewProps
-
-export const Cancelled = (a: any) => <View {...a} />;
-Cancelled.args = {
- talerWithdrawUri: 'http://something',
- details: {
- amount: 'USD:2',
- },
- cancelled: true
} as ViewProps
export const CompleteWithExchange = (a: any) => <View {...a} />;
CompleteWithExchange.args = {
- talerWithdrawUri: 'http://something',
details: {
amount: 'USD:2',
},
@@ -59,7 +44,6 @@ CompleteWithExchange.args = {
export const CompleteWithoutExchange = (a: any) => <View {...a} />;
CompleteWithoutExchange.args = {
- talerWithdrawUri: 'http://something',
details: {
amount: 'USD:2',
},
diff --git a/packages/taler-wallet-webextension/src/wallet/Withdraw.tsx b/packages/taler-wallet-webextension/src/wallet/Withdraw.tsx
index 4cb8ebfa1..442ee7dae 100644
--- a/packages/taler-wallet-webextension/src/wallet/Withdraw.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/Withdraw.tsx
@@ -32,6 +32,7 @@ import {
} from "../wxApi";
import { WithdrawUriInfoResponse } from "@gnu-taler/taler-util";
import { JSX } from "preact/jsx-runtime";
+import { WalletPage } from '../components/styled';
interface Props {
talerWithdrawUri?: string;
@@ -39,79 +40,72 @@ interface Props {
export interface ViewProps {
talerWithdrawUri?: string;
- details?: WithdrawUriInfoResponse;
- cancelled?: boolean;
+ details: WithdrawUriInfoResponse;
selectedExchange?: string;
accept: () => Promise<void>;
setCancelled: (b: boolean) => void;
setSelecting: (b: boolean) => void;
};
-export function View({ talerWithdrawUri, details, cancelled, selectedExchange, accept, setCancelled, setSelecting }: ViewProps) {
- const [state, setState] = useState(1)
- setTimeout(() => {
- setState(s => s + 1)
- }, 1000);
- if (!talerWithdrawUri) {
- return <span><i18n.Translate>missing withdraw uri</i18n.Translate></span>;
- }
-
- if (!details) {
- return <span><i18n.Translate>Loading...</i18n.Translate></span>;
- }
-
- if (cancelled) {
- return <span><i18n.Translate>Withdraw operation has been cancelled.{state}</i18n.Translate></span>;
- }
+export function View({ details, selectedExchange, accept, setCancelled, setSelecting }: ViewProps) {
return (
- <div>
- <h1><i18n.Translate>Digital Cash Withdrawal</i18n.Translate></h1>
- <p><i18n.Translate>
- You are about to withdraw{" "}
- <strong>{renderAmount(details.amount)}</strong> from your bank account
- into your wallet.
- </i18n.Translate></p>
- {selectedExchange ? (
- <p><i18n.Translate>
- The exchange <strong>{selectedExchange}</strong> will be used as the
- Taler payment service provider.
- </i18n.Translate></p>
- ) : null}
-
- <div>
- <button
- class="pure-button button-success"
- disabled={!selectedExchange}
- onClick={() => accept()}
- >
- {i18n.str`Accept fees and withdraw`}
- </button>
- <p>
- <span
- role="button"
- tabIndex={0}
- style={{ textDecoration: "underline", cursor: "pointer" }}
- onClick={() => setSelecting(true)}
- >
- {i18n.str`Chose different exchange provider`}
- </span>
- <br />
- <span
- role="button"
- tabIndex={0}
- style={{ textDecoration: "underline", cursor: "pointer" }}
- onClick={() => setCancelled(true)}
- >
- {i18n.str`Cancel withdraw operation`}
- </span>
- </p>
+ <WalletPage>
+ <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>
+ <div class="fade">
+ <div>
+ <h1><i18n.Translate>Digital Cash Withdrawal</i18n.Translate></h1>
+ <p><i18n.Translate>
+ You are about to withdraw{" "}
+ <strong>{renderAmount(details.amount)}</strong> from your bank account
+ into your wallet.
+ </i18n.Translate></p>
+ {selectedExchange ? (
+ <p><i18n.Translate>
+ The exchange <strong>{selectedExchange}</strong> will be used as the
+ Taler payment service provider.
+ </i18n.Translate></p>
+ ) : null}
+
+ <div>
+ <button
+ class="pure-button button-success"
+ disabled={!selectedExchange}
+ onClick={() => accept()}
+ >
+ {i18n.str`Accept fees and withdraw`}
+ </button>
+ <p>
+ <span
+ role="button"
+ tabIndex={0}
+ style={{ textDecoration: "underline", cursor: "pointer" }}
+ onClick={() => setSelecting(true)}
+ >
+ {i18n.str`Chose different exchange provider`}
+ </span>
+ <br />
+ <span
+ role="button"
+ tabIndex={0}
+ style={{ textDecoration: "underline", cursor: "pointer" }}
+ onClick={() => setCancelled(true)}
+ >
+ {i18n.str`Cancel withdraw operation`}
+ </span>
+ </p>
+ </div>
+ </div>
</div>
- </div>
+ </WalletPage>
)
}
-export function WithdrawPage({ talerWithdrawUri }: Props): JSX.Element {
+export function WithdrawPage({ talerWithdrawUri, ...rest }: Props): JSX.Element {
const [details, setDetails] = useState<WithdrawUriInfoResponse | undefined>(undefined);
const [selectedExchange, setSelectedExchange] = useState<
string | undefined
@@ -120,27 +114,44 @@ export function WithdrawPage({ talerWithdrawUri }: Props): JSX.Element {
const [selecting, setSelecting] = useState(false);
const [errMsg, setErrMsg] = useState<string | undefined>("");
const [updateCounter, setUpdateCounter] = useState(1);
+ const [state, setState] = useState(1)
+
+ // setTimeout(() => {
+ // console.log('tick...')
+ // setState(s => s + 1)
+ // }, 1000);
useEffect(() => {
return onUpdateNotification(() => {
+ console.log('updating...')
setUpdateCounter(updateCounter + 1);
});
}, []);
useEffect(() => {
+ console.log('on effect yes', talerWithdrawUri)
if (!talerWithdrawUri) return
const fetchData = async (): Promise<void> => {
- const res = await getWithdrawalDetailsForUri({ talerWithdrawUri });
- setDetails(res);
- if (res.defaultExchangeBaseUrl) {
- setSelectedExchange(res.defaultExchangeBaseUrl);
+ console.log('que pasa')
+ try {
+ const res = await getWithdrawalDetailsForUri({ talerWithdrawUri });
+ console.log('res', res)
+ setDetails(res);
+ if (res.defaultExchangeBaseUrl) {
+ setSelectedExchange(res.defaultExchangeBaseUrl);
+ }
+ } catch (e) {
+ console.error(e)
}
};
fetchData();
- }, [selectedExchange, errMsg, selecting, talerWithdrawUri, updateCounter]);
+ }, [selectedExchange, errMsg, selecting, talerWithdrawUri, updateCounter, state]);
+
+ if (!talerWithdrawUri) {
+ return <span><i18n.Translate>missing withdraw uri</i18n.Translate></span>;
+ }
const accept = async (): Promise<void> => {
- if (!talerWithdrawUri) return
if (!selectedExchange) {
throw Error("can't accept, no exchange selected");
}
@@ -152,10 +163,16 @@ export function WithdrawPage({ talerWithdrawUri }: Props): JSX.Element {
}
};
+ if (!details) {
+ return <span><i18n.Translate>Loading...</i18n.Translate></span>;
+ }
+ if (cancelled) {
+ return <span><i18n.Translate>Withdraw operation has been cancelled.</i18n.Translate></span>;
+ }
+
return <View accept={accept}
setCancelled={setCancelled} setSelecting={setSelecting}
- cancelled={cancelled} details={details} selectedExchange={selectedExchange}
- talerWithdrawUri={talerWithdrawUri}
+ details={details} selectedExchange={selectedExchange}
/>
}
diff --git a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx
index 004fcc717..f487e54fc 100644
--- a/packages/taler-wallet-webextension/src/walletEntryPoint.tsx
+++ b/packages/taler-wallet-webextension/src/walletEntryPoint.tsx
@@ -32,7 +32,6 @@ import { RefundPage } from "./wallet/Refund";
import { TipPage } from './wallet/Tip';
import Router, { route, Route } from "preact-router";
-
function main(): void {
try {
const container = document.getElementById("container");
@@ -67,64 +66,15 @@ enum Pages {
}
function Application() {
- const sp = new URL(document.location.href).searchParams
- const queryParams: any = {}
- sp.forEach((v, k) => { queryParams[k] = v; });
-
- return <Router history={createHashHistory()} >
-
- <Route path={Pages.welcome} component={() => {
- return <section class="main">
- <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>
- <div>
- <WelcomePage />
- </div>
- </section>
- }} />
-
- <Route path={Pages.pay} component={() => {
- return <section class="main">
- <h1>GNU Taler Wallet</h1>
- <article class="fade">
- <PayPage talerPayUri={queryParams.talerPayUri} />
- </article>
- </section>
- }} />
-
- <Route path={Pages.refund} component={() => {
- return <section class="main">
- <h1>GNU Taler Wallet</h1>
- <article class="fade">
- <RefundPage talerRefundUri={queryParams.talerRefundUri} />
- </article>
- </section>
- }} />
-
- <Route path={Pages.tips} component={() => {
- return <section class="main">
- <h1>GNU Taler Wallet</h1>
- <div>
- <TipPage talerTipUri={queryParams.talerTipUri} />
- </div>
- </section>
- }} />
- <Route path={Pages.withdraw} component={() => {
- return <section class="main">
- <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>
- <div class="fade">
- <WithdrawPage talerWithdrawUri={queryParams.talerWithdrawUri} />
- </div>
- </section>
- }} />
+ const h = createHashHistory();
+ return <Router history={h} >
+
+ <Route path={Pages.welcome} component={WelcomePage} />
+ <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 path={Pages.reset_required} component={() => <div>no yet implemented</div>} />
<Route path={Pages.payback} component={() => <div>no yet implemented</div>} />
diff --git a/packages/taler-wallet-webextension/src/wxBackend.ts b/packages/taler-wallet-webextension/src/wxBackend.ts
index e1517c4cf..c474c940c 100644
--- a/packages/taler-wallet-webextension/src/wxBackend.ts
+++ b/packages/taler-wallet-webextension/src/wxBackend.ts
@@ -216,12 +216,8 @@ function makeSyncWalletRedirect(
): Record<string, unknown> {
const innerUrl = new URL(chrome.extension.getURL(url));
if (params) {
- for (const key in params) {
- const p = params[key];
- if (p) {
- innerUrl.searchParams.set(key, p);
- }
- }
+ const hParams = Object.keys(params).map(k => `${k}=${params[k]}`).join('&')
+ innerUrl.hash = innerUrl.hash + '?' + hParams
}
if (isFirefox()) {
// Some platforms don't support the sync redirect (yet), so fall back to
diff --git a/packages/taler-wallet-webextension/static/wallet.html b/packages/taler-wallet-webextension/static/wallet.html
index 2b500b56f..817e8bfb8 100644
--- a/packages/taler-wallet-webextension/static/wallet.html
+++ b/packages/taler-wallet-webextension/static/wallet.html
@@ -4,6 +4,7 @@
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="/static/style/pure.css" />
<link rel="stylesheet" type="text/css" href="/static/style/wallet.css" />
+ <link rel="stylesheet" type="text/css" href="/dist/styles.css" />
<link rel="icon" href="/static/img/icon.png" />
<script src="/dist/walletEntryPoint.js"></script>
</head>