aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xpackages/taler-wallet-webextension/clean_and_build.sh1
-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.ts34
-rw-r--r--packages/taler-wallet-webextension/src/popup/Backup.stories.tsx23
-rw-r--r--packages/taler-wallet-webextension/src/popup/BackupPage.tsx75
-rw-r--r--packages/taler-wallet-webextension/src/popup/ProviderAddPage.tsx9
-rw-r--r--packages/taler-wallet-webextension/src/popup/ProviderDetailPage.tsx15
-rw-r--r--packages/taler-wallet-webextension/src/popupEntryPoint.tsx20
-rw-r--r--packages/taler-wallet-webextension/src/wxApi.ts9
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