/* 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 { NotificationType, ObservabilityEventType, TalerErrorCode, TalerErrorDetail, TransactionMajorState, 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, JSX, 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 { Button } from "../mui/Button.js"; import { Modal } from "./Modal.js"; import { Time } from "./Time.js"; import { useSettings } from "../hooks/useSettings.js"; interface Props extends JSX.HTMLAttributes { } export function WalletActivity({ }: Props): VNode { const { i18n } = useTranslationContext() const [settings, updateSettings] = useSettings() useEffect(() => { document.body.style.marginBottom = "250px" return () => { document.body.style.marginBottom = "0px" } }) const [table, setTable] = useState<"tasks" | "events" | "calls">("tasks") return (
{ updateSettings("showWalletActivity", false) }}> close
{(function (): VNode { switch (table) { case "events": { return } case "calls": { return } case "tasks": { return } default: { assertUnreachable(table) } } })()}
); } export function WalletCallsTable({ }: {}): VNode { return
} const notifications: WalletNotification[] = [] export function ObservavilityEventsTable({ }: {}): VNode { const { i18n } = useTranslationContext() const listenAllEvents = Array.from({ length: 1 }); listenAllEvents.includes = () => true const api = useBackendContext(); const [lastEvent, setLastEvent] = useState(new Date()) useEffect(() => { return api.listener.onUpdateNotification(listenAllEvents, (notif) => { notifications.unshift(notif) setLastEvent(new Date()) }); }); const [showError, setShowError] = useState() return
{showError && { setShowError(undefined) })} />} {notifications.map((not) => { return (
{not.type} {(function () { switch (not.type) { case NotificationType.BalanceChange: { return
Transaction
{not.hintTransactionId.substring(0, 10)}
} case NotificationType.BackupOperationError: { return
Error
{ e.preventDefault(); setShowError(not.error) }}>{TalerErrorCode[not.error.code]}
} case NotificationType.TransactionStateTransition: { 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
{ e.preventDefault(); setShowError({ code: not.errorInfo!.code, hint: not.errorInfo!.hint, message: not.errorInfo!.message, }) }}>{TalerErrorCode[not.errorInfo!.code]}
: undefined}
Experimental
                      {JSON.stringify(not.experimentalUserData, undefined, 2)}
                    
} case NotificationType.ExchangeStateTransition: { return
Exchange
{not.exchangeBaseUrl}
Entry status
{not.newExchangeState.exchangeEntryStatus}
Update status
{not.newExchangeState.exchangeUpdateStatus}
Tos status
{not.newExchangeState.tosStatus}
} case NotificationType.TaskObservabilityEvent: { return
Task
{not.taskId}
Event
{not.event.type}
{(function () { switch (not.event.type) { case ObservabilityEventType.HttpFetchStart: case ObservabilityEventType.HttpFetchFinishError: case ObservabilityEventType.HttpFetchFinishSuccess: { return
Request
{not.event.url}
} case ObservabilityEventType.DbQueryStart: case ObservabilityEventType.DbQueryFinishSuccess: case ObservabilityEventType.DbQueryFinishError: { return
Location
{not.event.location}
Name
{not.event.name}
} case ObservabilityEventType.TaskStart: case ObservabilityEventType.TaskStop: case ObservabilityEventType.DeclareTaskDependency: case ObservabilityEventType.TaskReset: { return
Task
{not.event.taskId}
} case ObservabilityEventType.ShepherdTaskResult: { return
result
{not.event.resultType}
} case ObservabilityEventType.CryptoStart: case ObservabilityEventType.CryptoFinishSuccess: case ObservabilityEventType.CryptoFinishError: { return
operation
{not.event.operation}
} case ObservabilityEventType.RequestStart: case ObservabilityEventType.RequestFinishSuccess: case ObservabilityEventType.RequestFinishError: { return } } })()} } case NotificationType.RequestObservabilityEvent: { return
Operation
{not.operation}
Request
{not.requestId}
Event type
{not.event.type}
{(function () { switch (not.event.type) { case ObservabilityEventType.HttpFetchStart: case ObservabilityEventType.HttpFetchFinishError: case ObservabilityEventType.HttpFetchFinishSuccess: { return
Request
{not.event.url}
} case ObservabilityEventType.DbQueryStart: case ObservabilityEventType.DbQueryFinishSuccess: case ObservabilityEventType.DbQueryFinishError: { return
Location
{not.event.location}
Name
{not.event.name}
} case ObservabilityEventType.TaskStart: case ObservabilityEventType.TaskStop: case ObservabilityEventType.DeclareTaskDependency: case ObservabilityEventType.TaskReset: { return
Task
{not.event.taskId}
} case ObservabilityEventType.ShepherdTaskResult: { return
result
{not.event.resultType}
} case ObservabilityEventType.CryptoStart: case ObservabilityEventType.CryptoFinishSuccess: case ObservabilityEventType.CryptoFinishError: { return
operation
{not.event.operation}
} case ObservabilityEventType.RequestStart: case ObservabilityEventType.RequestFinishSuccess: case ObservabilityEventType.RequestFinishError: { return } } })()} } } })()}
); })}
} function ErroDetailModal({ error, onClose }: { error: TalerErrorDetail, onClose: () => void }): VNode { return
Code
{TalerErrorCode[error.code]} ({error.code})
Hint
{error.hint ?? "--"}
Time
      {JSON.stringify(error, undefined, 2)}
    
} export function ActiveTasksTable({ }: {}): VNode { const { i18n } = useTranslationContext() const listenAllEvents = Array.from({ length: 1 }); listenAllEvents.includes = () => true const api = useBackendContext(); const state = useAsyncAsHook(() => api.wallet.call(WalletApiOperation.GetActiveTasks, {}), ); const [showError, setShowError] = useState() const tasks = state && !state.hasError ? state.response.tasks : []; useEffect(() => { return api.listener.onUpdateNotification(listenAllEvents, (notif) => { state?.retry() }); }); return {showError && { setShowError(undefined) })} />} {tasks.map((task) => { const [type, id] = task.id.split(":") return ( ); })}
Type Id Since Next try Error Transaction
{type} {id.substring(0, 10)} {!task.lastError?.code ? "" : { e.preventDefault(); setShowError(task.lastError) }}>{TalerErrorCode[task.lastError.code]}} {task.transaction ? {task.transaction.substring(0, 10)} : "--"}
}