gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-wallet-core] branch master updated: observe UI, WIP


From: gnunet
Subject: [taler-wallet-core] branch master updated: observe UI, WIP
Date: Thu, 29 Feb 2024 19:45:28 +0100

This is an automated email from the git hooks/post-receive script.

sebasjm pushed a commit to branch master
in repository wallet-core.

The following commit(s) were added to refs/heads/master by this push:
     new 206780bb0 observe UI, WIP
206780bb0 is described below

commit 206780bb0ee763bcf50a3f4f9f78579a8adcdb3a
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Thu Feb 29 15:45:22 2024 -0300

    observe UI, WIP
---
 .../src/components/WalletActivity.tsx              | 845 +++++++++++++++------
 .../src/components/styled/index.tsx                |   1 -
 .../taler-wallet-webextension/src/platform/api.ts  |  12 +-
 .../src/platform/chrome.ts                         |  13 +
 .../taler-wallet-webextension/src/platform/dev.ts  |   1 +
 .../taler-wallet-webextension/src/test-utils.ts    |   3 +
 .../src/wallet/DeveloperPage.tsx                   |   2 +-
 packages/taler-wallet-webextension/src/wxApi.ts    |  10 +
 8 files changed, 641 insertions(+), 246 deletions(-)

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 <http://www.gnu.org/licenses/>
  */
 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 (
     <div style={{ position: "fixed", bottom: 0, background: "white", zIndex: 
1, height: 250, overflowY: "scroll", width: "100%" }}>
       <div style={{ display: "flex", justifyContent: "space-between", float: 
"right" }}>
         <div />
-        <div onClick={() => {
-          updateSettings("showWalletActivity", false)
-        }}>
-          close
+        <div>
+          <div style={{ padding: 4, margin: 2, border: "solid 1px black" }} 
onClick={() => {
+            updateSettings("showWalletActivity", false)
+          }}>
+            close
+          </div>
         </div>
       </div>
-      <div>
+      <div style={{ display: "flex", justifyContent: "space-around" }}>
         <Button variant={table === "tasks" ? "contained" : "outlined"}
+          style={{ margin: 4 }}
           onClick={async () => {
             setTable("tasks")
           }}
@@ -67,6 +73,7 @@ export function WalletActivity({ }: Props): VNode {
           <i18n.Translate>Tasks</i18n.Translate>
         </Button>
         <Button variant={table === "events" ? "contained" : "outlined"}
+          style={{ margin: 4 }}
           onClick={async () => {
             setTable("events")
           }}
@@ -74,22 +81,12 @@ export function WalletActivity({ }: Props): VNode {
           <i18n.Translate>Events</i18n.Translate>
         </Button>
 
-        <Button variant={table === "calls" ? "contained" : "outlined"}
-          onClick={async () => {
-            setTable("calls")
-          }}
-        >
-          <i18n.Translate>Calls</i18n.Translate>
-        </Button>
       </div>
       {(function (): VNode {
         switch (table) {
           case "events": {
             return <ObservavilityEventsTable />
           }
-          case "calls": {
-            return <WalletCallsTable />
-          }
           case "tasks": {
             return <ActiveTasksTable />
           }
@@ -102,238 +99,599 @@ export function WalletActivity({ }: Props): VNode {
   );
 }
 
-export function WalletCallsTable({ }: {}): VNode {
-  return <div />
+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 <Fragment />;
+  const not = events[0];
+  if (not.type !== NotificationType.BalanceChange) return <Fragment />;
+  return <Fragment>
+    <dt>Transaction</dt>
+    <dd>
+      <a title={not.hintTransactionId} href={Pages.balanceTransaction({ tid: 
not.hintTransactionId })}>{not.hintTransactionId.substring(0, 10)}</a>
+    </dd>
+  </Fragment>
+}
+
+function ShowBackupOperationError({ events, onClick }: MoreInfoPRops): VNode {
+  if (!events.length) return <Fragment />;
+  const not = events[0];
+  if (not.type !== NotificationType.BackupOperationError) return <Fragment />;
+  return <Fragment>
+    <dt>Error</dt>
+    <dd>
+      <a href="#" onClick={(e) => {
+        e.preventDefault();
+        const error = not.error
+        onClick(<Fragment>
+          <dl>
+            <dt>Code</dt>
+            <dd>{TalerErrorCode[error.code]} ({error.code})</dd>
+            <dt>Hint</dt>
+            <dd>{error.hint ?? "--"}</dd>
+            <dt>Time</dt>
+            <dd><Time
+              timestamp={error.when}
+              format="yyyy/MM/dd HH:mm:ss"
+            /></dd>
+          </dl>
+          <pre style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}>
+            {JSON.stringify(error, undefined, 2)}
+          </pre>
+        </Fragment>)
+      }}>{TalerErrorCode[not.error.code]}</a>
+    </dd>
+  </Fragment>
+}
+
+function ShowTransactionStateTransition({ events, onClick }: MoreInfoPRops): 
VNode {
+  if (!events.length) return <Fragment />;
+  const not = events[0];
+  if (not.type !== NotificationType.TransactionStateTransition) return 
<Fragment />;
+  return <Fragment>
+    <dt>Old state</dt>
+    <dd>
+      {not.oldTxState.major} - {not.oldTxState.minor ?? ""}
+    </dd>
+    <dt>New state</dt>
+    <dd>
+      {not.newTxState.major} - {not.newTxState.minor ?? ""}
+    </dd>
+    <dt>Transaction</dt>
+    <dd>
+      <a title={not.transactionId} href={Pages.balanceTransaction({ tid: 
not.transactionId })}>{not.transactionId.substring(0, 10)}</a>
+    </dd>
+    {not.errorInfo ? <Fragment>
+      <dt>Error</dt>
+      <dd>
+        <a href="#" onClick={(e) => {
+          if (!not.errorInfo) return;
+          e.preventDefault();
+          const error = not.errorInfo;
+          onClick(<Fragment>
+            <dl>
+              <dt>Code</dt>
+              <dd>{TalerErrorCode[error.code]} ({error.code})</dd>
+              <dt>Hint</dt>
+              <dd>{error.hint ?? "--"}</dd>
+              <dt>Message</dt>
+              <dd>{error.message ?? "--"}</dd>
+            </dl>
+          </Fragment>)
+
+        }}>{TalerErrorCode[not.errorInfo.code]}</a>
+      </dd>
+    </Fragment> : undefined}
+    <dt>Experimental</dt>
+    <dd>
+      <pre style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}>
+        {JSON.stringify(not.experimentalUserData, undefined, 2)}
+      </pre>
+    </dd>
+
+
+  </Fragment>
+}
+function ShowExchangeStateTransition({ events, onClick }: MoreInfoPRops): 
VNode {
+  if (!events.length) return <Fragment />;
+  const not = events[0];
+  if (not.type !== NotificationType.ExchangeStateTransition) return <Fragment 
/>;
+  return <Fragment>
+    <dt>Exchange</dt>
+    <dd>
+      {not.exchangeBaseUrl}
+    </dd>
+    {not.oldExchangeState && not.newExchangeState.exchangeEntryStatus !== 
not.oldExchangeState?.exchangeEntryStatus && <Fragment>
+      <dt>Entry status</dt>
+      <dd>
+        from {not.oldExchangeState.exchangeEntryStatus} to 
{not.newExchangeState.exchangeEntryStatus}
+      </dd>
+    </Fragment>}
+    {not.oldExchangeState && not.newExchangeState.exchangeUpdateStatus !== 
not.oldExchangeState?.exchangeUpdateStatus && <Fragment>
+      <dt>Update status</dt>
+      <dd>
+        from {not.oldExchangeState.exchangeUpdateStatus} to 
{not.newExchangeState.exchangeUpdateStatus}
+      </dd>
+    </Fragment>}
+    {not.oldExchangeState && not.newExchangeState.tosStatus !== 
not.oldExchangeState?.tosStatus && <Fragment>
+      <dt>Tos status</dt>
+      <dd>
+        from {not.oldExchangeState.tosStatus} to 
{not.newExchangeState.tosStatus}
+      </dd>
+    </Fragment>}
+  </Fragment>
+}
+
+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 <Fragment />;
+
+    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 <ShowObervavilityDetails title={title} notif={not} 
onClick={onClick} />
+
+  })
+  return <table>
+    <thead>
+      <td>Event</td>
+      <td>Info</td>
+      <td>Start</td>
+      <td>End</td>
+    </thead>
+    <tbody>
+      {asd}
+    </tbody>
+  </table>
+}
+
+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 <tr>
+        <td><a href="#" onClick={(e) => {
+          e.preventDefault();
+          onClick(<Fragment>
+            <pre
+              style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}
+            >
+              {JSON.stringify({ event: notif, prev }, undefined, 2)}
+            </pre>
+          </Fragment>);
+        }}>{title}</a></td>
+        <td>
+          {notif.event.url} {
+            prev?.event.type === ObservabilityEventType.HttpFetchFinishSuccess 
? `(${prev.event.status})`
+              : prev?.event.type === 
ObservabilityEventType.HttpFetchFinishError ? <a href="#" onClick={(e) => {
+                e.preventDefault();
+                if (prev.event.type !== 
ObservabilityEventType.HttpFetchFinishError) return;
+                const error = prev.event.error
+                onClick(<Fragment>
+                  <dl>
+                    <dt>Code</dt>
+                    <dd>{TalerErrorCode[error.code]} ({error.code})</dd>
+                    <dt>Hint</dt>
+                    <dd>{error.hint ?? "--"}</dd>
+                    <dt>Time</dt>
+                    <dd><Time
+                      timestamp={error.when}
+                      format="yyyy/MM/dd HH:mm:ss"
+                    /></dd>
+                  </dl>
+                  <pre style={{ whiteSpace: "pre-wrap", wordBreak: 
"break-word" }}>
+                    {JSON.stringify(error, undefined, 2)}
+                  </pre>
+
+                </Fragment>)
+              }}>fail</a> : undefined
+          }
+        </td>
+        <td>          <Time
+          timestamp={notif.when}
+          format="yyyy/MM/dd HH:mm:ss"
+        /></td>
+        <td>          <Time
+          timestamp={prev?.when}
+          format="yyyy/MM/dd HH:mm:ss"
+        /></td>
+      </tr>
+
+    }
+    case ObservabilityEventType.DbQueryStart:
+    case ObservabilityEventType.DbQueryFinishSuccess:
+    case ObservabilityEventType.DbQueryFinishError: {
+      return <tr>
+        <td><a href="#" onClick={(e) => {
+          e.preventDefault();
+          onClick(<Fragment>
+            <pre
+              style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}
+            >
+              {JSON.stringify({ event: notif, prev }, undefined, 2)}
+            </pre>
+          </Fragment>);
+        }}>{title}</a></td>
+        <td>
+          {notif.event.location} {notif.event.name}
+        </td>
+        <td>
+          <Time
+            timestamp={notif.when}
+            format="yyyy/MM/dd HH:mm:ss"
+          />
+        </td>
+        <td>
+          <Time
+            timestamp={prev?.when}
+            format="yyyy/MM/dd HH:mm:ss"
+          />
+        </td>
+      </tr>
+    }
+
+    case ObservabilityEventType.TaskStart:
+    case ObservabilityEventType.TaskStop:
+    case ObservabilityEventType.DeclareTaskDependency:
+    case ObservabilityEventType.TaskReset: {
+      return <tr>
+        <td><a href="#" onClick={(e) => {
+          e.preventDefault();
+          onClick(<Fragment>
+            <pre
+              style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}
+            >
+              {JSON.stringify({ event: notif, prev }, undefined, 2)}
+            </pre>
+          </Fragment>);
+        }}>{title}</a></td>
+        <td>
+          {notif.event.taskId}
+        </td>
+        <td>
+          <Time
+            timestamp={notif.when}
+            format="yyyy/MM/dd HH:mm:ss"
+          />
+        </td>
+        <td>
+          <Time
+            timestamp={prev?.when}
+            format="yyyy/MM/dd HH:mm:ss"
+          />
+        </td>
+      </tr>
+    }
+    case ObservabilityEventType.ShepherdTaskResult: {
+      return <tr>
+        <td><a href="#" onClick={(e) => {
+          e.preventDefault();
+          onClick(<Fragment>
+            <pre
+              style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}
+            >
+              {JSON.stringify({ event: notif, prev }, undefined, 2)}
+            </pre>
+          </Fragment>);
+        }}>{title}</a></td>
+        <td>
+          {notif.event.resultType}
+        </td>
+        <td>
+          <Time
+            timestamp={notif.when}
+            format="yyyy/MM/dd HH:mm:ss"
+          />
+        </td>
+        <td>
+          <Time
+            timestamp={prev?.when}
+            format="yyyy/MM/dd HH:mm:ss"
+          />
+        </td>
+      </tr>
+
+    }
+    case ObservabilityEventType.CryptoStart:
+    case ObservabilityEventType.CryptoFinishSuccess:
+    case ObservabilityEventType.CryptoFinishError: {
+      return <tr>
+        <td><a href="#" onClick={(e) => {
+          e.preventDefault();
+          onClick(<Fragment>
+            <pre
+              style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}
+            >
+              {JSON.stringify({ event: notif, prev }, undefined, 2)}
+            </pre>
+          </Fragment>);
+        }}>{title}</a></td>
+        <td>
+          {notif.event.operation}
+        </td>
+        <td>
+          <Time
+            timestamp={notif.when}
+            format="yyyy/MM/dd HH:mm:ss"
+          />
+        </td>
+        <td>
+          <Time
+            timestamp={prev?.when}
+            format="yyyy/MM/dd HH:mm:ss"
+          />
+        </td>
+      </tr>
+    }
+    case ObservabilityEventType.RequestStart:
+    case ObservabilityEventType.RequestFinishSuccess:
+    case ObservabilityEventType.RequestFinishError: {
+      return <tr >
+        <td><a href="#" onClick={(e) => {
+          e.preventDefault();
+          onClick(<Fragment>
+            <pre
+              style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}
+            >
+              {JSON.stringify({ event: notif, prev }, undefined, 2)}
+            </pre>
+          </Fragment>);
+        }}>{title}</a></td>
+        <td>
+          {notif.event.type}
+        </td>
+        <td>
+          <Time
+            timestamp={notif.when}
+            format="yyyy/MM/dd HH:mm:ss"
+          />
+        </td>
+        <td>
+          <Time
+            timestamp={prev?.when}
+            format="yyyy/MM/dd HH:mm:ss"
+          />
+        </td>
+      </tr>
+    }
+  }
+}
+
+function getNotificationFor(id: string, event: WalletNotification, start: 
AbsoluteTime, list: Notif[]): Notif | undefined {
+  const eventWithTime = { ...event, when: start }
+  switch (event.type) {
+    case NotificationType.BalanceChange: {
+      return ({
+        id,
+        events: [eventWithTime],
+        reference: {
+          eventType: event.type,
+          referenceType: "transaction",
+          id: event.hintTransactionId,
+        },
+        description: "Balance change",
+        start,
+        end: AbsoluteTime.never(),
+        MoreInfo: ShowBalanceChange
+      })
+    }
+    case NotificationType.BackupOperationError: {
+      return ({
+        id,
+        events: [eventWithTime],
+        reference: undefined,
+        description: "Backup error",
+        start,
+        end: AbsoluteTime.never(),
+        MoreInfo: ShowBackupOperationError
+      })
+    }
+    case NotificationType.TransactionStateTransition: {
+      const found = list.find(a => a.reference?.eventType === event.type && 
a.reference.id === event.transactionId)
+      if (found) {
+        found.end = start;
+        found.events.unshift(eventWithTime)
+        return undefined
+      }
+      return ({
+        id,
+        events: [eventWithTime],
+        reference: {
+          eventType: event.type,
+          referenceType: "transaction",
+          id: event.transactionId,
+        },
+        description: event.type,
+        start,
+        end: AbsoluteTime.never(),
+        MoreInfo: ShowTransactionStateTransition
+      })
+    }
+    case NotificationType.ExchangeStateTransition: {
+      const found = list.find(a => a.reference?.eventType === event.type && 
a.reference.id === event.exchangeBaseUrl)
+      if (found) {
+        found.end = start;
+        found.events.unshift(eventWithTime)
+        return undefined
+      }
+      return ({
+        id,
+        events: [eventWithTime],
+        description: "Exchange update",
+        reference: {
+          eventType: event.type,
+          referenceType: "exchange",
+          id: event.exchangeBaseUrl,
+        },
+        start,
+        end: AbsoluteTime.never(),
+        MoreInfo: ShowExchangeStateTransition
+      })
+    }
+    case NotificationType.TaskObservabilityEvent: {
+      const found = list.find(a => a.reference?.eventType === event.type && 
a.reference.id === event.taskId)
+      if (found) {
+        found.end = start;
+        found.events.unshift(eventWithTime)
+        return undefined
+      }
+      return ({
+        id,
+        events: [eventWithTime],
+        reference: {
+          eventType: event.type,
+          referenceType: "task",
+          id: event.taskId,
+        },
+        description: `Task update ${event.taskId}`,
+        start,
+        end: AbsoluteTime.never(),
+        MoreInfo: ShowObservabilityEvent
+      })
+    }
+    case NotificationType.RequestObservabilityEvent: {
+      const found = list.find(a => a.reference?.eventType === event.type && 
a.reference.id === event.requestId)
+      if (found) {
+        found.end = start;
+        found.events.unshift(eventWithTime)
+        return undefined
+      }
+      return ({
+        id,
+        events: [eventWithTime],
+        reference: {
+          eventType: event.type,
+          referenceType: "operation",
+          id: event.requestId,
+        },
+        description: `wallet.${event.operation}(${event.requestId})`,
+        start,
+        end: AbsoluteTime.never(),
+        MoreInfo: ShowObservabilityEvent
+      })
+    }
+    default: {
+      assertUnreachable(event)
+    }
+  }
 }
