2021-08-24 18:29:37 +02:00
|
|
|
/*
|
|
|
|
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";
|
2021-11-15 15:18:58 +01:00
|
|
|
import {
|
|
|
|
ProviderInfo,
|
|
|
|
ProviderPaymentStatus,
|
|
|
|
ProviderPaymentType,
|
|
|
|
} from "@gnu-taler/taler-wallet-core";
|
2021-08-24 18:29:37 +02:00
|
|
|
import { format, formatDuration, intervalToDuration } from "date-fns";
|
|
|
|
import { Fragment, VNode, h } from "preact";
|
|
|
|
import { ErrorMessage } from "../components/ErrorMessage";
|
2021-11-15 15:18:58 +01:00
|
|
|
import {
|
|
|
|
Button,
|
|
|
|
ButtonDestructive,
|
|
|
|
ButtonPrimary,
|
|
|
|
PaymentStatus,
|
|
|
|
WalletBox,
|
|
|
|
SmallLightText,
|
|
|
|
} from "../components/styled";
|
2021-08-24 18:29:37 +02:00
|
|
|
import { useProviderStatus } from "../hooks/useProviderStatus";
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
pid: string;
|
|
|
|
onBack: () => void;
|
|
|
|
}
|
|
|
|
|
|
|
|
export function ProviderDetailPage({ pid, onBack }: Props): VNode {
|
2021-11-15 15:18:58 +01:00
|
|
|
const status = useProviderStatus(pid);
|
2021-08-24 18:29:37 +02:00
|
|
|
if (!status) {
|
2021-11-15 15:18:58 +01:00
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<i18n.Translate>Loading...</i18n.Translate>
|
|
|
|
</div>
|
|
|
|
);
|
2021-08-24 18:29:37 +02:00
|
|
|
}
|
|
|
|
if (!status.info) {
|
2021-11-15 15:18:58 +01:00
|
|
|
onBack();
|
|
|
|
return <div />;
|
2021-08-24 18:29:37 +02:00
|
|
|
}
|
2021-11-15 15:18:58 +01:00
|
|
|
return (
|
|
|
|
<ProviderView
|
|
|
|
info={status.info}
|
|
|
|
onSync={status.sync}
|
|
|
|
onDelete={() => status.remove().then(onBack)}
|
|
|
|
onBack={onBack}
|
|
|
|
onExtend={() => {
|
|
|
|
null;
|
|
|
|
}}
|
|
|
|
/>
|
|
|
|
);
|
2021-08-24 18:29:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
export interface ViewProps {
|
|
|
|
info: ProviderInfo;
|
|
|
|
onDelete: () => void;
|
|
|
|
onSync: () => void;
|
|
|
|
onBack: () => void;
|
|
|
|
onExtend: () => void;
|
|
|
|
}
|
|
|
|
|
2021-11-15 15:18:58 +01:00
|
|
|
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;
|
2021-08-24 18:29:37 +02:00
|
|
|
return (
|
|
|
|
<WalletBox>
|
2021-08-24 20:16:11 +02:00
|
|
|
<Error info={info} />
|
2021-08-24 18:29:37 +02:00
|
|
|
<header>
|
2021-11-15 15:18:58 +01:00
|
|
|
<h3>
|
|
|
|
{info.name}{" "}
|
|
|
|
<SmallLightText>{info.syncProviderBaseUrl}</SmallLightText>
|
|
|
|
</h3>
|
|
|
|
<PaymentStatus color={isPaid ? "rgb(28, 184, 65)" : "rgb(202, 60, 60)"}>
|
|
|
|
{isPaid ? "Paid" : "Unpaid"}
|
|
|
|
</PaymentStatus>
|
2021-08-24 18:29:37 +02:00
|
|
|
</header>
|
|
|
|
<section>
|
2021-11-15 15:18:58 +01:00
|
|
|
<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>
|
|
|
|
)}
|
2021-08-24 18:29:37 +02:00
|
|
|
<p>{descriptionByStatus(info.paymentStatus)}</p>
|
2021-11-15 15:18:58 +01:00
|
|
|
<ButtonPrimary disabled onClick={onExtend}>
|
|
|
|
<i18n.Translate>Extend</i18n.Translate>
|
|
|
|
</ButtonPrimary>
|
2021-08-24 18:29:37 +02:00
|
|
|
|
2021-11-15 15:18:58 +01:00
|
|
|
{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>
|
|
|
|
)}
|
2021-08-24 18:29:37 +02:00
|
|
|
</section>
|
|
|
|
<footer>
|
2021-11-15 15:18:58 +01:00
|
|
|
<Button onClick={onBack}>
|
|
|
|
<i18n.Translate> < back</i18n.Translate>
|
|
|
|
</Button>
|
2021-08-24 18:29:37 +02:00
|
|
|
<div>
|
2021-11-15 15:18:58 +01:00
|
|
|
<ButtonDestructive onClick={onDelete}>
|
|
|
|
<i18n.Translate>remove provider</i18n.Translate>
|
|
|
|
</ButtonDestructive>
|
2021-08-24 18:29:37 +02:00
|
|
|
</div>
|
|
|
|
</footer>
|
|
|
|
</WalletBox>
|
2021-11-15 15:18:58 +01:00
|
|
|
);
|
2021-08-24 18:29:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function daysSince(d?: Timestamp) {
|
2021-11-15 15:18:58 +01:00
|
|
|
if (!d || d.t_ms === "never") return "never synced";
|
2021-08-24 18:29:37 +02:00
|
|
|
const duration = intervalToDuration({
|
|
|
|
start: d.t_ms,
|
|
|
|
end: new Date(),
|
2021-11-15 15:18:58 +01:00
|
|
|
});
|
2021-08-24 18:29:37 +02:00
|
|
|
const str = formatDuration(duration, {
|
2021-11-15 15:18:58 +01:00
|
|
|
delimiter: ", ",
|
2021-08-24 18:29:37 +02:00
|
|
|
format: [
|
2021-11-15 15:18:58 +01:00
|
|
|
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`;
|
2021-08-24 18:29:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function Error({ info }: { info: ProviderInfo }) {
|
|
|
|
if (info.lastError) {
|
2021-11-15 15:18:58 +01:00
|
|
|
return <ErrorMessage title={info.lastError.hint} />;
|
2021-08-24 18:29:37 +02:00
|
|
|
}
|
|
|
|
if (info.backupProblem) {
|
|
|
|
switch (info.backupProblem.type) {
|
|
|
|
case "backup-conflicting-device":
|
2021-11-15 15:18:58 +01:00
|
|
|
return (
|
|
|
|
<ErrorMessage
|
|
|
|
title={
|
|
|
|
<Fragment>
|
|
|
|
<i18n.Translate>
|
|
|
|
There is conflict with another backup from{" "}
|
|
|
|
<b>{info.backupProblem.otherDeviceId}</b>
|
|
|
|
</i18n.Translate>
|
|
|
|
</Fragment>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
);
|
2021-08-24 18:29:37 +02:00
|
|
|
case "backup-unreadable":
|
2021-11-15 15:18:58 +01:00
|
|
|
return <ErrorMessage title="Backup is not readable" />;
|
2021-08-24 18:29:37 +02:00
|
|
|
default:
|
2021-11-15 15:18:58 +01:00
|
|
|
return (
|
|
|
|
<ErrorMessage
|
|
|
|
title={
|
|
|
|
<Fragment>
|
|
|
|
<i18n.Translate>
|
|
|
|
Unknown backup problem: {JSON.stringify(info.backupProblem)}
|
|
|
|
</i18n.Translate>
|
|
|
|
</Fragment>
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
);
|
2021-08-24 18:29:37 +02:00
|
|
|
}
|
|
|
|
}
|
2021-11-15 15:18:58 +01:00
|
|
|
return null;
|
2021-08-24 18:29:37 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
function colorByStatus(status: ProviderPaymentType) {
|
|
|
|
switch (status) {
|
|
|
|
case ProviderPaymentType.InsufficientBalance:
|
2021-11-15 15:18:58 +01:00
|
|
|
return "rgb(223, 117, 20)";
|
2021-08-24 18:29:37 +02:00
|
|
|
case ProviderPaymentType.Unpaid:
|
2021-11-15 15:18:58 +01:00
|
|
|
return "rgb(202, 60, 60)";
|
2021-08-24 18:29:37 +02:00
|
|
|
case ProviderPaymentType.Paid:
|
2021-11-15 15:18:58 +01:00
|
|
|
return "rgb(28, 184, 65)";
|
2021-08-24 18:29:37 +02:00
|
|
|
case ProviderPaymentType.Pending:
|
2021-11-15 15:18:58 +01:00
|
|
|
return "gray";
|
2021-08-24 18:29:37 +02:00
|
|
|
case ProviderPaymentType.InsufficientBalance:
|
2021-11-15 15:18:58 +01:00
|
|
|
return "rgb(202, 60, 60)";
|
2021-08-24 18:29:37 +02:00
|
|
|
case ProviderPaymentType.TermsChanged:
|
2021-11-15 15:18:58 +01:00
|
|
|
return "rgb(202, 60, 60)";
|
2021-08-24 18:29:37 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function descriptionByStatus(status: ProviderPaymentStatus) {
|
|
|
|
switch (status.type) {
|
|
|
|
// return i18n.str`no enough balance to make the payment`
|
|
|
|
// return i18n.str`not paid yet`
|
|
|
|
case ProviderPaymentType.Paid:
|
|
|
|
case ProviderPaymentType.TermsChanged:
|
2021-11-15 15:18:58 +01:00
|
|
|
if (status.paidUntil.t_ms === "never") {
|
|
|
|
return i18n.str`service paid`;
|
2021-08-24 18:29:37 +02:00
|
|
|
} else {
|
2021-11-15 15:18:58 +01:00
|
|
|
return (
|
|
|
|
<Fragment>
|
|
|
|
<b>Backup valid until:</b>{" "}
|
|
|
|
{format(status.paidUntil.t_ms, "dd MMM yyyy")}
|
|
|
|
</Fragment>
|
|
|
|
);
|
2021-08-24 18:29:37 +02:00
|
|
|
}
|
|
|
|
case ProviderPaymentType.Unpaid:
|
|
|
|
case ProviderPaymentType.InsufficientBalance:
|
|
|
|
case ProviderPaymentType.Pending:
|
2021-11-15 15:18:58 +01:00
|
|
|
return "";
|
2021-08-24 18:29:37 +02:00
|
|
|
}
|
|
|
|
}
|