/* This file is part of GNU Taler (C) 2022 Taler Systems S.A. GNU 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. GNU 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 GNU Taler; see the file COPYING. If not, see */ import { AbsoluteTime, NotificationType, ObservabilityEventType, RequestProgressNotification, TalerErrorCode, TalerErrorDetail, TaskProgressNotification, WalletNotification, assertUnreachable, } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; import { useTranslationContext } from "@gnu-taler/web-util/browser"; import { Fragment, VNode, h } from "preact"; import { useEffect, useState } from "preact/hooks"; import { Pages } from "../NavigationBar.js"; import { useBackendContext } from "../context/backend.js"; import { useAsyncAsHook } from "../hooks/useAsyncAsHook.js"; import { useSettings } from "../hooks/useSettings.js"; import { Button } from "../mui/Button.js"; import { TextField } from "../mui/TextField.js"; import { SafeHandler } from "../mui/handlers.js"; import { WxApiType } from "../wxApi.js"; import { WalletActivityTrack } from "../wxBackend.js"; import { Modal } from "./Modal.js"; import { Time } from "./Time.js"; const OPEN_ACTIVITY_HEIGHT_PX = 250; const CLOSE_ACTIVITY_HEIGHT_PX = 40; export function WalletActivity(): VNode { const { i18n } = useTranslationContext(); const [, updateSettings] = useSettings(); const [collapsed, setCollcapsed] = useState(true); useEffect(() => { document.body.style.marginBottom = `${ collapsed ? CLOSE_ACTIVITY_HEIGHT_PX : OPEN_ACTIVITY_HEIGHT_PX }px`; return () => { document.body.style.marginBottom = "0px"; }; }, [collapsed]); const [table, setTable] = useState<"tasks" | "events">("events"); if (collapsed) { return (
{ setCollcapsed(!collapsed); }} >
Click here to open the wallet activity tab.
); } return (
{ setCollcapsed(!collapsed); }} >
{(function (): VNode { switch (table) { case "events": { return ; } case "tasks": { return ; } default: { assertUnreachable(table); } } })()}
); } interface MoreInfoPRops { events: (WalletNotification & { when: AbsoluteTime })[]; onClick: (content: VNode) => void; } function ShowBalanceChange({ events }: MoreInfoPRops): VNode { if (!events.length) return ; const not = events[0]; if (not.type !== NotificationType.BalanceChange) return ; return (
Transaction
{not.hintTransactionId.substring(0, 10)}
); } function ShowBackupOperationError({ events, onClick }: MoreInfoPRops): VNode { if (!events.length) return ; const not = events[0]; if (not.type !== NotificationType.BackupOperationError) return ; return (
Error
{ e.preventDefault(); const error = not.error; onClick(
Code
{TalerErrorCode[error.code]} ({error.code})
Hint
{error.hint ?? "--"}
Time
                  {JSON.stringify(error, undefined, 2)}
                
, ); }} > {TalerErrorCode[not.error.code]}
); } function ShowTransactionStateTransition({ events, onClick, }: MoreInfoPRops): VNode { if (!events.length) return ; const not = events[0]; if (not.type !== NotificationType.TransactionStateTransition) return ; return (
Old state
{not.oldTxState.major} - {not.oldTxState.minor ?? ""}
New state
{not.newTxState.major} - {not.newTxState.minor ?? ""}
Transaction
{not.transactionId.substring(0, 10)}
{not.errorInfo ? (
Error
{ if (!not.errorInfo) return; e.preventDefault(); const error = not.errorInfo; onClick(
Code
{TalerErrorCode[error.code]} ({error.code})
Hint
{error.hint ?? "--"}
Message
{error.message ?? "--"}
, ); }} > {TalerErrorCode[not.errorInfo.code]}
) : undefined}
Experimental
          {JSON.stringify(not.experimentalUserData, undefined, 2)}
        
); } function ShowExchangeStateTransition({ events }: MoreInfoPRops): VNode { if (!events.length) return ; const not = events[0]; if (not.type !== NotificationType.ExchangeStateTransition) return ; return (
Exchange
{not.exchangeBaseUrl}
{not.oldExchangeState && not.newExchangeState.exchangeEntryStatus !== not.oldExchangeState?.exchangeEntryStatus && (
Entry status
from {not.oldExchangeState.exchangeEntryStatus} to{" "} {not.newExchangeState.exchangeEntryStatus}
)} {not.oldExchangeState && not.newExchangeState.exchangeUpdateStatus !== not.oldExchangeState?.exchangeUpdateStatus && (
Update status
from {not.oldExchangeState.exchangeUpdateStatus} to{" "} {not.newExchangeState.exchangeUpdateStatus}
)} {not.oldExchangeState && not.newExchangeState.tosStatus !== not.oldExchangeState?.tosStatus && (
Tos status
from {not.oldExchangeState.tosStatus} to{" "} {not.newExchangeState.tosStatus}
)}
); } type ObservaNotifWithTime = ( | TaskProgressNotification | RequestProgressNotification ) & { when: AbsoluteTime; }; function ShowObservabilityEvent({ events, onClick }: MoreInfoPRops): VNode { // let prev: ObservaNotifWithTime; const asd = events.map((not, idx) => { if ( not.type !== NotificationType.RequestObservabilityEvent && not.type !== NotificationType.TaskObservabilityEvent ) return ; const title = (function () { switch (not.event.type) { case ObservabilityEventType.HttpFetchFinishError: case ObservabilityEventType.HttpFetchFinishSuccess: case ObservabilityEventType.HttpFetchStart: return "HTTP Request"; case ObservabilityEventType.DbQueryFinishSuccess: case ObservabilityEventType.DbQueryFinishError: case ObservabilityEventType.DbQueryStart: return "Database"; case ObservabilityEventType.RequestFinishSuccess: case ObservabilityEventType.RequestFinishError: case ObservabilityEventType.RequestStart: return "Wallet"; case ObservabilityEventType.CryptoFinishSuccess: case ObservabilityEventType.CryptoFinishError: case ObservabilityEventType.CryptoStart: return "Crypto"; case ObservabilityEventType.TaskStart: return "Task start"; case ObservabilityEventType.TaskStop: return "Task stop"; case ObservabilityEventType.TaskReset: return "Task reset"; case ObservabilityEventType.ShepherdTaskResult: return "Schedule"; case ObservabilityEventType.DeclareTaskDependency: return "Task dependency"; case ObservabilityEventType.Message: return "Message"; } })(); return ( ); }); return ( {asd}
Event Info Start End
); } function ShowObervavilityDetails({ title, notif, onClick, prev, }: { title: string; notif: ObservaNotifWithTime; prev?: ObservaNotifWithTime; onClick: (content: VNode) => void; }): VNode { switch (notif.event.type) { case ObservabilityEventType.HttpFetchStart: case ObservabilityEventType.HttpFetchFinishError: case ObservabilityEventType.HttpFetchFinishSuccess: { return ( { e.preventDefault(); onClick(
                      {JSON.stringify({ event: notif, prev }, undefined, 2)}
                    
, ); }} > {title}
{notif.event.url}{" "} {prev?.event.type === ObservabilityEventType.HttpFetchFinishSuccess ? ( `(${prev.event.status})` ) : prev?.event.type === ObservabilityEventType.HttpFetchFinishError ? ( { e.preventDefault(); if ( prev.event.type !== ObservabilityEventType.HttpFetchFinishError ) return; const error = prev.event.error; onClick(
Code
{TalerErrorCode[error.code]} ({error.code})
Hint
{error.hint ?? "--"}
Time
                        {JSON.stringify(error, undefined, 2)}
                      
, ); }} > fail
) : undefined} {" "}