wallet-core: simplify exchanges list response
This commit is contained in:
parent
e075134ffc
commit
bd88dcebbc
@ -900,11 +900,18 @@ export interface ExchangeFullDetails {
|
||||
globalFees: FeeDescription[];
|
||||
}
|
||||
|
||||
export enum ExchangeTosStatus {
|
||||
New = "new",
|
||||
Accepted = "accepted",
|
||||
Changed = "changed",
|
||||
NotFound = "not-found",
|
||||
}
|
||||
|
||||
export interface ExchangeListItem {
|
||||
exchangeBaseUrl: string;
|
||||
currency: string;
|
||||
paytoUris: string[];
|
||||
tos: ExchangeTosStatusDetails;
|
||||
tosStatus: ExchangeTosStatus;
|
||||
}
|
||||
|
||||
const codecForAuditorDenomSig = (): Codec<AuditorDenomSig> =>
|
||||
@ -976,7 +983,7 @@ export const codecForExchangeListItem = (): Codec<ExchangeListItem> =>
|
||||
.property("currency", codecForString())
|
||||
.property("exchangeBaseUrl", codecForString())
|
||||
.property("paytoUris", codecForList(codecForString()))
|
||||
.property("tos", codecForExchangeTos())
|
||||
.property("tosStatus", codecForAny())
|
||||
.build("ExchangeListItem");
|
||||
|
||||
export const codecForExchangesListResponse = (): Codec<ExchangesListResponse> =>
|
||||
@ -1146,6 +1153,8 @@ export interface GetExchangeTosResult {
|
||||
* Accepted content type
|
||||
*/
|
||||
contentType: string;
|
||||
|
||||
tosStatus: ExchangeTosStatus;
|
||||
}
|
||||
|
||||
export interface TestPayArgs {
|
||||
|
@ -22,6 +22,7 @@ import {
|
||||
Amounts,
|
||||
CoinRefreshRequest,
|
||||
CoinStatus,
|
||||
ExchangeTosStatus,
|
||||
j2s,
|
||||
Logger,
|
||||
RefreshReason,
|
||||
@ -31,7 +32,7 @@ import {
|
||||
TransactionIdStr,
|
||||
TransactionType,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { WalletStoresV1, CoinRecord } from "../db.js";
|
||||
import { WalletStoresV1, CoinRecord, ExchangeDetailsRecord } from "../db.js";
|
||||
import { makeErrorDetail, TalerError } from "../errors.js";
|
||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||
import { checkDbInvariant, checkLogicInvariant } from "../util/invariants.js";
|
||||
@ -307,3 +308,15 @@ export function makeTombstoneId(
|
||||
): TombstoneIdStr {
|
||||
return `tmb:${type}:${args.map((x) => encodeURIComponent(x)).join(":")}`;
|
||||
}
|
||||
|
||||
export function getExchangeTosStatus(
|
||||
exchangeDetails: ExchangeDetailsRecord,
|
||||
): ExchangeTosStatus {
|
||||
if (!exchangeDetails.tosAccepted) {
|
||||
return ExchangeTosStatus.New;
|
||||
}
|
||||
if (exchangeDetails.tosAccepted?.etag == exchangeDetails.tosCurrentEtag) {
|
||||
return ExchangeTosStatus.Accepted;
|
||||
}
|
||||
return ExchangeTosStatus.Changed;
|
||||
}
|
||||
|
@ -83,6 +83,7 @@ import {
|
||||
} from "../errors.js";
|
||||
import { InternalWalletState } from "../internal-wallet-state.js";
|
||||
import {
|
||||
getExchangeTosStatus,
|
||||
makeCoinAvailable,
|
||||
runOperationWithErrorReporting,
|
||||
} from "../operations/common.js";
|
||||
@ -1359,26 +1360,20 @@ export async function getWithdrawalDetailsForUri(
|
||||
.runReadOnly(async (tx) => {
|
||||
const exchangeRecords = await tx.exchanges.iter().toArray();
|
||||
for (const r of exchangeRecords) {
|
||||
const details = await ws.exchangeOps.getExchangeDetails(tx, r.baseUrl);
|
||||
const exchangeDetails = await ws.exchangeOps.getExchangeDetails(tx, r.baseUrl);
|
||||
const denominations = await tx.denominations.indexes.byExchangeBaseUrl
|
||||
.iter(r.baseUrl)
|
||||
.toArray();
|
||||
if (details && denominations) {
|
||||
if (exchangeDetails && denominations) {
|
||||
const tosRecord = await tx.exchangeTos.get([
|
||||
details.exchangeBaseUrl,
|
||||
details.tosCurrentEtag,
|
||||
exchangeDetails.exchangeBaseUrl,
|
||||
exchangeDetails.tosCurrentEtag,
|
||||
]);
|
||||
exchanges.push({
|
||||
exchangeBaseUrl: details.exchangeBaseUrl,
|
||||
currency: details.currency,
|
||||
// FIXME: We probably don't want to include the full ToS here!
|
||||
tos: {
|
||||
acceptedVersion: details.tosAccepted?.etag,
|
||||
currentVersion: details.tosCurrentEtag,
|
||||
contentType: tosRecord?.termsOfServiceContentType ?? "",
|
||||
content: tosRecord?.termsOfServiceText ?? "",
|
||||
},
|
||||
paytoUris: details.wireInfo.accounts.map((x) => x.payto_uri),
|
||||
exchangeBaseUrl: exchangeDetails.exchangeBaseUrl,
|
||||
currency: exchangeDetails.currency,
|
||||
paytoUris: exchangeDetails.wireInfo.accounts.map((x) => x.payto_uri),
|
||||
tosStatus: getExchangeTosStatus(exchangeDetails),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ import {
|
||||
} from "./operations/backup/index.js";
|
||||
import { setWalletDeviceId } from "./operations/backup/state.js";
|
||||
import { getBalances } from "./operations/balance.js";
|
||||
import { runOperationWithErrorReporting } from "./operations/common.js";
|
||||
import { getExchangeTosStatus, runOperationWithErrorReporting } from "./operations/common.js";
|
||||
import {
|
||||
createDepositGroup,
|
||||
getFeeForDeposit,
|
||||
@ -503,6 +503,7 @@ async function getExchangeTos(
|
||||
currentEtag,
|
||||
content,
|
||||
contentType,
|
||||
tosStatus: getExchangeTosStatus(exchangeDetails),
|
||||
};
|
||||
}
|
||||
|
||||
@ -519,6 +520,7 @@ async function getExchangeTos(
|
||||
currentEtag,
|
||||
content,
|
||||
contentType,
|
||||
tosStatus: getExchangeTosStatus(exchangeDetails),
|
||||
};
|
||||
}
|
||||
|
||||
@ -529,6 +531,7 @@ async function getExchangeTos(
|
||||
currentEtag: tosDownload.tosEtag,
|
||||
content: tosDownload.tosText,
|
||||
contentType: tosDownload.tosContentType,
|
||||
tosStatus: getExchangeTosStatus(exchangeDetails),
|
||||
};
|
||||
}
|
||||
|
||||
@ -665,7 +668,7 @@ async function getExchanges(
|
||||
exchanges.push({
|
||||
exchangeBaseUrl: r.baseUrl,
|
||||
currency,
|
||||
tos,
|
||||
tosStatus: getExchangeTosStatus(exchangeDetails),
|
||||
paytoUris: exchangeDetails.wireInfo.accounts.map((x) => x.payto_uri),
|
||||
});
|
||||
}
|
||||
|
@ -14,7 +14,7 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { GetExchangeTosResult } from "@gnu-taler/taler-util";
|
||||
import { ExchangeTosStatus, GetExchangeTosResult } from "@gnu-taler/taler-util";
|
||||
|
||||
export function buildTermsOfServiceState(
|
||||
tos: GetExchangeTosResult,
|
||||
@ -24,26 +24,7 @@ export function buildTermsOfServiceState(
|
||||
tos.content,
|
||||
);
|
||||
|
||||
const status: TermsStatus = buildTermsOfServiceStatus(
|
||||
tos.content,
|
||||
tos.acceptedEtag,
|
||||
tos.currentEtag,
|
||||
);
|
||||
|
||||
return { content, status, version: tos.currentEtag };
|
||||
}
|
||||
export function buildTermsOfServiceStatus(
|
||||
content: string | undefined,
|
||||
acceptedVersion: string | undefined,
|
||||
currentVersion: string | undefined,
|
||||
): TermsStatus {
|
||||
return !content
|
||||
? "notfound"
|
||||
: !acceptedVersion
|
||||
? "new"
|
||||
: acceptedVersion !== currentVersion
|
||||
? "changed"
|
||||
: "accepted";
|
||||
return { content, status: tos.tosStatus, version: tos.currentEtag };
|
||||
}
|
||||
|
||||
function parseTermsOfServiceContent(
|
||||
@ -91,12 +72,10 @@ function parseTermsOfServiceContent(
|
||||
|
||||
export type TermsState = {
|
||||
content: TermsDocument | undefined;
|
||||
status: TermsStatus;
|
||||
status: ExchangeTosStatus;
|
||||
version: string;
|
||||
};
|
||||
|
||||
type TermsStatus = "new" | "accepted" | "changed" | "notfound";
|
||||
|
||||
export type TermsDocument =
|
||||
| TermsDocumentXml
|
||||
| TermsDocumentHtml
|
||||
|
@ -29,6 +29,7 @@ import {
|
||||
import { ExchangeXmlTos } from "../../components/ExchangeToS.js";
|
||||
import { ToggleHandler } from "../../mui/handlers.js";
|
||||
import { Button } from "../../mui/Button.js";
|
||||
import { ExchangeTosStatus } from "@gnu-taler/taler-util";
|
||||
|
||||
export function LoadingUriView({ error }: State.LoadingUriError): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
@ -100,7 +101,7 @@ export function ShowButtonsNonAcceptedTosView({
|
||||
if (!ableToReviewTermsOfService) {
|
||||
return (
|
||||
<Fragment>
|
||||
{terms.status === "notfound" && (
|
||||
{terms.status === ExchangeTosStatus.NotFound && (
|
||||
<section style={{ justifyContent: "space-around", display: "flex" }}>
|
||||
<WarningText>
|
||||
<i18n.Translate>
|
||||
@ -115,7 +116,7 @@ export function ShowButtonsNonAcceptedTosView({
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{terms.status === "notfound" && (
|
||||
{terms.status === ExchangeTosStatus.NotFound && (
|
||||
<section style={{ justifyContent: "space-around", display: "flex" }}>
|
||||
<WarningText>
|
||||
<i18n.Translate>
|
||||
@ -163,7 +164,7 @@ export function ShowTosContentView({
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{terms.status !== "notfound" && !terms.content && (
|
||||
{terms.status !== ExchangeTosStatus.NotFound && !terms.content && (
|
||||
<section style={{ justifyContent: "space-around", display: "flex" }}>
|
||||
<WarningBox>
|
||||
<i18n.Translate>
|
||||
@ -204,7 +205,7 @@ export function ShowTosContentView({
|
||||
</LinkSuccess>
|
||||
</section>
|
||||
)}
|
||||
{termsAccepted && terms.status !== "notfound" && (
|
||||
{termsAccepted && terms.status !== ExchangeTosStatus.NotFound && (
|
||||
<section style={{ justifyContent: "space-around", display: "flex" }}>
|
||||
<CheckboxOutlined
|
||||
name="terms"
|
||||
|
@ -15,7 +15,12 @@
|
||||
*/
|
||||
|
||||
/* eslint-disable react-hooks/rules-of-hooks */
|
||||
import { AmountJson, Amounts, ExchangeListItem } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
AmountJson,
|
||||
Amounts,
|
||||
ExchangeListItem,
|
||||
ExchangeTosStatus,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { TalerError } from "@gnu-taler/taler-wallet-core";
|
||||
import { useState } from "preact/hooks";
|
||||
import { useAsyncAsHook } from "../../hooks/useAsyncAsHook.js";
|
||||
@ -173,10 +178,8 @@ function exchangeSelectionState(
|
||||
const [ageRestricted, setAgeRestricted] = useState(0);
|
||||
const currentExchange = selectedExchange.selected;
|
||||
const tosNeedToBeAccepted =
|
||||
!currentExchange.tos.acceptedVersion ||
|
||||
currentExchange.tos.currentVersion !==
|
||||
currentExchange.tos.acceptedVersion;
|
||||
|
||||
currentExchange.tosStatus == ExchangeTosStatus.New ||
|
||||
currentExchange.tosStatus == ExchangeTosStatus.Changed;
|
||||
/**
|
||||
* With the exchange and amount, ask the wallet the information
|
||||
* about the withdrawal
|
||||
|
@ -22,6 +22,7 @@
|
||||
import {
|
||||
Amounts,
|
||||
ExchangeFullDetails,
|
||||
ExchangeTosStatus,
|
||||
GetExchangeTosResult,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { expect } from "chai";
|
||||
@ -169,6 +170,7 @@ describe("Withdraw CTA states", () => {
|
||||
content: "just accept",
|
||||
acceptedEtag: "v1",
|
||||
currentEtag: "v1",
|
||||
tosStatus: ExchangeTosStatus.Accepted,
|
||||
}),
|
||||
} as any,
|
||||
),
|
||||
@ -254,6 +256,7 @@ describe("Withdraw CTA states", () => {
|
||||
content: "just accept",
|
||||
acceptedEtag: "v1",
|
||||
currentEtag: "v2",
|
||||
tosStatus: ExchangeTosStatus.Changed,
|
||||
}),
|
||||
setExchangeTosAccepted: async () => ({}),
|
||||
} as any,
|
||||
|
@ -37,6 +37,7 @@ import editIcon from "../../svg/edit_24px.svg";
|
||||
import { ExchangeDetails, WithdrawDetails } from "../../wallet/Transaction.js";
|
||||
import { TermsOfService } from "../../components/TermsOfService/index.js";
|
||||
import { State } from "./index.js";
|
||||
import { ExchangeTosStatus } from "@gnu-taler/taler-util";
|
||||
|
||||
export function LoadingUriView({ error }: State.LoadingUriError): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
@ -65,8 +66,7 @@ export function LoadingInfoView({ error }: State.LoadingInfoError): VNode {
|
||||
export function SuccessView(state: State.Success): VNode {
|
||||
const { i18n } = useTranslationContext();
|
||||
const currentTosVersionIsAccepted =
|
||||
state.currentExchange.tos.acceptedVersion ===
|
||||
state.currentExchange.tos.currentVersion;
|
||||
state.currentExchange.tosStatus === ExchangeTosStatus.Accepted;
|
||||
return (
|
||||
<WalletAction>
|
||||
<LogoHeader />
|
||||
|
@ -17,6 +17,7 @@
|
||||
import {
|
||||
Amounts,
|
||||
CoinDumpJson,
|
||||
CoinStatus,
|
||||
ExchangeListItem,
|
||||
NotificationType,
|
||||
} from "@gnu-taler/taler-util";
|
||||
@ -86,7 +87,7 @@ type CoinsInfo = CoinDumpJson["coins"];
|
||||
type CalculatedCoinfInfo = {
|
||||
ageKeysCount: number | undefined;
|
||||
denom_value: number;
|
||||
remain_value: number;
|
||||
//remain_value: number;
|
||||
status: string;
|
||||
from_refresh: boolean;
|
||||
id: string;
|
||||
@ -143,10 +144,10 @@ export function View({
|
||||
prev[cur.exchange_base_url].push({
|
||||
ageKeysCount: cur.ageCommitmentProof?.proof.privateKeys.length,
|
||||
denom_value: parseFloat(Amounts.stringifyValue(denom)),
|
||||
remain_value: parseFloat(
|
||||
Amounts.stringifyValue(Amounts.parseOrThrow(cur.remaining_value)),
|
||||
),
|
||||
status: cur.coin_suspended ? "suspended" : "ok",
|
||||
// remain_value: parseFloat(
|
||||
// Amounts.stringifyValue(Amounts.parseOrThrow(cur.remaining_value)),
|
||||
// ),
|
||||
status: cur.coin_status,
|
||||
from_refresh: cur.refresh_parent_coin_pub !== undefined,
|
||||
id: cur.coin_pub,
|
||||
});
|
||||
@ -254,8 +255,8 @@ export function View({
|
||||
|
||||
const coins = allcoins.reduce(
|
||||
(prev, cur) => {
|
||||
if (cur.remain_value > 0) prev.usable.push(cur);
|
||||
if (cur.remain_value === 0) prev.spent.push(cur);
|
||||
if (cur.status === CoinStatus.Fresh) prev.usable.push(cur);
|
||||
if (cur.status === CoinStatus.Dormant) prev.spent.push(cur);
|
||||
return prev;
|
||||
},
|
||||
{
|
||||
@ -356,7 +357,6 @@ function ShowAllCoins({
|
||||
<tr key={idx}>
|
||||
<td>{c.id.substring(0, 5)}</td>
|
||||
<td>{c.denom_value}</td>
|
||||
<td>{c.remain_value}</td>
|
||||
<td>{c.status}</td>
|
||||
<td>{c.from_refresh ? "true" : "false"}</td>
|
||||
<td>{String(c.ageKeysCount)}</td>
|
||||
@ -396,7 +396,6 @@ function ShowAllCoins({
|
||||
<tr key={idx}>
|
||||
<td>{c.id.substring(0, 5)}</td>
|
||||
<td>{c.denom_value}</td>
|
||||
<td>{c.remain_value}</td>
|
||||
<td>{c.status}</td>
|
||||
<td>{c.from_refresh ? "true" : "false"}</td>
|
||||
</tr>
|
||||
|
@ -14,7 +14,11 @@
|
||||
GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
import { ExchangeListItem, WalletCoreVersion } from "@gnu-taler/taler-util";
|
||||
import {
|
||||
ExchangeListItem,
|
||||
ExchangeTosStatus,
|
||||
WalletCoreVersion,
|
||||
} from "@gnu-taler/taler-util";
|
||||
import { Fragment, h, VNode } from "preact";
|
||||
import { Checkbox } from "../components/Checkbox.js";
|
||||
import { ErrorTalerOperation } from "../components/ErrorTalerOperation.js";
|
||||
@ -36,7 +40,6 @@ import { useBackupDeviceName } from "../hooks/useBackupDeviceName.js";
|
||||
import { useAutoOpenPermissions } from "../hooks/useAutoOpenPermissions.js";
|
||||
import { ToggleHandler } from "../mui/handlers.js";
|
||||
import { Pages } from "../NavigationBar.js";
|
||||
import { buildTermsOfServiceStatus } from "../components/TermsOfService/utils.js";
|
||||
import * as wxApi from "../wxApi.js";
|
||||
import { platform } from "../platform/api.js";
|
||||
import { useClipboardPermissions } from "../hooks/useClipboardPermissions.js";
|
||||
@ -181,26 +184,21 @@ export function SettingsView({
|
||||
<tbody>
|
||||
{knownExchanges.map((e, idx) => {
|
||||
function Status(): VNode {
|
||||
const status = buildTermsOfServiceStatus(
|
||||
e.tos.content,
|
||||
e.tos.acceptedVersion,
|
||||
e.tos.currentVersion,
|
||||
);
|
||||
switch (status) {
|
||||
case "accepted":
|
||||
switch (e.tosStatus) {
|
||||
case ExchangeTosStatus.Accepted:
|
||||
return (
|
||||
<SuccessText>
|
||||
<i18n.Translate>ok</i18n.Translate>
|
||||
</SuccessText>
|
||||
);
|
||||
case "changed":
|
||||
case ExchangeTosStatus.Changed:
|
||||
return (
|
||||
<WarningText>
|
||||
<i18n.Translate>changed</i18n.Translate>
|
||||
</WarningText>
|
||||
);
|
||||
case "new":
|
||||
case "notfound":
|
||||
case ExchangeTosStatus.New:
|
||||
case ExchangeTosStatus.NotFound:
|
||||
return (
|
||||
<DestructiveText>
|
||||
<i18n.Translate>not accepted</i18n.Translate>
|
||||
|
Loading…
Reference in New Issue
Block a user