From 1d4815c66c395f4fcc86c30e20f3d005e3cb9ff5 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Mon, 15 Nov 2021 11:18:58 -0300 Subject: prettier --- packages/taler-wallet-webextension/package.json | 3 +- .../src/NavigationBar.tsx | 75 +-- .../src/browserCryptoWorkerFactory.js | 35 +- .../src/browserCryptoWorkerFactory.ts | 5 +- packages/taler-wallet-webextension/src/compat.js | 57 ++- .../src/components/Checkbox.tsx | 33 +- .../src/components/CheckboxOutlined.tsx | 47 +- .../src/components/DebugCheckbox.tsx | 13 +- .../src/components/Diagnostics.tsx | 12 +- .../src/components/EditableText.tsx | 66 ++- .../src/components/ErrorMessage.tsx | 41 +- .../src/components/ExchangeToS.tsx | 86 ++-- .../src/components/LogoHeader.tsx | 31 +- .../src/components/Part.tsx | 28 +- .../src/components/QR.tsx | 53 +- .../src/components/SelectList.tsx | 95 ++-- .../src/components/TransactionItem.tsx | 76 ++- .../src/components/styled/index.tsx | 219 ++++----- .../src/context/devContext.ts | 24 +- .../src/context/translation.ts | 56 ++- .../src/cta/Pay.stories.tsx | 190 ++++---- packages/taler-wallet-webextension/src/cta/Pay.tsx | 351 ++++++++----- .../src/cta/Refund.stories.tsx | 72 ++- .../taler-wallet-webextension/src/cta/Refund.tsx | 53 +- .../src/cta/Tip.stories.tsx | 48 +- packages/taler-wallet-webextension/src/cta/Tip.tsx | 44 +- .../src/cta/Withdraw.stories.tsx | 542 +++++++++++---------- .../taler-wallet-webextension/src/cta/Withdraw.tsx | 500 ++++++++++++------- .../taler-wallet-webextension/src/cta/payback.tsx | 2 +- .../src/cta/reset-required.tsx | 2 +- .../src/cta/return-coins.tsx | 2 +- packages/taler-wallet-webextension/src/custom.d.ts | 2 +- .../src/hooks/useAsyncAsHook.ts | 4 +- .../src/hooks/useBackupDeviceName.ts | 25 +- .../src/hooks/useBackupStatus.ts | 50 +- .../src/hooks/useBalances.ts | 3 +- .../src/hooks/useDiagnostics.ts | 6 +- .../src/hooks/useExtendedPermissions.ts | 9 +- .../taler-wallet-webextension/src/hooks/useLang.ts | 13 +- .../src/hooks/useLocalStorage.ts | 44 +- .../src/hooks/useProviderStatus.ts | 4 +- .../src/hooks/useTalerActionURL.ts | 9 +- .../taler-wallet-webextension/src/i18n/strings.ts | 2 +- .../src/popup/Backup.stories.tsx | 307 ++++++------ .../src/popup/BackupPage.tsx | 176 ++++--- .../src/popup/Balance.stories.tsx | 253 +++++----- .../src/popup/BalancePage.tsx | 219 +++++---- .../taler-wallet-webextension/src/popup/Debug.tsx | 8 +- .../src/popup/History.stories.tsx | 184 +++---- .../src/popup/History.tsx | 122 +++-- .../src/popup/Popup.stories.tsx | 29 +- .../popup/ProviderAddConfirmProvider.stories.tsx | 41 +- .../src/popup/ProviderAddSetUrl.stories.tsx | 40 +- .../src/popup/ProviderDetail.stories.tsx | 323 ++++++------ .../src/popup/ProviderDetailPage.tsx | 269 ++++++---- .../src/popup/Settings.stories.tsx | 19 +- .../src/popup/Settings.tsx | 93 ++-- .../src/popup/TalerActionFound.stories.tsx | 24 +- .../src/popup/TalerActionFound.tsx | 161 ++++-- .../src/popupEntryPoint.tsx | 77 +-- .../taler-wallet-webextension/src/renderHtml.tsx | 38 +- .../taler-wallet-webextension/src/test-utils.ts | 16 +- .../src/wallet/Backup.stories.tsx | 307 ++++++------ .../src/wallet/BackupPage.tsx | 177 ++++--- .../src/wallet/Balance.stories.tsx | 84 ++-- .../src/wallet/BalancePage.tsx | 129 +++-- .../src/wallet/CreateManualWithdraw.stories.tsx | 39 +- .../src/wallet/CreateManualWithdraw.tsx | 94 +++- .../src/wallet/History.stories.tsx | 198 ++++---- .../src/wallet/History.tsx | 103 ++-- .../src/wallet/ManualWithdrawPage.tsx | 92 ++-- .../wallet/ProviderAddConfirmProvider.stories.tsx | 41 +- .../src/wallet/ProviderAddSetUrl.stories.tsx | 40 +- .../src/wallet/ProviderDetail.stories.tsx | 323 ++++++------ .../src/wallet/ProviderDetailPage.tsx | 269 ++++++---- .../src/wallet/ReserveCreated.stories.tsx | 25 +- .../src/wallet/ReserveCreated.tsx | 54 +- .../src/wallet/Settings.stories.tsx | 32 +- .../src/wallet/Settings.tsx | 109 +++-- .../src/wallet/Transaction.stories.tsx | 213 ++++---- .../src/wallet/Transaction.tsx | 442 +++++++++++------ .../src/wallet/Welcome.stories.tsx | 20 +- .../src/wallet/Welcome.tsx | 76 +-- .../src/walletEntryPoint.tsx | 128 +++-- packages/taler-wallet-webextension/src/wxApi.ts | 104 ++-- packages/taler-wallet-webextension/tsconfig.json | 20 +- 86 files changed, 4970 insertions(+), 3580 deletions(-) diff --git a/packages/taler-wallet-webextension/package.json b/packages/taler-wallet-webextension/package.json index 3a43f1e76..b3d0b10af 100644 --- a/packages/taler-wallet-webextension/package.json +++ b/packages/taler-wallet-webextension/package.json @@ -13,6 +13,7 @@ "compile": "tsc && rollup -c", "build-storybook": "build-storybook", "storybook": "start-storybook -s . -p 6006", + "pretty": "prettier --write src", "watch": "tsc --watch & rollup -w -c" }, "dependencies": { @@ -80,4 +81,4 @@ "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga|po)$": "/tests/__mocks__/fileTransformer.js" } } -} +} \ No newline at end of file diff --git a/packages/taler-wallet-webextension/src/NavigationBar.tsx b/packages/taler-wallet-webextension/src/NavigationBar.tsx index 9edd8ca67..f206fa2dd 100644 --- a/packages/taler-wallet-webextension/src/NavigationBar.tsx +++ b/packages/taler-wallet-webextension/src/NavigationBar.tsx @@ -28,29 +28,29 @@ import { i18n } from "@gnu-taler/taler-util"; import { ComponentChildren, JSX, h } from "preact"; import Match from "preact-router/match"; import { useDevContext } from "./context/devContext"; -import { PopupNavigation } from './components/styled' +import { PopupNavigation } from "./components/styled"; export enum Pages { - welcome = '/welcome', - balance = '/balance', - manual_withdraw = '/manual-withdraw', - settings = '/settings', - dev = '/dev', - cta = '/cta', - backup = '/backup', - history = '/history', - transaction = '/transaction/:tid', - provider_detail = '/provider/:pid', - provider_add = '/provider/add', + welcome = "/welcome", + balance = "/balance", + manual_withdraw = "/manual-withdraw", + settings = "/settings", + dev = "/dev", + cta = "/cta", + backup = "/backup", + history = "/history", + transaction = "/transaction/:tid", + provider_detail = "/provider/:pid", + provider_add = "/provider/add", - reset_required = '/reset-required', - payback = '/payback', - return_coins = '/return-coins', + reset_required = "/reset-required", + payback = "/payback", + return_coins = "/return-coins", - pay = '/pay', - refund = '/refund', - tips = '/tip', - withdraw = '/withdraw', + pay = "/pay", + refund = "/refund", + tips = "/tip", + withdraw = "/withdraw", } interface TabProps { @@ -71,23 +71,28 @@ function Tab(props: TabProps): JSX.Element { ); } -export function NavBar({ devMode, path }: { path: string, devMode: boolean }) { - return -
- {i18n.str`Balance`} - {i18n.str`History`} - {i18n.str`Backup`} - {i18n.str`Settings`} - {devMode && {i18n.str`Dev`}} -
-
+export function NavBar({ devMode, path }: { path: string; devMode: boolean }) { + return ( + +
+ {i18n.str`Balance`} + {i18n.str`History`} + {i18n.str`Backup`} + {i18n.str`Settings`} + {devMode && {i18n.str`Dev`}} +
+
+ ); } export function WalletNavBar() { - const { devMode } = useDevContext() - return {({ path }: any) => { - console.log("path", path) - return - }} + const { devMode } = useDevContext(); + return ( + + {({ path }: any) => { + console.log("path", path); + return ; + }} + + ); } - diff --git a/packages/taler-wallet-webextension/src/browserCryptoWorkerFactory.js b/packages/taler-wallet-webextension/src/browserCryptoWorkerFactory.js index e9492a2fb..8d958d6bd 100644 --- a/packages/taler-wallet-webextension/src/browserCryptoWorkerFactory.js +++ b/packages/taler-wallet-webextension/src/browserCryptoWorkerFactory.js @@ -21,24 +21,23 @@ exports.BrowserCryptoWorkerFactory = void 0; * @author Florian Dold */ class BrowserCryptoWorkerFactory { - startWorker() { - const workerCtor = Worker; - const workerPath = "/browserWorkerEntry.js"; - return new workerCtor(workerPath); - } - getConcurrency() { - let concurrency = 2; - try { - // only works in the browser - // tslint:disable-next-line:no-string-literal - concurrency = navigator["hardwareConcurrency"]; - concurrency = Math.max(1, Math.ceil(concurrency / 2)); - } - catch (e) { - concurrency = 2; - } - return concurrency; + startWorker() { + const workerCtor = Worker; + const workerPath = "/browserWorkerEntry.js"; + return new workerCtor(workerPath); + } + getConcurrency() { + let concurrency = 2; + try { + // only works in the browser + // tslint:disable-next-line:no-string-literal + concurrency = navigator["hardwareConcurrency"]; + concurrency = Math.max(1, Math.ceil(concurrency / 2)); + } catch (e) { + concurrency = 2; } + return concurrency; + } } exports.BrowserCryptoWorkerFactory = BrowserCryptoWorkerFactory; -//# sourceMappingURL=browserCryptoWorkerFactory.js.map \ No newline at end of file +//# sourceMappingURL=browserCryptoWorkerFactory.js.map diff --git a/packages/taler-wallet-webextension/src/browserCryptoWorkerFactory.ts b/packages/taler-wallet-webextension/src/browserCryptoWorkerFactory.ts index a8315dc6d..ab20228ef 100644 --- a/packages/taler-wallet-webextension/src/browserCryptoWorkerFactory.ts +++ b/packages/taler-wallet-webextension/src/browserCryptoWorkerFactory.ts @@ -19,7 +19,10 @@ * @author Florian Dold */ -import type { CryptoWorker, CryptoWorkerFactory } from "@gnu-taler/taler-wallet-core"; +import type { + CryptoWorker, + CryptoWorkerFactory, +} from "@gnu-taler/taler-wallet-core"; export class BrowserCryptoWorkerFactory implements CryptoWorkerFactory { startWorker(): CryptoWorker { diff --git a/packages/taler-wallet-webextension/src/compat.js b/packages/taler-wallet-webextension/src/compat.js index fdfcbd4b9..48e49a0a7 100644 --- a/packages/taler-wallet-webextension/src/compat.js +++ b/packages/taler-wallet-webextension/src/compat.js @@ -21,41 +21,44 @@ exports.getPermissionsApi = exports.isNode = exports.isFirefox = void 0; * WebExtension APIs consistently. */ function isFirefox() { - const rt = chrome.runtime; - if (typeof rt.getBrowserInfo === "function") { - return true; - } - return false; + const rt = chrome.runtime; + if (typeof rt.getBrowserInfo === "function") { + return true; + } + return false; } exports.isFirefox = isFirefox; /** * Check if we are running under nodejs. */ function isNode() { - return typeof process !== "undefined" && process.release.name === "node"; + return typeof process !== "undefined" && process.release.name === "node"; } exports.isNode = isNode; function getPermissionsApi() { - const myBrowser = globalThis.browser; - if (typeof myBrowser === "object" && - typeof myBrowser.permissions === "object") { - return { - addPermissionsListener: () => { - // Not supported yet. - }, - contains: myBrowser.permissions.contains, - request: myBrowser.permissions.request, - remove: myBrowser.permissions.remove, - }; - } - else { - return { - addPermissionsListener: chrome.permissions.onAdded.addListener.bind(chrome.permissions.onAdded), - contains: chrome.permissions.contains, - request: chrome.permissions.request, - remove: chrome.permissions.remove, - }; - } + const myBrowser = globalThis.browser; + if ( + typeof myBrowser === "object" && + typeof myBrowser.permissions === "object" + ) { + return { + addPermissionsListener: () => { + // Not supported yet. + }, + contains: myBrowser.permissions.contains, + request: myBrowser.permissions.request, + remove: myBrowser.permissions.remove, + }; + } else { + return { + addPermissionsListener: chrome.permissions.onAdded.addListener.bind( + chrome.permissions.onAdded, + ), + contains: chrome.permissions.contains, + request: chrome.permissions.request, + remove: chrome.permissions.remove, + }; + } } exports.getPermissionsApi = getPermissionsApi; -//# sourceMappingURL=compat.js.map \ No newline at end of file +//# sourceMappingURL=compat.js.map diff --git a/packages/taler-wallet-webextension/src/components/Checkbox.tsx b/packages/taler-wallet-webextension/src/components/Checkbox.tsx index 2d7b98087..276ac9ff0 100644 --- a/packages/taler-wallet-webextension/src/components/Checkbox.tsx +++ b/packages/taler-wallet-webextension/src/components/Checkbox.tsx @@ -24,7 +24,13 @@ interface Props { name: string; description?: string; } -export function Checkbox({ name, enabled, onToggle, label, description }: Props): JSX.Element { +export function Checkbox({ + name, + enabled, + onToggle, + label, + description, +}: Props): JSX.Element { return (
+ style={{ width: "1.5em", height: "1.5em", verticalAlign: "middle" }} + /> - {description && - {description} - } + {description && ( + + {description} + + )}
); } diff --git a/packages/taler-wallet-webextension/src/components/CheckboxOutlined.tsx b/packages/taler-wallet-webextension/src/components/CheckboxOutlined.tsx index 5e30ee3d1..2fc8316f5 100644 --- a/packages/taler-wallet-webextension/src/components/CheckboxOutlined.tsx +++ b/packages/taler-wallet-webextension/src/components/CheckboxOutlined.tsx @@ -16,7 +16,7 @@ import { JSX } from "preact/jsx-runtime"; import { Outlined, StyledCheckboxLabel } from "./styled/index"; -import { h } from 'preact'; +import { h } from "preact"; interface Props { enabled: boolean; @@ -25,28 +25,39 @@ interface Props { name: string; } +const Tick = () => ( + +); -const Tick = () => - -export function CheckboxOutlined({ name, enabled, onToggle, label }: Props): JSX.Element { +export function CheckboxOutlined({ + name, + enabled, + onToggle, + label, +}: Props): JSX.Element { return ( - +
diff --git a/packages/taler-wallet-webextension/src/components/DebugCheckbox.tsx b/packages/taler-wallet-webextension/src/components/DebugCheckbox.tsx index f0c682ccb..952df15ae 100644 --- a/packages/taler-wallet-webextension/src/components/DebugCheckbox.tsx +++ b/packages/taler-wallet-webextension/src/components/DebugCheckbox.tsx @@ -14,9 +14,15 @@ TALER; see the file COPYING. If not, see */ - import { JSX, h } from "preact"; +import { JSX, h } from "preact"; -export function DebugCheckbox({ enabled, onToggle }: { enabled: boolean; onToggle: () => void; }): JSX.Element { +export function DebugCheckbox({ + enabled, + onToggle, +}: { + enabled: boolean; + onToggle: () => void; +}): JSX.Element { return (
+ style={{ width: "1.5em", height: "1.5em", verticalAlign: "middle" }} + />
diff --git a/packages/taler-wallet-webextension/src/components/EditableText.tsx b/packages/taler-wallet-webextension/src/components/EditableText.tsx index 6f3388bf9..8b3e6d375 100644 --- a/packages/taler-wallet-webextension/src/components/EditableText.tsx +++ b/packages/taler-wallet-webextension/src/components/EditableText.tsx @@ -25,25 +25,37 @@ interface Props { name: string; description?: string; } -export function EditableText({ name, value, onChange, label, description }: Props): JSX.Element { - const [editing, setEditing] = useState(false) - const ref = useRef(null) +export function EditableText({ + name, + value, + onChange, + label, + description, +}: Props): JSX.Element { + const [editing, setEditing] = useState(false); + const ref = useRef(null); let InputText; if (!editing) { - InputText = () =>
-

{value}

- -
+ InputText = () => ( +
+

{value}

+ +
+ ); } else { - InputText = () =>
- - -
+ InputText = () => ( +
+ + +
+ ); } return (
@@ -54,16 +66,18 @@ export function EditableText({ name, value, onChange, label, description }: Prop {label} - {description && - {description} - } + {description && ( + + {description} + + )}
); } diff --git a/packages/taler-wallet-webextension/src/components/ErrorMessage.tsx b/packages/taler-wallet-webextension/src/components/ErrorMessage.tsx index cfcef16d5..c6b64fb6a 100644 --- a/packages/taler-wallet-webextension/src/components/ErrorMessage.tsx +++ b/packages/taler-wallet-webextension/src/components/ErrorMessage.tsx @@ -13,22 +13,35 @@ You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ - import { VNode, h } from "preact"; +import { VNode, h } from "preact"; import { useState } from "preact/hooks"; -import arrowDown from '../../static/img/chevron-down.svg'; +import arrowDown from "../../static/img/chevron-down.svg"; import { ErrorBox } from "./styled"; -export function ErrorMessage({ title, description }: { title?: string|VNode; description?: string; }) { +export function ErrorMessage({ + title, + description, +}: { + title?: string | VNode; + description?: string; +}) { const [showErrorDetail, setShowErrorDetail] = useState(false); - if (!title) - return null; - return -
-

{title}

- { description && } -
- {showErrorDetail &&

{description}

} -
; + if (!title) return null; + return ( + +
+

{title}

+ {description && ( + + )} +
+ {showErrorDetail &&

{description}

} +
+ ); } diff --git a/packages/taler-wallet-webextension/src/components/ExchangeToS.tsx b/packages/taler-wallet-webextension/src/components/ExchangeToS.tsx index cfa20280f..6d2731cd8 100644 --- a/packages/taler-wallet-webextension/src/components/ExchangeToS.tsx +++ b/packages/taler-wallet-webextension/src/components/ExchangeToS.tsx @@ -13,66 +13,80 @@ You should have received a copy of the GNU General Public License along with GNU Taler; see the file COPYING. If not, see */ - import { Fragment, VNode } from "preact" -import { useState } from "preact/hooks" -import { JSXInternal } from "preact/src/jsx" -import { h } from 'preact'; +import { Fragment, VNode } from "preact"; +import { useState } from "preact/hooks"; +import { JSXInternal } from "preact/src/jsx"; +import { h } from "preact"; export function ExchangeXmlTos({ doc }: { doc: Document }) { - const termsNode = doc.querySelector('[ids=terms-of-service]') + const termsNode = doc.querySelector("[ids=terms-of-service]"); if (!termsNode) { - return
-

The exchange send us an xml but there is no node with 'ids=terms-of-service'. This is the content:

-
{new XMLSerializer().serializeToString(doc)}
-
+ return ( +
+

+ The exchange send us an xml but there is no node with + 'ids=terms-of-service'. This is the content: +

+
{new XMLSerializer().serializeToString(doc)}
+
+ ); } - return - {Array.from(termsNode.children).map(renderChild)} - + return {Array.from(termsNode.children).map(renderChild)}; } /** * Map XML elements into HTML - * @param child - * @returns + * @param child + * @returns */ function renderChild(child: Element): VNode { - const children = Array.from(child.children) + const children = Array.from(child.children); switch (child.nodeName) { - case 'title': return
{child.textContent}
- case '#text': return - case 'paragraph': return

{child.textContent}

- case 'section': { - return - {children.map(renderChild)} - + case "title": + return
{child.textContent}
; + case "#text": + return ; + case "paragraph": + return

{child.textContent}

; + case "section": { + return ( + + {children.map(renderChild)} + + ); } - case 'bullet_list': { - return
    {children.map(renderChild)}
+ case "bullet_list": { + return
    {children.map(renderChild)}
; } - case 'enumerated_list': { - return
    {children.map(renderChild)}
+ case "enumerated_list": { + return
    {children.map(renderChild)}
; } - case 'list_item': { - return
  • {children.map(renderChild)}
  • + case "list_item": { + return
  • {children.map(renderChild)}
  • ; } - case 'block_quote': { - return
    {children.map(renderChild)}
    + case "block_quote": { + return
    {children.map(renderChild)}
    ; } - default: return
    unknown tag {child.nodeName}
    + default: + return ( +
    + unknown tag {child.nodeName} +
    + ); } } /** * Simple anchor with a state persisted into 'data-open' prop - * @returns + * @returns */ -function AnchorWithOpenState(props: JSXInternal.HTMLAttributes) { - const [open, setOpen] = useState(false) +function AnchorWithOpenState( + props: JSXInternal.HTMLAttributes, +) { + const [open, setOpen] = useState(false); function doClick(e: JSXInternal.TargetedMouseEvent) { setOpen(!open); e.preventDefault(); } - return + return ; } - diff --git a/packages/taler-wallet-webextension/src/components/LogoHeader.tsx b/packages/taler-wallet-webextension/src/components/LogoHeader.tsx index 9b75c62a1..6c47dc92a 100644 --- a/packages/taler-wallet-webextension/src/components/LogoHeader.tsx +++ b/packages/taler-wallet-webextension/src/components/LogoHeader.tsx @@ -17,15 +17,22 @@ import { h } from "preact"; export function LogoHeader() { - return
    - -
    - -} \ No newline at end of file + return ( +
    + +
    + ); +} diff --git a/packages/taler-wallet-webextension/src/components/Part.tsx b/packages/taler-wallet-webextension/src/components/Part.tsx index 75c9df16f..c8ecb46d2 100644 --- a/packages/taler-wallet-webextension/src/components/Part.tsx +++ b/packages/taler-wallet-webextension/src/components/Part.tsx @@ -15,18 +15,28 @@ */ import { AmountLike } from "@gnu-taler/taler-util"; import { ExtraLargeText, LargeText, SmallLightText } from "./styled"; -import { h } from 'preact'; +import { h } from "preact"; -export type Kind = 'positive' | 'negative' | 'neutral'; +export type Kind = "positive" | "negative" | "neutral"; interface Props { - title: string, text: AmountLike, kind: Kind, big?: boolean + title: string; + text: AmountLike; + kind: Kind; + big?: boolean; } export function Part({ text, title, kind, big }: Props) { const Text = big ? ExtraLargeText : LargeText; - return
    - {title} - - {text} - -
    + return ( +
    + {title} + + {text} + +
    + ); } diff --git a/packages/taler-wallet-webextension/src/components/QR.tsx b/packages/taler-wallet-webextension/src/components/QR.tsx index 8e3f69295..4ff1af961 100644 --- a/packages/taler-wallet-webextension/src/components/QR.tsx +++ b/packages/taler-wallet-webextension/src/components/QR.tsx @@ -14,24 +14,35 @@ GNU Taler; see the file COPYING. If not, see */ - import { h, VNode } from "preact"; - import { useEffect, useRef } from "preact/hooks"; - import qrcode from "qrcode-generator"; - - export function QR({ text }: { text: string; }):VNode { - const divRef = useRef(null); - useEffect(() => { - if (!divRef.current) return - const qr = qrcode(0, 'L'); - qr.addData(text); - qr.make(); - divRef.current.innerHTML = qr.createSvgTag({ - scalable: true, - }); - }); - - return
    -
    -
    ; - } - \ No newline at end of file +import { h, VNode } from "preact"; +import { useEffect, useRef } from "preact/hooks"; +import qrcode from "qrcode-generator"; + +export function QR({ text }: { text: string }): VNode { + const divRef = useRef(null); + useEffect(() => { + if (!divRef.current) return; + const qr = qrcode(0, "L"); + qr.addData(text); + qr.make(); + divRef.current.innerHTML = qr.createSvgTag({ + scalable: true, + }); + }); + + return ( +
    +
    +
    + ); +} diff --git a/packages/taler-wallet-webextension/src/components/SelectList.tsx b/packages/taler-wallet-webextension/src/components/SelectList.tsx index 536e5b89a..f89ba19b2 100644 --- a/packages/taler-wallet-webextension/src/components/SelectList.tsx +++ b/packages/taler-wallet-webextension/src/components/SelectList.tsx @@ -23,46 +23,67 @@ interface Props { onChange: (s: string) => void; label: string; list: { - [label: string]: string - } + [label: string]: string; + }; name: string; description?: string; canBeNull?: boolean; } -export function SelectList({ name, value, list, canBeNull, onChange, label, description }: Props): JSX.Element { - return
    - - - - - {description && - {description} - } - -
    - +export function SelectList({ + name, + value, + list, + canBeNull, + onChange, + label, + description, +}: Props): JSX.Element { + return ( +
    + + + + + {description && ( + + {description} + + )} +
    + ); } diff --git a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx index 991e97c94..1917d5627 100644 --- a/packages/taler-wallet-webextension/src/components/TransactionItem.tsx +++ b/packages/taler-wallet-webextension/src/components/TransactionItem.tsx @@ -14,18 +14,33 @@ GNU Taler; see the file COPYING. If not, see */ -import { AmountString, Timestamp, Transaction, TransactionType } from '@gnu-taler/taler-util'; -import { format, formatDistance } from 'date-fns'; -import { h } from 'preact'; -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 { + AmountString, + Timestamp, + Transaction, + TransactionType, +} from "@gnu-taler/taler-util"; +import { format, formatDistance } from "date-fns"; +import { h } from "preact"; +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 { Pages } from "../NavigationBar"; -import { Column, ExtraLargeText, HistoryRow, SmallLightText, LargeText, LightText } from './styled/index'; +import { + Column, + ExtraLargeText, + HistoryRow, + SmallLightText, + LargeText, + LightText, +} from "./styled/index"; -export function TransactionItem(props: { tx: Transaction, multiCurrency: boolean }): JSX.Element { +export function TransactionItem(props: { + tx: Transaction; + multiCurrency: boolean; +}): JSX.Element { const tx = props.tx; switch (tx.type) { case TransactionType.Withdrawal: @@ -112,20 +127,26 @@ export function TransactionItem(props: { tx: Transaction, multiCurrency: boolean function TransactionLayout(props: TransactionLayoutProps): JSX.Element { const date = new Date(props.timestamp.t_ms); - const dateStr = format(date, 'dd MMM, hh:mm') + const dateStr = format(date, "dd MMM, hh:mm"); return ( - +
    {props.title}
    - {props.subtitle &&
    {props.subtitle}
    } + {props.subtitle && ( +
    + {props.subtitle} +
    + )}
    - {props.pending && - Waiting for confirmation - } - {dateStr} + {props.pending && ( + + Waiting for confirmation + + )} + {dateStr}
    + {sign} {amount} @@ -187,4 +212,3 @@ function TransactionAmount(props: TransactionAmountProps): JSX.Element { ); } - diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx b/packages/taler-wallet-webextension/src/components/styled/index.tsx index 65c1f49e9..8b36dbd31 100644 --- a/packages/taler-wallet-webextension/src/components/styled/index.tsx +++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx @@ -14,18 +14,17 @@ GNU Taler; see the file COPYING. If not, see */ - // need to import linaria types, otherwise compiler will complain -import type * as Linaria from '@linaria/core'; +import type * as Linaria from "@linaria/core"; -import { styled } from '@linaria/react'; +import { styled } from "@linaria/react"; export const PaymentStatus = styled.div<{ color: string }>` padding: 5px; border-radius: 5px; color: white; - background-color: ${p => p.color}; -` + background-color: ${(p) => p.color}; +`; export const WalletAction = styled.div` display: flex; @@ -36,9 +35,9 @@ export const WalletAction = styled.div` margin: auto; height: 100%; - + & h1:first-child { - margin-top: 0; + margin-top: 0; } section { margin-bottom: 2em; @@ -47,7 +46,7 @@ export const WalletAction = styled.div` margin-left: 8px; } } -` +`; export const WalletActionOld = styled.section` border: solid 5px black; border-radius: 10px; @@ -59,17 +58,17 @@ export const WalletActionOld = styled.section` margin: auto; height: 100%; - + & h1:first-child { - margin-top: 0; + margin-top: 0; } -` +`; export const DateSeparator = styled.div` color: gray; - margin: .2em; + margin: 0.2em; margin-top: 1em; -` +`; export const WalletBox = styled.div<{ noPadding?: boolean }>` display: flex; flex-direction: column; @@ -79,10 +78,10 @@ export const WalletBox = styled.div<{ noPadding?: boolean }>` width: 400px; } & > section { - padding-left: ${({ noPadding }) => noPadding ? '0px' : '8px'}; - padding-right: ${({ noPadding }) => noPadding ? '0px' : '8px'}; + 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; + margin-bottom: auto; overflow: auto; table td { @@ -128,13 +127,13 @@ export const WalletBox = styled.div<{ noPadding?: boolean }>` margin-left: 8px; } } -` +`; export const Middle = styled.div` - justify-content: space-around; - display: flex; - flex-direction: column; - height: 100%; -` + justify-content: space-around; + display: flex; + flex-direction: column; + height: 100%; +`; export const PopupBox = styled.div<{ noPadding?: boolean }>` height: 290px; @@ -144,9 +143,9 @@ export const PopupBox = styled.div<{ noPadding?: boolean }>` justify-content: space-between; & > section { - padding: ${({ noPadding }) => noPadding ? '0px' : '8px'}; + padding: ${({ noPadding }) => (noPadding ? "0px" : "8px")}; // this margin will send the section up when used with a header - margin-bottom: auto; + margin-bottom: auto; overflow-y: auto; table td { @@ -201,8 +200,7 @@ export const PopupBox = styled.div<{ noPadding?: boolean }>` margin-left: 8px; } } - -` +`; export const Button = styled.button<{ upperCased?: boolean }>` display: inline-block; @@ -214,7 +212,7 @@ export const Button = styled.button<{ upperCased?: boolean }>` cursor: pointer; user-select: none; box-sizing: border-box; - text-transform: ${({ upperCased }) => upperCased ? 'uppercase' : 'none'}; + text-transform: ${({ upperCased }) => (upperCased ? "uppercase" : "none")}; font-family: inherit; font-size: 100%; @@ -223,7 +221,7 @@ export const Button = styled.button<{ upperCased?: boolean }>` color: rgba(0, 0, 0, 0.8); /* rgba supported */ border: 1px solid #999; /*IE 6/7/8*/ border: none rgba(0, 0, 0, 0); /*IE9 + everything else*/ - background-color: '#e6e6e6'; + background-color: "#e6e6e6"; text-decoration: none; border-radius: 2px; @@ -263,7 +261,7 @@ export const Link = styled.a<{ upperCased?: boolean }>` cursor: pointer; user-select: none; box-sizing: border-box; - text-transform: ${({ upperCased }) => upperCased ? 'uppercase' : 'none'}; + text-transform: ${({ upperCased }) => (upperCased ? "uppercase" : "none")}; font-family: inherit; font-size: 100%; @@ -304,9 +302,9 @@ export const FontIcon = styled.div` text-align: center; font-weight: bold; /* vertical-align: text-top; */ -` +`; export const ButtonBox = styled(Button)` - padding: .5em; + padding: 0.5em; width: fit-content; height: 2em; @@ -322,89 +320,87 @@ export const ButtonBox = styled(Button)` border-radius: 4px; border-color: black; color: black; -` - +`; const ButtonVariant = styled(Button)` color: white; border-radius: 4px; text-shadow: 0 1px 1px rgba(0, 0, 0, 0.2); -` +`; export const ButtonPrimary = styled(ButtonVariant)` background-color: rgb(66, 184, 221); -` +`; export const ButtonBoxPrimary = styled(ButtonBox)` color: rgb(66, 184, 221); border-color: rgb(66, 184, 221); -` +`; export const ButtonSuccess = styled(ButtonVariant)` background-color: #388e3c; -` +`; export const LinkSuccess = styled(Link)` color: #388e3c; -` +`; export const ButtonBoxSuccess = styled(ButtonBox)` color: #388e3c; border-color: #388e3c; -` +`; export const ButtonWarning = styled(ButtonVariant)` background-color: rgb(223, 117, 20); -` +`; export const LinkWarning = styled(Link)` color: rgb(223, 117, 20); -` +`; export const ButtonBoxWarning = styled(ButtonBox)` color: rgb(223, 117, 20); border-color: rgb(223, 117, 20); -` +`; export const ButtonDestructive = styled(ButtonVariant)` background-color: rgb(202, 60, 60); -` +`; export const ButtonBoxDestructive = styled(ButtonBox)` color: rgb(202, 60, 60); border-color: rgb(202, 60, 60); -` - +`; export const BoldLight = styled.div` -color: gray; -font-weight: bold; -` + color: gray; + font-weight: bold; +`; export const Centered = styled.div` text-align: center; & > :not(:first-child) { margin-top: 15px; } -` +`; export const Row = styled.div` display: flex; margin: 0.5em 0; justify-content: space-between; 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; margin: 0em 1em; justify-content: space-between; -` +`; export const RowBorderGray = styled(Row)` border: 1px solid gray; /* border-radius: 0.5em; */ -` +`; export const RowLightBorderGray = styled(Row2)` border: 1px solid lightgray; @@ -414,7 +410,7 @@ export const RowLightBorderGray = styled(Row2)` border: 1px solid lightgray; background-color: red; } -` +`; export const HistoryRow = styled.a` text-decoration: none; @@ -423,7 +419,7 @@ export const HistoryRow = styled.a` display: flex; justify-content: space-between; padding: 0.5em; - + border: 1px solid lightgray; border-top: 0px; @@ -439,7 +435,7 @@ export const HistoryRow = styled.a` margin-left: auto; align-self: center; } -` +`; export const ListOfProducts = styled.div` & > div > a > img { @@ -453,62 +449,62 @@ export const ListOfProducts = styled.div` margin-right: auto; margin-left: 1em; } -` +`; export const LightText = styled.div` color: gray; -` +`; export const WarningText = styled.div` color: rgb(223, 117, 20); -` +`; export const SmallText = styled.div` - font-size: small; -` + font-size: small; +`; export const LargeText = styled.div` - font-size: large; -` + font-size: large; +`; export const ExtraLargeText = styled.div` - font-size: x-large; -` + font-size: x-large; +`; export const SmallLightText = styled(SmallText)` color: gray; -` +`; export const CenteredText = styled.div` white-space: nowrap; text-align: center; -` +`; export const CenteredBoldText = styled(CenteredText)` white-space: nowrap; text-align: center; font-weight: bold; color: ${((props: any): any => String(props.color) as any) as any}; -` +`; export const Input = styled.div<{ invalid?: boolean }>` & label { display: block; padding: 5px; - color: ${({ invalid }) => !invalid ? 'inherit' : 'red'} + color: ${({ invalid }) => (!invalid ? "inherit" : "red")}; } & input { display: block; padding: 5px; width: calc(100% - 4px - 10px); - border-color: ${({ invalid }) => !invalid ? 'inherit' : 'red'} + border-color: ${({ invalid }) => (!invalid ? "inherit" : "red")}; } -` +`; export const InputWithLabel = styled.div<{ invalid?: boolean }>` & label { display: block; padding: 5px; - color: ${({ invalid }) => !invalid ? 'inherit' : 'red'} + color: ${({ invalid }) => (!invalid ? "inherit" : "red")}; } & > div { position: relative; @@ -516,20 +512,20 @@ export const InputWithLabel = styled.div<{ invalid?: boolean }>` top: 0px; bottom: 0px; - & > div { + & > div { position: absolute; background-color: lightgray; padding: 5px; margin: 2px; } - & > input { + & > input { flex: 1; - padding: 5px; - border-color: ${({ invalid }) => !invalid ? 'inherit' : 'red'} + padding: 5px; + border-color: ${({ invalid }) => (!invalid ? "inherit" : "red")}; } } -` +`; export const ErrorBox = styled.div` border: 2px solid #f5c6cb; @@ -555,22 +551,22 @@ export const ErrorBox = styled.div` width: 28px; } } -` +`; export const SuccessBox = styled(ErrorBox)` color: #0f5132; background-color: #d1e7dd; border-color: #badbcc; -` +`; export const WarningBox = styled(ErrorBox)` color: #664d03; background-color: #fff3cd; border-color: #ffecb5; -` +`; export const PopupNavigation = styled.div<{ devMode?: boolean }>` - background-color:#0042b2; + background-color: #0042b2; height: 35px; justify-content: space-around; display: flex; @@ -582,7 +578,7 @@ export const PopupNavigation = styled.div<{ devMode?: boolean }>` & > div > a { color: #f8faf7; display: inline-block; - width: calc(400px / ${({ devMode }) => !devMode ? 4 : 5}); + width: calc(400px / ${({ devMode }) => (!devMode ? 4 : 5)}); text-align: center; text-decoration: none; vertical-align: middle; @@ -597,7 +593,6 @@ export const PopupNavigation = styled.div<{ devMode?: boolean }>` `; export const NiceSelect = styled.div` - & > select { -webkit-appearance: none; -moz-appearance: none; @@ -617,19 +612,19 @@ export const NiceSelect = styled.div` display: flex; /* width: 10em; */ overflow: hidden; - border-radius: .25em; + border-radius: 0.25em; &::after { - content: '\u25BC'; + content: "\u25BC"; position: absolute; top: 0; right: 0; padding: 0.5em 1em; cursor: pointer; pointer-events: none; - -webkit-transition: .25s all ease; - -o-transition: .25s all ease; - transition: .25s all ease; + -webkit-transition: 0.25s all ease; + -o-transition: 0.25s all ease; + transition: 0.25s all ease; } &:hover::after { @@ -639,7 +634,7 @@ export const NiceSelect = styled.div` &::-ms-expand { display: none; } -` +`; export const Outlined = styled.div` border: 2px solid #388e3c; @@ -647,13 +642,12 @@ export const Outlined = styled.div` width: fit-content; border-radius: 2px; color: #388e3c; -` +`; /* { width: "1.5em", height: "1.5em", verticalAlign: "middle" } */ export const CheckboxSuccess = styled.input` vertical-align: center; - -` +`; export const TermsSection = styled.a` border: 1px solid black; @@ -664,13 +658,13 @@ export const TermsSection = styled.a` text-decoration: none; color: inherit; flex-direction: column; - + display: flex; &[data-open="true"] { - display: flex; + display: flex; } &[data-open="false"] > *:not(:first-child) { - display: none; + display: none; } header { @@ -681,11 +675,11 @@ export const TermsSection = styled.a` height: auto; } - &[data-open="true"] header:after { - content: '\\2227'; + &[data-open="true"] header:after { + content: "\\2227"; } - &[data-open="false"] header:after { - content: '\\2228'; + &[data-open="false"] header:after { + content: "\\2228"; } `; @@ -712,13 +706,13 @@ export const TermsOfService = styled.div` padding: 1em; margin-top: 2px; margin-bottom: 2px; - + display: flex; &[data-open="true"] { - display: flex; + display: flex; } &[data-open="false"] > *:not(:first-child) { - display: none; + display: none; } header { @@ -729,22 +723,20 @@ export const TermsOfService = styled.div` height: auto; } - &[data-open="true"] > header:after { - content: '\\2227'; + &[data-open="true"] > header:after { + content: "\\2227"; } - &[data-open="false"] > header:after { - content: '\\2228'; + &[data-open="false"] > header:after { + content: "\\2228"; } } - -` +`; export const StyledCheckboxLabel = styled.div` color: green; text-transform: uppercase; /* font-weight: bold; */ text-align: center; span { - input { display: none; opacity: 0; @@ -758,7 +750,7 @@ export const StyledCheckboxLabel = styled.div` margin-right: 1em; border-radius: 2px; border: 2px solid currentColor; - + svg { transition: transform 0.1s ease-in 25ms; transform: scale(0); @@ -776,12 +768,11 @@ export const StyledCheckboxLabel = styled.div` } input:disabled + div { color: #959495; - }; + } input:disabled + div + label { color: #959495; - }; + } input:focus + div + label { box-shadow: 0 0 0 0.05em #fff, 0 0 0.15em 0.1em currentColor; } - -` \ No newline at end of file +`; diff --git a/packages/taler-wallet-webextension/src/context/devContext.ts b/packages/taler-wallet-webextension/src/context/devContext.ts index ea2ba4ceb..0344df057 100644 --- a/packages/taler-wallet-webextension/src/context/devContext.ts +++ b/packages/taler-wallet-webextension/src/context/devContext.ts @@ -15,13 +15,13 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { createContext, h, VNode } from 'preact' -import { useContext, useState } from 'preact/hooks' -import { useLocalStorage } from '../hooks/useLocalStorage'; +import { createContext, h, VNode } from "preact"; +import { useContext, useState } from "preact/hooks"; +import { useLocalStorage } from "../hooks/useLocalStorage"; interface Type { devMode: boolean; @@ -29,14 +29,14 @@ interface Type { } const Context = createContext({ devMode: false, - toggleDevMode: () => null -}) + toggleDevMode: () => null, +}); export const useDevContext = (): Type => useContext(Context); export const DevContextProvider = ({ children }: { children: any }): VNode => { - const [value, setter] = useLocalStorage('devMode') - const devMode = value === "true" - const toggleDevMode = () => setter(v => !v ? "true" : undefined) + const [value, setter] = useLocalStorage("devMode"); + const devMode = value === "true"; + const toggleDevMode = () => setter((v) => (!v ? "true" : undefined)); return h(Context.Provider, { value: { devMode, toggleDevMode }, children }); -} +}; diff --git a/packages/taler-wallet-webextension/src/context/translation.ts b/packages/taler-wallet-webextension/src/context/translation.ts index 5f57958de..105da9dcf 100644 --- a/packages/taler-wallet-webextension/src/context/translation.ts +++ b/packages/taler-wallet-webextension/src/context/translation.ts @@ -15,54 +15,58 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { createContext, h, VNode } from 'preact' -import { useContext, useEffect } from 'preact/hooks' -import { useLang } from '../hooks/useLang' +import { createContext, h, VNode } from "preact"; +import { useContext, useEffect } from "preact/hooks"; +import { useLang } from "../hooks/useLang"; //@ts-ignore: type declaration import * as jedLib from "jed"; import { strings } from "../i18n/strings"; -import { setupI18n } from '@gnu-taler/taler-util'; +import { setupI18n } from "@gnu-taler/taler-util"; interface Type { lang: string; changeLanguage: (l: string) => void; } const initial = { - lang: 'en', + lang: "en", changeLanguage: () => { // do not change anything - } -} -const Context = createContext(initial) + }, +}; +const Context = createContext(initial); interface Props { - initial?: string, - children: any, - forceLang?: string + initial?: string; + children: any; + forceLang?: string; } -//we use forceLang when we don't want to use the saved state, but sone forced -//runtime lang predefined lang -export const TranslationProvider = ({ initial, children, forceLang }: Props): VNode => { - const [lang, changeLanguage] = useLang(initial) +//we use forceLang when we don't want to use the saved state, but sone forced +//runtime lang predefined lang +export const TranslationProvider = ({ + initial, + children, + forceLang, +}: Props): VNode => { + const [lang, changeLanguage] = useLang(initial); useEffect(() => { if (forceLang) { - changeLanguage(forceLang) + changeLanguage(forceLang); } - }) - useEffect(()=> { - setupI18n(lang, strings) - },[lang]) + }); + useEffect(() => { + setupI18n(lang, strings); + }, [lang]); if (forceLang) { - setupI18n(forceLang, strings) + setupI18n(forceLang, strings); } else { - setupI18n(lang, strings) + setupI18n(lang, strings); } return h(Context.Provider, { value: { lang, changeLanguage }, children }); -} +}; export const useTranslationContext = (): Type => useContext(Context); diff --git a/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx b/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx index 622e7950f..c2d360d3b 100644 --- a/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Pay.stories.tsx @@ -15,150 +15,156 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { ContractTerms, PreparePayResultType } from '@gnu-taler/taler-util'; -import { createExample } from '../test-utils'; -import { PaymentRequestView as TestedComponent } from './Pay'; +import { ContractTerms, PreparePayResultType } from "@gnu-taler/taler-util"; +import { createExample } from "../test-utils"; +import { PaymentRequestView as TestedComponent } from "./Pay"; export default { - title: 'cta/pay', + title: "cta/pay", component: TestedComponent, - argTypes: { - }, + argTypes: {}, }; export const NoBalance = createExample(TestedComponent, { payStatus: { status: PreparePayResultType.InsufficientBalance, - noncePriv: '', + noncePriv: "", proposalId: "proposal1234", - contractTerms: { + contractTerms: ({ merchant: { - name: 'someone' + name: "someone", }, - summary: 'some beers', - amount: 'USD:10', - } as Partial as any, - amountRaw: 'USD:10', - } + summary: "some beers", + amount: "USD:10", + } as Partial) as any, + amountRaw: "USD:10", + }, }); export const NoEnoughBalance = createExample(TestedComponent, { payStatus: { status: PreparePayResultType.InsufficientBalance, - noncePriv: '', + noncePriv: "", proposalId: "proposal1234", - contractTerms: { + contractTerms: ({ merchant: { - name: 'someone' + name: "someone", }, - summary: 'some beers', - amount: 'USD:10', - } as Partial as any, - amountRaw: 'USD:10', + summary: "some beers", + amount: "USD:10", + } as Partial) as any, + amountRaw: "USD:10", }, balance: { - currency: 'USD', + currency: "USD", fraction: 40000000, - value: 9 - } + value: 9, + }, }); export const PaymentPossible = createExample(TestedComponent, { - uri: 'taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0', + uri: + "taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0", payStatus: { status: PreparePayResultType.PaymentPossible, - amountEffective: 'USD:10', - amountRaw: 'USD:10', - noncePriv: '', - contractTerms: { - nonce: '123213123', + amountEffective: "USD:10", + amountRaw: "USD:10", + noncePriv: "", + contractTerms: ({ + nonce: "123213123", merchant: { - name: 'someone' + name: "someone", }, - amount: 'USD:10', - summary: 'some beers', - } as Partial as any, - contractTermsHash: '123456', - proposalId: 'proposal1234' - } + amount: "USD:10", + summary: "some beers", + } as Partial) as any, + contractTermsHash: "123456", + proposalId: "proposal1234", + }, }); export const PaymentPossibleWithFee = createExample(TestedComponent, { - uri: 'taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0', + uri: + "taler://pay/merchant-backend.taler/2021.242-01G2X4275RBWG/?c=66BE594PDZR24744J6EQK52XM0", payStatus: { status: PreparePayResultType.PaymentPossible, - amountEffective: 'USD:10.20', - amountRaw: 'USD:10', - noncePriv: '', - contractTerms: { - nonce: '123213123', + amountEffective: "USD:10.20", + amountRaw: "USD:10", + noncePriv: "", + contractTerms: ({ + nonce: "123213123", merchant: { - name: 'someone' + name: "someone", }, - amount: 'USD:10', - summary: 'some beers', - } as Partial as any, - contractTermsHash: '123456', - proposalId: 'proposal1234' - } + amount: "USD:10", + summary: "some beers", + } as Partial) as any, + contractTermsHash: "123456", + proposalId: "proposal1234", + }, }); export const AlreadyConfirmedWithFullfilment = createExample(TestedComponent, { payStatus: { status: PreparePayResultType.AlreadyConfirmed, - amountEffective: 'USD:10', - amountRaw: 'USD:10', - contractTerms: { + amountEffective: "USD:10", + amountRaw: "USD:10", + contractTerms: ({ merchant: { - name: 'someone' + name: "someone", }, - fulfillment_message: 'congratulations! you are looking at the fulfillment message! ', - summary: 'some beers', - amount: 'USD:10', - } as Partial as any, - contractTermsHash: '123456', - proposalId: 'proposal1234', + fulfillment_message: + "congratulations! you are looking at the fulfillment message! ", + summary: "some beers", + amount: "USD:10", + } as Partial) 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' - }, - summary: 'some beers', - amount: 'USD:10', - } as Partial 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", + }, + summary: "some beers", + amount: "USD:10", + } as Partial) as any, + contractTermsHash: "123456", + proposalId: "proposal1234", + paid: false, + }, + }, +); export const AlreadyPaid = createExample(TestedComponent, { payStatus: { status: PreparePayResultType.AlreadyConfirmed, - amountEffective: 'USD:10', - amountRaw: 'USD:10', - contractTerms: { + amountEffective: "USD:10", + amountRaw: "USD:10", + contractTerms: ({ merchant: { - name: 'someone' + name: "someone", }, - fulfillment_message: 'congratulations! you are looking at the fulfillment message! ', - summary: 'some beers', - amount: 'USD:10', - } as Partial as any, - contractTermsHash: '123456', - proposalId: 'proposal1234', + fulfillment_message: + "congratulations! you are looking at the fulfillment message! ", + summary: "some beers", + amount: "USD:10", + } as Partial) as any, + contractTermsHash: "123456", + proposalId: "proposal1234", paid: true, - } + }, }); diff --git a/packages/taler-wallet-webextension/src/cta/Pay.tsx b/packages/taler-wallet-webextension/src/cta/Pay.tsx index 675b14ff9..1023013d2 100644 --- a/packages/taler-wallet-webextension/src/cta/Pay.tsx +++ b/packages/taler-wallet-webextension/src/cta/Pay.tsx @@ -24,18 +24,36 @@ */ // import * as i18n from "../i18n"; -import { AmountJson, AmountLike, Amounts, ConfirmPayResult, ConfirmPayResultDone, ConfirmPayResultType, ContractTerms, getJsonI18n, i18n, PreparePayResult, PreparePayResultType } from "@gnu-taler/taler-util"; -import { Fragment, JSX, VNode } from "preact"; +import { + AmountJson, + AmountLike, + Amounts, + ConfirmPayResult, + ConfirmPayResultDone, + ConfirmPayResultType, + ContractTerms, + i18n, + PreparePayResult, + PreparePayResultType, +} from "@gnu-taler/taler-util"; +import { h, Fragment, JSX, VNode } from "preact"; import { useEffect, useState } from "preact/hooks"; import { LogoHeader } from "../components/LogoHeader"; import { Part } from "../components/Part"; import { QR } from "../components/QR"; -import { ButtonSuccess, ErrorBox, LinkSuccess, SuccessBox, WalletAction, WarningBox } from "../components/styled"; +import { + ButtonSuccess, + ErrorBox, + LinkSuccess, + SuccessBox, + WalletAction, + WarningBox, +} from "../components/styled"; import { useBalances } from "../hooks/useBalances"; import * as wxApi from "../wxApi"; interface Props { - talerPayUri?: string + talerPayUri?: string; } // export function AlreadyPaid({ payStatus }: { payStatus: PreparePayResult }) { @@ -64,7 +82,9 @@ interface Props { // // } -const doPayment = async (payStatus: PreparePayResult): Promise => { +const doPayment = async ( + payStatus: PreparePayResult, +): Promise => { if (payStatus.status !== "payment-possible") { throw Error(`invalid state: ${payStatus.status}`); } @@ -80,18 +100,29 @@ const doPayment = async (payStatus: PreparePayResult): Promise(undefined); - const [payResult, setPayResult] = useState(undefined); + const [payStatus, setPayStatus] = useState( + undefined, + ); + const [payResult, setPayResult] = useState( + undefined, + ); const [payErrMsg, setPayErrMsg] = useState(undefined); - const balance = useBalances() - const balanceWithoutError = balance?.hasError ? [] : (balance?.response.balances || []) + const balance = useBalances(); + const balanceWithoutError = balance?.hasError + ? [] + : balance?.response.balances || []; - const foundBalance = balanceWithoutError.find(b => payStatus && Amounts.parseOrThrow(b.available).currency === Amounts.parseOrThrow(payStatus?.amountRaw).currency) - const foundAmount = foundBalance ? Amounts.parseOrThrow(foundBalance.available) : undefined + const foundBalance = balanceWithoutError.find( + (b) => + payStatus && + Amounts.parseOrThrow(b.available).currency === + Amounts.parseOrThrow(payStatus?.amountRaw).currency, + ); + const foundAmount = foundBalance + ? Amounts.parseOrThrow(foundBalance.available) + : undefined; useEffect(() => { if (!talerPayUri) return; @@ -101,7 +132,7 @@ export function PayPage({ talerPayUri }: Props): JSX.Element { setPayStatus(p); } catch (e) { if (e instanceof Error) { - setPayErrMsg(e.message) + setPayErrMsg(e.message); } } }; @@ -109,30 +140,28 @@ export function PayPage({ talerPayUri }: Props): JSX.Element { }, [talerPayUri]); if (!talerPayUri) { - return missing pay uri + return missing pay uri; } if (!payStatus) { if (payErrMsg) { - return - -

    - {i18n.str`Digital cash payment`} -

    -
    -

    Could not get the payment information for this order

    - - {payErrMsg} - -
    -
    + return ( + + +

    {i18n.str`Digital cash payment`}

    +
    +

    Could not get the payment information for this order

    + {payErrMsg} +
    +
    + ); } return Loading payment information ...; } const onClick = async () => { try { - const res = await doPayment(payStatus) + const res = await doPayment(payStatus); setPayResult(res); } catch (e) { console.error(e); @@ -140,13 +169,18 @@ export function PayPage({ talerPayUri }: Props): JSX.Element { setPayErrMsg(e.message); } } + }; - } - - return ; + return ( + + ); } export interface PaymentRequestViewProps { @@ -157,7 +191,14 @@ export interface PaymentRequestViewProps { uri: string; balance: AmountJson | undefined; } -export function PaymentRequestView({ uri, payStatus, payResult, onClick, payErrMsg, balance }: PaymentRequestViewProps) { +export function PaymentRequestView({ + uri, + payStatus, + payResult, + onClick, + payErrMsg, + balance, +}: PaymentRequestViewProps) { let totalFees: AmountJson = Amounts.getZero(payStatus.amountRaw); const contractTerms: ContractTerms = payStatus.contractTerms; @@ -185,116 +226,174 @@ export function PaymentRequestView({ uri, payStatus, payResult, onClick, payErrM } function Alternative() { - const [showQR, setShowQR] = useState(false) - const privateUri = payStatus.status !== PreparePayResultType.AlreadyConfirmed ? `${uri}&n=${payStatus.noncePriv}` : uri - return
    - setShowQR(qr => !qr)}> - {!showQR ? i18n.str`Pay with a mobile phone` : i18n.str`Hide QR`} - - {showQR && } -
    + const [showQR, setShowQR] = useState(false); + const privateUri = + payStatus.status !== PreparePayResultType.AlreadyConfirmed + ? `${uri}&n=${payStatus.noncePriv}` + : uri; + return ( +
    + setShowQR((qr) => !qr)}> + {!showQR ? i18n.str`Pay with a mobile phone` : i18n.str`Hide QR`} + + {showQR && ( +
    + + Scan the QR code or click here +
    + )} +
    + ); } function ButtonsSection() { if (payResult) { if (payResult.type === ConfirmPayResultType.Pending) { - return
    -
    -

    Processing...

    -
    -
    + return ( +
    +
    +

    Processing...

    +
    +
    + ); } - return null + return null; } if (payErrMsg) { - return
    -
    -

    Payment failed: {payErrMsg}

    - -
    -
    - } - if (payStatus.status === PreparePayResultType.PaymentPossible) { - return + return (
    - - {i18n.str`Pay`} {amountToString(payStatus.amountEffective)} - +
    +

    Payment failed: {payErrMsg}

    + +
    - -
    + ); + } + if (payStatus.status === PreparePayResultType.PaymentPossible) { + return ( + +
    + + {i18n.str`Pay`} {amountToString(payStatus.amountEffective)} + +
    + +
    + ); } if (payStatus.status === PreparePayResultType.InsufficientBalance) { - return -
    - {balance ? - Your balance of {amountToString(balance)} is not enough to pay for this purchase - : - Your balance is not enough to pay for this purchase. - } -
    -
    - - {i18n.str`Withdraw digital cash`} - -
    - -
    + return ( + +
    + {balance ? ( + + Your balance of {amountToString(balance)} is not enough to pay + for this purchase + + ) : ( + + Your balance is not enough to pay for this purchase. + + )} +
    +
    + + {i18n.str`Withdraw digital cash`} + +
    + +
    + ); } if (payStatus.status === PreparePayResultType.AlreadyConfirmed) { - return -
    - {payStatus.paid && contractTerms.fulfillment_message && } -
    - {!payStatus.paid && } -
    + return ( + +
    + {payStatus.paid && contractTerms.fulfillment_message && ( + + )} +
    + {!payStatus.paid && } +
    + ); } - return + return ; } - return - - -

    - {i18n.str`Digital cash payment`} -

    - {payStatus.status === PreparePayResultType.AlreadyConfirmed && - (payStatus.paid ? Already paid : Already claimed ) - } - {payResult && payResult.type === ConfirmPayResultType.Done && ( - -

    Payment complete

    -

    {!payResult.contractTerms.fulfillment_message ? - "You will now be sent back to the merchant you came from." : - payResult.contractTerms.fulfillment_message - }

    -
    - )} -
    - {payStatus.status !== PreparePayResultType.InsufficientBalance && Amounts.isNonZero(totalFees) && - - } - - {Amounts.isNonZero(totalFees) && - - - } - - - {contractTerms.order_id && } -
    - + return ( + + - +

    {i18n.str`Digital cash payment`}

    + {payStatus.status === PreparePayResultType.AlreadyConfirmed && + (payStatus.paid ? ( + Already paid + ) : ( + Already claimed + ))} + {payResult && payResult.type === ConfirmPayResultType.Done && ( + +

    Payment complete

    +

    + {!payResult.contractTerms.fulfillment_message + ? "You will now be sent back to the merchant you came from." + : payResult.contractTerms.fulfillment_message} +

    +
    + )} +
    + {payStatus.status !== PreparePayResultType.InsufficientBalance && + Amounts.isNonZero(totalFees) && ( + + )} + + {Amounts.isNonZero(totalFees) && ( + + + + )} + + + {contractTerms.order_id && ( + + )} +
    + +
    + ); } function amountToString(text: AmountLike) { - const aj = Amounts.jsonifyAmount(text) - const amount = Amounts.stringifyValue(aj, 2) - return `${amount} ${aj.currency}` + const aj = Amounts.jsonifyAmount(text); + const amount = Amounts.stringifyValue(aj, 2); + return `${amount} ${aj.currency}`; } diff --git a/packages/taler-wallet-webextension/src/cta/Refund.stories.tsx b/packages/taler-wallet-webextension/src/cta/Refund.stories.tsx index 88e714cb7..a0abcea58 100644 --- a/packages/taler-wallet-webextension/src/cta/Refund.stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Refund.stories.tsx @@ -15,63 +15,61 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { OrderShortInfo } from '@gnu-taler/taler-util'; -import { createExample } from '../test-utils'; -import { View as TestedComponent } from './Refund'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { OrderShortInfo } from "@gnu-taler/taler-util"; +import { createExample } from "../test-utils"; +import { View as TestedComponent } from "./Refund"; export default { - title: 'cta/refund', + title: "cta/refund", component: TestedComponent, - argTypes: { - }, + argTypes: {}, }; 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 as any, + amountEffectivePaid: "USD:10", + amountRefundGone: "USD:0", + amountRefundGranted: "USD:2", + contractTermsHash: "QWEASDZXC", + info: ({ + summary: "tasty cold beer", + contractTermsHash: "QWEASDZXC", + } as Partial) 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 as any, + amountEffectivePaid: "USD:10", + amountRefundGone: "USD:1", + amountRefundGranted: "USD:2", + contractTermsHash: "QWEASDZXC", + info: ({ + summary: "tasty cold beer", + contractTermsHash: "QWEASDZXC", + } as Partial) 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 as any, + amountEffectivePaid: "USD:10", + amountRefundGone: "USD:1", + amountRefundGranted: "USD:2", + contractTermsHash: "QWEASDZXC", + info: ({ + summary: "tasty cold beer", + contractTermsHash: "QWEASDZXC", + } as Partial) as any, pendingAtExchange: true, proposalId: "proposal123", - } + }, }); diff --git a/packages/taler-wallet-webextension/src/cta/Refund.tsx b/packages/taler-wallet-webextension/src/cta/Refund.tsx index 943095360..aa11dca6a 100644 --- a/packages/taler-wallet-webextension/src/cta/Refund.tsx +++ b/packages/taler-wallet-webextension/src/cta/Refund.tsx @@ -22,45 +22,46 @@ import * as wxApi from "../wxApi"; import { AmountView } from "../renderHtml"; -import { - ApplyRefundResponse, - Amounts, -} from "@gnu-taler/taler-util"; +import { ApplyRefundResponse, Amounts } from "@gnu-taler/taler-util"; import { useEffect, useState } from "preact/hooks"; import { JSX } from "preact/jsx-runtime"; -import { h } from 'preact'; +import { h } from "preact"; interface Props { - talerRefundUri?: string + talerRefundUri?: string; } export interface ViewProps { applyResult: ApplyRefundResponse; } export function View({ applyResult }: ViewProps) { - return
    -

    GNU Taler Wallet

    -
    -

    Refund Status

    -

    - The product {applyResult.info.summary} has received a total - effective refund of{" "} - . -

    - {applyResult.pendingAtExchange ? ( -

    Refund processing is still in progress.

    - ) : null} - {!Amounts.isZero(applyResult.amountRefundGone) ? ( + return ( +
    +

    GNU Taler Wallet

    +
    +

    Refund Status

    - The refund amount of{" "} - {" "} - could not be applied. + The product {applyResult.info.summary} has received a total + effective refund of{" "} + .

    - ) : null} -
    -
    + {applyResult.pendingAtExchange ? ( +

    Refund processing is still in progress.

    + ) : null} + {!Amounts.isZero(applyResult.amountRefundGone) ? ( +

    + The refund amount of{" "} + could not be + applied. +

    + ) : null} +
    +
    + ); } export function RefundPage({ talerRefundUri }: Props): JSX.Element { - const [applyResult, setApplyResult] = useState(undefined); + const [applyResult, setApplyResult] = useState< + ApplyRefundResponse | undefined + >(undefined); const [errMsg, setErrMsg] = useState(undefined); useEffect(() => { diff --git a/packages/taler-wallet-webextension/src/cta/Tip.stories.tsx b/packages/taler-wallet-webextension/src/cta/Tip.stories.tsx index 389b183f0..8da599513 100644 --- a/packages/taler-wallet-webextension/src/cta/Tip.stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Tip.stories.tsx @@ -15,45 +15,43 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { createExample } from '../test-utils'; -import { View as TestedComponent } from './Tip'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { createExample } from "../test-utils"; +import { View as TestedComponent } from "./Tip"; export default { - title: 'cta/tip', + title: "cta/tip", component: TestedComponent, - argTypes: { - }, + argTypes: {}, }; export const Accepted = createExample(TestedComponent, { prepareTipResult: { accepted: true, - merchantBaseUrl: '', - exchangeBaseUrl: '', - expirationTimestamp : { - t_ms: 0 + merchantBaseUrl: "", + exchangeBaseUrl: "", + expirationTimestamp: { + t_ms: 0, }, - tipAmountEffective: 'USD:10', - tipAmountRaw: 'USD:5', - walletTipId: 'id' - } + 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 + merchantBaseUrl: "http://merchant.url/", + exchangeBaseUrl: "http://exchange.url/", + expirationTimestamp: { + t_ms: 0, }, - tipAmountEffective: 'USD:10', - tipAmountRaw: 'USD:5', - walletTipId: 'id' - } + tipAmountEffective: "USD:10", + tipAmountRaw: "USD:5", + walletTipId: "id", + }, }); diff --git a/packages/taler-wallet-webextension/src/cta/Tip.tsx b/packages/taler-wallet-webextension/src/cta/Tip.tsx index dc1feaed3..0a1c1238c 100644 --- a/packages/taler-wallet-webextension/src/cta/Tip.tsx +++ b/packages/taler-wallet-webextension/src/cta/Tip.tsx @@ -25,43 +25,43 @@ import { PrepareTipResult } from "@gnu-taler/taler-util"; import { AmountView } from "../renderHtml"; import * as wxApi from "../wxApi"; import { JSX } from "preact/jsx-runtime"; -import { h } from 'preact'; +import { h } from "preact"; interface Props { - talerTipUri?: string + talerTipUri?: string; } export interface ViewProps { prepareTipResult: PrepareTipResult; onAccept: () => void; onIgnore: () => void; - } export function View({ prepareTipResult, onAccept, onIgnore }: ViewProps) { - return
    -

    GNU Taler Wallet

    -
    - {prepareTipResult.accepted ? ( - - Tip from {prepareTipResult.merchantBaseUrl} accepted. Check - your transactions list for more details. - - ) : ( + return ( +
    +

    GNU Taler Wallet

    +
    + {prepareTipResult.accepted ? ( + + Tip from {prepareTipResult.merchantBaseUrl} accepted. + Check your transactions list for more details. + + ) : (

    The merchant {prepareTipResult.merchantBaseUrl} is - offering you a tip of{" "} + offering you a tip of{" "} {" "} - via the exchange {prepareTipResult.exchangeBaseUrl} + via the exchange {prepareTipResult.exchangeBaseUrl}

    )} -
    -
    - +
    +
    + ); } export function TipPage({ talerTipUri }: Props): JSX.Element { @@ -105,7 +105,11 @@ export function TipPage({ talerTipUri }: Props): JSX.Element { return Loading ...; } - return + return ( + + ); } diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx index 5e29a3e39..90df2a27e 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw.stories.tsx @@ -15,23 +15,22 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ - -import { amountFractionalBase, Amounts } from '@gnu-taler/taler-util'; -import { ExchangeRecord } from '@gnu-taler/taler-wallet-core'; -import { ExchangeWithdrawDetails } from '@gnu-taler/taler-wallet-core/src/operations/withdraw'; -import { getMaxListeners } from 'process'; -import { createExample } from '../test-utils'; -import { View as TestedComponent } from './Withdraw'; + * + * @author Sebastian Javier Marchano (sebasjm) + */ +import { amountFractionalBase, Amounts } from "@gnu-taler/taler-util"; +import { ExchangeRecord } from "@gnu-taler/taler-wallet-core"; +import { ExchangeWithdrawDetails } from "@gnu-taler/taler-wallet-core/src/operations/withdraw"; +import { getMaxListeners } from "process"; +import { createExample } from "../test-utils"; +import { View as TestedComponent } from "./Withdraw"; export default { - title: 'cta/withdraw', + title: "cta/withdraw", component: TestedComponent, argTypes: { - onSwitchExchange: { action: 'onRetry' }, + onSwitchExchange: { action: "onRetry" }, }, }; @@ -48,7 +47,7 @@ const termsHtml = `
    -` +`; const termsPlain = ` Terms Of Service **************** @@ -432,7 +431,7 @@ Questions or comments We welcome comments, questions, concerns, or suggestions. Please send us a message on our contact page at legal@taler-systems.com. -` +`; const termsXml = ` @@ -781,120 +780,131 @@ const termsXml = ` `; export const NewTerms = createExample(TestedComponent, { - knownExchanges: [{ - currency: 'USD', - exchangeBaseUrl: 'exchange.demo.taler.net', - paytoUris: ['asd'], - }, { - currency: 'USD', - exchangeBaseUrl: 'exchange.test.taler.net', - paytoUris: ['asd'], - }], - exchangeBaseUrl: 'exchange.demo.taler.net', + knownExchanges: [ + { + currency: "USD", + exchangeBaseUrl: "exchange.demo.taler.net", + paytoUris: ["asd"], + }, + { + currency: "USD", + exchangeBaseUrl: "exchange.test.taler.net", + paytoUris: ["asd"], + }, + ], + exchangeBaseUrl: "exchange.demo.taler.net", details: { - content: '', - contentType: '', - currentEtag: '', + content: "", + contentType: "", + currentEtag: "", acceptedEtag: undefined, }, withdrawalFee: { - currency: 'USD', + currency: "USD", fraction: 0, - value: 0 + value: 0, }, amount: { - currency: 'USD', + currency: "USD", value: 2, - fraction: 10000000 + fraction: 10000000, }, - onSwitchExchange: async () => { }, + onSwitchExchange: async () => {}, terms: { value: { - type: 'xml', + type: "xml", document: new DOMParser().parseFromString(termsXml, "text/xml"), }, - status: 'new' + status: "new", }, -}) +}); export const TermsReviewingPLAIN = createExample(TestedComponent, { - knownExchanges: [{ - currency: 'USD', - exchangeBaseUrl: 'exchange.demo.taler.net', - paytoUris: ['asd'], - }, { - currency: 'USD', - exchangeBaseUrl: 'exchange.test.taler.net', - paytoUris: ['asd'], - }], - exchangeBaseUrl: 'exchange.demo.taler.net', + knownExchanges: [ + { + currency: "USD", + exchangeBaseUrl: "exchange.demo.taler.net", + paytoUris: ["asd"], + }, + { + currency: "USD", + exchangeBaseUrl: "exchange.test.taler.net", + paytoUris: ["asd"], + }, + ], + exchangeBaseUrl: "exchange.demo.taler.net", details: { - content: '', - contentType: '', - currentEtag: '', + content: "", + contentType: "", + currentEtag: "", acceptedEtag: undefined, }, withdrawalFee: { - currency: 'USD', + currency: "USD", fraction: 0, - value: 0 + value: 0, }, amount: { - currency: 'USD', + currency: "USD", value: 2, - fraction: 10000000 + fraction: 10000000, }, - onSwitchExchange: async () => { }, + onSwitchExchange: async () => {}, terms: { value: { - type: 'plain', - content: termsPlain + type: "plain", + content: termsPlain, }, - status: 'new' + status: "new", }, - reviewing: true -}) + reviewing: true, +}); export const TermsReviewingHTML = createExample(TestedComponent, { - knownExchanges: [{ - currency: 'USD', - exchangeBaseUrl: 'exchange.demo.taler.net', - paytoUris: ['asd'], - }, { - currency: 'USD', - exchangeBaseUrl: 'exchange.test.taler.net', - paytoUris: ['asd'], - }], - exchangeBaseUrl: 'exchange.demo.taler.net', + knownExchanges: [ + { + currency: "USD", + exchangeBaseUrl: "exchange.demo.taler.net", + paytoUris: ["asd"], + }, + { + currency: "USD", + exchangeBaseUrl: "exchange.test.taler.net", + paytoUris: ["asd"], + }, + ], + exchangeBaseUrl: "exchange.demo.taler.net", details: { - content: '', - contentType: '', - currentEtag: '', + content: "", + contentType: "", + currentEtag: "", acceptedEtag: undefined, }, withdrawalFee: { - currency: 'USD', + currency: "USD", fraction: 0, - value: 0 + value: 0, }, amount: { - currency: 'USD', + currency: "USD", value: 2, - fraction: 10000000 + fraction: 10000000, }, - onSwitchExchange: async () => { }, + onSwitchExchange: async () => {}, terms: { value: { - type: 'html', - href: new URL(`data:text/html;base64,${Buffer.from(termsHtml).toString('base64')}`), + type: "html", + href: new URL( + `data:text/html;base64,${Buffer.from(termsHtml).toString("base64")}`, + ), }, - status: 'new' + status: "new", }, - reviewing: true -}) + reviewing: true, +}); const termsPdf = ` %PDF-1.2 @@ -909,306 +919,330 @@ endobj trailer << /Root 3 0 R >> %%EOF -` +`; export const TermsReviewingPDF = createExample(TestedComponent, { - knownExchanges: [{ - currency: 'USD', - exchangeBaseUrl: 'exchange.demo.taler.net', - paytoUris: ['asd'], - }, { - currency: 'USD', - exchangeBaseUrl: 'exchange.test.taler.net', - paytoUris: ['asd'], - }], - exchangeBaseUrl: 'exchange.demo.taler.net', + knownExchanges: [ + { + currency: "USD", + exchangeBaseUrl: "exchange.demo.taler.net", + paytoUris: ["asd"], + }, + { + currency: "USD", + exchangeBaseUrl: "exchange.test.taler.net", + paytoUris: ["asd"], + }, + ], + exchangeBaseUrl: "exchange.demo.taler.net", details: { - content: '', - contentType: '', - currentEtag: '', + content: "", + contentType: "", + currentEtag: "", acceptedEtag: undefined, }, withdrawalFee: { - currency: 'USD', + currency: "USD", fraction: 0, - value: 0 + value: 0, }, amount: { - currency: 'USD', + currency: "USD", value: 2, - fraction: 10000000 + fraction: 10000000, }, - onSwitchExchange: async () => { }, + onSwitchExchange: async () => {}, terms: { value: { - type: 'pdf', - location: new URL(`data:text/html;base64,${Buffer.from(termsPdf).toString('base64')}`), + type: "pdf", + location: new URL( + `data:text/html;base64,${Buffer.from(termsPdf).toString("base64")}`, + ), }, - status: 'new' + status: "new", }, - reviewing: true -}) - + reviewing: true, +}); export const TermsReviewingXML = createExample(TestedComponent, { - knownExchanges: [{ - currency: 'USD', - exchangeBaseUrl: 'exchange.demo.taler.net', - paytoUris: ['asd'], - }, { - currency: 'USD', - exchangeBaseUrl: 'exchange.test.taler.net', - paytoUris: ['asd'], - }], - exchangeBaseUrl: 'exchange.demo.taler.net', + knownExchanges: [ + { + currency: "USD", + exchangeBaseUrl: "exchange.demo.taler.net", + paytoUris: ["asd"], + }, + { + currency: "USD", + exchangeBaseUrl: "exchange.test.taler.net", + paytoUris: ["asd"], + }, + ], + exchangeBaseUrl: "exchange.demo.taler.net", details: { - content: '', - contentType: '', - currentEtag: '', + content: "", + contentType: "", + currentEtag: "", acceptedEtag: undefined, }, withdrawalFee: { - currency: 'USD', + currency: "USD", fraction: 0, - value: 0 + value: 0, }, amount: { - currency: 'USD', + currency: "USD", value: 2, - fraction: 10000000 + fraction: 10000000, }, - onSwitchExchange: async () => { }, + onSwitchExchange: async () => {}, terms: { value: { - type: 'xml', + type: "xml", document: new DOMParser().parseFromString(termsXml, "text/xml"), }, - status: 'new' + status: "new", }, - reviewing: true -}) + reviewing: true, +}); export const NewTermsAccepted = createExample(TestedComponent, { - knownExchanges: [{ - currency: 'USD', - exchangeBaseUrl: 'exchange.demo.taler.net', - paytoUris: ['asd'], - }, { - currency: 'USD', - exchangeBaseUrl: 'exchange.test.taler.net', - paytoUris: ['asd'], - }], - exchangeBaseUrl: 'exchange.demo.taler.net', + knownExchanges: [ + { + currency: "USD", + exchangeBaseUrl: "exchange.demo.taler.net", + paytoUris: ["asd"], + }, + { + currency: "USD", + exchangeBaseUrl: "exchange.test.taler.net", + paytoUris: ["asd"], + }, + ], + exchangeBaseUrl: "exchange.demo.taler.net", details: { - content: '', - contentType: '', - currentEtag: '', + content: "", + contentType: "", + currentEtag: "", acceptedEtag: undefined, }, withdrawalFee: { - currency: 'USD', + currency: "USD", fraction: 0, - value: 0 + value: 0, }, amount: { - currency: 'USD', + currency: "USD", value: 2, - fraction: 10000000 + fraction: 10000000, }, - onSwitchExchange: async () => { }, + onSwitchExchange: async () => {}, terms: { value: { - type: 'xml', + type: "xml", document: new DOMParser().parseFromString(termsXml, "text/xml"), }, - status: 'new' + status: "new", }, - reviewed: true -}) + reviewed: true, +}); export const TermsShowAgainXML = createExample(TestedComponent, { - knownExchanges: [{ - currency: 'USD', - exchangeBaseUrl: 'exchange.demo.taler.net', - paytoUris: ['asd'], - }, { - currency: 'USD', - exchangeBaseUrl: 'exchange.test.taler.net', - paytoUris: ['asd'], - }], - exchangeBaseUrl: 'exchange.demo.taler.net', + knownExchanges: [ + { + currency: "USD", + exchangeBaseUrl: "exchange.demo.taler.net", + paytoUris: ["asd"], + }, + { + currency: "USD", + exchangeBaseUrl: "exchange.test.taler.net", + paytoUris: ["asd"], + }, + ], + exchangeBaseUrl: "exchange.demo.taler.net", details: { - content: '', - contentType: '', - currentEtag: '', + content: "", + contentType: "", + currentEtag: "", acceptedEtag: undefined, }, withdrawalFee: { - currency: 'USD', + currency: "USD", fraction: 0, - value: 0 + value: 0, }, amount: { - currency: 'USD', + currency: "USD", value: 2, - fraction: 10000000 + fraction: 10000000, }, - onSwitchExchange: async () => { }, + onSwitchExchange: async () => {}, terms: { value: { - type: 'xml', + type: "xml", document: new DOMParser().parseFromString(termsXml, "text/xml"), }, - status: 'new' + status: "new", }, reviewed: true, reviewing: true, -}) +}); export const TermsChanged = createExample(TestedComponent, { - knownExchanges: [{ - currency: 'USD', - exchangeBaseUrl: 'exchange.demo.taler.net', - paytoUris: ['asd'], - }, { - currency: 'USD', - exchangeBaseUrl: 'exchange.test.taler.net', - paytoUris: ['asd'], - }], - exchangeBaseUrl: 'exchange.demo.taler.net', + knownExchanges: [ + { + currency: "USD", + exchangeBaseUrl: "exchange.demo.taler.net", + paytoUris: ["asd"], + }, + { + currency: "USD", + exchangeBaseUrl: "exchange.test.taler.net", + paytoUris: ["asd"], + }, + ], + exchangeBaseUrl: "exchange.demo.taler.net", details: { - content: '', - contentType: '', - currentEtag: '', + content: "", + contentType: "", + currentEtag: "", acceptedEtag: undefined, }, withdrawalFee: { - currency: 'USD', + currency: "USD", fraction: 0, - value: 0 + value: 0, }, amount: { - currency: 'USD', + currency: "USD", value: 2, - fraction: 10000000 + fraction: 10000000, }, - onSwitchExchange: async () => { }, + onSwitchExchange: async () => {}, terms: { value: { - type: 'xml', + type: "xml", document: new DOMParser().parseFromString(termsXml, "text/xml"), }, - status: 'changed' + status: "changed", }, -}) +}); export const TermsNotFound = createExample(TestedComponent, { - knownExchanges: [{ - currency: 'USD', - exchangeBaseUrl: 'exchange.demo.taler.net', - paytoUris: ['asd'], - }, { - currency: 'USD', - exchangeBaseUrl: 'exchange.test.taler.net', - paytoUris: ['asd'], - }], - exchangeBaseUrl: 'exchange.demo.taler.net', + knownExchanges: [ + { + currency: "USD", + exchangeBaseUrl: "exchange.demo.taler.net", + paytoUris: ["asd"], + }, + { + currency: "USD", + exchangeBaseUrl: "exchange.test.taler.net", + paytoUris: ["asd"], + }, + ], + exchangeBaseUrl: "exchange.demo.taler.net", details: { - content: '', - contentType: '', - currentEtag: '', + content: "", + contentType: "", + currentEtag: "", acceptedEtag: undefined, }, withdrawalFee: { - currency: 'USD', + currency: "USD", fraction: 0, - value: 0 + value: 0, }, amount: { - currency: 'USD', + currency: "USD", value: 2, - fraction: 10000000 + fraction: 10000000, }, - onSwitchExchange: async () => { }, + onSwitchExchange: async () => {}, terms: { - status: 'notfound' + status: "notfound", }, -}) +}); export const TermsAlreadyAccepted = createExample(TestedComponent, { - knownExchanges: [{ - currency: 'USD', - exchangeBaseUrl: 'exchange.demo.taler.net', - paytoUris: ['asd'], - }, { - currency: 'USD', - exchangeBaseUrl: 'exchange.test.taler.net', - paytoUris: ['asd'], - }], - exchangeBaseUrl: 'exchange.demo.taler.net', + knownExchanges: [ + { + currency: "USD", + exchangeBaseUrl: "exchange.demo.taler.net", + paytoUris: ["asd"], + }, + { + currency: "USD", + exchangeBaseUrl: "exchange.test.taler.net", + paytoUris: ["asd"], + }, + ], + exchangeBaseUrl: "exchange.demo.taler.net", details: { - content: '', - contentType: '', - currentEtag: '', + content: "", + contentType: "", + currentEtag: "", acceptedEtag: undefined, }, withdrawalFee: { - currency: 'USD', + currency: "USD", fraction: amountFractionalBase * 0.5, - value: 0 + value: 0, }, amount: { - currency: 'USD', + currency: "USD", value: 2, - fraction: 10000000 + fraction: 10000000, }, - onSwitchExchange: async () => { }, + onSwitchExchange: async () => {}, terms: { - status: 'accepted' + status: "accepted", }, -}) - +}); export const WithoutFee = createExample(TestedComponent, { - knownExchanges: [{ - currency: 'USD', - exchangeBaseUrl: 'exchange.demo.taler.net', - paytoUris: ['asd'], - }, { - currency: 'USD', - exchangeBaseUrl: 'exchange.test.taler.net', - paytoUris: ['asd'], - }], - exchangeBaseUrl: 'exchange.demo.taler.net', + knownExchanges: [ + { + currency: "USD", + exchangeBaseUrl: "exchange.demo.taler.net", + paytoUris: ["asd"], + }, + { + currency: "USD", + exchangeBaseUrl: "exchange.test.taler.net", + paytoUris: ["asd"], + }, + ], + exchangeBaseUrl: "exchange.demo.taler.net", details: { - content: '', - contentType: '', - currentEtag: '', + content: "", + contentType: "", + currentEtag: "", acceptedEtag: undefined, }, withdrawalFee: { - currency: 'USD', + currency: "USD", fraction: 0, value: 0, }, amount: { - currency: 'USD', + currency: "USD", value: 2, - fraction: 10000000 + fraction: 10000000, }, - onSwitchExchange: async () => { }, + onSwitchExchange: async () => {}, terms: { value: { - type: 'xml', + type: "xml", document: new DOMParser().parseFromString(termsXml, "text/xml"), }, - status: 'accepted', - } -}) \ No newline at end of file + status: "accepted", + }, +}); diff --git a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx index 6ef72cbe6..603dafcde 100644 --- a/packages/taler-wallet-webextension/src/cta/Withdraw.tsx +++ b/packages/taler-wallet-webextension/src/cta/Withdraw.tsx @@ -21,21 +21,39 @@ * @author Florian Dold */ -import { AmountJson, Amounts, ExchangeListItem, GetExchangeTosResult, i18n, WithdrawUriInfoResponse } from '@gnu-taler/taler-util'; -import { ExchangeWithdrawDetails } from '@gnu-taler/taler-wallet-core/src/operations/withdraw'; +import { + AmountJson, + Amounts, + ExchangeListItem, + GetExchangeTosResult, + i18n, + WithdrawUriInfoResponse, +} from "@gnu-taler/taler-util"; +import { VNode, h } from "preact"; import { useState } from "preact/hooks"; -import { Fragment } from 'preact/jsx-runtime'; -import { CheckboxOutlined } from '../components/CheckboxOutlined'; -import { ExchangeXmlTos } from '../components/ExchangeToS'; -import { LogoHeader } from '../components/LogoHeader'; -import { Part } from '../components/Part'; -import { SelectList } from '../components/SelectList'; -import { ButtonSuccess, ButtonWarning, LinkSuccess, LinkWarning, TermsOfService, WalletAction, WarningText } from '../components/styled'; -import { useAsyncAsHook } from '../hooks/useAsyncAsHook'; +import { Fragment } from "preact/jsx-runtime"; +import { CheckboxOutlined } from "../components/CheckboxOutlined"; +import { ExchangeXmlTos } from "../components/ExchangeToS"; +import { LogoHeader } from "../components/LogoHeader"; +import { Part } from "../components/Part"; +import { SelectList } from "../components/SelectList"; +import { + ButtonSuccess, + ButtonWarning, + LinkSuccess, + TermsOfService, + WalletAction, + WarningText, +} from "../components/styled"; +import { useAsyncAsHook } from "../hooks/useAsyncAsHook"; import { - acceptWithdrawal, getExchangeWithdrawalInfo, getWithdrawalDetailsForUri, setExchangeTosAccepted, listExchanges, getExchangeTos + acceptWithdrawal, + getExchangeTos, + getExchangeWithdrawalInfo, + getWithdrawalDetailsForUri, + listExchanges, + setExchangeTosAccepted, } from "../wxApi"; -import { wxMain } from '../wxBackend.js'; interface Props { talerWithdrawUri?: string; @@ -58,145 +76,193 @@ export interface ViewProps { status: TermsStatus; }; knownExchanges: ExchangeListItem[]; +} -}; - -type TermsStatus = 'new' | 'accepted' | 'changed' | 'notfound'; +type TermsStatus = "new" | "accepted" | "changed" | "notfound"; -type TermsDocument = TermsDocumentXml | TermsDocumentHtml | TermsDocumentPlain | TermsDocumentJson | TermsDocumentPdf; +type TermsDocument = + | TermsDocumentXml + | TermsDocumentHtml + | TermsDocumentPlain + | TermsDocumentJson + | TermsDocumentPdf; interface TermsDocumentXml { - type: 'xml', - document: Document, + type: "xml"; + document: Document; } interface TermsDocumentHtml { - type: 'html', - href: URL, + type: "html"; + href: URL; } interface TermsDocumentPlain { - type: 'plain', - content: string, + type: "plain"; + content: string; } interface TermsDocumentJson { - type: 'json', - data: any, + type: "json"; + data: any; } interface TermsDocumentPdf { - type: 'pdf', - location: URL, + type: "pdf"; + location: URL; } function amountToString(text: AmountJson) { - const aj = Amounts.jsonifyAmount(text) - const amount = Amounts.stringifyValue(aj) - return `${amount} ${aj.currency}` + const aj = Amounts.jsonifyAmount(text); + const amount = Amounts.stringifyValue(aj); + return `${amount} ${aj.currency}`; } -export function View({ details, withdrawalFee, exchangeBaseUrl, knownExchanges, amount, onWithdraw, onSwitchExchange, terms, reviewing, onReview, onAccept, reviewed, confirmed }: ViewProps) { - const needsReview = terms.status === 'changed' || terms.status === 'new' - - const [switchingExchange, setSwitchingExchange] = useState(undefined) - const exchanges = knownExchanges.reduce((prev, ex) => ({ ...prev, [ex.exchangeBaseUrl]: ex.exchangeBaseUrl }), {}) +export function View({ + details, + withdrawalFee, + exchangeBaseUrl, + knownExchanges, + amount, + onWithdraw, + onSwitchExchange, + terms, + reviewing, + onReview, + onAccept, + reviewed, + confirmed, +}: ViewProps) { + const needsReview = terms.status === "changed" || terms.status === "new"; + + const [switchingExchange, setSwitchingExchange] = useState< + string | undefined + >(undefined); + const exchanges = knownExchanges.reduce( + (prev, ex) => ({ ...prev, [ex.exchangeBaseUrl]: ex.exchangeBaseUrl }), + {}, + ); return ( -

    - {i18n.str`Digital cash withdrawal`} -

    +

    {i18n.str`Digital cash withdrawal`}

    - - - {Amounts.isNonZero(withdrawalFee) && - - } - + + + {Amounts.isNonZero(withdrawalFee) && ( + + )} +
    - {!reviewing && + {!reviewing && (
    - {switchingExchange !== undefined ? -
    - -
    - onSwitchExchange(switchingExchange)}> - {i18n.str`Confirm exchange selection`} - -
    - : setSwitchingExchange("")}> + {switchingExchange !== undefined ? ( + +
    + +
    + onSwitchExchange(switchingExchange)} + > + {i18n.str`Confirm exchange selection`} + +
    + ) : ( + setSwitchingExchange("")}> {i18n.str`Switch exchange`} - } - +
    + )}
    - } - {!reviewing && reviewed && + )} + {!reviewing && reviewed && (
    - onReview(true)} - > + onReview(true)}> {i18n.str`Show terms of service`}
    - } - {terms.status === 'notfound' && + )} + {terms.status === "notfound" && (
    {i18n.str`Exchange doesn't have terms of service`}
    - } - {reviewing && + )} + {reviewing && (
    - {terms.status !== 'accepted' && terms.value && terms.value.type === 'xml' && - - - - } - {terms.status !== 'accepted' && terms.value && terms.value.type === 'plain' && -
    -
    {terms.value.content}
    -
    - } - {terms.status !== 'accepted' && terms.value && terms.value.type === 'html' && -