Browse Source

Display connection errors to the daemon in the UI

Periodically send a keepalive message; if the daemon does not come back with a
response, display a persistent toast notification (at the top of the screen, so
it's harder to miss or confuse).

Notification goes away when the connection to the daemon is re-established.
fix/sql-oddness
Mariusz Klochowicz 3 years ago
parent
commit
1105e041be
No known key found for this signature in database GPG Key ID: 470C865699C8D4D
  1. 2
      frontend/src/MakerApp.tsx
  2. 2
      frontend/src/TakerApp.tsx
  3. 50
      frontend/src/components/BackendMonitor.tsx
  4. 1
      frontend/vite.config.ts

2
frontend/src/MakerApp.tsx

@ -19,6 +19,7 @@ import {
import React, { useEffect, useState } from "react";
import { useAsync } from "react-async";
import { useEventSource } from "react-sse-hooks";
import { useBackendMonitor } from "./components/BackendMonitor";
import { CfdTable } from "./components/cfdtables/CfdTable";
import CurrencyInputField from "./components/CurrencyInputField";
import CurrentPrice from "./components/CurrentPrice";
@ -43,6 +44,7 @@ export default function App() {
const priceInfo = useLatestEvent<PriceInfo>(source, "quote");
const toast = useToast();
useBackendMonitor(toast, 5000); // 5s timeout
let [minQuantity, setMinQuantity] = useState<string>("10");
let [maxQuantity, setMaxQuantity] = useState<string>("100");

2
frontend/src/TakerApp.tsx

@ -18,6 +18,7 @@ import {
import React, { useEffect, useState } from "react";
import { useAsync } from "react-async";
import { useEventSource } from "react-sse-hooks";
import { useBackendMonitor } from "./components/BackendMonitor";
import { CfdTable } from "./components/cfdtables/CfdTable";
import CurrencyInputField from "./components/CurrencyInputField";
import createErrorToast from "./components/ErrorToast";
@ -63,6 +64,7 @@ async function getMargin(payload: MarginRequestPayload): Promise<MarginResponse>
export default function App() {
const toast = useToast();
useBackendMonitor(toast, 5000); // 5s timeout
document.title = "Hermes Taker";

50
frontend/src/components/BackendMonitor.tsx

@ -0,0 +1,50 @@
import { useEffect, useRef } from "react";
async function fetchWithTimeout(resource: any, timeout: number) {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await fetch(resource, {
signal: controller.signal,
});
clearTimeout(id);
return response;
}
// Check for backend's presence by sending a request to '/alive' endpoint.
// When the backend's not there, the request is likely to timeout, triggering a
// persistent toast notification that goes away when the daemon is back online.
export function useBackendMonitor(toast: any, timeout_ms: number): void {
const toastIdRef = useRef();
const checkForBackend: () => void = async () => {
try {
const res = await fetchWithTimeout("/alive", timeout_ms);
// In case we get a response, but it's not what we expected
if (res.status.toString().startsWith("2")) {
if (toastIdRef.current) {
toast.close(toastIdRef.current);
}
} else {
throw new Error(res.statusText);
}
} catch (e) {
if (!toastIdRef.current) {
toastIdRef.current = toast({
title: "Connection Error",
description: "Daemon process is not running",
status: "error",
position: "top",
duration: timeout_ms * 100000, // we don't want this to be closed
isClosable: false,
});
}
}
};
useEffect(() => {
const interval = setInterval(() => checkForBackend(), timeout_ms);
return () => {
clearInterval(interval);
};
});
}

1
frontend/vite.config.ts

@ -33,6 +33,7 @@ export default defineConfig({
server: {
proxy: {
"/api": `http://localhost:${backendPorts[app]}`,
"/alive": `http://localhost:${backendPorts[app]}`,
},
},
});

Loading…
Cancel
Save