diff --git a/src/components/Orderbook.tsx b/src/components/Orderbook.tsx index 71c70c0e..ae992984 100644 --- a/src/components/Orderbook.tsx +++ b/src/components/Orderbook.tsx @@ -1,4 +1,4 @@ -import { ReactElement, ChangeEvent, useCallback, useEffect, useMemo, useState } from 'react' +import { ReactElement, useCallback, useEffect, useMemo, useState } from 'react' import { Table, Header, HeaderRow, HeaderCell, Body, Row, Cell } from '@table-library/react-table-library/table' import { usePagination } from '@table-library/react-table-library/pagination' import { useSort, HeaderCellSort, SortToggleType } from '@table-library/react-table-library/sort' @@ -14,7 +14,10 @@ import Balance from './Balance' import Sprite from './Sprite' import TablePagination from './TablePagination' import { factorToPercentage, isAbsoluteOffer, isRelativeOffer } from '../utils' -import { isDevMode } from '../constants/debugFeatures' +import { isDebugFeatureEnabled, isDevMode } from '../constants/debugFeatures' +import ToggleSwitch from './ToggleSwitch' +import { pseudoRandomNumber } from './Send/helpers' +import { JM_DUST_THRESHOLD } from '../constants/config' import styles from './Orderbook.module.css' const TABLE_THEME = { @@ -151,6 +154,27 @@ const renderOrderFee = (val: string, settings: any) => { ) } +const renderOrderAsRow = (item: OrderTableRow, settings: any) => { + return ( + + {item.counterparty} + {item.orderId} + {renderOrderType(item.type)} + {renderOrderFee(item.fee.displayValue, settings)} + + + + + + + + + + {item.bondValue.displayValue} + + ) +} + interface OrderbookTableProps { data: TableTypes.Data } @@ -210,6 +234,14 @@ const OrderbookTable = ({ data }: OrderbookTableProps) => { }, ) + const pinnedOfferRows = useMemo( + () => + data.nodes + .filter((item: OrderTableRow) => item.__pinned === true) + .map((item: OrderTableRow) => renderOrderAsRow(item, settings)), + [data, settings], + ) + return ( <> { - {tableList.map((item: OrderTableRow) => { - return ( - - {item.counterparty} - {item.orderId} - {renderOrderType(item.type)} - {renderOrderFee(item.fee.displayValue, settings)} - - - - - - - - - - {item.bondValue.displayValue} - - ) - })} + {pinnedOfferRows} + {tableList + .filter((item: OrderTableRow) => item.__pinned !== true) + .map((item: OrderTableRow) => renderOrderAsRow(item, settings))} )} @@ -318,7 +330,9 @@ export function Orderbook({ entries, refresh, nickname }: OrderbookProps) { const [search, setSearch] = useState('') const [isLoadingRefresh, setIsLoadingRefresh] = useState(false) const [isHighlightOwnOffers, setIsHighlightOwnOffers] = useState(false) + const [isPinToTopOwnOffers, setIsPinToTopOwnOffers] = useState(false) const [highlightedOrders, setHighlightedOrders] = useState([]) + const [pinToTopOrders, setPinToTopOrders] = useState([]) const tableData: TableTypes.Data = useMemo(() => { const searchVal = search.replace('.', '').toLowerCase() @@ -340,11 +354,12 @@ export function Orderbook({ entries, refresh, nickname }: OrderbookProps) { const nodes = filteredOrders.map((order) => ({ ...order, id: `${order.counterparty}_${order.orderId}`, - _highlighted: highlightedOrders.includes(order), + __highlighted: highlightedOrders.includes(order), + __pinned: pinToTopOrders.includes(order), })) return { nodes } - }, [entries, search, highlightedOrders]) + }, [entries, search, highlightedOrders, pinToTopOrders]) const counterpartyCount = useMemo(() => new Set(entries.map((it) => it.counterparty)).size, [entries]) const counterpartyCountFiltered = useMemo( @@ -352,13 +367,17 @@ export function Orderbook({ entries, refresh, nickname }: OrderbookProps) { [tableData], ) + const ownOffers = useMemo(() => { + return nickname ? entries.filter((it) => it.counterparty === nickname) : [] + }, [nickname, entries]) + useEffect(() => { - if (!nickname || !isHighlightOwnOffers) { - setHighlightedOrders([]) - } else { - setHighlightedOrders(entries.filter((it) => it.counterparty === nickname)) - } - }, [entries, nickname, isHighlightOwnOffers]) + setHighlightedOrders(isHighlightOwnOffers ? ownOffers : []) + }, [ownOffers, isHighlightOwnOffers]) + + useEffect(() => { + setPinToTopOrders(isPinToTopOwnOffers ? ownOffers : []) + }, [ownOffers, isPinToTopOwnOffers]) return (
@@ -423,16 +442,32 @@ export function Orderbook({ entries, refresh, nickname }: OrderbookProps) { ) : ( <> {nickname && ( -
- ) => { - setIsHighlightOwnOffers(e.target.checked) - }} - /> -
+ <> +
+ setIsHighlightOwnOffers(isToggled)} + disabled={isLoadingRefresh || ownOffers.length === 0} + /> + {ownOffers.length > 0 && ( + { + setIsPinToTopOwnOffers(isToggled) + if (isToggled) { + setIsHighlightOwnOffers(true) + } + }} + disabled={isLoadingRefresh} + /> + )} +
+
+ )} @@ -453,6 +488,26 @@ export function OrderbookOverlay({ nickname, show, onHide }: OrderbookOverlayPro const [isLoading, setIsLoading] = useState(true) const [offers, setOffers] = useState() const tableEntries = useMemo(() => offers && offers.map((offer) => offerToTableEntry(offer, t)), [offers, t]) + const [__dev_showGenerateDemoOfferButton] = useState(isDebugFeatureEnabled('enableDemoOrderbook')) + + const __dev_generateDemoReportEntryButton = () => { + const randomMinsize = pseudoRandomNumber(JM_DUST_THRESHOLD, JM_DUST_THRESHOLD + 100_000) + const randomOrdertype = Math.random() > 0.5 ? 'sw0absoffer' : 'sw0reloffer' + const randomCounterparty = `demo_` + pseudoRandomNumber(0, 10) + setOffers((it) => { + const randomOffer = { + counterparty: randomCounterparty, + oid: (it || []).filter((e) => e.counterparty === randomCounterparty).length, + ordertype: randomOrdertype, + minsize: randomMinsize, + maxsize: randomMinsize + pseudoRandomNumber(21_000, 21_000_000), + txfee: 0, + cjfee: randomOrdertype === 'sw0absoffer' ? pseudoRandomNumber(0, 10_000) : Math.random().toFixed(5), + fidelity_bond_value: Math.random() > 0.25 ? 0 : pseudoRandomNumber(1_000, 21_000_000), + } + return [...(it || []), randomOffer] + }) + } const refresh = useCallback( (signal: AbortSignal) => { @@ -538,6 +593,26 @@ export function OrderbookOverlay({ nickname, show, onHide }: OrderbookOverlayPro }) ) : ( <> + {__dev_showGenerateDemoOfferButton && ( + + + __dev_generateDemoReportEntryButton()} + > +
+ Generate demo entry + +
+ + dev + +
+
+
+ )} {alert && {alert.message}} {tableEntries && ( diff --git a/src/constants/debugFeatures.ts b/src/constants/debugFeatures.ts index 06ea5781..9ef497a2 100644 --- a/src/constants/debugFeatures.ts +++ b/src/constants/debugFeatures.ts @@ -9,6 +9,7 @@ interface DebugFeatures { allowFeeValuesReset: boolean fastThemeToggle: boolean enableDemoEarnReport: boolean + enableDemoOrderbook: boolean } const devMode = process.env.NODE_ENV === 'development' && process.env.REACT_APP_JAM_DEV_MODE === 'true' @@ -24,6 +25,7 @@ const debugFeatures: DebugFeatures = { allowFeeValuesReset: devMode, fastThemeToggle: devMode, enableDemoEarnReport: devMode, + enableDemoOrderbook: devMode, } type DebugFeature = keyof DebugFeatures diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index fe6ce379..4c778510 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -606,6 +606,9 @@ "placeholder_search": "Search", "text_orderbook_filter_count": "{{ filterCount }} entries match given search criteria", "label_highlight_own_orders": "Highlight my offers", + "text_highlight_own_orders_subtitle": "It might take some time for your offer and your bond to appear in your local orderbook.", + "label_pin_to_top_own_orders": "Pin my offers", + "text_pin_to_top_own_orders_subtitle": "Displays your offers at the top of the table", "table": { "heading_type": "Type", "heading_counterparty": "Counterparty",