From 206780bb0ee763bcf50a3f4f9f78579a8adcdb3a Mon Sep 17 00:00:00 2001 From: Sebastian Date: Thu, 29 Feb 2024 15:45:22 -0300 Subject: observe UI, WIP --- .../src/components/WalletActivity.tsx | 845 +++++++++++++++------ .../src/components/styled/index.tsx | 1 - 2 files changed, 602 insertions(+), 244 deletions(-) (limited to 'packages/taler-wallet-webextension/src/components') diff --git a/packages/taler-wallet-webextension/src/components/WalletActivity.tsx b/packages/taler-wallet-webextension/src/components/WalletActivity.tsx index 8c55d1fc9..41932f143 100644 --- a/packages/taler-wallet-webextension/src/components/WalletActivity.tsx +++ b/packages/taler-wallet-webextension/src/components/WalletActivity.tsx @@ -14,11 +14,13 @@ GNU Taler; see the file COPYING. If not, see */ import { + AbsoluteTime, NotificationType, ObservabilityEventType, + RequestProgressNotification, TalerErrorCode, TalerErrorDetail, - TransactionMajorState, + TaskProgressNotification, WalletNotification, assertUnreachable } from "@gnu-taler/taler-util"; @@ -29,10 +31,10 @@ 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 { Modal } from "./Modal.js"; import { Time } from "./Time.js"; -import { useSettings } from "../hooks/useSettings.js"; interface Props extends JSX.HTMLAttributes { } @@ -41,25 +43,29 @@ interface Props extends JSX.HTMLAttributes { export function WalletActivity({ }: Props): VNode { const { i18n } = useTranslationContext() const [settings, updateSettings] = useSettings() + const api = useBackendContext(); useEffect(() => { document.body.style.marginBottom = "250px" return () => { document.body.style.marginBottom = "0px" } }) - const [table, setTable] = useState<"tasks" | "events" | "calls">("tasks") + const [table, setTable] = useState<"tasks" | "events">("tasks") return (
-
{ - updateSettings("showWalletActivity", false) - }}> - close +
+
{ + updateSettings("showWalletActivity", false) + }}> + close +
-
+
-
{(function (): VNode { switch (table) { case "events": { return } - case "calls": { - return - } case "tasks": { return } @@ -102,238 +99,599 @@ export function WalletActivity({ }: Props): VNode { ); } -export function WalletCallsTable({ }: {}): VNode { - return
+interface MoreInfoPRops { events: (WalletNotification & { when: AbsoluteTime })[], onClick: (content: VNode) => void } +type Notif = { + id: string; + events: (WalletNotification & { when: AbsoluteTime })[]; + description: string; + start: AbsoluteTime; + end: AbsoluteTime; + reference: { + eventType: NotificationType, + referenceType: "task" | "transaction" | "operation" | "exchange", + id: string; + } | undefined, + MoreInfo: (p: MoreInfoPRops) => VNode; +} + +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, onClick }: 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 => { + 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 dependecy" + } + })(); + + return + + }) + return + + + + + + + + {asd} + +
EventInfoStartEnd
+} + +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 + } + +