taler-util,wallet-core: make AbsoluteTime opaque

This commit is contained in:
Florian Dold 2023-05-26 13:52:00 +02:00
parent cd8f76db61
commit 96d9ea3840
No known key found for this signature in database
GPG Key ID: D2E4F00F29D02A4B
20 changed files with 109 additions and 91 deletions

View File

@ -70,9 +70,7 @@ export function useComponentState({ account }: Props): State {
} }
const when: AbsoluteTime = !date const when: AbsoluteTime = !date
? AbsoluteTime.never() ? AbsoluteTime.never()
: { : AbsoluteTime.fromMilliseconds(date);
t_ms: date,
};
const amount = Amounts.parse(`${anyItem.currency}:${anyItem.amount}`); const amount = Amounts.parse(`${anyItem.currency}:${anyItem.amount}`);
const subject = anyItem.subject; const subject = anyItem.subject;
return { return {

View File

@ -21,6 +21,7 @@
import * as tests from "@gnu-taler/web-util/testing"; import * as tests from "@gnu-taler/web-util/testing";
import { ReadyView } from "./views.js"; import { ReadyView } from "./views.js";
import { AbsoluteTime } from "@gnu-taler/taler-util";
export default { export default {
title: "transaction list", title: "transaction list",
@ -37,9 +38,7 @@ export const Ready = tests.createExample(ReadyView, {
counterpart: "ASD", counterpart: "ASD",
negative: false, negative: false,
subject: "Some", subject: "Some",
when: { when: AbsoluteTime.now(),
t_ms: new Date().getTime(),
},
}, },
], ],
}); });

View File

