diff options
-rwxr-xr-x | packages/taler-wallet-webextension/clean_and_build.sh | 1 | ||||
-rw-r--r-- | packages/taler-wallet-webextension/src/hooks/useBackupStatus.ts (renamed from packages/taler-wallet-webextension/src/hooks/useProvidersByCurrency.ts) | 13 | ||||
-rw-r--r-- | packages/taler-wallet-webextension/src/hooks/useProviderStatus.ts | 34 | ||||
-rw-r--r-- | packages/taler-wallet-webextension/src/popup/Backup.stories.tsx | 23 | ||||
-rw-r--r-- | packages/taler-wallet-webextension/src/popup/BackupPage.tsx | 75 | ||||
-rw-r--r-- | packages/taler-wallet-webextension/src/popup/ProviderAddPage.tsx | 9 | ||||
-rw-r--r-- | packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx | 15 | ||||
-rw-r--r-- | packages/taler-wallet-webextension/src/popupEntryPoint.tsx | 20 | ||||
-rw-r--r-- | packages/taler-wallet-webextension/src/wxApi.ts | 9 |
9 files changed, 143 insertions, 56 deletions
diff --git a/packages/taler-wallet-webextension/clean_and_build.sh b/packages/taler-wallet-webextension/clean_and_build.sh index fa9e8d74b..e862be37e 100755 --- a/packages/taler-wallet-webextension/clean_and_build.sh +++ b/packages/taler-wallet-webextension/clean_and_build.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash # This file is in the public domain. +[ "also-wallet" == "$1" ] && { pnpm -C ../taler-wallet-core/ compile || exit 1; } pnpm clean && pnpm compile && rm -rf extension/ && ./pack.sh && (cd extension/ && unzip taler*.zip) diff --git a/packages/taler-wallet-webextension/src/hooks/useProvidersByCurrency.ts b/packages/taler-wallet-webextension/src/hooks/useBackupStatus.ts index 09f61e468..2d51cb303 100644 --- a/packages/taler-wallet-webextension/src/hooks/useProvidersByCurrency.ts +++ b/packages/taler-wallet-webextension/src/hooks/useBackupStatus.ts @@ -1,12 +1,12 @@ -import { Amounts } from "@gnu-taler/taler-util"; import { ProviderInfo, ProviderPaymentPaid, ProviderPaymentStatus, ProviderPaymentType } from "@gnu-taler/taler-wallet-core"; import { useEffect, useState } from "preact/hooks"; - import * as wxApi from "../wxApi"; + export interface BackupStatus { deviceName: string; - providers: ProviderInfo[] + providers: ProviderInfo[]; + sync: () => Promise<void>; } function getStatusTypeOrder(t: ProviderPaymentStatus) { @@ -40,7 +40,11 @@ export function useBackupStatus(): BackupStatus | undefined { return getStatusTypeOrder(a.paymentStatus) - getStatusTypeOrder(b.paymentStatus) }) - setStatus({ deviceName: status.deviceId, providers }) + async function sync() { + await wxApi.syncAllProviders() + } + + setStatus({ deviceName: status.deviceId, providers, sync }) } run() }, []) @@ -48,3 +52,4 @@ export function useBackupStatus(): BackupStatus | undefined { return status } + diff --git a/packages/taler-wallet-webextension/src/hooks/useProviderStatus.ts b/packages/taler-wallet-webextension/src/hooks/useProviderStatus.ts new file mode 100644 index 000000000..42eab5d80 --- /dev/null +++ b/packages/taler-wallet-webextension/src/hooks/useProviderStatus.ts @@ -0,0 +1,34 @@ +import { ProviderInfo } from "@gnu-taler/taler-wallet-core"; +import { useEffect, useState } from "preact/hooks"; +import * as wxApi from "../wxApi"; + +export interface ProviderStatus { + info?: ProviderInfo; + sync: () => Promise<void>; +} + +export function useProviderStatus(url: string): ProviderStatus | undefined { + const [status, setStatus] = useState<ProviderStatus | undefined>(undefined); + + useEffect(() => { + async function run() { + //create a first list of backup info by currency + const status = await wxApi.getBackupInfo(); + + const providers = status.providers.filter(p => p.syncProviderBaseUrl === url); + const info = providers.length ? providers[0] : undefined; + + async function sync() { + console.log("que tiene info", info) + if (info) { + await wxApi.syncOneProvider(info.syncProviderBaseUrl); + } + } + + setStatus({ info, sync }); + } + run(); + }, []); + + return status; +} diff --git a/packages/taler-wallet-webextension/src/popup/Backup.stories.tsx b/packages/taler-wallet-webextension/src/popup/Backup.stories.tsx index cd40d69a9..2d28a6ddc 100644 --- a/packages/taler-wallet-webextension/src/popup/Backup.stories.tsx +++ b/packages/taler-wallet-webextension/src/popup/Backup.stories.tsx @@ -20,6 +20,7 @@ */ import { ProviderPaymentType } from '@gnu-taler/taler-wallet-core'; +import { addDays } from 'date-fns'; import { FunctionalComponent } from 'preact'; import { BackupView as TestedComponent } from './BackupPage'; @@ -61,7 +62,27 @@ export const LotOfProviders = createExample(TestedComponent, { "storageLimitInMegabytes": 16, "supportedProtocolVersion": "0.0" } - }, { + },{ + "active": true, + "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" + } + },{ "active": false, "syncProviderBaseUrl": "http://sync.demo.taler.net/", "paymentProposalIds": [], diff --git a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx b/packages/taler-wallet-webextension/src/popup/BackupPage.tsx index 91f1782cc..d7a2d863c 100644 --- a/packages/taler-wallet-webextension/src/popup/BackupPage.tsx +++ b/packages/taler-wallet-webextension/src/popup/BackupPage.tsx @@ -17,9 +17,9 @@ import { i18n, Timestamp } from "@gnu-taler/taler-util"; import { ProviderInfo } from "@gnu-taler/taler-wallet-core"; -import { formatDuration, intervalToDuration } from "date-fns"; -import { JSX, VNode } from "preact"; -import { useBackupStatus } from "../hooks/useProvidersByCurrency"; +import { differenceInMonths, formatDuration, intervalToDuration } from "date-fns"; +import { Fragment, JSX, VNode } from "preact"; +import { useBackupStatus } from "../hooks/useBackupStatus"; import { Pages } from "./popup"; interface Props { @@ -31,59 +31,59 @@ export function BackupPage({ onAddProvider }: Props): VNode { if (!status) { return <div>Loading...</div> } - return <BackupView providers={status.providers} onAddProvider={onAddProvider} />; + return <BackupView providers={status.providers} onAddProvider={onAddProvider} onSyncAll={status.sync}/>; } export interface ViewProps { providers: ProviderInfo[], onAddProvider: () => void; + onSyncAll: () => Promise<void>; } -export function BackupView({ providers, onAddProvider }: ViewProps): VNode { +export function BackupView({ providers, onAddProvider, onSyncAll }: ViewProps): VNode { return ( <div style={{ height: 'calc(320px - 34px - 16px)', overflow: 'auto' }}> <div style={{ display: 'flex', flexDirection: 'column' }}> <section style={{ flex: '1 0 auto', height: 'calc(320px - 34px - 34px - 16px)', overflow: 'auto' }}> {!!providers.length && <div> - {providers.map((provider, idx) => { + {providers.map((provider) => { return <BackupLayout status={provider.paymentStatus} timestamp={provider.lastSuccessfulBackupTimestamp} - id={idx} + id={provider.syncProviderBaseUrl} active={provider.active} - subtitle={provider.syncProviderBaseUrl} title={provider.syncProviderBaseUrl} /> })} </div>} - {!providers.length && <div> - There is not backup providers configured, add one with the button below + {!providers.length && <div style={{ color: 'gray', fontWeight: 'bold', marginTop: 80, textAlign: 'center' }}> + <div>No backup providers configured</div> + <button class="pure-button button-success" style={{ marginTop: 15 }} onClick={onAddProvider}><i18n.Translate>Add provider</i18n.Translate></button> </div>} </section> - <footer style={{ marginTop: 'auto', display: 'flex', flexShrink: 0 }}> + {!!providers.length && <footer style={{ marginTop: 'auto', display: 'flex', flexShrink: 0 }}> <div style={{ width: '100%', flexDirection: 'row', justifyContent: 'flex-end', display: 'flex' }}> - <button class="pure-button button-secondary" disabled={!providers.length} style={{ marginLeft: 5 }} onClick={onAddProvider}>{ + <button class="pure-button button-secondary" style={{ marginLeft: 5 }} onClick={onSyncAll}>{ providers.length > 1 ? - <i18n.Translate>sync all now</i18n.Translate>: - <i18n.Translate>sync now</i18n.Translate> + <i18n.Translate>Sync all backups</i18n.Translate> : + <i18n.Translate>Sync now</i18n.Translate> }</button> - <button class="pure-button button-success" style={{ marginLeft: 5 }} onClick={onAddProvider}><i18n.Translate>add provider</i18n.Translate></button> + <button class="pure-button button-success" style={{ marginLeft: 5 }} onClick={onAddProvider}><i18n.Translate>Add provider</i18n.Translate></button> </div> - </footer> + </footer>} </div> </div> ) } interface TransactionLayoutProps { - status?: any; + status: any; timestamp?: Timestamp; title: string; - id: number; - subtitle?: string; - active?: boolean; + id: string; + active: boolean; } function BackupLayout(props: TransactionLayoutProps): JSX.Element { @@ -107,13 +107,12 @@ function BackupLayout(props: TransactionLayoutProps): JSX.Element { <div style={{ display: "flex", flexDirection: "column", color: !props.active ? "gray" : undefined }} > - - <div style={{ fontVariant: "small-caps", fontSize: "x-large" }}> - <a href={Pages.provider_detail.replace(':pid', String(props.id))}><span>{props.title}</span></a> + <div style={{ }}> + <a href={Pages.provider_detail.replace(':pid', encodeURIComponent(props.id))}><span>{props.title}</span></a> </div> - {dateStr && <div style={{ fontSize: "small" }}>Last time synced: {dateStr}</div>} - {!dateStr && <div style={{ fontSize: "small", color: "red" }}>never synced</div>} + {dateStr && <div style={{ fontSize: "small", marginTop: '0.5em' }}>Last synced: {dateStr}</div>} + {!dateStr && <div style={{ fontSize: "small", color: 'gray' }}>Not synced</div>} </div> <div style={{ marginLeft: "auto", @@ -122,16 +121,32 @@ function BackupLayout(props: TransactionLayoutProps): JSX.Element { alignItems: "center", alignSelf: "center" }}> - <div style={{ whiteSpace: 'nowrap' }}> - {!props.status ? "missing" : ( - props.status?.type === 'paid' ? daysUntil(props.status.paidUntil) : 'unpaid' - )} + <div style={{ display: 'flex', flexDirection: 'column' }}> + { + props.status?.type === 'paid' ? + <Fragment> + <div style={{ whiteSpace: 'nowrap', textAlign: 'center' }}> + Expires in + </div> + <div style={{ whiteSpace: 'nowrap', textAlign: 'center', fontWeight: 'bold', color: colorByTimeToExpire(props.status.paidUntil) }}> + {daysUntil(props.status.paidUntil)} + </div> + </Fragment> + : + 'unpaid' + } </div> </div> </div> ); } +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)'; +} + function daysUntil(d: Timestamp) { if (d.t_ms === 'never') return undefined const duration = intervalToDuration({ @@ -150,5 +165,5 @@ function daysUntil(d: Timestamp) { ) ] }) - return `${str} left` + return `${str}` }
\ No newline at end of file diff --git a/packages/taler-wallet-webextension/src/popup/ProviderAddPage.tsx b/packages/taler-wallet-webextension/src/popup/ProviderAddPage.tsx index 1e4a44df1..ac22a5f83 100644 --- a/packages/taler-wallet-webextension/src/popup/ProviderAddPage.tsx +++ b/packages/taler-wallet-webextension/src/popup/ProviderAddPage.tsx @@ -5,6 +5,7 @@ import * as wxApi from "../wxApi"; interface Props { currency: string; + onBack: () => void; } function getJsonIfOk(r: Response) { @@ -20,16 +21,14 @@ function getJsonIfOk(r: Response) { } -export function ProviderAddPage({ }: Props): VNode { +export function ProviderAddPage({ onBack }: Props): VNode { const [verifying, setVerifying] = useState<{ url: string, provider: BackupBackupProviderTerms } | undefined>(undefined) const [readingTerms, setReadingTerms] = useState<boolean | undefined>(undefined) const alreadyCheckedTheTerms = readingTerms === false if (!verifying) { return <SetUrlView - onCancel={() => { - setVerifying(undefined); - }} + onCancel={onBack} onVerify={(url) => { return fetch(`${url}/config`) .catch(e => { throw new Error(`Network error`) }) @@ -56,7 +55,7 @@ export function ProviderAddPage({ }: Props): VNode { setReadingTerms(true) }} onConfirm={() => { - wxApi.addBackupProvider(verifying.url).then(_ => history.go(-1)) + wxApi.addBackupProvider(verifying.url).then(onBack) }} /> diff --git a/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx b/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx index 1b8abf44d..b7a6f847c 100644 --- a/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx +++ b/packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx @@ -21,7 +21,8 @@ import { ContractTermsUtil } from "@gnu-taler/taler-wallet-core/src/util/contrac import { formatDuration, intervalToDuration, format } from "date-fns"; import { Fragment, VNode } from "preact"; import { useRef, useState } from "preact/hooks"; -import { useBackupStatus } from "../hooks/useProvidersByCurrency"; +import { useBackupStatus } from "../hooks/useBackupStatus"; +import { useProviderStatus } from "../hooks/useProviderStatus.js"; import * as wxApi from "../wxApi"; interface Props { @@ -30,18 +31,16 @@ interface Props { } export function ProviderDetailPage({ pid, onBack }: Props): VNode { - const status = useBackupStatus() + const status = useProviderStatus(pid) if (!status) { return <div>Loading...</div> } - const idx = parseInt(pid, 10) - if (Number.isNaN(idx) || !(status.providers[idx])) { + if (!status.info) { onBack() return <div /> } - const info = status.providers[idx]; - return <ProviderView info={info} - onSync={() => { null }} + return <ProviderView info={status.info} + onSync={status.sync} onDelete={() => { null }} onBack={onBack} onExtend={() => { null }} @@ -63,7 +62,7 @@ export function ProviderView({ info, onDelete, onSync, onBack, onExtend }: ViewP <div style={{ width: '100%', flexDirection: 'row', justifyContent: 'flex-end', display: 'flex' }}> {info && <button class="pure-button button-destructive" disabled onClick={onDelete}><i18n.Translate>remove</i18n.Translate></button>} {info && <button class="pure-button button-secondary" disabled style={{ marginLeft: 5 }} onClick={onExtend}><i18n.Translate>extend</i18n.Translate></button>} - {info && <button class="pure-button button-secondary" disabled style={{ marginLeft: 5 }} onClick={onSync}><i18n.Translate>sync now</i18n.Translate></button>} + {info && <button class="pure-button button-secondary" style={{ marginLeft: 5 }} onClick={onSync}><i18n.Translate>sync now</i18n.Translate></button>} </div> </footer> } diff --git a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx index 80a2a2bd3..42e9ab90e 100644 --- a/packages/taler-wallet-webextension/src/popupEntryPoint.tsx +++ b/packages/taler-wallet-webextension/src/popupEntryPoint.tsx @@ -99,17 +99,21 @@ function Application() { <Route path={Pages.settings} component={SettingsPage} /> <Route path={Pages.dev} component={DeveloperPage} /> <Route path={Pages.history} component={HistoryPage} /> - <Route path={Pages.backup} component={BackupPage} + <Route path={Pages.backup} component={BackupPage} onAddProvider={() => { route(Pages.provider_add) - }} - /> - <Route path={Pages.provider_detail} component={ProviderDetailPage} - onBack={() => { - route(Pages.backup) - }} + }} + /> + <Route path={Pages.provider_detail} component={ProviderDetailPage} + onBack={() => { + route(Pages.backup) + }} + /> + <Route path={Pages.provider_add} component={ProviderAddPage} + onBack={() => { + route(Pages.backup) + }} /> - <Route path={Pages.provider_add} component={ProviderAddPage} /> <Route path={Pages.transaction} component={TransactionPage} /> <Route default component={Redirect} to={Pages.balance} /> </Router> diff --git a/packages/taler-wallet-webextension/src/wxApi.ts b/packages/taler-wallet-webextension/src/wxApi.ts index db440e913..60ad26e7f 100644 --- a/packages/taler-wallet-webextension/src/wxApi.ts +++ b/packages/taler-wallet-webextension/src/wxApi.ts @@ -190,6 +190,15 @@ export function syncAllProviders(): Promise<void> { return callBackend("runBackupCycle", {}) } +export function syncOneProvider(url: string): Promise<void> { + return callBackend("runBackupCycle", { providers: [url] }) +} +export function removeProvider(url: string): Promise<void> { + return callBackend("removeBackupProvider", { provider: url }) +} +export function extendedProvider(url: string): Promise<void> { + return callBackend("extendBackupProvider", { provider: url }) +} /** * Retry a transaction |