-const notifications: WalletNotification[] = []
+
 export function ObservavilityEventsTable({ }: {}): VNode {
   const { i18n } = useTranslationContext()
   const listenAllEvents = Array.from<NotificationType>({ length: 1 });
   listenAllEvents.includes = () => true
   const api = useBackendContext();
   const [lastEvent, setLastEvent] = useState<Date>(new Date())
+  const [lastShow, setLastShow] = useState<Date>(new Date())
+
+  const [notifications, setNotifications] = useState<{ notif: 
WalletNotification, when: AbsoluteTime }[]>([])
   useEffect(() => {
-    return api.listener.onUpdateNotification(listenAllEvents, (notif) => {
-      notifications.unshift(notif)
-      setLastEvent(new Date())
+    return api.listener.onUpdateNotification(listenAllEvents, (not) => {
+      console.log(not)
+      const time = new Date();
+      setLastEvent(time)
+      notifications.unshift({
+        notif: not,
+        when: AbsoluteTime.now()
+      })
+      setNotifications(Array.from(notifications))
     });
   });
-  const [showError, setShowError] = useState<TalerErrorDetail>()
-  return <div>
-    {showError && <ErroDetailModal error={showError} onClose={(async () => { 
setShowError(undefined) })} />}
-    {notifications.map((not) => {
-      return (
-        <details>
-          <summary>{not.type}</summary>
-          {(function () {
-            switch (not.type) {
-              case NotificationType.BalanceChange: {
-                return <Fragment>
-                  <dt>Transaction</dt>
-                  <dd>
-                    <a title={not.hintTransactionId} 
href={Pages.balanceTransaction({ tid: not.hintTransactionId 
})}>{not.hintTransactionId.substring(0, 10)}</a>
-                  </dd>
-                </Fragment>
-              }
-              case NotificationType.BackupOperationError: {
-                return <Fragment>
-                  <dt>Error</dt>
-                  <dd>
-                    <a href="#" onClick={(e) => { e.preventDefault(); 
setShowError(not.error) }}>{TalerErrorCode[not.error.code]}</a>
-                  </dd>
-                </Fragment>
-              }
-              case NotificationType.TransactionStateTransition: {
-                return <Fragment>
-                  <dt>Old state</dt>
-                  <dd>
-                    {not.oldTxState.major} - {not.oldTxState.minor ?? ""}
-                  </dd>
-                  <dt>New state</dt>
-                  <dd>
-                    {not.newTxState.major} - {not.newTxState.minor ?? ""}
-                  </dd>
-                  <dt>Transaction</dt>
-                  <dd>
-                    <a title={not.transactionId} 
href={Pages.balanceTransaction({ tid: not.transactionId 
})}>{not.transactionId.substring(0, 10)}</a>
-                  </dd>
-                  {not.errorInfo ? <Fragment>
-                    <dt>Error</dt>
-                    <dd>
-                      <a href="#" onClick={(e) => {
-                        e.preventDefault(); setShowError({
-                          code: not.errorInfo!.code,
-                          hint: not.errorInfo!.hint,
-                          message: not.errorInfo!.message,
-                        })
-                      }}>{TalerErrorCode[not.errorInfo!.code]}</a>
-                    </dd>
-                  </Fragment> : undefined}
-                  <dt>Experimental</dt>
-                  <dd>
-                    <pre>
-                      {JSON.stringify(not.experimentalUserData, undefined, 2)}
-                    </pre>
-                  </dd>
 
-                </Fragment>
-              }
-              case NotificationType.ExchangeStateTransition: {
-                return <Fragment>
-                  <dt>Exchange</dt>
-                  <dd>
-                    {not.exchangeBaseUrl}
-                  </dd>
-                  <dt>Entry status</dt>
-                  <dd>
-                    {not.newExchangeState.exchangeEntryStatus}
-                  </dd>
-                  <dt>Update status</dt>
-                  <dd>
-                    {not.newExchangeState.exchangeUpdateStatus}
-                  </dd>
-                  <dt>Tos status</dt>
-                  <dd>
-                    {not.newExchangeState.tosStatus}
-                  </dd>
-                </Fragment>
-              }
-              case NotificationType.TaskObservabilityEvent: {
-                return <Fragment>
-                  <dt>Task</dt>
-                  <dd>
-                    {not.taskId}
-                  </dd>
-                  <dt>Event</dt>
-                  <dd>
-                    {not.event.type}
-                  </dd>
-                  {(function () {
-                    switch (not.event.type) {
-                      case ObservabilityEventType.HttpFetchStart:
-                      case ObservabilityEventType.HttpFetchFinishError:
-                      case ObservabilityEventType.HttpFetchFinishSuccess: {
-                        return <Fragment>
-                          <dt>Request</dt>
-                          <dd>{not.event.url}</dd>
-                        </Fragment>
-                      }
-                      case ObservabilityEventType.DbQueryStart:
-                      case ObservabilityEventType.DbQueryFinishSuccess:
-                      case ObservabilityEventType.DbQueryFinishError: {
-                        return <Fragment>
-                          <dt>Location</dt>
-                          <dd>{not.event.location}</dd>
-                          <dt>Name</dt>
-                          <dd>{not.event.name}</dd>
-                        </Fragment>
-                      }
-
-                      case ObservabilityEventType.TaskStart:
-                      case ObservabilityEventType.TaskStop:
-                      case ObservabilityEventType.DeclareTaskDependency:
-                      case ObservabilityEventType.TaskReset: {
-                        return <Fragment>
-                          <dt>Task</dt>
-                          <dd>{not.event.taskId}</dd>
-                        </Fragment>
-                      }
-                      case ObservabilityEventType.ShepherdTaskResult: {
-                        return <Fragment>
-                          <dt>result</dt>
-                          <dd>{not.event.resultType}</dd>
-                        </Fragment>
-
-                      }
-                      case ObservabilityEventType.CryptoStart:
-                      case ObservabilityEventType.CryptoFinishSuccess:
-                      case ObservabilityEventType.CryptoFinishError: {
-                        return <Fragment>
-                          <dt>operation</dt>
-                          <dd>{not.event.operation}</dd>
-                        </Fragment>
-                      }
-                      case ObservabilityEventType.RequestStart:
-                      case ObservabilityEventType.RequestFinishSuccess:
-                      case ObservabilityEventType.RequestFinishError: {
-                        return <Fragment />
-                      }
-                    }
-                  })()}
-                </Fragment>
-              }
-              case NotificationType.RequestObservabilityEvent: {
-                return <Fragment>
-                  <dt>Operation</dt>
-                  <dd>
-                    {not.operation}
-                  </dd>
-                  <dt>Request</dt>
-                  <dd>
-                    {not.requestId}
-                  </dd>
-                  <dt>Event type</dt>
-                  <dd>
-                    {not.event.type}
-                  </dd>
-                  {(function () {
-                    switch (not.event.type) {
-                      case ObservabilityEventType.HttpFetchStart:
-                      case ObservabilityEventType.HttpFetchFinishError:
-                      case ObservabilityEventType.HttpFetchFinishSuccess: {
-                        return <Fragment>
-                          <dt>Request</dt>
-                          <dd>{not.event.url}</dd>
-                        </Fragment>
-                      }
-                      case ObservabilityEventType.DbQueryStart:
-                      case ObservabilityEventType.DbQueryFinishSuccess:
-                      case ObservabilityEventType.DbQueryFinishError: {
-                        return <Fragment>
-                          <dt>Location</dt>
-                          <dd>{not.event.location}</dd>
-                          <dt>Name</dt>
-                          <dd>{not.event.name}</dd>
-                        </Fragment>
-                      }
-
-                      case ObservabilityEventType.TaskStart:
-                      case ObservabilityEventType.TaskStop:
-                      case ObservabilityEventType.DeclareTaskDependency:
-                      case ObservabilityEventType.TaskReset: {
-                        return <Fragment>
-                          <dt>Task</dt>
-                          <dd>{not.event.taskId}</dd>
-                        </Fragment>
-                      }
-                      case ObservabilityEventType.ShepherdTaskResult: {
-                        return <Fragment>
-                          <dt>result</dt>
-                          <dd>{not.event.resultType}</dd>
-                        </Fragment>
-
-                      }
-                      case ObservabilityEventType.CryptoStart:
-                      case ObservabilityEventType.CryptoFinishSuccess:
-                      case ObservabilityEventType.CryptoFinishError: {
-                        return <Fragment>
-                          <dt>operation</dt>
-                          <dd>{not.event.operation}</dd>
-                        </Fragment>
-                      }
-                      case ObservabilityEventType.RequestStart:
-                      case ObservabilityEventType.RequestFinishSuccess:
-                      case ObservabilityEventType.RequestFinishError: {
-                        return <Fragment />
-                      }
-                    }
-                  })()}
-
-                </Fragment>
+  const [showDetails, setShowDetails] = useState<VNode>()
+  const [notif, setnotif] = useState<Notif[]>([])
+  return <div>
+    <div style={{ display: "flex", justifyContent: "space-between" }}>
+      {lastShow === lastEvent ?
+        <div>last event {lastEvent.toString()}</div>
+        : <div>there are more events, update to see them</div>}
+      <div>
+        {lastShow !== lastEvent ?
+          <div style={{ padding: 4, margin: 2, border: "solid 1px black" }} 
onClick={() => {
+            const list = [...notif]
+            for (const pepe of notifications) {
+              const event = getNotificationFor(String(list.length), 
pepe.notif, pepe.when, list)
+              if (event) {
+                list.push(event)
               }
             }
-          })()}
+            setnotif(list)
+            setLastShow(lastEvent)
+          }}>
+            update
+          </div>
+          : <div />}
+        <div style={{ padding: 4, margin: 2, border: "solid 1px black" }} 
onClick={() => {
+          setNotifications([])
+        }}>
+          clear
+        </div>
 
+      </div>
+    </div>
+    {showDetails && <Modal title="event details" onClose={{ onClick: (async () 
=> { setShowDetails(undefined) }) as any }} >
+      {showDetails}
+    </Modal>}
+    {notif.map((not) => {
+      return (
+        <details key={not.id}>
+          <summary>
+            <div style={{ width: "90%", display: "inline-flex", 
justifyContent: "space-between", padding: 4 }}>
+              <div style={{ padding: 4 }}>
+                {not.description}
+              </div>
+              <div style={{ padding: 4 }}>
+                <Time
+                  timestamp={not.start}
+                  format="yyyy/MM/dd HH:mm:ss"
+                />
+              </div>
+              <div style={{ padding: 4 }}><Time
+                timestamp={not.end}
+                format="yyyy/MM/dd HH:mm:ss"
+              /></div>
+            </div>
+          </summary>
+          <not.MoreInfo events={not.events} onClick={(details) => {
+            setShowDetails(details)
+          }} />
         </details>
       );
     })}
@@ -355,7 +713,7 @@ function ErroDetailModal({ error, onClose }: { error: 
TalerErrorDetail, onClose:
         format="yyyy/MM/dd HH:mm:ss"
       /></dd>
     </dl>
-    <pre>
+    <pre style={{ whiteSpace: "pre-wrap", wordBreak: "break-word" }}>
       {JSON.stringify(error, undefined, 2)}
     </pre>
   </Modal>
@@ -371,14 +729,15 @@ export function ActiveTasksTable({ }: {}): VNode {
   );
   const [showError, setShowError] = useState<TalerErrorDetail>()
   const tasks = state && !state.hasError ? state.response.tasks : [];
-  useEffect(() => {
-    return api.listener.onUpdateNotification(listenAllEvents, (notif) => {
-      state?.retry()
-    });
-  });
+  // useEffect(() => {
+  //   return api.listener.onUpdateNotification(listenAllEvents, (notif) => {
+  //     state?.retry()
+  //   });
+  // });
   return <Fragment>
     {showError && <ErroDetailModal error={showError} onClose={(async () => { 
setShowError(undefined) })} />}
-    <table>
+
+    <table style={{ width: "100%" }}>
       <thead>
         <tr>
           <th>
diff --git a/packages/taler-wallet-webextension/src/components/styled/index.tsx 
b/packages/taler-wallet-webextension/src/components/styled/index.tsx
index 2501c61c8..89678c74a 100644
--- a/packages/taler-wallet-webextension/src/components/styled/index.tsx
+++ b/packages/taler-wallet-webextension/src/components/styled/index.tsx
@@ -35,7 +35,6 @@ export const WalletAction = styled.div`
   align-items: center;
 
   margin: auto;
-  height: 100%;
 
   & h1:first-child {
     margin-top: 0;
diff --git a/packages/taler-wallet-webextension/src/platform/api.ts 
b/packages/taler-wallet-webextension/src/platform/api.ts
index e3afe35bd..faf0f2820 100644
--- a/packages/taler-wallet-webextension/src/platform/api.ts
+++ b/packages/taler-wallet-webextension/src/platform/api.ts
@@ -55,6 +55,7 @@ export interface CrossBrowserPermissionsApi {
 
 export enum ExtensionNotificationType {
   SettingsChange = "settings-change",
+  ClearNotifications = "clear-notifications",
 }
 
 export interface SettingsChangeNotification {
@@ -62,8 +63,11 @@ export interface SettingsChangeNotification {
 
   currentValue: Settings;
 }
+export interface ClearNotificaitonNotification {
+  type: ExtensionNotificationType.ClearNotifications;
+}
 
-export type ExtensionNotification = SettingsChangeNotification
+export type ExtensionNotification = SettingsChangeNotification | 
ClearNotificaitonNotification
 
 export type MessageFromBackend = {
   type: "wallet",
@@ -299,6 +303,12 @@ export interface ForegroundPlatformAPI {
     message: MessageFromFrontend<Op>,
   ): Promise<MessageResponse>;
 
+  /**
+ * Used by the wallet frontend to send notification about new information
+ * @param message
+ */
+  triggerWalletEvent(message: MessageFromBackend): void;
+
   /**
    * Used from the frontend to receive notifications about new information
    * @param listener
diff --git a/packages/taler-wallet-webextension/src/platform/chrome.ts 
b/packages/taler-wallet-webextension/src/platform/chrome.ts
index d88dae460..fc2d1db09 100644
--- a/packages/taler-wallet-webextension/src/platform/chrome.ts
+++ b/packages/taler-wallet-webextension/src/platform/chrome.ts
@@ -45,6 +45,7 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
   findTalerUriInClipboard,
   getPermissionsApi,
   getWalletWebExVersion,
+  triggerWalletEvent,
   listenToWalletBackground,
   notifyWhenAppIsReady,
   openWalletPage,
@@ -324,6 +325,18 @@ function listenToWalletBackground(listener: (m: any) => 
void): () => void {
 
 const allPorts: chrome.runtime.Port[] = [];
 
+
+function triggerWalletEvent(message: MessageFromBackend): void {
+  for (const notif of allPorts) {
+    // const message: MessageFromBackend = { type: msg.type };
+    try {
+      notif.postMessage(message);
+    } catch (e) {
+      logger.error("error posting a message", e);
+    }
+  }
+}
+
 function sendMessageToAllChannels(message: MessageFromBackend): void {
   for (const notif of allPorts) {
     // const message: MessageFromBackend = { type: msg.type };
diff --git a/packages/taler-wallet-webextension/src/platform/dev.ts 
b/packages/taler-wallet-webextension/src/platform/dev.ts
index 2993c88bc..7cbbe5b25 100644
--- a/packages/taler-wallet-webextension/src/platform/dev.ts
+++ b/packages/taler-wallet-webextension/src/platform/dev.ts
@@ -36,6 +36,7 @@ const api: BackgroundPlatformAPI & ForegroundPlatformAPI = {
   findTalerUriInClipboard: async () => undefined,
   listenNetworkConnectionState,
   openNewURLFromPopup: () => undefined,
+  triggerWalletEvent: () => undefined,
   getPermissionsApi: () => ({
     containsClipboardPermissions: async () => true,
     removeClipboardPermissions: async () => false,
diff --git a/packages/taler-wallet-webextension/src/test-utils.ts 
b/packages/taler-wallet-webextension/src/test-utils.ts
index d25326942..90037819f 100644
--- a/packages/taler-wallet-webextension/src/test-utils.ts
+++ b/packages/taler-wallet-webextension/src/test-utils.ts
@@ -115,6 +115,9 @@ export function createWalletApiMock(): {
       },
     }),
     listener: {
+      trigger: () => {
+
+      },
       onUpdateNotification(
         mTypes: NotificationType[],
         callback: ((d: WalletNotification) => void) | undefined,
diff --git a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx 
b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
index cdd3994d7..c98538755 100644
--- a/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
+++ b/packages/taler-wallet-webextension/src/wallet/DeveloperPage.tsx
@@ -97,7 +97,7 @@ export function DeveloperPage({ }: Props): VNode {
   const { safely } = useAlertContext();
 
   const listenAllEvents = Array.from<NotificationType>({ length: 1 });
-  listenAllEvents.includes = () => true
+  // listenAllEvents.includes = () => true
 
   const hook = useAsyncAsHook(async () => {
     const list = await api.wallet.call(WalletApiOperation.ListExchanges, {});
diff --git a/packages/taler-wallet-webextension/src/wxApi.ts 
b/packages/taler-wallet-webextension/src/wxApi.ts
index df99d3f17..a1c09ef8d 100644
--- a/packages/taler-wallet-webextension/src/wxApi.ts
+++ b/packages/taler-wallet-webextension/src/wxApi.ts
@@ -41,6 +41,7 @@ import {
   WalletCoreResponseType,
 } from "@gnu-taler/taler-wallet-core";
 import {
+  ExtensionNotification,
   MessageFromBackend,
   MessageFromFrontendBackground,
   MessageFromFrontendWallet,
@@ -192,14 +193,23 @@ export type WxApiType = {
   wallet: WalletCoreApiClient;
   background: BackgroundApiClient;
   listener: {
+    trigger: (d: ExtensionNotification) => void;
     onUpdateNotification: typeof onUpdateNotification;
   };
 };
 
+function trigger(w:ExtensionNotification) {
+  platform.triggerWalletEvent({
+    type: "web-extension",
+    notification: w,
+  })
+}
+
 export const wxApi = {
   wallet: new WalletApiClientImpl(),
   background: new BackgroundApiClientImpl(),
   listener: {
+    trigger,
     onUpdateNotification,
   },
 };

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]