From 922ecc1f56c25671af4bd2f158ca3b7b21c9574a Mon Sep 17 00:00:00 2001 From: Mariusz Klochowicz Date: Wed, 3 Nov 2021 13:49:38 +1030 Subject: [PATCH] Display error toasts from failed actions in the maker --- frontend/src/MakerApp.tsx | 12 +++-------- frontend/src/MakerClient.tsx | 5 ++++- frontend/src/components/ErrorToast.tsx | 28 ++++++++++++++++++++++++++ frontend/src/components/HttpError.tsx | 18 +++++++++++++++++ 4 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 frontend/src/components/ErrorToast.tsx create mode 100644 frontend/src/components/HttpError.tsx diff --git a/frontend/src/MakerApp.tsx b/frontend/src/MakerApp.tsx index ac9ef8d..768562d 100644 --- a/frontend/src/MakerApp.tsx +++ b/frontend/src/MakerApp.tsx @@ -22,6 +22,7 @@ import { useEventSource } from "react-sse-hooks"; import { CfdTable } from "./components/cfdtables/CfdTable"; import CurrencyInputField from "./components/CurrencyInputField"; import CurrentPrice from "./components/CurrentPrice"; +import createErrorToast from "./components/ErrorToast"; import useLatestEvent from "./components/Hooks"; import OrderTile from "./components/OrderTile"; import { Cfd, intoCfd, intoOrder, Order, PriceInfo, StateGroupKey, WalletInfo } from "./components/Types"; @@ -42,6 +43,7 @@ export default function App() { const priceInfo = useLatestEvent(source, "quote"); const toast = useToast(); + let [minQuantity, setMinQuantity] = useState("10"); let [maxQuantity, setMaxQuantity] = useState("100"); let [orderPrice, setOrderPrice] = useState("0"); @@ -58,15 +60,7 @@ export default function App() { try { await postCfdSellOrderRequest(payload as CfdSellOrderPayload); } 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/frontend/src/MakerClient.tsx b/frontend/src/MakerClient.tsx index d98b6da..a98d0a4 100644 --- a/frontend/src/MakerClient.tsx +++ b/frontend/src/MakerClient.tsx @@ -1,3 +1,5 @@ +import { HttpError } from "./components/HttpError"; + export interface CfdSellOrderPayload { price: number; min_quantity: number; @@ -16,6 +18,7 @@ export async function postCfdSellOrderRequest(payload: CfdSellOrderPayload) { if (!res.status.toString().startsWith("2")) { console.log("Status: " + res.status + ", " + res.statusText); - throw new Error("failed to publish new order"); + const resp = await res.json(); + throw new HttpError(resp); } } diff --git a/frontend/src/components/ErrorToast.tsx b/frontend/src/components/ErrorToast.tsx new file mode 100644 index 0000000..48fd9f7 --- /dev/null +++ b/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/frontend/src/components/HttpError.tsx b/frontend/src/components/HttpError.tsx new file mode 100644 index 0000000..b80ece6 --- /dev/null +++ b/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); + } +}