From 37ce8bcd18773a11093c96e1cb34d7bf3f9784a4 Mon Sep 17 00:00:00 2001 From: Philipp Hoenisch Date: Sat, 25 Sep 2021 09:07:28 +1000 Subject: [PATCH 1/8] Remove unused css --- frontend/src/App.css | 42 ------------------------------------------ frontend/src/Maker.tsx | 1 - frontend/src/Taker.tsx | 1 - 3 files changed, 44 deletions(-) delete mode 100644 frontend/src/App.css diff --git a/frontend/src/App.css b/frontend/src/App.css deleted file mode 100644 index 8da3fde..0000000 --- a/frontend/src/App.css +++ /dev/null @@ -1,42 +0,0 @@ -.App { - text-align: center; -} - -.App-logo { - height: 40vmin; - pointer-events: none; -} - -@media (prefers-reduced-motion: no-preference) { - .App-logo { - animation: App-logo-spin infinite 20s linear; - } -} - -.App-header { - background-color: #282c34; - min-height: 100vh; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; -} - -.App-link { - color: #61dafb; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -button { - font-size: calc(10px + 2vmin); -} diff --git a/frontend/src/Maker.tsx b/frontend/src/Maker.tsx index 788297b..5237bd9 100644 --- a/frontend/src/Maker.tsx +++ b/frontend/src/Maker.tsx @@ -2,7 +2,6 @@ import { Button, Container, Flex, Grid, GridItem, HStack, Stack, Text, useToast, import React, { useState } from "react"; import { useAsync } from "react-async"; import { useEventSource } from "react-sse-hooks"; -import "./App.css"; import CfdTile from "./components/CfdTile"; import CurrencyInputField from "./components/CurrencyInputField"; import useLatestEvent from "./components/Hooks"; diff --git a/frontend/src/Taker.tsx b/frontend/src/Taker.tsx index b42d920..a4998ca 100644 --- a/frontend/src/Taker.tsx +++ b/frontend/src/Taker.tsx @@ -2,7 +2,6 @@ import { Button, Container, Flex, Grid, GridItem, HStack, Stack, Text, useToast, import React, { useState } from "react"; import { useAsync } from "react-async"; import { useEventSource } from "react-sse-hooks"; -import "./App.css"; import CfdTile from "./components/CfdTile"; import CurrencyInputField from "./components/CurrencyInputField"; import useLatestEvent from "./components/Hooks"; From 95713fc4cd48483ee79524780661204b71509c78 Mon Sep 17 00:00:00 2001 From: Philipp Hoenisch Date: Sun, 26 Sep 2021 08:55:04 +1000 Subject: [PATCH 2/8] Add table for rendering CFDs --- frontend/package.json | 4 +- frontend/src/Maker.tsx | 68 ++++- frontend/src/Taker.tsx | 62 ++++- .../src/components/cfdtables/CfdTable.tsx | 259 ++++++++++++++++++ .../components/cfdtables/CfdTableMaker.tsx | 236 ++++++++++++++++ frontend/yarn.lock | 10 +- 6 files changed, 604 insertions(+), 35 deletions(-) create mode 100644 frontend/src/components/cfdtables/CfdTable.tsx create mode 100644 frontend/src/components/cfdtables/CfdTableMaker.tsx diff --git a/frontend/package.json b/frontend/package.json index c992357..b4e93c6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -21,7 +21,6 @@ "@types/node": "^12.0.0", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", - "@types/react-table": "^7.7.2", "@typescript-eslint/eslint-plugin": "^4.30.0", "@typescript-eslint/parser": "^4.30.0", "babel-eslint": "^10.1.0", @@ -44,13 +43,14 @@ "react-router-dom": "=6.0.0-beta.2", "react-scripts": "^4.0.3", "react-sse-hooks": "^1.0.5", - "react-table": "^7.7.0", + "react-table": "7.7.0", "typescript": "^4.4.2", "vite-jest": "^0.0.3", "web-vitals": "^1.0.1" }, "devDependencies": { "@types/eslint": "^7", + "@types/react-table": "7.7.3", "@types/react": "^17.0.0", "@types/react-dom": "^17.0.0", "@vitejs/plugin-react-refresh": "^1.3.1", diff --git a/frontend/src/Maker.tsx b/frontend/src/Maker.tsx index 5237bd9..0956881 100644 --- a/frontend/src/Maker.tsx +++ b/frontend/src/Maker.tsx @@ -1,8 +1,24 @@ -import { Button, Container, Flex, Grid, GridItem, HStack, Stack, Text, useToast, VStack } from "@chakra-ui/react"; +import { + Button, + Container, + Flex, + Grid, + GridItem, + HStack, + Tab, + TabList, + TabPanel, + TabPanels, + Tabs, + Text, + useToast, + VStack, +} from "@chakra-ui/react"; import React, { useState } from "react"; import { useAsync } from "react-async"; import { useEventSource } from "react-sse-hooks"; -import CfdTile from "./components/CfdTile"; +import { CfdTable } from "./components/cfdtables/CfdTable"; +import { CfdTableMaker } from "./components/cfdtables/CfdTableMaker"; import CurrencyInputField from "./components/CurrencyInputField"; import useLatestEvent from "./components/Hooks"; import OrderTile from "./components/OrderTile"; @@ -13,7 +29,8 @@ import { CfdSellOrderPayload, postCfdSellOrderRequest } from "./MakerClient"; export default function App() { let source = useEventSource({ source: "/api/feed", options: { withCredentials: true } }); - const cfds = useLatestEvent(source, "cfds"); + const cfdsOrUndefined = useLatestEvent(source, "cfds"); + let cfds = cfdsOrUndefined ? cfdsOrUndefined! : []; const order = useLatestEvent(source, "order"); console.log(cfds); @@ -46,20 +63,20 @@ export default function App() { }, }); + const runningStates = ["Accepted", "Contract Setup", "Pending Open"]; + const running = cfds.filter((value) => runningStates.includes(value.state)); + const openStates = ["Requested"]; + const open = cfds.filter((value) => openStates.includes(value.state)); + const closedStates = ["Rejected", "Closed"]; + const closed = cfds.filter((value) => closedStates.includes(value.state)); + // TODO: remove this. It just helps to detect immediately if we missed a state. + const unsorted = cfds.filter((value) => + !runningStates.includes(value.state) && !closedStates.includes(value.state) && !openStates.includes(value.state) + ); + return ( - - - {cfds && cfds.map((cfd, index) => - - )} - - @@ -115,6 +132,29 @@ export default function App() { + + + Running [{running.length}] + Open [{open.length}] + Closed [{closed.length}] + Unsorted [{unsorted.length}] (should be empty) + + + + + + + + + + + + + + + + + ); } diff --git a/frontend/src/Taker.tsx b/frontend/src/Taker.tsx index a4998ca..38d8eaf 100644 --- a/frontend/src/Taker.tsx +++ b/frontend/src/Taker.tsx @@ -1,8 +1,24 @@ -import { Button, Container, Flex, Grid, GridItem, HStack, Stack, Text, useToast, VStack } from "@chakra-ui/react"; +import { + Button, + Container, + Flex, + Grid, + GridItem, + HStack, + Stack, + Tab, + TabList, + TabPanel, + TabPanels, + Tabs, + Text, + useToast, + VStack, +} from "@chakra-ui/react"; import React, { useState } from "react"; import { useAsync } from "react-async"; import { useEventSource } from "react-sse-hooks"; -import CfdTile from "./components/CfdTile"; +import { CfdTable } from "./components/cfdtables/CfdTable"; import CurrencyInputField from "./components/CurrencyInputField"; import useLatestEvent from "./components/Hooks"; import { Cfd, Order, WalletInfo } from "./components/Types"; @@ -44,7 +60,8 @@ async function getMargin(payload: MarginRequestPayload): Promise export default function App() { let source = useEventSource({ source: "/api/feed" }); - const cfds = useLatestEvent(source, "cfds"); + const cfdsOrUndefined = useLatestEvent(source, "cfds"); + let cfds = cfdsOrUndefined ? cfdsOrUndefined! : []; const order = useLatestEvent(source, "order"); const walletInfo = useLatestEvent(source, "wallet"); @@ -92,20 +109,18 @@ export default function App() { }, }); + const runningStates = ["Request sent", "Requested", "Contract Setup", "Pending Open"]; + const running = cfds.filter((value) => runningStates.includes(value.state)); + const closedStates = ["Rejected", "Closed"]; + const closed = cfds.filter((value) => closedStates.includes(value.state)); + // TODO: remove this. It just helps to detect immediately if we missed a state. + const unsorted = cfds.filter((value) => + !runningStates.includes(value.state) && !closedStates.includes(value.state) + ); + return ( - - - {cfds && cfds.map((cfd, index) => - - )} - - @@ -167,6 +182,25 @@ export default function App() { + + + Running [{running.length}] + Closed [{closed.length}] + Unsorted [{unsorted.length}] (should be empty) + + + + + + + + + + + + + + ); } diff --git a/frontend/src/components/cfdtables/CfdTable.tsx b/frontend/src/components/cfdtables/CfdTable.tsx new file mode 100644 index 0000000..f64c7dd --- /dev/null +++ b/frontend/src/components/cfdtables/CfdTable.tsx @@ -0,0 +1,259 @@ +import { ChevronRightIcon, ChevronUpIcon, TriangleDownIcon, TriangleUpIcon } from "@chakra-ui/icons"; +import { Badge, Box, chakra, HStack, IconButton, Table as CUITable, Tbody, Td, Th, Thead, Tr } from "@chakra-ui/react"; +import React from "react"; +import { Column, Row, useExpanded, useSortBy, useTable } from "react-table"; +import { Cfd } from "../Types"; + +interface CfdTableProps { + data: Cfd[]; +} + +export function CfdTable( + { data }: CfdTableProps, +) { + const tableData = React.useMemo( + () => data, + [data], + ); + + const columns: Array> = React.useMemo( + () => [ + { + id: "expander", + Header: () => null, + Cell: ({ row }: any) => ( + + {row.isExpanded + ? } + onClick={() => { + row.toggleRowExpanded(); + }} + /> + : } + onClick={() => { + row.toggleRowExpanded(); + }} + />} + + ), + }, + { + Header: "OrderId", + accessor: "order_id", // accessor is the "key" in the data + }, + { + Header: "Position", + accessor: ({ position }) => { + let colorScheme = "green"; + if (position.toLocaleLowerCase() === "buy") { + colorScheme = "purple"; + } + return ( + {position} + ); + }, + isNumeric: true, + }, + + { + Header: "Quantity", + accessor: ({ quantity_usd }) => { + return (); + }, + isNumeric: true, + }, + { + Header: "Leverage", + accessor: "leverage", + isNumeric: true, + }, + { + Header: "Margin", + accessor: "margin", + isNumeric: true, + }, + { + Header: "Initial Price", + accessor: ({ initial_price }) => { + return (); + }, + isNumeric: true, + }, + { + Header: "Liquidation Price", + isNumeric: true, + accessor: ({ liquidation_price }) => { + return (); + }, + }, + { + Header: "Unrealized P/L", + accessor: ({ profit_usd }) => { + return (); + }, + isNumeric: true, + }, + { + Header: "Timestamp", + accessor: "state_transition_timestamp", + }, + { + Header: "State", + accessor: ({ state }) => { + let colorScheme = "gray"; + if (state.toLowerCase() === "rejected") { + colorScheme = "red"; + } + if (state.toLowerCase() === "contract setup") { + colorScheme = "green"; + } + return ( + {state} + ); + }, + }, + ], + [], + ); + + // if we mark certain columns only as hidden, they are still around and we can render them in the sub-row + const hiddenColumns = ["order_id", "leverage", "state_transition_timestamp"]; + + return ( + + ); +} + +function renderRowSubComponent(row: Row) { + // TODO: I would show additional information here such as txids, timestamps, actions + let cells = row.allCells + .filter((cell) => { + return ["state_transition_timestamp"].includes(cell.column.id); + }) + .map((cell) => { + return cell; + }); + + return ( + <> + Showing some more information here... + + {cells.map(cell => ( + + {cell.column.id} = {cell.render("Cell")} + + ))} + + + ); +} + +interface DollarsProps { + amount: number; +} +function Dollars({ amount }: DollarsProps) { + const price = Math.floor(amount * 100.0) / 100.0; + return ( + <> + $ {price} + + ); +} + +interface TableProps { + columns: Array>; + tableData: Cfd[]; + hiddenColumns: string[]; + renderDetails: any; +} + +export function Table({ columns, tableData, hiddenColumns, renderDetails }: TableProps) { + const { + getTableProps, + getTableBodyProps, + headerGroups, + rows, + prepareRow, + visibleColumns, + } = useTable( + { + columns, + data: tableData, + initialState: { + hiddenColumns, + }, + }, + useSortBy, + useExpanded, + ); + + return ( + <> + + + {headerGroups.map((headerGroup) => ( + + {headerGroup.headers.map((column) => ( + + ))} + + ))} + + + {rows.map((row) => { + prepareRow(row); + return ( + + row.toggleRowExpanded()}> + {row.cells.map((cell) => ( + + ))} + + {row.isExpanded + ? ( + + + + + ) + : null} + + ); + })} + + + + ); +} diff --git a/frontend/src/components/cfdtables/CfdTableMaker.tsx b/frontend/src/components/cfdtables/CfdTableMaker.tsx new file mode 100644 index 0000000..a7951f0 --- /dev/null +++ b/frontend/src/components/cfdtables/CfdTableMaker.tsx @@ -0,0 +1,236 @@ +import { CheckIcon, ChevronRightIcon, ChevronUpIcon, CloseIcon } from "@chakra-ui/icons"; +import { Badge, Box, HStack, IconButton, useToast } from "@chakra-ui/react"; +import React from "react"; +import { useAsync } from "react-async"; +import { Column, Row } from "react-table"; +import { postAcceptOrder, postRejectOrder } from "../../MakerClient"; +import { Cfd } from "../Types"; +import { Table } from "./CfdTable"; + +interface CfdMableTakerProps { + data: Cfd[]; +} + +export function CfdTableMaker( + { data }: CfdMableTakerProps, +) { + const tableData = React.useMemo( + () => data, + [data], + ); + + const toast = useToast(); + + let { run: acceptOrder, isLoading: isAccepting } = useAsync({ + deferFn: async ([orderId]: any[]) => { + try { + let payload = { + order_id: orderId, + }; + await postAcceptOrder(payload); + } catch (e) { + const description = typeof e === "string" ? e : JSON.stringify(e); + + toast({ + title: "Error", + description, + status: "error", + duration: 9000, + isClosable: true, + }); + } + }, + }); + + let { run: rejectOrder, isLoading: isRejecting } = useAsync({ + deferFn: async ([orderId]: any[]) => { + try { + let payload = { + order_id: orderId, + }; + await postRejectOrder(payload); + } catch (e) { + const description = typeof e === "string" ? e : JSON.stringify(e); + + toast({ + title: "Error", + description, + status: "error", + duration: 9000, + isClosable: true, + }); + } + }, + }); + + const columns: Array> = React.useMemo( + () => [ + { + id: "expander", + Header: () => null, + Cell: ({ row }: any) => ( + + {row.isExpanded + ? } + onClick={() => { + row.toggleRowExpanded(); + }} + /> + : } + onClick={() => { + row.toggleRowExpanded(); + }} + />} + + ), + }, + { + Header: "OrderId", + accessor: "order_id", + }, + { + Header: "Position", + accessor: ({ position }) => { + let colorScheme = "green"; + if (position.toLocaleLowerCase() === "buy") { + colorScheme = "purple"; + } + return ( + {position} + ); + }, + isNumeric: true, + }, + + { + Header: "Quantity", + accessor: ({ quantity_usd }) => { + return (); + }, + isNumeric: true, + }, + { + Header: "Leverage", + accessor: "leverage", + isNumeric: true, + }, + { + Header: "Margin", + accessor: "margin", + isNumeric: true, + }, + { + Header: "Initial Price", + accessor: ({ initial_price }) => { + return (); + }, + isNumeric: true, + }, + { + Header: "Liquidation Price", + isNumeric: true, + accessor: ({ liquidation_price }) => { + return (); + }, + }, + { + Header: "Unrealized P/L", + accessor: ({ profit_usd }) => { + return (); + }, + isNumeric: true, + }, + { + Header: "Timestamp", + accessor: "state_transition_timestamp", + }, + { + Header: "Action", + accessor: ({ state, order_id }) => { + if (state.toLowerCase() === "requested") { + return ( + } + onClick={async () => acceptOrder(order_id)} + isLoading={isAccepting} + /> + } + onClick={async () => rejectOrder(order_id)} + isLoading={isRejecting} + /> + ); + } + + let colorScheme = "gray"; + if (state.toLowerCase() === "rejected") { + colorScheme = "red"; + } + if (state.toLowerCase() === "contract setup") { + colorScheme = "green"; + } + return ( + {state} + ); + }, + }, + ], + [], + ); + + // if we mark certain columns only as hidden, they are still around and we can render them in the sub-row + const hiddenColumns = ["order_id", "leverage", "Unrealized P/L", "state_transition_timestamp"]; + + return ( +
+ {column.render("Header")} + + {column.isSorted + ? ( + column.isSortedDesc + ? ( + + ) + : ( + + ) + ) + : null} + +
+ {cell.render("Cell")} +
+ + {renderDetails(row)} +
+ ); +} + +function renderRowSubComponent(row: Row) { + // TODO: I would show additional information here such as txids, timestamps, actions + let cells = row.allCells + .filter((cell) => { + return ["state_transition_timestamp"].includes(cell.column.id); + }) + .map((cell) => { + return cell; + }); + + return ( + <> + Showing some more information here... + + {cells.map(cell => ( + + {cell.column.id} = {cell.render("Cell")} + + ))} + + + ); +} + +interface DollarsProps { + amount: number; +} +function Dollars({ amount }: DollarsProps) { + const price = Math.floor(amount * 100.0) / 100.0; + return ( + <> + $ {price} + + ); +} diff --git a/frontend/yarn.lock b/frontend/yarn.lock index 65989a8..732c141 100644 --- a/frontend/yarn.lock +++ b/frontend/yarn.lock @@ -2800,10 +2800,10 @@ dependencies: "@types/react" "*" -"@types/react-table@^7.7.2": - version "7.7.2" - resolved "https://registry.yarnpkg.com/@types/react-table/-/react-table-7.7.2.tgz#434f8230eb011c7eed8f3550fdf25befafebcfac" - integrity sha512-NwB78t3YV5pZ1NK3m2vylb/d0DKVyWH4y4GMCtlE4tg2n5ENM4ejzKnT46YKuqG2cPjWc+PIxuRVMd5OYX1z4A== +"@types/react-table@7.7.3": + version "7.7.3" + resolved "https://registry.yarnpkg.com/@types/react-table/-/react-table-7.7.3.tgz#0e5f952ec8562db1f6c950c766b53f27294a7e89" + integrity sha512-IL9DsA+V9AXUSPT6L+fFjo6YfEV40Fb+WmbrVxn+TjsPYUjkMZ0EZP1q0lTiosdrbrq3TeQI35Naxqc0ZTWEQg== dependencies: "@types/react" "*" @@ -10833,7 +10833,7 @@ react-style-singleton@^2.1.0: invariant "^2.2.4" tslib "^1.0.0" -react-table@^7.7.0: +react-table@7.7.0: version "7.7.0" resolved "https://registry.yarnpkg.com/react-table/-/react-table-7.7.0.tgz#e2ce14d7fe3a559f7444e9ecfe8231ea8373f912" integrity sha512-jBlj70iBwOTvvImsU9t01LjFjy4sXEtclBovl3mTiqjz23Reu0DKnRza4zlLtOPACx6j2/7MrQIthIK1Wi+LIA== From 662240271508a0200dcb93c5e13915c35f2db8a3 Mon Sep 17 00:00:00 2001 From: Philipp Hoenisch Date: Sun, 26 Sep 2021 10:55:31 +1000 Subject: [PATCH 3/8] Format wallet timestamp --- frontend/src/components/Wallet.tsx | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/frontend/src/components/Wallet.tsx b/frontend/src/components/Wallet.tsx index d3e0694..fd4e238 100644 --- a/frontend/src/components/Wallet.tsx +++ b/frontend/src/components/Wallet.tsx @@ -30,7 +30,16 @@ export default function Wallet( /> ); - timestamp = {unixTimestampToDate(walletInfo.last_updated_at).toString()}; + timestamp = + Updated: {unixTimestampToDate(walletInfo.last_updated_at).toLocaleDateString("en-US", { + year: "numeric", + month: "numeric", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + })} + ; } return ( From c208906fceff10115a1f5726885ea61ec9eef481 Mon Sep 17 00:00:00 2001 From: Philipp Hoenisch Date: Sun, 26 Sep 2021 11:12:00 +1000 Subject: [PATCH 4/8] Auto truncate wallet address --- frontend/src/components/Wallet.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/components/Wallet.tsx b/frontend/src/components/Wallet.tsx index fd4e238..3b68332 100644 --- a/frontend/src/components/Wallet.tsx +++ b/frontend/src/components/Wallet.tsx @@ -22,7 +22,7 @@ export default function Wallet( balance = {walletInfo.balance} BTC; address = ( - {walletInfo.address} + {walletInfo.address} : } From 182302215ede38e64a30dd91cc1f0b99caebe097 Mon Sep 17 00:00:00 2001 From: Philipp Hoenisch Date: Sun, 26 Sep 2021 11:04:06 +1000 Subject: [PATCH 5/8] Attempt to beautify taker ui --- frontend/src/Taker.tsx | 184 ++++++++++++++++++++--------------------- 1 file changed, 91 insertions(+), 93 deletions(-) diff --git a/frontend/src/Taker.tsx b/frontend/src/Taker.tsx index 38d8eaf..00cc02c 100644 --- a/frontend/src/Taker.tsx +++ b/frontend/src/Taker.tsx @@ -1,11 +1,8 @@ import { Button, Container, - Flex, - Grid, - GridItem, + Flex, Grid, GridItem, HStack, - Stack, Tab, TabList, TabPanel, @@ -15,13 +12,13 @@ import { useToast, VStack, } from "@chakra-ui/react"; -import React, { useState } from "react"; -import { useAsync } from "react-async"; -import { useEventSource } from "react-sse-hooks"; -import { CfdTable } from "./components/cfdtables/CfdTable"; +import React, {useState} from "react"; +import {useAsync} from "react-async"; +import {useEventSource} from "react-sse-hooks"; +import {CfdTable} from "./components/cfdtables/CfdTable"; import CurrencyInputField from "./components/CurrencyInputField"; import useLatestEvent from "./components/Hooks"; -import { Cfd, Order, WalletInfo } from "./components/Types"; +import {Cfd, Order, WalletInfo} from "./components/Types"; import Wallet from "./components/Wallet"; interface CfdOrderRequestPayload { @@ -40,7 +37,7 @@ interface MarginResponse { } async function postCfdOrderRequest(payload: CfdOrderRequestPayload) { - let res = await fetch(`/api/cfd`, { method: "POST", body: JSON.stringify(payload) }); + let res = await fetch(`/api/cfd`, {method: "POST", body: JSON.stringify(payload)}); if (!res.status.toString().startsWith("2")) { throw new Error("failed to create new CFD order request: " + res.status + ", " + res.statusText); @@ -48,7 +45,7 @@ async function postCfdOrderRequest(payload: CfdOrderRequestPayload) { } async function getMargin(payload: MarginRequestPayload): Promise { - let res = await fetch(`/api/calculate/margin`, { method: "POST", body: JSON.stringify(payload) }); + let res = await fetch(`/api/calculate/margin`, {method: "POST", body: JSON.stringify(payload)}); if (!res.status.toString().startsWith("2")) { throw new Error("failed to create new CFD order request: " + res.status + ", " + res.statusText); @@ -58,7 +55,7 @@ async function getMargin(payload: MarginRequestPayload): Promise } export default function App() { - let source = useEventSource({ source: "/api/feed" }); + let source = useEventSource({source: "/api/feed"}); const cfdsOrUndefined = useLatestEvent(source, "cfds"); let cfds = cfdsOrUndefined ? cfdsOrUndefined! : []; @@ -69,7 +66,7 @@ export default function App() { let [quantity, setQuantity] = useState("0"); let [margin, setMargin] = useState("0"); - let { run: calculateMargin } = useAsync({ + let {run: calculateMargin} = useAsync({ deferFn: async ([payload]: any[]) => { try { let res = await getMargin(payload as MarginRequestPayload); @@ -91,7 +88,7 @@ export default function App() { const format = (val: any) => `$` + val; const parse = (val: any) => val.replace(/^\$/, ""); - let { run: makeNewOrderRequest, isLoading: isCreatingNewOrderRequest } = useAsync({ + let {run: makeNewOrderRequest, isLoading: isCreatingNewOrderRequest} = useAsync({ deferFn: async ([payload]: any[]) => { try { await postCfdOrderRequest(payload as CfdOrderRequestPayload); @@ -120,87 +117,88 @@ export default function App() { return ( - - - - - - {/*TODO: Do we need this? does it make sense to only display the price from the order?*/} - Current Price (Kraken): - tbd - - - Order Price: - {order?.price} - - - Quantity: - { - setQuantity(parse(valueString)); - - if (!order) { - return; - } - - let quantity = valueString ? Number.parseFloat(valueString) : 0; - let payload: MarginRequestPayload = { - leverage: order.leverage, - price: order.price, - quantity, + + + + + + + {/*TODO: Do we need this? does it make sense to only display the price from the order?*/} + Current Price (Kraken): + tbd + + + Order Price: + {order?.price} + + + Quantity: + { + setQuantity(parse(valueString)); + if (!order) { + return; + } + let quantity = valueString ? Number.parseFloat(valueString) : 0; + let payload: MarginRequestPayload = { + leverage: order.leverage, + price: order.price, + quantity, + }; + calculateMargin(payload); + }} + value={format(quantity)} + /> + + + Margin in BTC: + {margin} + + Leverage: + {/* TODO: consider button group */} + + + + + + { - - - - {} - - - - - - Running [{running.length}] - Closed [{closed.length}] - Unsorted [{unsorted.length}] (should be empty) - - - - - - - - - - - - - - + > + BUY + } + + + + + + + Running [{running.length}] + Closed [{closed.length}] + Unsorted [{unsorted.length}] (should be empty) + + + + + + + + + + + + + + + ); } From ce7346023c7bc09d0bdd403738a40a564b7f2c52 Mon Sep 17 00:00:00 2001 From: Philipp Hoenisch Date: Sun, 26 Sep 2021 11:13:10 +1000 Subject: [PATCH 6/8] Attempt to beautify maker ui --- frontend/src/Maker.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/frontend/src/Maker.tsx b/frontend/src/Maker.tsx index 0956881..5e602ab 100644 --- a/frontend/src/Maker.tsx +++ b/frontend/src/Maker.tsx @@ -77,7 +77,7 @@ export default function App() { return ( - + @@ -127,10 +127,13 @@ export default function App() { > {order ? "Update Sell Order" : "Create Sell Order"} - {order && } + + + {order && } + From dff0f83a5cf56c93cc60c4a38978f651cb6c718a Mon Sep 17 00:00:00 2001 From: Philipp Hoenisch Date: Sun, 26 Sep 2021 11:17:39 +1000 Subject: [PATCH 7/8] Fmt and removed unused file --- frontend/src/Maker.tsx | 1 - frontend/src/Taker.tsx | 33 +++---- frontend/src/components/CfdTile.tsx | 138 ---------------------------- 3 files changed, 17 insertions(+), 155 deletions(-) delete mode 100644 frontend/src/components/CfdTile.tsx diff --git a/frontend/src/Maker.tsx b/frontend/src/Maker.tsx index 5e602ab..37eb215 100644 --- a/frontend/src/Maker.tsx +++ b/frontend/src/Maker.tsx @@ -127,7 +127,6 @@ export default function App() { > {order ? "Update Sell Order" : "Create Sell Order"} - diff --git a/frontend/src/Taker.tsx b/frontend/src/Taker.tsx index 00cc02c..6f2f33a 100644 --- a/frontend/src/Taker.tsx +++ b/frontend/src/Taker.tsx @@ -1,7 +1,9 @@ import { Button, Container, - Flex, Grid, GridItem, + Flex, + Grid, + GridItem, HStack, Tab, TabList, @@ -12,13 +14,13 @@ import { useToast, VStack, } from "@chakra-ui/react"; -import React, {useState} from "react"; -import {useAsync} from "react-async"; -import {useEventSource} from "react-sse-hooks"; -import {CfdTable} from "./components/cfdtables/CfdTable"; +import React, { useState } from "react"; +import { useAsync } from "react-async"; +import { useEventSource } from "react-sse-hooks"; +import { CfdTable } from "./components/cfdtables/CfdTable"; import CurrencyInputField from "./components/CurrencyInputField"; import useLatestEvent from "./components/Hooks"; -import {Cfd, Order, WalletInfo} from "./components/Types"; +import { Cfd, Order, WalletInfo } from "./components/Types"; import Wallet from "./components/Wallet"; interface CfdOrderRequestPayload { @@ -37,7 +39,7 @@ interface MarginResponse { } async function postCfdOrderRequest(payload: CfdOrderRequestPayload) { - let res = await fetch(`/api/cfd`, {method: "POST", body: JSON.stringify(payload)}); + let res = await fetch(`/api/cfd`, { method: "POST", body: JSON.stringify(payload) }); if (!res.status.toString().startsWith("2")) { throw new Error("failed to create new CFD order request: " + res.status + ", " + res.statusText); @@ -45,7 +47,7 @@ async function postCfdOrderRequest(payload: CfdOrderRequestPayload) { } async function getMargin(payload: MarginRequestPayload): Promise { - let res = await fetch(`/api/calculate/margin`, {method: "POST", body: JSON.stringify(payload)}); + let res = await fetch(`/api/calculate/margin`, { method: "POST", body: JSON.stringify(payload) }); if (!res.status.toString().startsWith("2")) { throw new Error("failed to create new CFD order request: " + res.status + ", " + res.statusText); @@ -55,7 +57,7 @@ async function getMargin(payload: MarginRequestPayload): Promise } export default function App() { - let source = useEventSource({source: "/api/feed"}); + let source = useEventSource({ source: "/api/feed" }); const cfdsOrUndefined = useLatestEvent(source, "cfds"); let cfds = cfdsOrUndefined ? cfdsOrUndefined! : []; @@ -66,7 +68,7 @@ export default function App() { let [quantity, setQuantity] = useState("0"); let [margin, setMargin] = useState("0"); - let {run: calculateMargin} = useAsync({ + let { run: calculateMargin } = useAsync({ deferFn: async ([payload]: any[]) => { try { let res = await getMargin(payload as MarginRequestPayload); @@ -88,7 +90,7 @@ export default function App() { const format = (val: any) => `$` + val; const parse = (val: any) => val.replace(/^\$/, ""); - let {run: makeNewOrderRequest, isLoading: isCreatingNewOrderRequest} = useAsync({ + let { run: makeNewOrderRequest, isLoading: isCreatingNewOrderRequest } = useAsync({ deferFn: async ([payload]: any[]) => { try { await postCfdOrderRequest(payload as CfdOrderRequestPayload); @@ -120,7 +122,7 @@ export default function App() { - + {/*TODO: Do we need this? does it make sense to only display the price from the order?*/} @@ -176,7 +178,6 @@ export default function App() { BUY } - @@ -188,13 +189,13 @@ export default function App() { - + - + - + diff --git a/frontend/src/components/CfdTile.tsx b/frontend/src/components/CfdTile.tsx deleted file mode 100644 index 00873d5..0000000 --- a/frontend/src/components/CfdTile.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { Box, Button, HStack, SimpleGrid, Text, useToast, VStack } from "@chakra-ui/react"; -import React from "react"; -import { useAsync } from "react-async"; -import { postAcceptOrder, postRejectOrder } from "../MakerClient"; -import { Cfd, unixTimestampToDate } from "./Types"; - -interface CfdTileProps { - index: number; - cfd: Cfd; -} - -export default function CfdTile( - { - index, - cfd, - }: CfdTileProps, -) { - const toast = useToast(); - - let { run: acceptOrder, isLoading: isAccepting } = useAsync({ - deferFn: async ([args]: any[]) => { - try { - let payload = { - order_id: args.order_id, - }; - await postAcceptOrder(payload); - } catch (e) { - const description = typeof e === "string" ? e : JSON.stringify(e); - - toast({ - title: "Error", - description, - status: "error", - duration: 9000, - isClosable: true, - }); - } - }, - }); - - let { run: rejectOrder, isLoading: isRejecting } = useAsync({ - deferFn: async ([args]: any[]) => { - try { - let payload = { - order_id: args.order_id, - }; - await postRejectOrder(payload); - } catch (e) { - const description = typeof e === "string" ? e : JSON.stringify(e); - - toast({ - title: "Error", - description, - status: "error", - duration: 9000, - isClosable: true, - }); - } - }, - }); - - let actionButtons; - if (cfd.state === "Open") { - actionButtons = - - ; - } else if (cfd.state == "Requested") { - actionButtons = ( - - - - - - - - - ); - } - - return ( - - - - CFD #{index} - - - Trading Pair - {cfd.trading_pair} - Position - {cfd.position} - CFD Price - {cfd.initial_price} - Leverage - {cfd.leverage} - Quantity - {cfd.quantity_usd} - Margin - {cfd.margin} - Liquidation Price - - {cfd.liquidation_price} - - Profit - {cfd.profit_usd} - Open since - {/* TODO: Format date in a more compact way */} - - {unixTimestampToDate(cfd.state_transition_timestamp).toString()} - - Status - {cfd.state} - - {actionButtons} - - - ); -} From b8ed118fe9c4b61765ab110e5cce0f77b1e672c7 Mon Sep 17 00:00:00 2001 From: Philipp Hoenisch Date: Sun, 26 Sep 2021 11:56:36 +1000 Subject: [PATCH 8/8] TS ignore all the things. This is needed due to missing type declarations --- frontend/src/components/cfdtables/CfdTable.tsx | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/frontend/src/components/cfdtables/CfdTable.tsx b/frontend/src/components/cfdtables/CfdTable.tsx index f64c7dd..52899fe 100644 --- a/frontend/src/components/cfdtables/CfdTable.tsx +++ b/frontend/src/components/cfdtables/CfdTable.tsx @@ -204,13 +204,17 @@ export function Table({ columns, tableData, hiddenColumns, renderDetails }: Tabl {headerGroup.headers.map((column) => ( row.toggleRowExpanded()}> + + // @ts-ignore + row.toggleRowExpanded()} + > {row.cells.map((cell) => ( + // @ts-ignore ))} - {row.isExpanded + + {// @ts-ignore + row.isExpanded ? (
{column.render("Header")} - {column.isSorted + {// @ts-ignore + column.isSorted ? ( + // @ts-ignore column.isSortedDesc ? ( @@ -231,14 +235,22 @@ export function Table({ columns, tableData, hiddenColumns, renderDetails }: Tabl prepareRow(row); return ( -
{cell.render("Cell")}