From 46898aef5f6e238dbfe1b54cf1cf99a276b7d114 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 29 Feb 2024 01:24:49 -0300 Subject: wip wallet activity --- .../src/components/WalletActivity.tsx | 433 +++++++++++++++++++-- 1 file changed, 396 insertions(+), 37 deletions(-) (limited to 'packages/taler-wallet-webextension/src/components/WalletActivity.tsx') diff --git a/packages/taler-wallet-webextension/src/components/WalletActivity.tsx b/packages/taler-wallet-webextension/src/components/WalletActivity.tsx index a63ee97cb..8c55d1fc9 100644 --- a/packages/taler-wallet-webextension/src/components/WalletActivity.tsx +++ b/packages/taler-wallet-webextension/src/components/WalletActivity.tsx @@ -14,61 +14,420 @@ GNU Taler; see the file COPYING. If not, see */ import { - AbsoluteTime, - Amounts, NotificationType, - Transaction, + ObservabilityEventType, + TalerErrorCode, + TalerErrorDetail, TransactionMajorState, + WalletNotification, + assertUnreachable } from "@gnu-taler/taler-util"; import { WalletApiOperation } from "@gnu-taler/taler-wallet-core"; -import { Fragment, h, JSX, VNode } from "preact"; -import { useEffect } from "preact/hooks"; -import { useBackendContext } from "../context/backend.js"; 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 { Avatar } from "../mui/Avatar.js"; -import { Grid } from "../mui/Grid.js"; -import { Typography } from "../mui/Typography.js"; -import Banner from "./Banner.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 { } -/** - * this cache will save the tx from the previous render - */ -const cache = { tx: [] as Transaction[] }; -export function WalletActivity({ }: Props): VNode { - const api = useBackendContext(); - const state = useAsyncAsHook(() => - api.wallet.call(WalletApiOperation.GetTransactions, {}), +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) + } + } + })()} +
); - const listenAllEvents = Array.from({ length: 1 }); +} +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) => { - console.log(notif) + 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}
+
- const transactions = - !state || state.hasError - ? cache.tx - : state.response.transactions.filter( - (t) => t.txState.major === TransactionMajorState.Pending, + } + 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 + } + } + })()} + + + } + } + })()} + +
); + })} +
+} - if (state && !state.hasError) { - cache.tx = transactions; - } - if (!transactions.length) { - return ; - } - return ( -
- this is shown below -
- ); +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)} : "--"} +
+
+} \ No newline at end of file -- cgit v1.2.3