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;
+ }
}