diff options
author | Sebastian <sebasjm@gmail.com> | 2021-11-15 11:18:58 -0300 |
---|---|---|
committer | Sebastian <sebasjm@gmail.com> | 2021-11-15 11:18:58 -0300 |
commit | 1d4815c66c395f4fcc86c30e20f3d005e3cb9ff5 (patch) | |
tree | 99e8241a5eb5af4d752be93a460004bc0c6255aa /packages/taler-wallet-webextension/src/popup | |
parent | 9692f589c687a2ba39a705ca4238cf123f444c61 (diff) | |
download | wallet-core-1d4815c66c395f4fcc86c30e20f3d005e3cb9ff5.tar.xz |
prettier
Diffstat (limited to 'packages/taler-wallet-webextension/src/popup')
16 files changed, 1310 insertions, 958 deletions
diff --git a/packages/taler-wallet-webextension/src/popup/Backup.stories.tsx b/packages/taler-wallet-webextension/src/popup/Backup.stories.tsx index d256f6d98..232b0da73 100644 --- a/packages/taler-wallet-webextension/src/popup/Backup.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/Backup.stories.tsx @@ -15,179 +15,184 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { ProviderPaymentType } from '@gnu-taler/taler-wallet-core'; -import { addDays } from 'date-fns'; -import { BackupView as TestedComponent } from './BackupPage'; -import { createExample } from '../test-utils'; +import { ProviderPaymentType } from "@gnu-taler/taler-wallet-core"; +import { addDays } from "date-fns"; +import { BackupView as TestedComponent } from "./BackupPage"; +import { createExample } from "../test-utils"; export default { - title: 'popup/backup/list', + title: "popup/backup/list", component: TestedComponent, argTypes: { - onRetry: { action: 'onRetry' }, - onDelete: { action: 'onDelete' }, - onBack: { action: 'onBack' }, - } + onRetry: { action: "onRetry" }, + onDelete: { action: "onDelete" }, + onBack: { action: "onBack" }, + }, }; - export const LotOfProviders = createExample(TestedComponent, { - providers: [{ - "active": true, - name:'sync.demo', - "syncProviderBaseUrl": "http://sync.taler:9967/", - "lastSuccessfulBackupTimestamp": { - "t_ms": 1625063925078 - }, - "paymentProposalIds": [ - "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG" - ], - "paymentStatus": { - "type": ProviderPaymentType.Paid, - "paidUntil": { - "t_ms": 1656599921000 - } - }, - "terms": { - "annualFee": "ARS:1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - }, { - "active": true, - name:'sync.demo', - "syncProviderBaseUrl": "http://sync.taler:9967/", - "lastSuccessfulBackupTimestamp": { - "t_ms": 1625063925078 + providers: [ + { + active: true, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.taler:9967/", + lastSuccessfulBackupTimestamp: { + t_ms: 1625063925078, + }, + paymentProposalIds: [ + "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG", + ], + paymentStatus: { + type: ProviderPaymentType.Paid, + paidUntil: { + t_ms: 1656599921000, + }, + }, + terms: { + annualFee: "ARS:1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, }, - "paymentProposalIds": [ - "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG" - ], - "paymentStatus": { - "type": ProviderPaymentType.Paid, - "paidUntil": { - "t_ms": addDays(new Date(), 13).getTime() - } + { + active: true, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.taler:9967/", + lastSuccessfulBackupTimestamp: { + t_ms: 1625063925078, + }, + paymentProposalIds: [ + "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG", + ], + paymentStatus: { + type: ProviderPaymentType.Paid, + paidUntil: { + t_ms: addDays(new Date(), 13).getTime(), + }, + }, + terms: { + annualFee: "ARS:1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, }, - "terms": { - "annualFee": "ARS:1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - }, { - "active": false, - name:'sync.demo', - "syncProviderBaseUrl": "http://sync.demo.taler.net/", - "paymentProposalIds": [], - "paymentStatus": { - "type": ProviderPaymentType.Pending, + { + active: false, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.demo.taler.net/", + paymentProposalIds: [], + paymentStatus: { + type: ProviderPaymentType.Pending, + }, + terms: { + annualFee: "KUDOS:0.1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, }, - "terms": { - "annualFee": "KUDOS:0.1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - }, { - "active": false, - name:'sync.demo', - "syncProviderBaseUrl": "http://sync.demo.taler.net/", - "paymentProposalIds": [], - "paymentStatus": { - "type": ProviderPaymentType.InsufficientBalance, + { + active: false, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.demo.taler.net/", + paymentProposalIds: [], + paymentStatus: { + type: ProviderPaymentType.InsufficientBalance, + }, + terms: { + annualFee: "KUDOS:0.1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, }, - "terms": { - "annualFee": "KUDOS:0.1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - }, { - "active": false, - name:'sync.demo', - "syncProviderBaseUrl": "http://sync.demo.taler.net/", - "paymentProposalIds": [], - "paymentStatus": { - "type": ProviderPaymentType.TermsChanged, - newTerms: { - annualFee: 'USD:2', - storageLimitInMegabytes: 8, - supportedProtocolVersion: '2', - }, - oldTerms: { - annualFee: 'USD:1', + { + active: false, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.demo.taler.net/", + paymentProposalIds: [], + paymentStatus: { + type: ProviderPaymentType.TermsChanged, + newTerms: { + annualFee: "USD:2", + storageLimitInMegabytes: 8, + supportedProtocolVersion: "2", + }, + oldTerms: { + annualFee: "USD:1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "1", + }, + paidUntil: { + t_ms: "never", + }, + }, + terms: { + annualFee: "KUDOS:0.1", storageLimitInMegabytes: 16, - supportedProtocolVersion: '1', - + supportedProtocolVersion: "0.0", }, - paidUntil: { - t_ms: 'never' - } }, - "terms": { - "annualFee": "KUDOS:0.1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - }, { - "active": false, - name:'sync.demo', - "syncProviderBaseUrl": "http://sync.demo.taler.net/", - "paymentProposalIds": [], - "paymentStatus": { - "type": ProviderPaymentType.Unpaid, + { + active: false, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.demo.taler.net/", + paymentProposalIds: [], + paymentStatus: { + type: ProviderPaymentType.Unpaid, + }, + terms: { + annualFee: "KUDOS:0.1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, }, - "terms": { - "annualFee": "KUDOS:0.1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - }, { - "active": false, - name:'sync.demo', - "syncProviderBaseUrl": "http://sync.demo.taler.net/", - "paymentProposalIds": [], - "paymentStatus": { - "type": ProviderPaymentType.Unpaid, + { + active: false, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.demo.taler.net/", + paymentProposalIds: [], + paymentStatus: { + type: ProviderPaymentType.Unpaid, + }, + terms: { + annualFee: "KUDOS:0.1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, }, - "terms": { - "annualFee": "KUDOS:0.1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - }] + ], }); - export const OneProvider = createExample(TestedComponent, { - providers: [{ - "active": true, - name:'sync.demo', - "syncProviderBaseUrl": "http://sync.taler:9967/", - "lastSuccessfulBackupTimestamp": { - "t_ms": 1625063925078 - }, - "paymentProposalIds": [ - "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG" - ], - "paymentStatus": { - "type": ProviderPaymentType.Paid, - "paidUntil": { - "t_ms": 1656599921000 - } + providers: [ + { + active: true, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.taler:9967/", + lastSuccessfulBackupTimestamp: { + t_ms: 1625063925078, + }, + paymentProposalIds: [ + "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG", + ], + paymentStatus: { + type: ProviderPaymentType.Paid, + paidUntil: { + t_ms: 1656599921000, + }, + }, + terms: { + annualFee: "ARS:1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, }, - "terms": { - "annualFee": "ARS:1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - }] + ], }); - export const Empty = createExample(TestedComponent, { - providers: [] + providers: [], }); - diff --git a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx b/packages/taler-wallet-webextension/src/popup/BackupPage.tsx index dcc5e5313..894c8a791 100644 --- a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx +++ b/packages/taler-wallet-webextension/src/popup/BackupPage.tsx @@ -14,15 +14,28 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ - import { i18n, Timestamp } from "@gnu-taler/taler-util"; -import { ProviderInfo, ProviderPaymentStatus } from "@gnu-taler/taler-wallet-core"; -import { differenceInMonths, formatDuration, intervalToDuration } from "date-fns"; +import { + ProviderInfo, + ProviderPaymentStatus, +} from "@gnu-taler/taler-wallet-core"; +import { + differenceInMonths, + formatDuration, + intervalToDuration, +} from "date-fns"; import { Fragment, JSX, VNode, h } from "preact"; import { - BoldLight, ButtonPrimary, ButtonSuccess, Centered, - CenteredText, CenteredBoldText, PopupBox, RowBorderGray, - SmallText, SmallLightText + BoldLight, + ButtonPrimary, + ButtonSuccess, + Centered, + CenteredText, + CenteredBoldText, + PopupBox, + RowBorderGray, + SmallText, + SmallLightText, } from "../components/styled"; import { useBackupStatus } from "../hooks/useBackupStatus"; import { Pages } from "../NavigationBar"; @@ -32,49 +45,68 @@ interface Props { } export function BackupPage({ onAddProvider }: Props): VNode { - const status = useBackupStatus() + const status = useBackupStatus(); if (!status) { - return <div>Loading...</div> + return <div>Loading...</div>; } - return <BackupView providers={status.providers} onAddProvider={onAddProvider} onSyncAll={status.sync} />; + return ( + <BackupView + providers={status.providers} + onAddProvider={onAddProvider} + onSyncAll={status.sync} + /> + ); } export interface ViewProps { - providers: ProviderInfo[], + providers: ProviderInfo[]; onAddProvider: () => void; onSyncAll: () => Promise<void>; } -export function BackupView({ providers, onAddProvider, onSyncAll }: ViewProps): VNode { +export function BackupView({ + providers, + onAddProvider, + onSyncAll, +}: ViewProps): VNode { return ( <PopupBox> <section> - {providers.map((provider) => <BackupLayout - status={provider.paymentStatus} - timestamp={provider.lastSuccessfulBackupTimestamp} - id={provider.syncProviderBaseUrl} - active={provider.active} - title={provider.name} - /> + {providers.map((provider) => ( + <BackupLayout + status={provider.paymentStatus} + timestamp={provider.lastSuccessfulBackupTimestamp} + id={provider.syncProviderBaseUrl} + active={provider.active} + title={provider.name} + /> + ))} + {!providers.length && ( + <Centered style={{ marginTop: 100 }}> + <BoldLight>No backup providers configured</BoldLight> + <ButtonSuccess onClick={onAddProvider}> + <i18n.Translate>Add provider</i18n.Translate> + </ButtonSuccess> + </Centered> )} - {!providers.length && <Centered style={{marginTop: 100}}> - <BoldLight>No backup providers configured</BoldLight> - <ButtonSuccess onClick={onAddProvider}><i18n.Translate>Add provider</i18n.Translate></ButtonSuccess> - </Centered>} </section> - {!!providers.length && <footer> - <div /> - <div> - <ButtonPrimary onClick={onSyncAll}>{ - providers.length > 1 ? - <i18n.Translate>Sync all backups</i18n.Translate> : - <i18n.Translate>Sync now</i18n.Translate> - }</ButtonPrimary> - <ButtonSuccess onClick={onAddProvider}>Add provider</ButtonSuccess> - </div> - </footer>} + {!!providers.length && ( + <footer> + <div /> + <div> + <ButtonPrimary onClick={onSyncAll}> + {providers.length > 1 ? ( + <i18n.Translate>Sync all backups</i18n.Translate> + ) : ( + <i18n.Translate>Sync now</i18n.Translate> + )} + </ButtonPrimary> + <ButtonSuccess onClick={onAddProvider}>Add provider</ButtonSuccess> + </div> + </footer> + )} </PopupBox> - ) + ); } interface TransactionLayoutProps { @@ -92,55 +124,73 @@ function BackupLayout(props: TransactionLayoutProps): JSX.Element { timeStyle: "short", } as any); - return ( <RowBorderGray> <div style={{ color: !props.active ? "grey" : undefined }}> - <a href={Pages.provider_detail.replace(':pid', encodeURIComponent(props.id))}><span>{props.title}</span></a> - - {dateStr && <SmallText style={{marginTop: 5}}>Last synced: {dateStr}</SmallText>} - {!dateStr && <SmallLightText style={{marginTop: 5}}>Not synced</SmallLightText>} + <a + href={Pages.provider_detail.replace( + ":pid", + encodeURIComponent(props.id), + )} + > + <span>{props.title}</span> + </a> + + {dateStr && ( + <SmallText style={{ marginTop: 5 }}>Last synced: {dateStr}</SmallText> + )} + {!dateStr && ( + <SmallLightText style={{ marginTop: 5 }}>Not synced</SmallLightText> + )} </div> <div> - {props.status?.type === 'paid' ? - <ExpirationText until={props.status.paidUntil} /> : + {props.status?.type === "paid" ? ( + <ExpirationText until={props.status.paidUntil} /> + ) : ( <div>{props.status.type}</div> - } + )} </div> </RowBorderGray> ); } function ExpirationText({ until }: { until: Timestamp }) { - return <Fragment> - <CenteredText> Expires in </CenteredText> - <CenteredBoldText {...({ color: colorByTimeToExpire(until) })}> {daysUntil(until)} </CenteredBoldText> - </Fragment> + return ( + <Fragment> + <CenteredText> Expires in </CenteredText> + <CenteredBoldText {...{ color: colorByTimeToExpire(until) }}> + {" "} + {daysUntil(until)}{" "} + </CenteredBoldText> + </Fragment> + ); } function colorByTimeToExpire(d: Timestamp) { - if (d.t_ms === 'never') return 'rgb(28, 184, 65)' - const months = differenceInMonths(d.t_ms, new Date()) - return months > 1 ? 'rgb(28, 184, 65)' : 'rgb(223, 117, 20)'; + if (d.t_ms === "never") return "rgb(28, 184, 65)"; + const months = differenceInMonths(d.t_ms, new Date()); + return months > 1 ? "rgb(28, 184, 65)" : "rgb(223, 117, 20)"; } function daysUntil(d: Timestamp) { - if (d.t_ms === 'never') return undefined + if (d.t_ms === "never") return undefined; const duration = intervalToDuration({ start: d.t_ms, end: new Date(), - }) + }); const str = formatDuration(duration, { - delimiter: ', ', + delimiter: ", ", format: [ - duration?.years ? 'years' : ( - duration?.months ? 'months' : ( - duration?.days ? 'days' : ( - duration.hours ? 'hours' : 'minutes' - ) - ) - ) - ] - }) - return `${str}` -}
\ No newline at end of file + duration?.years + ? "years" + : duration?.months + ? "months" + : duration?.days + ? "days" + : duration.hours + ? "hours" + : "minutes", + ], + }); + return `${str}`; +} diff --git a/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx b/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx index 382f9b549..80203f6d3 100644 --- a/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/Balance.stories.tsx @@ -15,28 +15,25 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { createExample, NullLink } from '../test-utils'; -import { BalanceView as TestedComponent } from './BalancePage'; +import { createExample, NullLink } from "../test-utils"; +import { BalanceView as TestedComponent } from "./BalancePage"; export default { - title: 'popup/balance', + title: "popup/balance", component: TestedComponent, - argTypes: { - } + argTypes: {}, }; - -export const NotYetLoaded = createExample(TestedComponent, { -}); +export const NotYetLoaded = createExample(TestedComponent, {}); export const GotError = createExample(TestedComponent, { balance: { hasError: true, - message: 'Network error' + message: "Network error", }, Linker: NullLink, }); @@ -45,7 +42,7 @@ export const EmptyBalance = createExample(TestedComponent, { balance: { hasError: false, response: { - balances: [] + balances: [], }, }, Linker: NullLink, @@ -55,13 +52,15 @@ export const SomeCoins = createExample(TestedComponent, { balance: { hasError: false, response: { - balances: [{ - available: 'USD:10.5', - hasPendingTransactions: false, - pendingIncoming: 'USD:0', - pendingOutgoing: 'USD:0', - requiresUserInput: false - }] + balances: [ + { + available: "USD:10.5", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, + }, + ], }, }, Linker: NullLink, @@ -71,13 +70,15 @@ export const SomeCoinsAndIncomingMoney = createExample(TestedComponent, { balance: { hasError: false, response: { - balances: [{ - available: 'USD:2.23', - hasPendingTransactions: false, - pendingIncoming: 'USD:5.11', - pendingOutgoing: 'USD:0', - requiresUserInput: false - }] + balances: [ + { + available: "USD:2.23", + hasPendingTransactions: false, + pendingIncoming: "USD:5.11", + pendingOutgoing: "USD:0", + requiresUserInput: false, + }, + ], }, }, Linker: NullLink, @@ -87,13 +88,15 @@ export const SomeCoinsAndOutgoingMoney = createExample(TestedComponent, { balance: { hasError: false, response: { - balances: [{ - available: 'USD:2.23', - hasPendingTransactions: false, - pendingIncoming: 'USD:0', - pendingOutgoing: 'USD:5.11', - requiresUserInput: false - }] + balances: [ + { + available: "USD:2.23", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:5.11", + requiresUserInput: false, + }, + ], }, }, Linker: NullLink, @@ -103,13 +106,15 @@ export const SomeCoinsAndMovingMoney = createExample(TestedComponent, { balance: { hasError: false, response: { - balances: [{ - available: 'USD:2.23', - hasPendingTransactions: false, - pendingIncoming: 'USD:2', - pendingOutgoing: 'USD:5.11', - requiresUserInput: false - }] + balances: [ + { + available: "USD:2.23", + hasPendingTransactions: false, + pendingIncoming: "USD:2", + pendingOutgoing: "USD:5.11", + requiresUserInput: false, + }, + ], }, }, Linker: NullLink, @@ -119,19 +124,22 @@ export const SomeCoinsInTwoCurrencies = createExample(TestedComponent, { balance: { hasError: false, response: { - balances: [{ - available: 'USD:2', - hasPendingTransactions: false, - pendingIncoming: 'USD:5.1', - pendingOutgoing: 'USD:0', - requiresUserInput: false - },{ - available: 'EUR:4', - hasPendingTransactions: false, - pendingIncoming: 'EUR:0', - pendingOutgoing: 'EUR:3.01', - requiresUserInput: false - }] + balances: [ + { + available: "USD:2", + hasPendingTransactions: false, + pendingIncoming: "USD:5.1", + pendingOutgoing: "USD:0", + requiresUserInput: false, + }, + { + available: "EUR:4", + hasPendingTransactions: false, + pendingIncoming: "EUR:0", + pendingOutgoing: "EUR:3.01", + requiresUserInput: false, + }, + ], }, }, Linker: NullLink, @@ -141,78 +149,89 @@ export const SomeCoinsInTreeCurrencies = createExample(TestedComponent, { balance: { hasError: false, response: { - balances: [{ - available: 'USD:1', - hasPendingTransactions: false, - pendingIncoming: 'USD:0', - pendingOutgoing: 'USD:0', - requiresUserInput: false - },{ - available: 'COL:2000', - hasPendingTransactions: false, - pendingIncoming: 'USD:0', - pendingOutgoing: 'USD:0', - requiresUserInput: false - },{ - available: 'EUR:4', - hasPendingTransactions: false, - pendingIncoming: 'EUR:15', - pendingOutgoing: 'EUR:0', - requiresUserInput: false - }] + balances: [ + { + available: "USD:1", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, + }, + { + available: "COL:2000", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, + }, + { + available: "EUR:4", + hasPendingTransactions: false, + pendingIncoming: "EUR:15", + pendingOutgoing: "EUR:0", + requiresUserInput: false, + }, + ], }, }, Linker: NullLink, }); - export const SomeCoinsInFiveCurrencies = createExample(TestedComponent, { balance: { hasError: false, response: { - balances: [{ - available: 'USD:13451', - hasPendingTransactions: false, - pendingIncoming: 'USD:0', - pendingOutgoing: 'USD:0', - requiresUserInput: false - },{ - available: 'EUR:202.02', - hasPendingTransactions: false, - pendingIncoming: 'EUR:0', - pendingOutgoing: 'EUR:0', - requiresUserInput: false - },{ - available: 'ARS:30', - hasPendingTransactions: false, - pendingIncoming: 'USD:0', - pendingOutgoing: 'USD:0', - requiresUserInput: false - },{ - available: 'JPY:51223233', - hasPendingTransactions: false, - pendingIncoming: 'EUR:0', - pendingOutgoing: 'EUR:0', - requiresUserInput: false - },{ - available: 'JPY:51223233', - hasPendingTransactions: false, - pendingIncoming: 'EUR:0', - pendingOutgoing: 'EUR:0', - requiresUserInput: false - },{ - available: 'DEMOKUDOS:6', - hasPendingTransactions: false, - pendingIncoming: 'USD:0', - pendingOutgoing: 'USD:0', - requiresUserInput: false - },{ - available: 'TESTKUDOS:6', - hasPendingTransactions: false, - pendingIncoming: 'USD:5', - pendingOutgoing: 'USD:0', - requiresUserInput: false - }] + balances: [ + { + available: "USD:13451", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, + }, + { + available: "EUR:202.02", + hasPendingTransactions: false, + pendingIncoming: "EUR:0", + pendingOutgoing: "EUR:0", + requiresUserInput: false, + }, + { + available: "ARS:30", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, + }, + { + available: "JPY:51223233", + hasPendingTransactions: false, + pendingIncoming: "EUR:0", + pendingOutgoing: "EUR:0", + requiresUserInput: false, + }, + { + available: "JPY:51223233", + hasPendingTransactions: false, + pendingIncoming: "EUR:0", + pendingOutgoing: "EUR:0", + requiresUserInput: false, + }, + { + available: "DEMOKUDOS:6", + hasPendingTransactions: false, + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + requiresUserInput: false, + }, + { + available: "TESTKUDOS:6", + hasPendingTransactions: false, + pendingIncoming: "USD:5", + pendingOutgoing: "USD:0", + requiresUserInput: false, + }, + ], }, }, Linker: NullLink, diff --git a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx index 8e5c5c42e..2913f60e0 100644 --- a/packages/taler-wallet-webextension/src/popup/BalancePage.tsx +++ b/packages/taler-wallet-webextension/src/popup/BalancePage.tsx @@ -15,20 +15,37 @@ */ import { - amountFractionalBase, Amounts, - Balance, BalancesResponse, - i18n + amountFractionalBase, + Amounts, + Balance, + BalancesResponse, + i18n, } from "@gnu-taler/taler-util"; import { JSX, h, Fragment } from "preact"; import { ErrorMessage } from "../components/ErrorMessage"; -import { PopupBox, Centered, ButtonPrimary, ErrorBox, Middle } from "../components/styled/index"; +import { + PopupBox, + Centered, + ButtonPrimary, + ErrorBox, + Middle, +} from "../components/styled/index"; import { BalancesHook, useBalances } from "../hooks/useBalances"; import { PageLink, renderAmount } from "../renderHtml"; - -export function BalancePage({ goToWalletManualWithdraw }: { goToWalletManualWithdraw: () => void }) { - const balance = useBalances() - return <BalanceView balance={balance} Linker={PageLink} goToWalletManualWithdraw={goToWalletManualWithdraw} /> +export function BalancePage({ + goToWalletManualWithdraw, +}: { + goToWalletManualWithdraw: () => void; +}) { + const balance = useBalances(); + return ( + <BalanceView + balance={balance} + Linker={PageLink} + goToWalletManualWithdraw={goToWalletManualWithdraw} + /> + ); } export interface BalanceViewProps { balance: BalancesHook; @@ -46,22 +63,26 @@ function formatPending(entry: Balance): JSX.Element { if (!Amounts.isZero(pendingIncoming)) { incoming = ( - <span><i18n.Translate> - <span style={{ color: "darkgreen" }} title="incoming amount"> - {"+"} - {renderAmount(entry.pendingIncoming)} - </span>{" "} - </i18n.Translate></span> + <span> + <i18n.Translate> + <span style={{ color: "darkgreen" }} title="incoming amount"> + {"+"} + {renderAmount(entry.pendingIncoming)} + </span>{" "} + </i18n.Translate> + </span> ); } if (!Amounts.isZero(pendingOutgoing)) { payment = ( - <span><i18n.Translate> - <span style={{ color: "darkred" }} title="outgoing amount"> - {"-"} - {renderAmount(entry.pendingOutgoing)} - </span>{" "} - </i18n.Translate></span> + <span> + <i18n.Translate> + <span style={{ color: "darkred" }} title="outgoing amount"> + {"-"} + {renderAmount(entry.pendingOutgoing)} + </span>{" "} + </i18n.Translate> + </span> ); } @@ -80,76 +101,110 @@ function formatPending(entry: Balance): JSX.Element { ); } - -export function BalanceView({ balance, Linker, goToWalletManualWithdraw }: BalanceViewProps) { - +export function BalanceView({ + balance, + Linker, + goToWalletManualWithdraw, +}: BalanceViewProps) { function Content() { if (!balance) { - return <span /> + return <span />; } if (balance.hasError) { - return (<section> - <ErrorBox>{balance.message}</ErrorBox> - <p> - Click <Linker pageName="welcome">here</Linker> for help and - diagnostics. - </p> - </section>) + return ( + <section> + <ErrorBox>{balance.message}</ErrorBox> + <p> + Click <Linker pageName="welcome">here</Linker> for help and + diagnostics. + </p> + </section> + ); } if (balance.response.balances.length === 0) { - return (<section data-expanded> - <Middle> - <p><i18n.Translate> - You have no balance to show. Need some{" "} - <Linker pageName="/welcome">help</Linker> getting started? - </i18n.Translate></p> - </Middle> - </section>) + return ( + <section data-expanded> + <Middle> + <p> + <i18n.Translate> + You have no balance to show. Need some{" "} + <Linker pageName="/welcome">help</Linker> getting started? + </i18n.Translate> + </p> + </Middle> + </section> + ); } - return <section data-expanded data-centered> - <table style={{width:'100%'}}>{balance.response.balances.map((entry) => { - const av = Amounts.parseOrThrow(entry.available); - // Create our number formatter. - let formatter; - try { - formatter = new Intl.NumberFormat('en-US', { - style: 'currency', - currency: av.currency, - currencyDisplay: 'symbol' - // These options are needed to round to whole numbers if that's what you want. - //minimumFractionDigits: 0, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1) - //maximumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501) - }); - } catch { - formatter = new Intl.NumberFormat('en-US', { - // style: 'currency', - // currency: av.currency, - // These options are needed to round to whole numbers if that's what you want. - //minimumFractionDigits: 0, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1) - //maximumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501) - }); - } - - const v = formatter.format(av.value + av.fraction / amountFractionalBase); - const fontSize = v.length < 8 ? '3em' : (v.length < 13 ? '2em' : '1em') - return (<tr> - <td style={{ height: 50, fontSize, width: '60%', textAlign: 'right', padding: 0 }}>{v}</td> - <td style={{ maxWidth: '2em', overflowX: 'hidden' }}>{av.currency}</td> - <td style={{ fontSize: 'small', color: 'gray' }}>{formatPending(entry)}</td> - </tr> - ); - })}</table> - </section> + return ( + <section data-expanded data-centered> + <table style={{ width: "100%" }}> + {balance.response.balances.map((entry) => { + const av = Amounts.parseOrThrow(entry.available); + // Create our number formatter. + let formatter; + try { + formatter = new Intl.NumberFormat("en-US", { + style: "currency", + currency: av.currency, + currencyDisplay: "symbol", + // These options are needed to round to whole numbers if that's what you want. + //minimumFractionDigits: 0, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1) + //maximumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501) + }); + } catch { + formatter = new Intl.NumberFormat("en-US", { + // style: 'currency', + // currency: av.currency, + // These options are needed to round to whole numbers if that's what you want. + //minimumFractionDigits: 0, // (this suffices for whole numbers, but will print 2500.10 as $2,500.1) + //maximumFractionDigits: 0, // (causes 2500.99 to be printed as $2,501) + }); + } + + const v = formatter.format( + av.value + av.fraction / amountFractionalBase, + ); + const fontSize = + v.length < 8 ? "3em" : v.length < 13 ? "2em" : "1em"; + return ( + <tr> + <td + style={{ + height: 50, + fontSize, + width: "60%", + textAlign: "right", + padding: 0, + }} + > + {v} + </td> + <td style={{ maxWidth: "2em", overflowX: "hidden" }}> + {av.currency} + </td> + <td style={{ fontSize: "small", color: "gray" }}> + {formatPending(entry)} + </td> + </tr> + ); + })} + </table> + </section> + ); } - return <PopupBox> - {/* <section> */} - <Content /> - {/* </section> */} - <footer> - <div /> - <ButtonPrimary onClick={goToWalletManualWithdraw}>Withdraw</ButtonPrimary> - </footer> - </PopupBox> + return ( + <PopupBox> + {/* <section> */} + <Content /> + {/* </section> */} + <footer> + <div /> + <ButtonPrimary onClick={goToWalletManualWithdraw}> + Withdraw + </ButtonPrimary> + </footer> + </PopupBox> + ); } diff --git a/packages/taler-wallet-webextension/src/popup/Debug.tsx b/packages/taler-wallet-webextension/src/popup/Debug.tsx index ccc747466..8722c1cf8 100644 --- a/packages/taler-wallet-webextension/src/popup/Debug.tsx +++ b/packages/taler-wallet-webextension/src/popup/Debug.tsx @@ -19,13 +19,14 @@ 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> - <button onClick={openExtensionPage("/static/popup.html")}>wallet tab</button> + <button onClick={openExtensionPage("/static/popup.html")}> + wallet tab + </button> <br /> <button onClick={confirmReset}>reset</button> <Diagnostics diagnostics={status} timedOut={timedOut} /> @@ -46,7 +47,7 @@ export async function confirmReset(): Promise<void> { if ( confirm( "Do you want to IRREVOCABLY DESTROY everything inside your" + - " wallet and LOSE ALL YOUR COINS?", + " wallet and LOSE ALL YOUR COINS?", ) ) { await wxApi.resetDb(); @@ -61,4 +62,3 @@ export function openExtensionPage(page: string) { }); }; } - diff --git a/packages/taler-wallet-webextension/src/popup/History.stories.tsx b/packages/taler-wallet-webextension/src/popup/History.stories.tsx index daa263a81..95f4a547a 100644 --- a/packages/taler-wallet-webextension/src/popup/History.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/History.stories.tsx @@ -15,135 +15,149 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ import { PaymentStatus, - TransactionCommon, TransactionDeposit, TransactionPayment, - TransactionRefresh, TransactionRefund, TransactionTip, TransactionType, + TransactionCommon, + TransactionDeposit, + TransactionPayment, + TransactionRefresh, + TransactionRefund, + TransactionTip, + TransactionType, TransactionWithdrawal, - WithdrawalType -} from '@gnu-taler/taler-util'; -import { createExample } from '../test-utils'; -import { HistoryView as TestedComponent } from './History'; + WithdrawalType, +} from "@gnu-taler/taler-util"; +import { createExample } from "../test-utils"; +import { HistoryView as TestedComponent } from "./History"; export default { - title: 'popup/history/list', + title: "popup/history/list", component: TestedComponent, }; const commonTransaction = { - amountRaw: 'USD:10', - amountEffective: 'USD:9', + amountRaw: "USD:10", + amountEffective: "USD:9", pending: false, timestamp: { - t_ms: new Date().getTime() + t_ms: new Date().getTime(), }, - transactionId: '12', -} as TransactionCommon + transactionId: "12", +} as TransactionCommon; const exampleData = { withdraw: { ...commonTransaction, type: TransactionType.Withdrawal, - exchangeBaseUrl: 'http://exchange.demo.taler.net', + exchangeBaseUrl: "http://exchange.demo.taler.net", withdrawalDetails: { confirmed: false, - exchangePaytoUris: ['payto://x-taler-bank/bank/account'], + exchangePaytoUris: ["payto://x-taler-bank/bank/account"], type: WithdrawalType.ManualTransfer, - } + }, } as TransactionWithdrawal, payment: { ...commonTransaction, - amountEffective: 'USD:11', + amountEffective: "USD:11", type: TransactionType.Payment, info: { - contractTermsHash: 'ASDZXCASD', + contractTermsHash: "ASDZXCASD", merchant: { - name: 'the merchant', + name: "the merchant", }, - orderId: '2021.167-03NPY6MCYMVGT', + orderId: "2021.167-03NPY6MCYMVGT", products: [], - summary: 'the summary', - fulfillmentMessage: '', + summary: "the summary", + fulfillmentMessage: "", }, - proposalId: '1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0', + proposalId: "1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0", status: PaymentStatus.Accepted, } as TransactionPayment, deposit: { ...commonTransaction, type: TransactionType.Deposit, - depositGroupId: '#groupId', - targetPaytoUri: 'payto://x-taler-bank/bank/account', + depositGroupId: "#groupId", + targetPaytoUri: "payto://x-taler-bank/bank/account", } as TransactionDeposit, refresh: { ...commonTransaction, type: TransactionType.Refresh, - exchangeBaseUrl: 'http://exchange.taler', + exchangeBaseUrl: "http://exchange.taler", } as TransactionRefresh, tip: { ...commonTransaction, type: TransactionType.Tip, - merchantBaseUrl: 'http://merchant.taler', + merchantBaseUrl: "http://merchant.taler", } as TransactionTip, refund: { ...commonTransaction, type: TransactionType.Refund, - refundedTransactionId: 'payment:1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0', + refundedTransactionId: + "payment:1EMJJH8EP1NX3XF7733NCYS2DBEJW4Q2KA5KEB37MCQJQ8Q5HMC0", info: { - contractTermsHash: 'ASDZXCASD', + contractTermsHash: "ASDZXCASD", merchant: { - name: 'the merchant', + name: "the merchant", }, - orderId: '2021.167-03NPY6MCYMVGT', + orderId: "2021.167-03NPY6MCYMVGT", products: [], - summary: 'the summary', - fulfillmentMessage: '', + summary: "the summary", + fulfillmentMessage: "", }, } as TransactionRefund, -} +}; export const EmptyWithBalance = createExample(TestedComponent, { list: [], - balances: [{ - available: 'TESTKUDOS:10', - pendingIncoming: 'TESTKUDOS:0', - pendingOutgoing: 'TESTKUDOS:0', - hasPendingTransactions: false, - requiresUserInput: false, - }] + balances: [ + { + available: "TESTKUDOS:10", + pendingIncoming: "TESTKUDOS:0", + pendingOutgoing: "TESTKUDOS:0", + hasPendingTransactions: false, + requiresUserInput: false, + }, + ], }); export const EmptyWithNoBalance = createExample(TestedComponent, { list: [], - balances: [] + balances: [], }); export const One = createExample(TestedComponent, { list: [exampleData.withdraw], - balances: [{ - available: 'USD:10', - pendingIncoming: 'USD:0', - pendingOutgoing: 'USD:0', - hasPendingTransactions: false, - requiresUserInput: false, - }] + balances: [ + { + available: "USD:10", + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + hasPendingTransactions: false, + requiresUserInput: false, + }, + ], }); export const OnePending = createExample(TestedComponent, { - list: [{ - ...exampleData.withdraw, - pending: true, - }], - balances: [{ - available: 'USD:10', - pendingIncoming: 'USD:0', - pendingOutgoing: 'USD:0', - hasPendingTransactions: false, - requiresUserInput: false, - }] + list: [ + { + ...exampleData.withdraw, + pending: true, + }, + ], + balances: [ + { + available: "USD:10", + pendingIncoming: "USD:0", + pendingOutgoing: "USD:0", + hasPendingTransactions: false, + requiresUserInput: false, + }, + ], }); export const Several = createExample(TestedComponent, { @@ -157,13 +171,15 @@ export const Several = createExample(TestedComponent, { exampleData.tip, exampleData.deposit, ], - balances: [{ - available: 'TESTKUDOS:10', - pendingIncoming: 'TESTKUDOS:0', - pendingOutgoing: 'TESTKUDOS:0', - hasPendingTransactions: false, - requiresUserInput: false, - }] + balances: [ + { + available: "TESTKUDOS:10", + pendingIncoming: "TESTKUDOS:0", + pendingOutgoing: "TESTKUDOS:0", + hasPendingTransactions: false, + requiresUserInput: false, + }, + ], }); export const SeveralWithTwoCurrencies = createExample(TestedComponent, { @@ -177,18 +193,20 @@ export const SeveralWithTwoCurrencies = createExample(TestedComponent, { 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, - }] + 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, + }, + ], }); - diff --git a/packages/taler-wallet-webextension/src/popup/History.tsx b/packages/taler-wallet-webextension/src/popup/History.tsx index 1447da9b0..8fe6de16c 100644 --- a/packages/taler-wallet-webextension/src/popup/History.tsx +++ b/packages/taler-wallet-webextension/src/popup/History.tsx @@ -14,7 +14,13 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ -import { AmountString, Balance, i18n, Transaction, TransactionsResponse } from "@gnu-taler/taler-util"; +import { + AmountString, + Balance, + i18n, + Transaction, + TransactionsResponse, +} from "@gnu-taler/taler-util"; import { h, JSX } from "preact"; import { useEffect, useState } from "preact/hooks"; import { PopupBox } from "../components/styled"; @@ -22,13 +28,14 @@ import { TransactionItem } from "../components/TransactionItem"; import { useBalances } from "../hooks/useBalances"; import * as wxApi from "../wxApi"; - export function HistoryPage(props: any): JSX.Element { const [transactions, setTransactions] = useState< TransactionsResponse | undefined >(undefined); - const balance = useBalances() - const balanceWithoutError = balance?.hasError ? [] : (balance?.response.balances || []) + const balance = useBalances(); + const balanceWithoutError = balance?.hasError + ? [] + : balance?.response.balances || []; useEffect(() => { const fetchData = async (): Promise<void> => { @@ -42,46 +49,79 @@ export function HistoryPage(props: any): JSX.Element { return <div>Loading ...</div>; } - return <HistoryView balances={balanceWithoutError} list={[...transactions.transactions].reverse()} />; + return ( + <HistoryView + balances={balanceWithoutError} + list={[...transactions.transactions].reverse()} + /> + ); } function amountToString(c: AmountString) { - const idx = c.indexOf(':') - return `${c.substring(idx + 1)} ${c.substring(0, idx)}` + const idx = c.indexOf(":"); + return `${c.substring(idx + 1)} ${c.substring(0, idx)}`; } - - -export function HistoryView({ list, balances }: { list: Transaction[], balances: Balance[] }) { - const multiCurrency = balances.length > 1 - return <PopupBox noPadding> - {balances.length > 0 && <header> - {multiCurrency ? <div class="title"> - Balance: <ul style={{ margin: 0 }}> - {balances.map(b => <li>{b.available}</li>)} - </ul> - </div> : <div class="title"> - Balance: <span>{amountToString(balances[0].available)}</span> - </div>} - </header>} - {list.length === 0 ? <section data-expanded data-centered> - <p><i18n.Translate> - You have no history yet, here you will be able to check your last transactions. - </i18n.Translate></p> - </section> : - <section> - {list.slice(0, 3).map((tx, i) => ( - <TransactionItem key={i} tx={tx} multiCurrency={multiCurrency} /> - ))} - </section> - } - <footer style={{ justifyContent: 'space-around' }}> - {list.length > 0 && - <a target="_blank" - rel="noopener noreferrer" - style={{ color: 'darkgreen', textDecoration: 'none' }} - href={chrome.extension ? chrome.extension.getURL(`/static/wallet.html#/history`) : '#'}>VIEW MORE TRANSACTIONS</a> - } - </footer> - </PopupBox> +export function HistoryView({ + list, + balances, +}: { + list: Transaction[]; + balances: Balance[]; +}) { + const multiCurrency = balances.length > 1; + return ( + <PopupBox noPadding> + {balances.length > 0 && ( + <header> + {multiCurrency ? ( + <div class="title"> + Balance:{" "} + <ul style={{ margin: 0 }}> + {balances.map((b) => ( + <li>{b.available}</li> + ))} + </ul> + </div> + ) : ( + <div class="title"> + Balance: <span>{amountToString(balances[0].available)}</span> + </div> + )} + </header> + )} + {list.length === 0 ? ( + <section data-expanded data-centered> + <p> + <i18n.Translate> + You have no history yet, here you will be able to check your last + transactions. + </i18n.Translate> + </p> + </section> + ) : ( + <section> + {list.slice(0, 3).map((tx, i) => ( + <TransactionItem key={i} tx={tx} multiCurrency={multiCurrency} /> + ))} + </section> + )} + <footer style={{ justifyContent: "space-around" }}> + {list.length > 0 && ( + <a + target="_blank" + rel="noopener noreferrer" + style={{ color: "darkgreen", textDecoration: "none" }} + href={ + chrome.extension + ? chrome.extension.getURL(`/static/wallet.html#/history`) + : "#" + } + > + VIEW MORE TRANSACTIONS + </a> + )} + </footer> + </PopupBox> + ); } diff --git a/packages/taler-wallet-webextension/src/popup/Popup.stories.tsx b/packages/taler-wallet-webextension/src/popup/Popup.stories.tsx index cd443e9d4..5009684c5 100644 --- a/packages/taler-wallet-webextension/src/popup/Popup.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/Popup.stories.tsx @@ -15,30 +15,29 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { createExample } from '../test-utils'; -import { NavBar as TestedComponent } from '../NavigationBar'; +import { createExample } from "../test-utils"; +import { NavBar as TestedComponent } from "../NavigationBar"; export default { - title: 'popup/header', + title: "popup/header", // component: TestedComponent, argTypes: { - onRetry: { action: 'onRetry' }, - onDelete: { action: 'onDelete' }, - onBack: { action: 'onBack' }, - } + onRetry: { action: "onRetry" }, + onDelete: { action: "onDelete" }, + onBack: { action: "onBack" }, + }, }; - export const OnBalance = createExample(TestedComponent, { - devMode:false, - path:'/balance' + devMode: false, + path: "/balance", }); export const OnHistoryWithDevMode = createExample(TestedComponent, { - devMode:true, - path:'/history' + devMode: true, + path: "/history", }); diff --git a/packages/taler-wallet-webextension/src/popup/ProviderAddConfirmProvider.stories.tsx b/packages/taler-wallet-webextension/src/popup/ProviderAddConfirmProvider.stories.tsx index de1f67b96..0cff7f75f 100644 --- a/packages/taler-wallet-webextension/src/popup/ProviderAddConfirmProvider.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/ProviderAddConfirmProvider.stories.tsx @@ -15,38 +15,37 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { createExample } from '../test-utils'; -import { ConfirmProviderView as TestedComponent } from './ProviderAddPage'; +import { createExample } from "../test-utils"; +import { ConfirmProviderView as TestedComponent } from "./ProviderAddPage"; export default { - title: 'popup/backup/confirm', + title: "popup/backup/confirm", component: TestedComponent, argTypes: { - onRetry: { action: 'onRetry' }, - onDelete: { action: 'onDelete' }, - onBack: { action: 'onBack' }, - } + onRetry: { action: "onRetry" }, + onDelete: { action: "onDelete" }, + onBack: { action: "onBack" }, + }, }; - export const DemoService = createExample(TestedComponent, { - url: 'https://sync.demo.taler.net/', + url: "https://sync.demo.taler.net/", provider: { - annual_fee: 'KUDOS:0.1', - storage_limit_in_megabytes: 20, - supported_protocol_version: '1' - } + annual_fee: "KUDOS:0.1", + storage_limit_in_megabytes: 20, + supported_protocol_version: "1", + }, }); export const FreeService = createExample(TestedComponent, { - url: 'https://sync.taler:9667/', + url: "https://sync.taler:9667/", provider: { - annual_fee: 'ARS:0', - storage_limit_in_megabytes: 20, - supported_protocol_version: '1' - } + annual_fee: "ARS:0", + storage_limit_in_megabytes: 20, + supported_protocol_version: "1", + }, }); diff --git a/packages/taler-wallet-webextension/src/popup/ProviderAddSetUrl.stories.tsx b/packages/taler-wallet-webextension/src/popup/ProviderAddSetUrl.stories.tsx index 2daf49e0c..9a2f97051 100644 --- a/packages/taler-wallet-webextension/src/popup/ProviderAddSetUrl.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/ProviderAddSetUrl.stories.tsx @@ -15,39 +15,37 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { createExample } from '../test-utils'; -import { SetUrlView as TestedComponent } from './ProviderAddPage'; +import { createExample } from "../test-utils"; +import { SetUrlView as TestedComponent } from "./ProviderAddPage"; export default { - title: 'popup/backup/add', + title: "popup/backup/add", component: TestedComponent, argTypes: { - onRetry: { action: 'onRetry' }, - onDelete: { action: 'onDelete' }, - onBack: { action: 'onBack' }, - } + onRetry: { action: "onRetry" }, + onDelete: { action: "onDelete" }, + onBack: { action: "onBack" }, + }, }; - -export const Initial = createExample(TestedComponent, { -}); +export const Initial = createExample(TestedComponent, {}); export const WithValue = createExample(TestedComponent, { - initialValue: 'sync.demo.taler.net' -}); + initialValue: "sync.demo.taler.net", +}); export const WithConnectionError = createExample(TestedComponent, { - withError: 'Network error' -}); + withError: "Network error", +}); export const WithClientError = createExample(TestedComponent, { - withError: 'URL may not be right: (404) Not Found' -}); + withError: "URL may not be right: (404) Not Found", +}); export const WithServerError = createExample(TestedComponent, { - withError: 'Try another server: (500) Internal Server Error' -}); + withError: "Try another server: (500) Internal Server Error", +}); diff --git a/packages/taler-wallet-webextension/src/popup/ProviderDetail.stories.tsx b/packages/taler-wallet-webextension/src/popup/ProviderDetail.stories.tsx index 4416608f8..fab21398a 100644 --- a/packages/taler-wallet-webextension/src/popup/ProviderDetail.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/ProviderDetail.stories.tsx @@ -15,224 +15,221 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { ProviderPaymentType } from '@gnu-taler/taler-wallet-core'; -import { createExample } from '../test-utils'; -import { ProviderView as TestedComponent } from './ProviderDetailPage'; +import { ProviderPaymentType } from "@gnu-taler/taler-wallet-core"; +import { createExample } from "../test-utils"; +import { ProviderView as TestedComponent } from "./ProviderDetailPage"; export default { - title: 'popup/backup/details', + title: "popup/backup/details", component: TestedComponent, argTypes: { - onRetry: { action: 'onRetry' }, - onDelete: { action: 'onDelete' }, - onBack: { action: 'onBack' }, - } + onRetry: { action: "onRetry" }, + onDelete: { action: "onDelete" }, + onBack: { action: "onBack" }, + }, }; - export const Active = createExample(TestedComponent, { info: { - "active": true, - name:'sync.demo', - "syncProviderBaseUrl": "http://sync.taler:9967/", - "lastSuccessfulBackupTimestamp": { - "t_ms": 1625063925078 - }, - "paymentProposalIds": [ - "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG" + active: true, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.taler:9967/", + lastSuccessfulBackupTimestamp: { + t_ms: 1625063925078, + }, + paymentProposalIds: [ + "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG", ], - "paymentStatus": { - "type": ProviderPaymentType.Paid, - "paidUntil": { - "t_ms": 1656599921000 - } - }, - "terms": { - "annualFee": "EUR:1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - } + paymentStatus: { + type: ProviderPaymentType.Paid, + paidUntil: { + t_ms: 1656599921000, + }, + }, + terms: { + annualFee: "EUR:1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, + }, }); export const ActiveErrorSync = createExample(TestedComponent, { info: { - "active": true, - name:'sync.demo', - "syncProviderBaseUrl": "http://sync.taler:9967/", - "lastSuccessfulBackupTimestamp": { - "t_ms": 1625063925078 + active: true, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.taler:9967/", + lastSuccessfulBackupTimestamp: { + t_ms: 1625063925078, }, lastAttemptedBackupTimestamp: { - "t_ms": 1625063925078 + t_ms: 1625063925078, }, - "paymentProposalIds": [ - "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG" + paymentProposalIds: [ + "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG", ], - "paymentStatus": { - "type": ProviderPaymentType.Paid, - "paidUntil": { - "t_ms": 1656599921000 - } + paymentStatus: { + type: ProviderPaymentType.Paid, + paidUntil: { + t_ms: 1656599921000, + }, }, lastError: { code: 2002, - details: 'details', - hint: 'error hint from the server', - message: 'message' - }, - "terms": { - "annualFee": "EUR:1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - } + details: "details", + hint: "error hint from the server", + message: "message", + }, + terms: { + annualFee: "EUR:1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, + }, }); export const ActiveBackupProblemUnreadable = createExample(TestedComponent, { info: { - "active": true, - name:'sync.demo', - "syncProviderBaseUrl": "http://sync.taler:9967/", - "lastSuccessfulBackupTimestamp": { - "t_ms": 1625063925078 - }, - "paymentProposalIds": [ - "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG" + active: true, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.taler:9967/", + lastSuccessfulBackupTimestamp: { + t_ms: 1625063925078, + }, + paymentProposalIds: [ + "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG", ], - "paymentStatus": { - "type": ProviderPaymentType.Paid, - "paidUntil": { - "t_ms": 1656599921000 - } + paymentStatus: { + type: ProviderPaymentType.Paid, + paidUntil: { + t_ms: 1656599921000, + }, }, backupProblem: { - type: 'backup-unreadable' - }, - "terms": { - "annualFee": "EUR:1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - } + type: "backup-unreadable", + }, + terms: { + annualFee: "EUR:1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, + }, }); export const ActiveBackupProblemDevice = createExample(TestedComponent, { info: { - "active": true, - name:'sync.demo', - "syncProviderBaseUrl": "http://sync.taler:9967/", - "lastSuccessfulBackupTimestamp": { - "t_ms": 1625063925078 - }, - "paymentProposalIds": [ - "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG" + active: true, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.taler:9967/", + lastSuccessfulBackupTimestamp: { + t_ms: 1625063925078, + }, + paymentProposalIds: [ + "43Q5WWRJPNS4SE9YKS54H9THDS94089EDGXW9EHBPN6E7M184XEG", ], - "paymentStatus": { - "type": ProviderPaymentType.Paid, - "paidUntil": { - "t_ms": 1656599921000 - } + paymentStatus: { + type: ProviderPaymentType.Paid, + paidUntil: { + t_ms: 1656599921000, + }, }, backupProblem: { - type: 'backup-conflicting-device', - myDeviceId: 'my-device-id', - otherDeviceId: 'other-device-id', + type: "backup-conflicting-device", + myDeviceId: "my-device-id", + otherDeviceId: "other-device-id", backupTimestamp: { - "t_ms": 1656599921000 - } - }, - "terms": { - "annualFee": "EUR:1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - } + t_ms: 1656599921000, + }, + }, + terms: { + annualFee: "EUR:1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, + }, }); export const InactiveUnpaid = createExample(TestedComponent, { info: { - "active": false, - name:'sync.demo', - "syncProviderBaseUrl": "http://sync.demo.taler.net/", - "paymentProposalIds": [], - "paymentStatus": { - "type": ProviderPaymentType.Unpaid, - }, - "terms": { - "annualFee": "EUR:0.1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - } + active: false, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.demo.taler.net/", + paymentProposalIds: [], + paymentStatus: { + type: ProviderPaymentType.Unpaid, + }, + terms: { + annualFee: "EUR:0.1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, + }, }); export const InactiveInsufficientBalance = createExample(TestedComponent, { info: { - "active": false, - name:'sync.demo', - "syncProviderBaseUrl": "http://sync.demo.taler.net/", - "paymentProposalIds": [], - "paymentStatus": { - "type": ProviderPaymentType.InsufficientBalance, - }, - "terms": { - "annualFee": "EUR:0.1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - } + active: false, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.demo.taler.net/", + paymentProposalIds: [], + paymentStatus: { + type: ProviderPaymentType.InsufficientBalance, + }, + terms: { + annualFee: "EUR:0.1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, + }, }); export const InactivePending = createExample(TestedComponent, { info: { - "active": false, - name:'sync.demo', - "syncProviderBaseUrl": "http://sync.demo.taler.net/", - "paymentProposalIds": [], - "paymentStatus": { - "type": ProviderPaymentType.Pending, - }, - "terms": { - "annualFee": "EUR:0.1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - } + active: false, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.demo.taler.net/", + paymentProposalIds: [], + paymentStatus: { + type: ProviderPaymentType.Pending, + }, + terms: { + annualFee: "EUR:0.1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, + }, }); - export const ActiveTermsChanged = createExample(TestedComponent, { info: { - "active": true, - name:'sync.demo', - "syncProviderBaseUrl": "http://sync.demo.taler.net/", - "paymentProposalIds": [], - "paymentStatus": { - "type": ProviderPaymentType.TermsChanged, + active: true, + name: "sync.demo", + syncProviderBaseUrl: "http://sync.demo.taler.net/", + paymentProposalIds: [], + paymentStatus: { + type: ProviderPaymentType.TermsChanged, paidUntil: { - t_ms: 1656599921000 + t_ms: 1656599921000, }, newTerms: { - "annualFee": "EUR:10", - "storageLimitInMegabytes": 8, - "supportedProtocolVersion": "0.0" + annualFee: "EUR:10", + storageLimitInMegabytes: 8, + supportedProtocolVersion: "0.0", }, oldTerms: { - "annualFee": "EUR:0.1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - }, - "terms": { - "annualFee": "EUR:0.1", - "storageLimitInMegabytes": 16, - "supportedProtocolVersion": "0.0" - } - } + annualFee: "EUR:0.1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, + }, + terms: { + annualFee: "EUR:0.1", + storageLimitInMegabytes: 16, + supportedProtocolVersion: "0.0", + }, + }, }); - diff --git a/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx b/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx index 04adbb21c..9617c9a41 100644 --- a/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx +++ b/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx @@ -14,13 +14,23 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ - import { i18n, Timestamp } from "@gnu-taler/taler-util"; -import { ProviderInfo, ProviderPaymentStatus, ProviderPaymentType } from "@gnu-taler/taler-wallet-core"; +import { + ProviderInfo, + ProviderPaymentStatus, + ProviderPaymentType, +} from "@gnu-taler/taler-wallet-core"; import { format, formatDuration, intervalToDuration } from "date-fns"; import { Fragment, VNode, h } from "preact"; import { ErrorMessage } from "../components/ErrorMessage"; -import { Button, ButtonDestructive, ButtonPrimary, PaymentStatus, PopupBox, SmallLightText } from "../components/styled"; +import { + Button, + ButtonDestructive, + ButtonPrimary, + PaymentStatus, + PopupBox, + SmallLightText, +} from "../components/styled"; import { useProviderStatus } from "../hooks/useProviderStatus"; interface Props { @@ -29,20 +39,29 @@ interface Props { } export function ProviderDetailPage({ pid, onBack }: Props): VNode { - const status = useProviderStatus(pid) + const status = useProviderStatus(pid); if (!status) { - return <div><i18n.Translate>Loading...</i18n.Translate></div> + return ( + <div> + <i18n.Translate>Loading...</i18n.Translate> + </div> + ); } if (!status.info) { - onBack() - return <div /> + onBack(); + return <div />; } - return <ProviderView info={status.info} - onSync={status.sync} - onDelete={() => status.remove().then(onBack)} - onBack={onBack} - onExtend={() => { null }} - />; + return ( + <ProviderView + info={status.info} + onSync={status.sync} + onDelete={() => status.remove().then(onBack)} + onBack={onBack} + onExtend={() => { + null; + }} + /> + ); } export interface ViewProps { @@ -53,124 +72,185 @@ export interface ViewProps { onExtend: () => void; } -export function ProviderView({ info, onDelete, onSync, onBack, onExtend }: ViewProps): VNode { - const lb = info?.lastSuccessfulBackupTimestamp - const isPaid = info.paymentStatus.type === ProviderPaymentType.Paid || info.paymentStatus.type === ProviderPaymentType.TermsChanged +export function ProviderView({ + info, + onDelete, + onSync, + onBack, + onExtend, +}: ViewProps): VNode { + const lb = info?.lastSuccessfulBackupTimestamp; + const isPaid = + info.paymentStatus.type === ProviderPaymentType.Paid || + info.paymentStatus.type === ProviderPaymentType.TermsChanged; return ( <PopupBox> <Error info={info} /> <header> - <h3>{info.name} <SmallLightText>{info.syncProviderBaseUrl}</SmallLightText></h3> - <PaymentStatus color={isPaid ? 'rgb(28, 184, 65)' : 'rgb(202, 60, 60)'}>{isPaid ? 'Paid' : 'Unpaid'}</PaymentStatus> + <h3> + {info.name}{" "} + <SmallLightText>{info.syncProviderBaseUrl}</SmallLightText> + </h3> + <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> - <ButtonPrimary onClick={onSync}><i18n.Translate>Back up</i18n.Translate></ButtonPrimary> - {info.terms && <Fragment> - <p><b>Provider fee:</b> {info.terms && info.terms.annualFee} per year</p> - </Fragment> - } + <p> + <b>Last backup:</b>{" "} + {lb == null || lb.t_ms == "never" + ? "never" + : format(lb.t_ms, "dd MMM yyyy")}{" "} + </p> + <ButtonPrimary onClick={onSync}> + <i18n.Translate>Back up</i18n.Translate> + </ButtonPrimary> + {info.terms && ( + <Fragment> + <p> + <b>Provider fee:</b> {info.terms && info.terms.annualFee} per year + </p> + </Fragment> + )} <p>{descriptionByStatus(info.paymentStatus)}</p> - <ButtonPrimary disabled onClick={onExtend}><i18n.Translate>Extend</i18n.Translate></ButtonPrimary> - - {info.paymentStatus.type === ProviderPaymentType.TermsChanged && <div> - <p><i18n.Translate>terms has changed, extending the service will imply accepting the new terms of service</i18n.Translate></p> - <table> - <thead> - <tr> - <td></td> - <td><i18n.Translate>old</i18n.Translate></td> - <td> -></td> - <td><i18n.Translate>new</i18n.Translate></td> - </tr> - </thead> - <tbody> - - <tr> - <td><i18n.Translate>fee</i18n.Translate></td> - <td>{info.paymentStatus.oldTerms.annualFee}</td> - <td>-></td> - <td>{info.paymentStatus.newTerms.annualFee}</td> - </tr> - <tr> - <td><i18n.Translate>storage</i18n.Translate></td> - <td>{info.paymentStatus.oldTerms.storageLimitInMegabytes}</td> - <td>-></td> - <td>{info.paymentStatus.newTerms.storageLimitInMegabytes}</td> - </tr> - </tbody> - </table> - </div>} + <ButtonPrimary disabled onClick={onExtend}> + <i18n.Translate>Extend</i18n.Translate> + </ButtonPrimary> + {info.paymentStatus.type === ProviderPaymentType.TermsChanged && ( + <div> + <p> + <i18n.Translate> + terms has changed, extending the service will imply accepting + the new terms of service + </i18n.Translate> + </p> + <table> + <thead> + <tr> + <td></td> + <td> + <i18n.Translate>old</i18n.Translate> + </td> + <td> -></td> + <td> + <i18n.Translate>new</i18n.Translate> + </td> + </tr> + </thead> + <tbody> + <tr> + <td> + <i18n.Translate>fee</i18n.Translate> + </td> + <td>{info.paymentStatus.oldTerms.annualFee}</td> + <td>-></td> + <td>{info.paymentStatus.newTerms.annualFee}</td> + </tr> + <tr> + <td> + <i18n.Translate>storage</i18n.Translate> + </td> + <td>{info.paymentStatus.oldTerms.storageLimitInMegabytes}</td> + <td>-></td> + <td>{info.paymentStatus.newTerms.storageLimitInMegabytes}</td> + </tr> + </tbody> + </table> + </div> + )} </section> <footer> - <Button onClick={onBack}><i18n.Translate> < back</i18n.Translate></Button> + <Button onClick={onBack}> + <i18n.Translate> < back</i18n.Translate> + </Button> <div> - <ButtonDestructive onClick={onDelete}><i18n.Translate>remove provider</i18n.Translate></ButtonDestructive> + <ButtonDestructive onClick={onDelete}> + <i18n.Translate>remove provider</i18n.Translate> + </ButtonDestructive> </div> </footer> </PopupBox> - ) + ); } function daysSince(d?: Timestamp) { - if (!d || d.t_ms === 'never') return 'never synced' + if (!d || d.t_ms === "never") return "never synced"; const duration = intervalToDuration({ start: d.t_ms, end: new Date(), - }) + }); const str = formatDuration(duration, { - delimiter: ', ', + delimiter: ", ", format: [ - duration?.years ? i18n.str`years` : ( - duration?.months ? i18n.str`months` : ( - duration?.days ? i18n.str`days` : ( - duration?.hours ? i18n.str`hours` : ( - duration?.minutes ? i18n.str`minutes` : i18n.str`seconds` - ) - ) - ) - ) - ] - }) - return `synced ${str} ago` + duration?.years + ? i18n.str`years` + : duration?.months + ? i18n.str`months` + : duration?.days + ? i18n.str`days` + : duration?.hours + ? i18n.str`hours` + : duration?.minutes + ? i18n.str`minutes` + : i18n.str`seconds`, + ], + }); + return `synced ${str} ago`; } function Error({ info }: { info: ProviderInfo }) { if (info.lastError) { - return <ErrorMessage title={info.lastError.hint} /> + return <ErrorMessage title={info.lastError.hint} />; } if (info.backupProblem) { switch (info.backupProblem.type) { case "backup-conflicting-device": - return <ErrorMessage title={<Fragment> - <i18n.Translate>There is conflict with another backup from <b>{info.backupProblem.otherDeviceId}</b></i18n.Translate> - </Fragment>} /> + return ( + <ErrorMessage + title={ + <Fragment> + <i18n.Translate> + There is conflict with another backup from{" "} + <b>{info.backupProblem.otherDeviceId}</b> + </i18n.Translate> + </Fragment> + } + /> + ); case "backup-unreadable": - return <ErrorMessage title="Backup is not readable" /> + return <ErrorMessage title="Backup is not readable" />; default: - return <ErrorMessage title={<Fragment> - <i18n.Translate>Unknown backup problem: {JSON.stringify(info.backupProblem)}</i18n.Translate> - </Fragment>} /> + return ( + <ErrorMessage + title={ + <Fragment> + <i18n.Translate> + Unknown backup problem: {JSON.stringify(info.backupProblem)} + </i18n.Translate> + </Fragment> + } + /> + ); } } - return null + return null; } function colorByStatus(status: ProviderPaymentType) { switch (status) { case ProviderPaymentType.InsufficientBalance: - return 'rgb(223, 117, 20)' + return "rgb(223, 117, 20)"; case ProviderPaymentType.Unpaid: - return 'rgb(202, 60, 60)' + return "rgb(202, 60, 60)"; case ProviderPaymentType.Paid: - return 'rgb(28, 184, 65)' + return "rgb(28, 184, 65)"; case ProviderPaymentType.Pending: - return 'gray' + return "gray"; case ProviderPaymentType.InsufficientBalance: - return 'rgb(202, 60, 60)' + return "rgb(202, 60, 60)"; case ProviderPaymentType.TermsChanged: - return 'rgb(202, 60, 60)' + return "rgb(202, 60, 60)"; } } @@ -180,16 +260,19 @@ function descriptionByStatus(status: ProviderPaymentStatus) { // return i18n.str`not paid yet` case ProviderPaymentType.Paid: case ProviderPaymentType.TermsChanged: - if (status.paidUntil.t_ms === 'never') { - return i18n.str`service paid` + if (status.paidUntil.t_ms === "never") { + return i18n.str`service paid`; } else { - return <Fragment> - <b>Backup valid until:</b> {format(status.paidUntil.t_ms, 'dd MMM yyyy')} - </Fragment> + return ( + <Fragment> + <b>Backup valid until:</b>{" "} + {format(status.paidUntil.t_ms, "dd MMM yyyy")} + </Fragment> + ); } case ProviderPaymentType.Unpaid: case ProviderPaymentType.InsufficientBalance: case ProviderPaymentType.Pending: - return '' + return ""; } } diff --git a/packages/taler-wallet-webextension/src/popup/Settings.stories.tsx b/packages/taler-wallet-webextension/src/popup/Settings.stories.tsx index 06e33c9d3..ae8e54ba1 100644 --- a/packages/taler-wallet-webextension/src/popup/Settings.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/Settings.stories.tsx @@ -15,29 +15,28 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { createExample } from '../test-utils'; -import { SettingsView as TestedComponent } from './Settings'; +import { createExample } from "../test-utils"; +import { SettingsView as TestedComponent } from "./Settings"; export default { - title: 'popup/settings', + title: "popup/settings", component: TestedComponent, argTypes: { setDeviceName: () => Promise.resolve(), - } + }, }; export const AllOff = createExample(TestedComponent, { - deviceName: 'this-is-the-device-name', + deviceName: "this-is-the-device-name", setDeviceName: () => Promise.resolve(), }); export const OneChecked = createExample(TestedComponent, { - deviceName: 'this-is-the-device-name', + deviceName: "this-is-the-device-name", permissionsEnabled: true, setDeviceName: () => Promise.resolve(), }); - diff --git a/packages/taler-wallet-webextension/src/popup/Settings.tsx b/packages/taler-wallet-webextension/src/popup/Settings.tsx index 8595c87ff..3b83f0762 100644 --- a/packages/taler-wallet-webextension/src/popup/Settings.tsx +++ b/packages/taler-wallet-webextension/src/popup/Settings.tsx @@ -14,7 +14,6 @@ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> */ - import { i18n } from "@gnu-taler/taler-util"; import { VNode, h } from "preact"; import { Checkbox } from "../components/Checkbox"; @@ -28,15 +27,21 @@ import { useLang } from "../hooks/useLang"; export function SettingsPage(): VNode { const [permissionsEnabled, togglePermissions] = useExtendedPermissions(); - const { devMode, toggleDevMode } = useDevContext() - const { name, update } = useBackupDeviceName() - const [lang, changeLang] = useLang() - return <SettingsView - lang={lang} changeLang={changeLang} - deviceName={name} setDeviceName={update} - permissionsEnabled={permissionsEnabled} togglePermissions={togglePermissions} - developerMode={devMode} toggleDeveloperMode={toggleDevMode} - />; + const { devMode, toggleDevMode } = useDevContext(); + const { name, update } = useBackupDeviceName(); + const [lang, changeLang] = useLang(); + return ( + <SettingsView + lang={lang} + changeLang={changeLang} + deviceName={name} + setDeviceName={update} + permissionsEnabled={permissionsEnabled} + togglePermissions={togglePermissions} + developerMode={devMode} + toggleDeveloperMode={toggleDevMode} + /> + ); } export interface ViewProps { @@ -50,23 +55,31 @@ export interface ViewProps { toggleDeveloperMode: () => void; } -import { strings as messages } from '../i18n/strings' +import { strings as messages } from "../i18n/strings"; type LangsNames = { - [P in keyof typeof messages]: string -} + [P in keyof typeof messages]: string; +}; const names: LangsNames = { - es: 'Español [es]', - en: 'English [en]', - fr: 'Français [fr]', - de: 'Deutsch [de]', - sv: 'Svenska [sv]', - it: 'Italiano [it]', -} - + es: "Español [es]", + en: "English [en]", + fr: "Français [fr]", + de: "Deutsch [de]", + sv: "Svenska [sv]", + it: "Italiano [it]", +}; -export function SettingsView({ lang, changeLang, deviceName, setDeviceName, permissionsEnabled, togglePermissions, developerMode, toggleDeveloperMode }: ViewProps): VNode { +export function SettingsView({ + lang, + changeLang, + deviceName, + setDeviceName, + permissionsEnabled, + togglePermissions, + developerMode, + toggleDeveloperMode, +}: ViewProps): VNode { return ( <PopupBox> <section> @@ -86,25 +99,39 @@ export function SettingsView({ lang, changeLang, deviceName, setDeviceName, perm label={i18n.str`Device name`} description="(This is how you will recognize the wallet in the backup provider)" /> */} - <h2><i18n.Translate>Permissions</i18n.Translate></h2> - <Checkbox label="Automatically open wallet based on page content" + <h2> + <i18n.Translate>Permissions</i18n.Translate> + </h2> + <Checkbox + label="Automatically open wallet based on page content" name="perm" description="(Enabling this option below will make using the wallet faster, but requires more permissions from your browser.)" - enabled={permissionsEnabled} onToggle={togglePermissions} + enabled={permissionsEnabled} + onToggle={togglePermissions} /> <h2>Config</h2> - <Checkbox label="Developer mode" + <Checkbox + label="Developer mode" name="devMode" description="(More options and information useful for debugging)" - enabled={developerMode} onToggle={toggleDeveloperMode} + enabled={developerMode} + onToggle={toggleDeveloperMode} /> </section> - <footer style={{ justifyContent: 'space-around' }}> - <a target="_blank" + <footer style={{ justifyContent: "space-around" }}> + <a + target="_blank" rel="noopener noreferrer" - style={{ color: 'darkgreen', textDecoration: 'none' }} - href={chrome.extension ? chrome.extension.getURL(`/static/wallet.html#/settings`) : '#'}>VIEW MORE SETTINGS</a> + style={{ color: "darkgreen", textDecoration: "none" }} + href={ + chrome.extension + ? chrome.extension.getURL(`/static/wallet.html#/settings`) + : "#" + } + > + VIEW MORE SETTINGS + </a> </footer> </PopupBox> - ) -}
\ No newline at end of file + ); +} diff --git a/packages/taler-wallet-webextension/src/popup/TalerActionFound.stories.tsx b/packages/taler-wallet-webextension/src/popup/TalerActionFound.stories.tsx index 88c7c725e..f20403d6a 100644 --- a/packages/taler-wallet-webextension/src/popup/TalerActionFound.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/TalerActionFound.stories.tsx @@ -15,38 +15,38 @@ */ /** -* -* @author Sebastian Javier Marchano (sebasjm) -*/ + * + * @author Sebastian Javier Marchano (sebasjm) + */ -import { createExample } from '../test-utils'; -import { TalerActionFound as TestedComponent } from './TalerActionFound'; +import { createExample } from "../test-utils"; +import { TalerActionFound as TestedComponent } from "./TalerActionFound"; export default { - title: 'popup/TalerActionFound', + title: "popup/TalerActionFound", component: TestedComponent, }; export const PayAction = createExample(TestedComponent, { - url: 'taler://pay/something' + url: "taler://pay/something", }); export const WithdrawalAction = createExample(TestedComponent, { - url: 'taler://withdraw/something' + url: "taler://withdraw/something", }); export const TipAction = createExample(TestedComponent, { - url: 'taler://tip/something' + url: "taler://tip/something", }); export const NotifyAction = createExample(TestedComponent, { - url: 'taler://notify-reserve/something' + url: "taler://notify-reserve/something", }); export const RefundAction = createExample(TestedComponent, { - url: 'taler://refund/something' + url: "taler://refund/something", }); export const InvalidAction = createExample(TestedComponent, { - url: 'taler://something/asd' + url: "taler://something/asd", }); diff --git a/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx b/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx index ef0ec341c..cbdcbeb15 100644 --- a/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx +++ b/packages/taler-wallet-webextension/src/popup/TalerActionFound.tsx @@ -1,5 +1,31 @@ +/* + 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 { classifyTalerUri, TalerUriType } from "@gnu-taler/taler-util"; -import { ButtonPrimary, ButtonSuccess, PopupBox } from "../components/styled/index"; +import { + ButtonPrimary, + ButtonSuccess, + PopupBox, +} from "../components/styled/index"; +import { h } from "preact"; export interface Props { url: string; @@ -8,54 +34,89 @@ export interface Props { export function TalerActionFound({ url, onDismiss }: Props) { const uriType = classifyTalerUri(url); - return <PopupBox> - <section> - <h1>Taler Action </h1> - {uriType === TalerUriType.TalerPay && <div> - <p>This page has pay action.</p> - <ButtonSuccess onClick={() => { chrome.tabs.create({ "url": actionForTalerUri(uriType, url) }); }}> - Open pay page - </ButtonSuccess> - </div>} - {uriType === TalerUriType.TalerWithdraw && <div> - <p>This page has a withdrawal action.</p> - <ButtonSuccess onClick={() => { chrome.tabs.create({ "url": actionForTalerUri(uriType, url) }); }}> - Open withdraw page - </ButtonSuccess> - </div>} - {uriType === TalerUriType.TalerTip && <div> - <p>This page has a tip action.</p> - <ButtonSuccess onClick={() => { chrome.tabs.create({ "url": actionForTalerUri(uriType, url) }); }}> - Open tip page - </ButtonSuccess> - </div>} - {uriType === TalerUriType.TalerNotifyReserve && <div> - <p>This page has a notify reserve action.</p> - <ButtonSuccess onClick={() => { chrome.tabs.create({ "url": actionForTalerUri(uriType, url) }); }}> - Notify - </ButtonSuccess> - </div>} - {uriType === TalerUriType.TalerRefund && <div> - <p>This page has a refund action.</p> - <ButtonSuccess onClick={() => { chrome.tabs.create({ "url": actionForTalerUri(uriType, url) }); }}> - Open refund page - </ButtonSuccess> - </div>} - {uriType === TalerUriType.Unknown && <div> - <p>This page has a malformed taler uri.</p> - <p>{url}</p> - </div>} - - </section> - <footer> - <div /> - <ButtonPrimary onClick={() => onDismiss()}> Dismiss </ButtonPrimary> - </footer> - </PopupBox>; - + return ( + <PopupBox> + <section> + <h1>Taler Action </h1> + {uriType === TalerUriType.TalerPay && ( + <div> + <p>This page has pay action.</p> + <ButtonSuccess + onClick={() => { + chrome.tabs.create({ url: actionForTalerUri(uriType, url) }); + }} + > + Open pay page + </ButtonSuccess> + </div> + )} + {uriType === TalerUriType.TalerWithdraw && ( + <div> + <p>This page has a withdrawal action.</p> + <ButtonSuccess + onClick={() => { + chrome.tabs.create({ url: actionForTalerUri(uriType, url) }); + }} + > + Open withdraw page + </ButtonSuccess> + </div> + )} + {uriType === TalerUriType.TalerTip && ( + <div> + <p>This page has a tip action.</p> + <ButtonSuccess + onClick={() => { + chrome.tabs.create({ url: actionForTalerUri(uriType, url) }); + }} + > + Open tip page + </ButtonSuccess> + </div> + )} + {uriType === TalerUriType.TalerNotifyReserve && ( + <div> + <p>This page has a notify reserve action.</p> + <ButtonSuccess + onClick={() => { + chrome.tabs.create({ url: actionForTalerUri(uriType, url) }); + }} + > + Notify + </ButtonSuccess> + </div> + )} + {uriType === TalerUriType.TalerRefund && ( + <div> + <p>This page has a refund action.</p> + <ButtonSuccess + onClick={() => { + chrome.tabs.create({ url: actionForTalerUri(uriType, url) }); + }} + > + Open refund page + </ButtonSuccess> + </div> + )} + {uriType === TalerUriType.Unknown && ( + <div> + <p>This page has a malformed taler uri.</p> + <p>{url}</p> + </div> + )} + </section> + <footer> + <div /> + <ButtonPrimary onClick={() => onDismiss()}> Dismiss </ButtonPrimary> + </footer> + </PopupBox> + ); } -function actionForTalerUri(uriType: TalerUriType, talerUri: string): string | undefined { +function actionForTalerUri( + uriType: TalerUriType, + talerUri: string, +): string | undefined { switch (uriType) { case TalerUriType.TalerWithdraw: return makeExtensionUrlWithParams("static/wallet.html#/withdraw", { @@ -91,8 +152,10 @@ function makeExtensionUrlWithParams( ): string { const innerUrl = new URL(chrome.extension.getURL("/" + url)); if (params) { - const hParams = Object.keys(params).map(k => `${k}=${params[k]}`).join('&') - innerUrl.hash = innerUrl.hash + '?' + hParams + const hParams = Object.keys(params) + .map((k) => `${k}=${params[k]}`) + .join("&"); + innerUrl.hash = innerUrl.hash + "?" + hParams; } return innerUrl.href; } |