diff --git a/taker-frontend/src/App.tsx b/taker-frontend/src/App.tsx index 168e057..237bf2e 100644 --- a/taker-frontend/src/App.tsx +++ b/taker-frontend/src/App.tsx @@ -6,6 +6,7 @@ import { Route, Switch } from "react-router-dom"; import { useEventSource } from "react-sse-hooks"; import useWebSocket from "react-use-websocket"; import { useBackendMonitor } from "./components/BackendMonitor"; +import createErrorToast from "./components/ErrorToast"; import History from "./components/History"; import Nav from "./components/NavBar"; import Trade from "./components/Trade"; @@ -83,15 +84,7 @@ export const App = () => { let res = await getMargin(payload as MarginRequestPayload); setMargin(res.margin.toString()); } catch (e) { - const description = typeof e === "string" ? e : JSON.stringify(e); - - toast({ - title: "Error", - description, - status: "error", - duration: 9000, - isClosable: true, - }); + createErrorToast(toast, e); } }, }); @@ -101,16 +94,7 @@ export const App = () => { try { await postCfdOrderRequest(payload as CfdOrderRequestPayload); } catch (e) { - console.error(`Error received: ${JSON.stringify(e)}`); - const description = typeof e === "string" ? e : JSON.stringify(e); - - toast({ - title: "Error", - description, - status: "error", - duration: 9000, - isClosable: true, - }); + createErrorToast(toast, e); } }, }); diff --git a/taker-frontend/src/components/ErrorToast.tsx b/taker-frontend/src/components/ErrorToast.tsx new file mode 100644 index 0000000..48fd9f7 --- /dev/null +++ b/taker-frontend/src/components/ErrorToast.tsx @@ -0,0 +1,28 @@ +import { HttpError } from "./HttpError"; + +// A generic way of creating an error toast +// TODO: Don't use any (`toast: typeof useToast` did not work :( ) +export default function createErrorToast(toast: any, e: any) { + if (e instanceof HttpError) { + const description = e.detail ? e.detail : ""; + + toast({ + title: "Error: " + e.title, + description, + status: "error", + duration: 10000, + isClosable: true, + }); + } else { + console.log(e); + const description = typeof e === "string" ? e : JSON.stringify(e); + + toast({ + title: "Error", + description, + status: "error", + duration: 10000, + isClosable: true, + }); + } +} diff --git a/taker-frontend/src/components/History.tsx b/taker-frontend/src/components/History.tsx index 8752108..54596e2 100644 --- a/taker-frontend/src/components/History.tsx +++ b/taker-frontend/src/components/History.tsx @@ -30,6 +30,7 @@ import { } from "@chakra-ui/react"; import * as React from "react"; import { useAsync } from "react-async"; +import createErrorToast from "./ErrorToast"; import { Cfd, StateGroupKey, StateKey, Tx, TxLabel } from "./Types"; interface HistoryProps { @@ -90,15 +91,7 @@ const CfdDetails = ({ cfd }: CfdDetailsProps) => { console.log(`Closing: ${orderId} ${action}`); await doPostAction(orderId, action); } catch (e) { - const description = typeof e === "string" ? e : JSON.stringify(e); - - toast({ - title: "Error", - description, - status: "error", - duration: 9000, - isClosable: true, - }); + createErrorToast(toast, e); } }, }); diff --git a/taker-frontend/src/components/HttpError.tsx b/taker-frontend/src/components/HttpError.tsx new file mode 100644 index 0000000..b80ece6 --- /dev/null +++ b/taker-frontend/src/components/HttpError.tsx @@ -0,0 +1,18 @@ +// A wrapper to parse RFC 7807 +// Pass result of `await response.json()` into the constructor. +export class HttpError extends Error { + title: string; + detail?: string; + + // FIXME: Constructor can't be async, so we can't pass `Response` here + constructor(json_resp: any) { + let title = json_resp.title; + super(title); + this.title = title; + if (json_resp.detail) { + this.detail = json_resp.detail; + } + + Object.setPrototypeOf(this, HttpError.prototype); + } +}