From fca376dd6a985d4c1197648d8f5f48c20d9af271 Mon Sep 17 00:00:00 2001 From: Randall Naar Date: Wed, 26 Aug 2026 15:43:09 -0400 Subject: [PATCH] Mobile improvements and changes to network selector. Fixed mobile text overflows, added border to network selector, and added percentage delta to bitcoin price chart. --- client/src/components/info-card.js | 11 +++- client/src/lib/pending-block-details.js | 4 +- client/src/views/overview.js | 39 ++++++++++++++- .../src/views/pending-block-details-card.js | 10 ++-- flavors/liquid/extras.css | 1 + test/overview.test.js | 29 ++++++++++- www/style.css | 50 ++++++++++++++++--- 7 files changed, 127 insertions(+), 17 deletions(-) diff --git a/client/src/components/info-card.js b/client/src/components/info-card.js index 831e0272..bee34709 100644 --- a/client/src/components/info-card.js +++ b/client/src/components/info-card.js @@ -11,6 +11,7 @@ export const InfoCard = ({ footer, body, className, + infoCardHeaderValueClass, } = {}) => (
) : null} {headerValue !== undefined ? ( -

{headerValue}

+ + {headerValue} + ) : null}
{value !== undefined ?

{value}

: null} diff --git a/client/src/lib/pending-block-details.js b/client/src/lib/pending-block-details.js index b93a5bab..abb01f83 100644 --- a/client/src/lib/pending-block-details.js +++ b/client/src/lib/pending-block-details.js @@ -50,9 +50,9 @@ export const formatPanelPercentage = (value, fallback = "N/A") => export const formatFeeBoundary = (value, fallback = "N/A") => Number.isFinite(value) ? value.toFixed(2) : fallback; -export const formatWeight = (value, fallback = "N/A") => +export const formatWeight = (value, fallback = "N/A", omitSuffix = false) => Number.isFinite(value) - ? `${formatTrimmedDecimal(value / 1_000_000)} MWU` + ? `${formatTrimmedDecimal(value / 1_000_000)} ${omitSuffix ? "" : "MWU"}` : fallback; export const formatMegabytes = (value, fallback = "N/A") => diff --git a/client/src/views/overview.js b/client/src/views/overview.js index 0bd0f498..3781b09b 100644 --- a/client/src/views/overview.js +++ b/client/src/views/overview.js @@ -13,6 +13,38 @@ import { formatFeeRate, formatUsd } from "./util"; const staticRoot = process.env.STATIC_ROOT || ""; +const getPercentDelta = (chartPrices, unavailable = "N/A") => { + if (!Array.isArray(chartPrices) || chartPrices.length < 2) { + return unavailable; + } + + const startingPrice = chartPrices[0]; + const endingPrice = chartPrices[chartPrices.length - 1]; + + if ( + !Number.isFinite(startingPrice) || + startingPrice === 0 || + !Number.isFinite(endingPrice) + ) { + return unavailable; + } + + const priceDeltaPercentage = + ((endingPrice - startingPrice) / startingPrice) * 100; + + if (!Number.isFinite(priceDeltaPercentage)) { + return unavailable; + } + + const formattedPercentage = priceDeltaPercentage.toFixed(2); + + if (priceDeltaPercentage < 0) { + return {`${formattedPercentage}%`}; + } + + return {`+${formattedPercentage}%`}; +}; + const getChartPrices = (marketChart) => getBitcoinPrices(marketChart).slice(-24); @@ -76,7 +108,12 @@ export const overview = ({ + {formatUsd(currentBitcoinPrice, t`N/A`)} + {getPercentDelta(chartPrices, t`N/A`)} + + } body={ - - {t`Live`} - + + + {t`Live`} + ) : undefined } + infoCardHeaderValueClass="block-transactions-status-badge-container" value={ @@ -680,6 +681,7 @@ const PendingBlockDetailsCard = ({ ? `${formatWeight( metrics.totalWeight, unavailable, + true )} / ${formatWeight( metrics.weightLimit, unavailable, diff --git a/flavors/liquid/extras.css b/flavors/liquid/extras.css index a479380f..71f07821 100644 --- a/flavors/liquid/extras.css +++ b/flavors/liquid/extras.css @@ -5,6 +5,7 @@ --surface-primary-color: #0C1537; --surface-secondary-color: #172146; --surface-primary-alt-color: #111F3C; + --field-border-color: #172146; } .search-bar { diff --git a/test/overview.test.js b/test/overview.test.js index 8a39b279..10393925 100644 --- a/test/overview.test.js +++ b/test/overview.test.js @@ -15,7 +15,7 @@ test("formats the recommended fee rate and dollar estimate", () => { blocks: [], feeEst: { 3: 6 }, mempool: { vsize: 0 }, - bitcoinMarketChart: { prices: [[1, 50_000]] }, + bitcoinMarketChart: { prices: [[1, 40_000], [2, 50_000]] }, t, })); @@ -27,6 +27,33 @@ test("formats the recommended fee rate and dollar estimate", () => { ); assert(html.includes(expectedFeeUsd), expectedFeeUsd); assert.match(html, /\$50,000\.00 USD/); + assert.match(html, /\+25\.00%/); +}); + +test("formats negative Bitcoin price changes", () => { + const html = render(overview({ + blocks: [], + bitcoinMarketChart: { prices: [[1, 50_000], [2, 40_000]] }, + t, + })); + + assert.match(html, /class="text-danger">-20\.00%/); +}); + +test("shows an unavailable Bitcoin price change without a valid baseline", () => { + const singlePriceHtml = render(overview({ + blocks: [], + bitcoinMarketChart: { prices: [[1, 50_000]] }, + t, + })); + const zeroBaselineHtml = render(overview({ + blocks: [], + bitcoinMarketChart: { prices: [[1, 0], [2, 50_000]] }, + t, + })); + + assert.match(singlePriceHtml, /\$50,000\.00 USD<\/span>N\/A/); + assert.match(zeroBaselineHtml, /\$50,000\.00 USD<\/span>N\/A/); }); test("shows unavailable overview fee and price values explicitly", () => { diff --git a/www/style.css b/www/style.css index 82cd233c..ccfb04bb 100644 --- a/www/style.css +++ b/www/style.css @@ -2711,6 +2711,7 @@ dl.mempool-histogram .bar:before { background-color: var(--surface-primary-color); border-radius: 8px; padding: 12px 16px; + border: 1px solid var(--field-border-color); } .network-hover-menu-option-container { @@ -3483,7 +3484,6 @@ a.back-link img{ .tooltip { display: flex; position: relative; - margin-left: 6px; padding: 0; border: 0; color: inherit; @@ -3515,7 +3515,7 @@ a.back-link img{ max-width: 200px; padding: 6px; border-radius: 4px; - background-color: var(--surface-secondary-color); + background-color: var(--surface-primary-color); font-size: 14px; color: #FAFAFA; top: 10px; @@ -3524,7 +3524,7 @@ a.back-link img{ white-space: normal; overflow-wrap: anywhere; z-index: 999; - border: .5px solid var(--field-border-color); + border: 1px solid var(--field-border-color); } @media only screen and (min-width: 1329px) { @@ -3702,6 +3702,7 @@ a.back-link img{ .usage-and-tooltip { display: flex; align-items: center; + gap: 6px; } .usage-number { @@ -3885,11 +3886,12 @@ a.back-link img{ .table-header { display: flex; align-items: center; + gap: 6px; white-space: nowrap; } .table-header-title { - margin-left: 16px; + margin-left: 10px; color: #B5BDC2; font-family: "Rigid Square", sans-serif; font-size: 20px; @@ -4193,6 +4195,11 @@ a.back-link img{ font-size: 10px; } +.overview-bitcoin-price-chart-header-value { + display: flex; + gap: 4px; +} + .overview-bitcoin-price-chart { height: 100%; width: 100%; @@ -4494,6 +4501,7 @@ a.back-link img{ .block-details-card-progress-title { display: flex; align-items: center; + gap: 6px; } .block-details-card-progress-header .usage-number { @@ -4913,6 +4921,26 @@ a.back-link img{ .asset-table-issuance-link { margin-left: 0; } + + .overview-fees { + flex-direction: column; + gap: 0px; + align-items: initial; + } + + .transaction-table-footer-confirmation-text{ + flex-wrap: wrap; + } + + .info-card-header { + flex-wrap: wrap; + } +} + +@media only screen and (max-width: 485px) { + .block-transactions-status-badge-container { + width: 100%; + } } .pending-block-details-card { @@ -5248,6 +5276,7 @@ a.back-link img{ flex: .55; flex-direction: column; gap: 12px; + margin-top: 12px; } .expanded-pending-block-row { @@ -5412,6 +5441,7 @@ a.back-link img{ @media only screen and (max-width: 1328px) { .overview-body { overflow-x: auto; + overflow-y: hidden; scrollbar-width: none; -ms-overflow-style: none; } @@ -5433,10 +5463,6 @@ a.back-link img{ font-size: 14px; } - .expanded-pending-block-row { - flex-direction: column; - } - .pending-block-container { width: 100%; height: auto; @@ -5759,6 +5785,10 @@ a.back-link img{ margin-left: auto; margin-right: auto; } + + .metric-bar-header-value { + font-size: 12px; + } } @media only screen and (max-width: 1328px) { @@ -5988,4 +6018,8 @@ a.blockstream-footer-contact:focus { .blockstream-footer-product-columns { column-gap: 24px; } + + .table-title-row { + display: none; + } }