@ -18,9 +18,9 @@ export function InputDate<T extends object, K extends keyof T>(
converter={{ converter={{
//@ts-ignore //@ts-ignore
fromStringUI: (v): AbsoluteTime => { fromStringUI: (v): AbsoluteTime => {
if (!v) return { t_ms: "never" }; if (!v) return AbsoluteTime.never();
const t_ms = parse(v, pattern, Date.now()).getTime(); const t_ms = parse(v, pattern, Date.now()).getTime();
return { t_ms }; return AbsoluteTime.fromMilliseconds(t_ms);
}, },
//@ts-ignore //@ts-ignore
toStringUI: (v: AbsoluteTime) => { toStringUI: (v: AbsoluteTime) => {

View File

@ -9,6 +9,7 @@ import { v1 as form_902_4e_v1 } from "../forms/902_4e.js";
import { v1 as form_902_5e_v1 } from "../forms/902_5e.js"; import { v1 as form_902_5e_v1 } from "../forms/902_5e.js";
import { v1 as form_902_9e_v1 } from "../forms/902_9e.js"; import { v1 as form_902_9e_v1 } from "../forms/902_9e.js";
import { DocumentDuplicateIcon } from "@heroicons/react/24/solid"; import { DocumentDuplicateIcon } from "@heroicons/react/24/solid";
import { AbsoluteTime } from "@gnu-taler/taler-util";
export function AntiMoneyLaunderingForm({ number }: { number?: string }) { export function AntiMoneyLaunderingForm({ number }: { number?: string }) {
const selectedForm = Number.parseInt(number ?? "0", 10); const selectedForm = Number.parseInt(number ?? "0", 10);
@ -18,9 +19,7 @@ export function AntiMoneyLaunderingForm({ number }: { number?: string }) {
const showingFrom = allForms[selectedForm].impl; const showingFrom = allForms[selectedForm].impl;
const storedValue = { const storedValue = {
fullName: "loggedIn_user_fullname", fullName: "loggedIn_user_fullname",
when: { when: AbsoluteTime.now(),
t_ms: new Date().getTime(),
},
}; };
return ( return (
<NiceForm initial={storedValue} form={showingFrom} onUpdate={() => {}} /> <NiceForm initial={storedValue} form={showingFrom} onUpdate={() => {}} />

View File

@ -410,9 +410,7 @@ export function getExpiry(
if (Number.isNaN(expiryDateMs)) { if (Number.isNaN(expiryDateMs)) {
t = AbsoluteTime.now(); t = AbsoluteTime.now();
} else { } else {
t = { t = AbsoluteTime.fromMilliseconds(expiryDateMs);
t_ms: expiryDateMs,
};
} }
if (opt.minDuration) { if (opt.minDuration) {
const t2 = AbsoluteTime.addDuration(AbsoluteTime.now(), opt.minDuration); const t2 = AbsoluteTime.addDuration(AbsoluteTime.now(), opt.minDuration);

View File

@ -27,6 +27,8 @@ declare const flavor_AbsoluteTime: unique symbol;
declare const flavor_TalerProtocolTimestamp: unique symbol; declare const flavor_TalerProtocolTimestamp: unique symbol;
declare const flavor_TalerPreciseTimestamp: unique symbol; declare const flavor_TalerPreciseTimestamp: unique symbol;
const opaque_AbsoluteTime: unique symbol = Symbol("opaque_AbsoluteTime");
// FIXME: Make this opaque! // FIXME: Make this opaque!
export interface AbsoluteTime { export interface AbsoluteTime {
/** /**
@ -35,6 +37,10 @@ export interface AbsoluteTime {
readonly t_ms: number | "never"; readonly t_ms: number | "never";
readonly _flavor?: typeof flavor_AbsoluteTime; readonly _flavor?: typeof flavor_AbsoluteTime;
// Make the type opaque, we only want our constructors
// to able to create an AbsoluteTime value.
[opaque_AbsoluteTime]: true;
} }
export interface TalerProtocolTimestamp { export interface TalerProtocolTimestamp {
@ -69,7 +75,7 @@ export namespace TalerPreciseTimestamp {
export function round(t: TalerPreciseTimestamp): TalerProtocolTimestamp { export function round(t: TalerPreciseTimestamp): TalerProtocolTimestamp {
return { return {
t_s: t.t_s, t_s: t.t_s,
} };
} }
export function fromSeconds(s: number): TalerPreciseTimestamp { export function fromSeconds(s: number): TalerPreciseTimestamp {
@ -78,6 +84,13 @@ export namespace TalerPreciseTimestamp {
off_us: (s - Math.floor(s)) / 1000 / 1000, off_us: (s - Math.floor(s)) / 1000 / 1000,
}; };
} }
export function fromMilliseconds(ms: number): TalerPreciseTimestamp {
return {
t_s: Math.floor(ms / 1000),
off_us: Math.floor((ms - Math.floor(ms / 100) * 1000) * 1000),
}
}
} }
export namespace TalerProtocolTimestamp { export namespace TalerProtocolTimestamp {
@ -269,12 +282,21 @@ export namespace AbsoluteTime {
export function now(): AbsoluteTime { export function now(): AbsoluteTime {
return { return {
t_ms: new Date().getTime() + timeshift, t_ms: new Date().getTime() + timeshift,
[opaque_AbsoluteTime]: true,
}; };
} }
export function never(): AbsoluteTime { export function never(): AbsoluteTime {
return { return {
t_ms: "never", t_ms: "never",
[opaque_AbsoluteTime]: true,
};
}
export function fromMilliseconds(ms: number): AbsoluteTime {
return {
t_ms: ms,
[opaque_AbsoluteTime]: true,
}; };
} }
@ -299,22 +321,22 @@ export namespace AbsoluteTime {
export function min(t1: AbsoluteTime, t2: AbsoluteTime): AbsoluteTime { export function min(t1: AbsoluteTime, t2: AbsoluteTime): AbsoluteTime {
if (t1.t_ms === "never") { if (t1.t_ms === "never") {
return { t_ms: t2.t_ms }; return { t_ms: t2.t_ms, [opaque_AbsoluteTime]: true };
} }
if (t2.t_ms === "never") { if (t2.t_ms === "never") {
return { t_ms: t2.t_ms }; return { t_ms: t2.t_ms, [opaque_AbsoluteTime]: true };
} }
return { t_ms: Math.min(t1.t_ms, t2.t_ms) }; return { t_ms: Math.min(t1.t_ms, t2.t_ms), [opaque_AbsoluteTime]: true };
} }
export function max(t1: AbsoluteTime, t2: AbsoluteTime): AbsoluteTime { export function max(t1: AbsoluteTime, t2: AbsoluteTime): AbsoluteTime {
if (t1.t_ms === "never") { if (t1.t_ms === "never") {
return { t_ms: "never" }; return { t_ms: "never", [opaque_AbsoluteTime]: true };
} }
if (t2.t_ms === "never") { if (t2.t_ms === "never") {
return { t_ms: "never" }; return { t_ms: "never", [opaque_AbsoluteTime]: true };
} }
return { t_ms: Math.max(t1.t_ms, t2.t_ms) }; return { t_ms: Math.max(t1.t_ms, t2.t_ms), [opaque_AbsoluteTime]: true };
} }
export function difference(t1: AbsoluteTime, t2: AbsoluteTime): Duration { export function difference(t1: AbsoluteTime, t2: AbsoluteTime): Duration {
@ -331,22 +353,26 @@ export namespace AbsoluteTime {
return cmp(t, now()) <= 0; return cmp(t, now()) <= 0;
} }
export function fromProtocolTimestamp(t: TalerProtocolTimestamp): AbsoluteTime { export function fromProtocolTimestamp(
t: TalerProtocolTimestamp,
): AbsoluteTime {
if (t.t_s === "never") { if (t.t_s === "never") {
return { t_ms: "never" }; return { t_ms: "never", [opaque_AbsoluteTime]: true };
} }
return { return {
t_ms: t.t_s * 1000, t_ms: t.t_s * 1000,
[opaque_AbsoluteTime]: true,
}; };
} }
export function fromPreciseTimestamp(t: TalerPreciseTimestamp): AbsoluteTime { export function fromPreciseTimestamp(t: TalerPreciseTimestamp): AbsoluteTime {
if (t.t_s === "never") { if (t.t_s === "never") {
return { t_ms: "never" }; return { t_ms: "never", [opaque_AbsoluteTime]: true };
} }
const offsetUs = t.off_us ?? 0; const offsetUs = t.off_us ?? 0;
return { return {
t_ms: t.t_s * 1000 + offsetUs / 1000, t_ms: t.t_s * 1000 + offsetUs / 1000,
[opaque_AbsoluteTime]: true,
}; };
} }
@ -361,10 +387,12 @@ export namespace AbsoluteTime {
return { return {
t_s, t_s,
off_us, off_us,
} };
} }
export function toProtocolTimestamp(at: AbsoluteTime): TalerProtocolTimestamp { export function toProtocolTimestamp(
at: AbsoluteTime,
): TalerProtocolTimestamp {
if (at.t_ms === "never") { if (at.t_ms === "never") {
return { t_s: "never" }; return { t_s: "never" };
} }
@ -397,9 +425,9 @@ export namespace AbsoluteTime {
export function addDuration(t1: AbsoluteTime, d: Duration): AbsoluteTime { export function addDuration(t1: AbsoluteTime, d: Duration): AbsoluteTime {
if (t1.t_ms === "never" || d.d_ms === "forever") { if (t1.t_ms === "never" || d.d_ms === "forever") {
return { t_ms: "never" }; return { t_ms: "never", [opaque_AbsoluteTime]: true };
} }
return { t_ms: t1.t_ms + d.d_ms }; return { t_ms: t1.t_ms + d.d_ms, [opaque_AbsoluteTime]: true };
} }
export function subtractDuraction( export function subtractDuraction(
@ -407,12 +435,12 @@ export namespace AbsoluteTime {
d: Duration, d: Duration,
): AbsoluteTime { ): AbsoluteTime {
if (t1.t_ms === "never") { if (t1.t_ms === "never") {
return { t_ms: "never" }; return { t_ms: "never", [opaque_AbsoluteTime]: true };
} }
if (d.d_ms === "forever") { if (d.d_ms === "forever") {
return { t_ms: 0 }; return { t_ms: 0, [opaque_AbsoluteTime]: true };
} }
return { t_ms: Math.max(0, t1.t_ms - d.d_ms) }; return { t_ms: Math.max(0, t1.t_ms - d.d_ms), [opaque_AbsoluteTime]: true };
} }
export function stringify(t: AbsoluteTime): string { export function stringify(t: AbsoluteTime): string {
@ -487,10 +515,10 @@ export const codecForAbsoluteTime: Codec<AbsoluteTime> = {
const t_ms = x.t_ms; const t_ms = x.t_ms;
if (typeof t_ms === "string") { if (typeof t_ms === "string") {
if (t_ms === "never") { if (t_ms === "never") {
return { t_ms: "never" }; return { t_ms: "never", [opaque_AbsoluteTime]: true };
} }
} else if (typeof t_ms === "number") { } else if (typeof t_ms === "number") {
return { t_ms }; return { t_ms, [opaque_AbsoluteTime]: true };
} }
throw Error(`expected timestamp at ${renderContext(c)}`); throw Error(`expected timestamp at ${renderContext(c)}`);
}, },

View File

@ -65,6 +65,7 @@ import {
} from "./taler-types.js"; } from "./taler-types.js";
import { import {
AbsoluteTime, AbsoluteTime,
TalerPreciseTimestamp,
TalerProtocolDuration, TalerProtocolDuration,
TalerProtocolTimestamp, TalerProtocolTimestamp,
codecForAbsoluteTime, codecForAbsoluteTime,
@ -2024,7 +2025,7 @@ interface AttentionPushPaymentReceived {
export type UserAttentionUnreadList = Array<{ export type UserAttentionUnreadList = Array<{
info: AttentionInfo; info: AttentionInfo;
when: AbsoluteTime; when: TalerPreciseTimestamp;
read: boolean; read: boolean;
}>; }>;

View File

@ -2074,8 +2074,10 @@ export interface UserAttentionRecord {
info: AttentionInfo; info: AttentionInfo;
entityId: string; entityId: string;
/** /**
* When the notification was created. * When the notification was created.
* FIXME: This should be a TalerPreciseTimestamp
*/ */
createdMs: number; createdMs: number;

View File

@ -74,9 +74,7 @@ export async function getUserAttentions(
return; return;
pending.push({ pending.push({
info: x.info, info: x.info,
when: { when: TalerPreciseTimestamp.fromMilliseconds(x.createdMs),
t_ms: x.createdMs,
},
read: x.read !== undefined, read: x.read !== undefined,
}); });
}); });

View File

@ -132,10 +132,10 @@ export function createPairTimeline(
//check which start after, add gap so both list starts at the same time //check which start after, add gap so both list starts at the same time
// one list may be empty // one list may be empty
const leftStartTime: AbsoluteTime = leftGroupIsEmpty const leftStartTime: AbsoluteTime = leftGroupIsEmpty
? { t_ms: "never" } ? AbsoluteTime.never()
: left[li].from; : left[li].from;
const rightStartTime: AbsoluteTime = rightGroupIsEmpty const rightStartTime: AbsoluteTime = rightGroupIsEmpty
? { t_ms: "never" } ? AbsoluteTime.never()
: right[ri].from; : right[ri].from;
//first time cut is the smallest time //first time cut is the smallest time

View File

@ -129,7 +129,7 @@ function updateTimeout(
throw Error("assertion failed"); throw Error("assertion failed");
} }
if (p.backoffDelta.d_ms === "forever") { if (p.backoffDelta.d_ms === "forever") {
r.nextRetry = { t_ms: "never" }; r.nextRetry = AbsoluteTime.never();
return; return;
} }
@ -141,7 +141,7 @@ function updateTimeout(
(p.maxTimeout.d_ms === "forever" (p.maxTimeout.d_ms === "forever"
? nextIncrement ? nextIncrement
: Math.min(p.maxTimeout.d_ms, nextIncrement)); : Math.min(p.maxTimeout.d_ms, nextIncrement));
r.nextRetry = { t_ms: t }; r.nextRetry = AbsoluteTime.fromMilliseconds(t);
} }
export namespace RetryInfo { export namespace RetryInfo {

View File

@ -19,7 +19,11 @@
* @author Sebastian Javier Marchano (sebasjm) * @author Sebastian Javier Marchano (sebasjm)
*/ */
import { PreparePayResult, PreparePayResultType } from "@gnu-taler/taler-util"; import {
AbsoluteTime,
PreparePayResult,
PreparePayResultType,
} from "@gnu-taler/taler-util";
import * as tests from "@gnu-taler/web-util/testing"; import * as tests from "@gnu-taler/web-util/testing";
import { ReadyView } from "./views.js"; import { ReadyView } from "./views.js";
@ -43,9 +47,9 @@ export const Ready = tests.createExample(ReadyView, {
status: PreparePayResultType.PaymentPossible, status: PreparePayResultType.PaymentPossible,
amountEffective: "ARS:1", amountEffective: "ARS:1",
} as PreparePayResult, } as PreparePayResult,
expiration: { expiration: AbsoluteTime.fromMilliseconds(
t_ms: new Date().getTime() + 1000 * 60 * 60, new Date().getTime() + 1000 * 60 * 60,
}, ),
accept: {}, accept: {},
cancel: {}, cancel: {},
}); });

View File

@ -21,6 +21,7 @@
import * as tests from "@gnu-taler/web-util/testing"; import * as tests from "@gnu-taler/web-util/testing";
import { ReadyView } from "./views.js"; import { ReadyView } from "./views.js";
import { AbsoluteTime } from "@gnu-taler/taler-util";
export default { export default {
title: "transfer pickup", title: "transfer pickup",
@ -38,9 +39,9 @@ export const Ready = tests.createExample(ReadyView, {
fraction: 0, fraction: 0,
}, },
summary: "some subject", summary: "some subject",
expiration: { expiration: AbsoluteTime.fromMilliseconds(
t_ms: new Date().getTime() + 1000 * 60 * 60, new Date().getTime() + 1000 * 60 * 60,
}, ),
accept: {}, accept: {},
cancel: {}, cancel: {},
}); });

View File

@ -26,7 +26,11 @@ import {
ShowRecoveryInfo, ShowRecoveryInfo,
} from "./BackupPage.js"; } from "./BackupPage.js";
import * as tests from "@gnu-taler/web-util/testing"; import * as tests from "@gnu-taler/web-util/testing";
import { TalerPreciseTimestamp, TalerProtocolTimestamp } from "@gnu-taler/taler-util"; import {
AbsoluteTime,
TalerPreciseTimestamp,
TalerProtocolTimestamp,
} from "@gnu-taler/taler-util";
export default { export default {
title: "backup", title: "backup",
@ -45,9 +49,7 @@ export const LotOfProviders = tests.createExample(TestedComponent, {
], ],
paymentStatus: { paymentStatus: {
type: ProviderPaymentType.Paid, type: ProviderPaymentType.Paid,
paidUntil: { paidUntil: AbsoluteTime.fromMilliseconds(1656599921000),
t_ms: 1656599921000,
},
}, },
terms: { terms: {
annualFee: "ARS:1", annualFee: "ARS:1",
@ -66,9 +68,9 @@ export const LotOfProviders = tests.createExample(TestedComponent, {
], ],
paymentStatus: { paymentStatus: {
type: ProviderPaymentType.Paid, type: ProviderPaymentType.Paid,
paidUntil: { paidUntil: AbsoluteTime.fromMilliseconds(
t_ms: addDays(new Date(), 13).getTime(), addDays(new Date(), 13).getTime(),
}, ),
}, },
terms: { terms: {
annualFee: "ARS:1", annualFee: "ARS:1",
@ -123,9 +125,7 @@ export const LotOfProviders = tests.createExample(TestedComponent, {
storageLimitInMegabytes: 16, storageLimitInMegabytes: 16,
supportedProtocolVersion: "1", supportedProtocolVersion: "1",
}, },
paidUntil: { paidUntil: AbsoluteTime.never(),
t_ms: "never",
},
}, },
terms: { terms: {
annualFee: "KUDOS:0.1", annualFee: "KUDOS:0.1",
@ -177,9 +177,7 @@ export const OneProvider = tests.createExample(TestedComponent, {
], ],
paymentStatus: { paymentStatus: {
type: ProviderPaymentType.Paid, type: ProviderPaymentType.Paid,
paidUntil: { paidUntil: AbsoluteTime.fromMilliseconds(1656599921000),
t_ms: 1656599921000,
},
}, },
terms: { terms: {
annualFee: "ARS:1", annualFee: "ARS:1",

View File

@ -22,7 +22,7 @@
import { PendingTaskType, TaskId } from "@gnu-taler/taler-wallet-core"; import { PendingTaskType, TaskId } from "@gnu-taler/taler-wallet-core";
import * as tests from "@gnu-taler/web-util/testing"; import * as tests from "@gnu-taler/web-util/testing";
import { View as TestedComponent } from "./DeveloperPage.js"; import { View as TestedComponent } from "./DeveloperPage.js";
import { PendingIdStr } from "@gnu-taler/taler-util"; import { AbsoluteTime, PendingIdStr } from "@gnu-taler/taler-util";
export default { export default {
title: "developer", title: "developer",
@ -41,9 +41,7 @@ export const AllOff = tests.createExample(TestedComponent, {
exchangeBaseUrl: "http://exchange.url.", exchangeBaseUrl: "http://exchange.url.",
givesLifeness: false, givesLifeness: false,
lastError: undefined, lastError: undefined,
timestampDue: { timestampDue: AbsoluteTime.fromMilliseconds(123123213),
t_ms: 123123213,
},
retryInfo: undefined, retryInfo: undefined,
isDue: false, isDue: false,
isLongpolling: false, isLongpolling: false,

View File

@ -15,6 +15,7 @@
*/ */
import { import {
AbsoluteTime,
Amounts, Amounts,
CoinDumpJson, CoinDumpJson,
CoinStatus, CoinStatus,
@ -404,7 +405,7 @@ export function View({
<i18n.Translate> <i18n.Translate>
Database exported at Database exported at
<Time <Time
timestamp={{ t_ms: downloadedDatabase.time.getTime() }} timestamp={AbsoluteTime.fromMilliseconds(downloadedDatabase.time.getTime())}
format="yyyy/MM/dd HH:mm:ss" format="yyyy/MM/dd HH:mm:ss"
/> />
<a <a

View File

@ -15,6 +15,7 @@
*/ */
import { import {
AbsoluteTime,
Amounts, Amounts,
Balance, Balance,
NotificationType, NotificationType,
@ -261,7 +262,7 @@ export function HistoryView({
<Fragment key={i}> <Fragment key={i}>
<DateSeparator> <DateSeparator>
<Time <Time
timestamp={{ t_ms: Number.parseInt(d, 10) }} timestamp={AbsoluteTime.fromMilliseconds(Number.parseInt(d, 10))}
format="dd MMMM yyyy" format="dd MMMM yyyy"
/> />
</DateSeparator> </DateSeparator>

View File

@ -22,6 +22,7 @@
import { import {
AbsoluteTime, AbsoluteTime,
AttentionType, AttentionType,
TalerPreciseTimestamp,
TransactionIdStr, TransactionIdStr,
} from "@gnu-taler/taler-util"; } from "@gnu-taler/taler-util";
import * as tests from "@gnu-taler/web-util/testing"; import * as tests from "@gnu-taler/web-util/testing";
@ -34,7 +35,7 @@ export default {
export const Ready = tests.createExample(ReadyView, { export const Ready = tests.createExample(ReadyView, {
list: [ list: [
{ {
when: AbsoluteTime.now(), when: TalerPreciseTimestamp.now(),
read: false, read: false,
info: { info: {
type: AttentionType.KycWithdrawal, type: AttentionType.KycWithdrawal,
@ -42,7 +43,7 @@ export const Ready = tests.createExample(ReadyView, {
}, },
}, },
{ {
when: AbsoluteTime.now(), when: TalerPreciseTimestamp.now(),
read: false, read: false,
info: { info: {
type: AttentionType.MerchantRefund, type: AttentionType.MerchantRefund,
@ -50,7 +51,7 @@ export const Ready = tests.createExample(ReadyView, {
}, },
}, },
{ {
when: AbsoluteTime.now(), when: TalerPreciseTimestamp.now(),
read: false, read: false,
info: { info: {
type: AttentionType.BackupUnpaid, type: AttentionType.BackupUnpaid,

View File

@ -52,7 +52,8 @@ export function ReadyView({ list }: State.Ready): VNode {
} }
const byDate = list.reduce((rv, x) => { const byDate = list.reduce((rv, x) => {
const theDate = x.when.t_ms === "never" ? 0 : normalizeToDay(x.when.t_ms); const theDate =
x.when.t_s === "never" ? 0 : normalizeToDay(x.when.t_s * 1000);
if (theDate) { if (theDate) {
(rv[theDate] = rv[theDate] || []).push(x); (rv[theDate] = rv[theDate] || []).push(x);
} }
@ -68,7 +69,9 @@ export function ReadyView({ list }: State.Ready): VNode {
<Fragment key={i}> <Fragment key={i}>
<DateSeparator> <DateSeparator>
<Time <Time
timestamp={{ t_ms: Number.parseInt(d, 10) }} timestamp={AbsoluteTime.fromMilliseconds(
Number.parseInt(d, 10),
)}
format="dd MMMM yyyy" format="dd MMMM yyyy"
/> />
</DateSeparator> </DateSeparator>
@ -77,7 +80,7 @@ export function ReadyView({ list }: State.Ready): VNode {
key={i} key={i}
info={n.info} info={n.info}
isRead={n.read} isRead={n.read}
timestamp={n.when} timestamp={AbsoluteTime.fromPreciseTimestamp(n.when)}
/> />
))} ))}
</Fragment> </Fragment>

View File

@ -46,9 +46,7 @@ export const Active = tests.createExample(TestedComponent, {
], ],
paymentStatus: { paymentStatus: {
type: ProviderPaymentType.Paid, type: ProviderPaymentType.Paid,
paidUntil: { paidUntil: AbsoluteTime.fromMilliseconds(1656599921000),
t_ms: 1656599921000,
},
}, },
terms: { terms: {
annualFee: "EUR:1", annualFee: "EUR:1",
@ -72,9 +70,7 @@ export const ActiveErrorSync = tests.createExample(TestedComponent, {
], ],
paymentStatus: { paymentStatus: {
type: ProviderPaymentType.Paid, type: ProviderPaymentType.Paid,
paidUntil: { paidUntil: AbsoluteTime.fromMilliseconds(1656599921000),
t_ms: 1656599921000,
},
}, },
lastError: { lastError: {
code: 2002, code: 2002,
@ -105,9 +101,7 @@ export const ActiveBackupProblemUnreadable = tests.createExample(
], ],
paymentStatus: { paymentStatus: {
type: ProviderPaymentType.Paid, type: ProviderPaymentType.Paid,
paidUntil: { paidUntil: AbsoluteTime.fromMilliseconds(1656599921000),
t_ms: 1656599921000,
},
}, },
backupProblem: { backupProblem: {
type: "backup-unreadable", type: "backup-unreadable",
@ -133,17 +127,13 @@ export const ActiveBackupProblemDevice = tests.createExample(TestedComponent, {
], ],
paymentStatus: { paymentStatus: {
type: ProviderPaymentType.Paid, type: ProviderPaymentType.Paid,
paidUntil: { paidUntil: AbsoluteTime.fromMilliseconds(1656599921000),
t_ms: 1656599921000,
},
}, },
backupProblem: { backupProblem: {
type: "backup-conflicting-device", type: "backup-conflicting-device",
myDeviceId: "my-device-id", myDeviceId: "my-device-id",
otherDeviceId: "other-device-id", otherDeviceId: "other-device-id",
backupTimestamp: { backupTimestamp: AbsoluteTime.fromMilliseconds(1656599921000),
t_ms: 1656599921000,
},
}, },
terms: { terms: {
annualFee: "EUR:1", annualFee: "EUR:1",
@ -217,9 +207,7 @@ export const ActiveTermsChanged = tests.createExample(TestedComponent, {
paymentProposalIds: [], paymentProposalIds: [],
paymentStatus: { paymentStatus: {
type: ProviderPaymentType.TermsChanged, type: ProviderPaymentType.TermsChanged,
paidUntil: { paidUntil: AbsoluteTime.fromMilliseconds(1656599921000),
t_ms: 1656599921000,
},
newTerms: { newTerms: {
annualFee: "EUR:10", annualFee: "EUR:10",
storageLimitInMegabytes: 8, storageLimitInMegabytes: 8,