From d792be8afdf4777d8c956a41184b0d679c53d927 Mon Sep 17 00:00:00 2001 From: theborakompanioni Date: Tue, 28 May 2024 08:08:19 +0200 Subject: [PATCH 1/5] fix(orderbook): do not follow redirects on refresh orderbook --- src/components/Orderbook.tsx | 4 ++-- src/libs/JmObwatchApi.ts | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/Orderbook.tsx b/src/components/Orderbook.tsx index ae992984..42086eeb 100644 --- a/src/components/Orderbook.tsx +++ b/src/components/Orderbook.tsx @@ -511,9 +511,9 @@ export function OrderbookOverlay({ nickname, show, onHide }: OrderbookOverlayPro const refresh = useCallback( (signal: AbortSignal) => { - return ObwatchApi.refreshOrderbook({ signal }) + return ObwatchApi.refreshOrderbook({ signal, redirect: 'manual' }) .then((res) => { - if (!res.ok) { + if (!res.ok && res.type !== 'opaqueredirect') { // e.g. error is raised if ob-watcher is not running return ApiHelper.throwError(res) } diff --git a/src/libs/JmObwatchApi.ts b/src/libs/JmObwatchApi.ts index 23d00987..021a2201 100644 --- a/src/libs/JmObwatchApi.ts +++ b/src/libs/JmObwatchApi.ts @@ -27,9 +27,10 @@ const fetchOrderbook = async (options: { signal: AbortSignal }): Promise (res.ok ? res.json() : ApiHelper.throwError(res))) } -const refreshOrderbook = async ({ signal }: { signal: AbortSignal }) => { +const refreshOrderbook = async ({ signal, redirect }: { signal: AbortSignal; redirect: RequestRedirect }) => { return await fetch(`${basePath()}/refreshorderbook`, { method: 'POST', + redirect, signal, }) } From d9dee78b12ace06cb3c7fa4ecf4aa95c1263f96f Mon Sep 17 00:00:00 2001 From: theborakompanioni Date: Tue, 28 May 2024 08:09:01 +0200 Subject: [PATCH 2/5] chore(dev): remove debugger statement in App.tsx --- src/components/App.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/components/App.tsx b/src/components/App.tsx index 1edbd5e6..5c1cd49c 100644 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -87,8 +87,6 @@ export default function App() { [reloadCurrentWalletInfo], ) - debugger - const router = createBrowserRouter( createRoutesFromElements( Date: Tue, 28 May 2024 08:20:27 +0200 Subject: [PATCH 3/5] fix(orderbook): display loading state correctly --- src/components/Orderbook.tsx | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/components/Orderbook.tsx b/src/components/Orderbook.tsx index 42086eeb..a7c6a09c 100644 --- a/src/components/Orderbook.tsx +++ b/src/components/Orderbook.tsx @@ -321,14 +321,14 @@ const offerToTableEntry = (offer: ObwatchApi.Offer, t: TFunction): OrderTableEnt interface OrderbookProps { entries: OrderTableEntry[] refresh: (signal: AbortSignal) => Promise + isLoading: boolean nickname?: string } -export function Orderbook({ entries, refresh, nickname }: OrderbookProps) { +export function Orderbook({ entries, refresh, isLoading: isLoadingRefresh, nickname }: OrderbookProps) { const { t } = useTranslation() const settings = useSettings() const [search, setSearch] = useState('') - const [isLoadingRefresh, setIsLoadingRefresh] = useState(false) const [isHighlightOwnOffers, setIsHighlightOwnOffers] = useState(false) const [isPinToTopOwnOffers, setIsPinToTopOwnOffers] = useState(false) const [highlightedOrders, setHighlightedOrders] = useState([]) @@ -389,12 +389,9 @@ export function Orderbook({ entries, refresh, nickname }: OrderbookProps) { onClick={() => { if (isLoadingRefresh) return - setIsLoadingRefresh(true) - const abortCtrl = new AbortController() refresh(abortCtrl.signal).finally(() => { - // as refreshing is fast most of the time, add a short delay to avoid flickering - setTimeout(() => setIsLoadingRefresh(false), 250) + console.log('Finished reloading orderbook.') }) }} > @@ -511,6 +508,7 @@ export function OrderbookOverlay({ nickname, show, onHide }: OrderbookOverlayPro const refresh = useCallback( (signal: AbortSignal) => { + setIsLoading(true) return ObwatchApi.refreshOrderbook({ signal, redirect: 'manual' }) .then((res) => { if (!res.ok && res.type !== 'opaqueredirect') { @@ -523,6 +521,7 @@ export function OrderbookOverlay({ nickname, show, onHide }: OrderbookOverlayPro .then((orderbook) => { if (signal.aborted) return + setIsLoading(false) setAlert(undefined) setOffers(orderbook.offers || []) @@ -532,6 +531,7 @@ export function OrderbookOverlay({ nickname, show, onHide }: OrderbookOverlayPro }) .catch((e) => { if (signal.aborted) return + setIsLoading(false) const message = t('orderbook.error_loading_orderbook_failed', { reason: e.message || t('global.errors.reason_unknown'), }) @@ -546,10 +546,8 @@ export function OrderbookOverlay({ nickname, show, onHide }: OrderbookOverlayPro const abortCtrl = new AbortController() - setIsLoading(true) refresh(abortCtrl.signal).finally(() => { if (abortCtrl.signal.aborted) return - setIsLoading(false) setIsInitialized(true) }) @@ -617,7 +615,7 @@ export function OrderbookOverlay({ nickname, show, onHide }: OrderbookOverlayPro {tableEntries && ( - + )} From 09bd26f4b14f53a935f6fa02f7bdc489f5487085 Mon Sep 17 00:00:00 2001 From: theborakompanioni Date: Tue, 28 May 2024 08:41:43 +0200 Subject: [PATCH 4/5] dev(orderbook): add fidelity bonds array to response --- src/libs/JmObwatchApi.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/libs/JmObwatchApi.ts b/src/libs/JmObwatchApi.ts index 021a2201..35e18348 100644 --- a/src/libs/JmObwatchApi.ts +++ b/src/libs/JmObwatchApi.ts @@ -13,8 +13,21 @@ export interface Offer { fidelity_bond_value: number // example: 0 (no fb) or 114557102085.28133 } +export interface FidelityBond { + counterparty: string // example: "J5Bv3JSxPFWm2Yjb" + bond_value: number // example: 82681607.26848702, + locktime: number // example: 1725148800 + amount: AmountSats // example: 312497098 + script: string // example: 002059e6f4a2afbb87c9967530955d091d7954f9364cd829b6012bdbcb38fab5e383 + utxo_confirmations: number // example: 10 + utxo_confirmation_timestamp: number // example: 1716876653 + utxo_pub: string // example: 02d46a9001a5430c0aa1e3ad0c004b409a932d3ae99b19617f0ab013b12076c082 + cert_expiry: number // example: 1 +} + export interface OrderbookJson { offers?: Offer[] + fidelitybonds?: FidelityBond[] } const orderbookJson = async ({ signal }: { signal: AbortSignal }) => { From da316fd5e5817e1c22b479e2c080ba6bba5378f9 Mon Sep 17 00:00:00 2001 From: theborakompanioni Date: Tue, 28 May 2024 10:15:47 +0200 Subject: [PATCH 5/5] feat(orderbook): display fidelity bond amount and locktime --- src/components/Orderbook.tsx | 70 +++++++++++++++++++++++++++++++----- 1 file changed, 62 insertions(+), 8 deletions(-) diff --git a/src/components/Orderbook.tsx b/src/components/Orderbook.tsx index a7c6a09c..7b4a93f6 100644 --- a/src/components/Orderbook.tsx +++ b/src/components/Orderbook.tsx @@ -5,19 +5,20 @@ import { useSort, HeaderCellSort, SortToggleType } from '@table-library/react-ta import * as TableTypes from '@table-library/react-table-library/types/table' import { useTheme } from '@table-library/react-table-library/theme' import * as rb from 'react-bootstrap' -import { TFunction } from 'i18next' +import { TFunction, i18n } from 'i18next' import { useTranslation } from 'react-i18next' -import { Helper as ApiHelper } from '../libs/JmWalletApi' +import { AmountSats, Helper as ApiHelper } from '../libs/JmWalletApi' import * as ObwatchApi from '../libs/JmObwatchApi' import { useSettings } from '../context/SettingsContext' import Balance from './Balance' import Sprite from './Sprite' import TablePagination from './TablePagination' -import { factorToPercentage, isAbsoluteOffer, isRelativeOffer } from '../utils' +import { BTC, factorToPercentage, isAbsoluteOffer, isRelativeOffer } from '../utils' import { isDebugFeatureEnabled, isDevMode } from '../constants/debugFeatures' import ToggleSwitch from './ToggleSwitch' import { pseudoRandomNumber } from './Send/helpers' import { JM_DUST_THRESHOLD } from '../constants/config' +import * as fb from './fb/utils' import styles from './Orderbook.module.css' const TABLE_THEME = { @@ -102,6 +103,10 @@ interface OrderTableEntry { bondValue: { value: number displayValue: string // example: "0" (no fb) or "114557102085.28133" + locktime?: number + displayLocktime?: string + displayExpiresIn?: string + amount?: AmountSats } } @@ -170,7 +175,34 @@ const renderOrderAsRow = (item: OrderTableRow, settings: any) => { - {item.bondValue.displayValue} + + {item.bondValue.value > 0 ? ( + ( + + +
+ {item.bondValue.displayLocktime} ({item.bondValue.displayExpiresIn}) +
+
+ )} + > + {item.bondValue.displayValue} +
+ ) : ( + <>{item.bondValue.displayValue} + )} +
) } @@ -290,9 +322,13 @@ const OrderbookTable = ({ data }: OrderbookTableProps) => { ) } -const offerToTableEntry = (offer: ObwatchApi.Offer, t: TFunction): OrderTableEntry => { +const offerToTableEntry = ( + offer: ObwatchApi.Offer, + fidelityBond: ObwatchApi.FidelityBond | undefined, + i18n: i18n, +): OrderTableEntry => { return { - type: orderTypeProps(offer, t), + type: orderTypeProps(offer, i18n.t), counterparty: offer.counterparty, orderId: String(offer.oid), fee: @@ -314,6 +350,17 @@ const offerToTableEntry = (offer: ObwatchApi.Offer, t: TFunction): OrderTableEnt bondValue: { value: offer.fidelity_bond_value, displayValue: String(offer.fidelity_bond_value.toFixed(0)), + locktime: fidelityBond?.locktime, + displayLocktime: + fidelityBond?.locktime !== undefined ? new Date(fidelityBond.locktime * 1_000).toDateString() : undefined, + displayExpiresIn: + fidelityBond?.locktime !== undefined + ? fb.time.humanReadableDuration({ + to: fidelityBond.locktime * 1_000, + locale: i18n.resolvedLanguage || i18n.language, + }) + : undefined, + amount: fidelityBond?.amount, }, } } @@ -479,13 +526,19 @@ type OrderbookOverlayProps = rb.OffcanvasProps & { } export function OrderbookOverlay({ nickname, show, onHide }: OrderbookOverlayProps) { - const { t } = useTranslation() + const { t, i18n } = useTranslation() const [alert, setAlert] = useState() const [isInitialized, setIsInitialized] = useState(false) const [isLoading, setIsLoading] = useState(true) const [offers, setOffers] = useState() - const tableEntries = useMemo(() => offers && offers.map((offer) => offerToTableEntry(offer, t)), [offers, t]) + const [fidelityBonds, setFidelityBonds] = useState>() const [__dev_showGenerateDemoOfferButton] = useState(isDebugFeatureEnabled('enableDemoOrderbook')) + const tableEntries = useMemo(() => { + return ( + offers && + offers.map((offer) => offerToTableEntry(offer, fidelityBonds && fidelityBonds.get(offer.counterparty), i18n)) + ) + }, [offers, fidelityBonds, i18n]) const __dev_generateDemoReportEntryButton = () => { const randomMinsize = pseudoRandomNumber(JM_DUST_THRESHOLD, JM_DUST_THRESHOLD + 100_000) @@ -524,6 +577,7 @@ export function OrderbookOverlay({ nickname, show, onHide }: OrderbookOverlayPro setIsLoading(false) setAlert(undefined) setOffers(orderbook.offers || []) + setFidelityBonds(new Map((orderbook.fidelitybonds || []).map((it) => [it.counterparty, it]))) if (isDevMode()) { console.table(orderbook.offers)