summaryrefslogtreecommitdiff
path: root/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/taler-wallet-webextension/src/wallet/BackupPage.tsx')
-rw-r--r--packages/taler-wallet-webextension/src/wallet/BackupPage.tsx146
1 files changed, 146 insertions, 0 deletions
diff --git a/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
new file mode 100644
index 000000000..8b88432e0
--- /dev/null
+++ b/packages/taler-wallet-webextension/src/wallet/BackupPage.tsx
@@ -0,0 +1,146 @@
+/*
+ This file is part of TALER
+ (C) 2016 GNUnet e.V.
+
+ 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.
+
+ 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
+ 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 { Fragment, JSX, VNode, h } from "preact";
+import {
+ BoldLight, ButtonPrimary, ButtonSuccess, Centered,
+ CenteredText, CenteredTextBold, PopupBox, RowBorderGray,
+ SmallText, SmallTextLight, WalletBox
+} from "../components/styled";
+import { useBackupStatus } from "../hooks/useBackupStatus";
+import { Pages } from "../NavigationBar";
+
+interface Props {
+ onAddProvider: () => void;
+}
+
+export function BackupPage({ onAddProvider }: Props): VNode {
+ const status = useBackupStatus()
+ if (!status) {
+ return <div>Loading...</div>
+ }
+ 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, onSyncAll }: ViewProps): VNode {
+ return (
+ <WalletBox>
+ <section>
+ {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>}
+ </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>}
+ </WalletBox>
+ )
+}
+
+interface TransactionLayoutProps {
+ status: ProviderPaymentStatus;
+ timestamp?: Timestamp;
+ title: string;
+ id: string;
+ active: boolean;
+}
+
+function BackupLayout(props: TransactionLayoutProps): JSX.Element {
+ const date = !props.timestamp ? undefined : new Date(props.timestamp.t_ms);
+ const dateStr = date?.toLocaleString([], {
+ dateStyle: "medium",
+ 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 && <SmallTextLight style={{marginTop: 5}}>Not synced</SmallTextLight>}
+ </div>
+ <div>
+ {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>
+ <CenteredTextBold {...({ color: colorByTimeToExpire(until) })}> {daysUntil(until)} </CenteredTextBold>
+ </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)';
+}
+
+function daysUntil(d: Timestamp) {
+ if (d.t_ms === 'never') return undefined
+ const duration = intervalToDuration({
+ start: d.t_ms,
+ end: new Date(),
+ })
+ const str = formatDuration(duration, {
+ delimiter: ', ',
+ format: [
+ duration?.years ? 'years' : (
+ duration?.months ? 'months' : (
+ duration?.days ? 'days' : (
+ duration.hours ? 'hours' : 'minutes'
+ )
+ )
+ )
+ ]
+ })
+ return `${str}`
+} \ No newline at end of file