From 8cdd687c1d46527afed72f81fb9d948ca29ba516 Mon Sep 17 00:00:00 2001 From: JCNoguera Date: Mon, 13 May 2024 12:27:45 +0200 Subject: [PATCH 1/3] feat: add metric units to landing page --- .../nova/landing/LandingEpochSection.tsx | 8 +++--- .../src/app/routes/nova/landing/Landing.tsx | 6 ++--- .../nova/formatAmountWithMetricUnit.ts | 27 +++++++++++++++++++ 3 files changed, 34 insertions(+), 7 deletions(-) create mode 100644 client/src/helpers/nova/formatAmountWithMetricUnit.ts diff --git a/client/src/app/components/nova/landing/LandingEpochSection.tsx b/client/src/app/components/nova/landing/LandingEpochSection.tsx index b9716657a..b1aa02708 100644 --- a/client/src/app/components/nova/landing/LandingEpochSection.tsx +++ b/client/src/app/components/nova/landing/LandingEpochSection.tsx @@ -9,11 +9,11 @@ import RightHalfArrow from "~assets/right-half-arrow.svg?react"; import ArrowUp from "~assets/arrow_up.svg?react"; import ProgressBar from "./ProgressBar"; import StatDisplay from "../../StatDisplay"; -import { formatAmount } from "~/helpers/stardust/valueFormatHelper"; import { clamp } from "~/helpers/clamp"; import { Link } from "react-router-dom"; -import "./LandingEpochSection.scss"; import { getTimeRemaining } from "~/helpers/nova/novaTimeUtils"; +import { formatAmountWithMetricUnit } from "~/helpers/nova/formatAmountWithMetricUnit"; +import "./LandingEpochSection.scss"; const EPOCH_DATE_FORMAT = "DD MMM YYYY HH:mm:ss"; @@ -50,11 +50,11 @@ const LandingEpochSection: React.FC = () => { subtitle: "Validators", }, { - title: `${totalCommitteeStake !== undefined ? formatAmount(totalCommitteeStake, tokenInfo) : "--"}`, + title: `${totalCommitteeStake !== undefined ? formatAmountWithMetricUnit(totalCommitteeStake, tokenInfo) : "--"}`, subtitle: "Staked in committee", }, { - title: `${commiiteeDelegatorStake !== undefined ? formatAmount(commiiteeDelegatorStake, tokenInfo) : "--"}`, + title: `${commiiteeDelegatorStake !== undefined ? formatAmountWithMetricUnit(commiiteeDelegatorStake, tokenInfo) : "--"}`, subtitle: "Delegated in committee", }, { diff --git a/client/src/app/routes/nova/landing/Landing.tsx b/client/src/app/routes/nova/landing/Landing.tsx index 2d869d243..155c435e9 100644 --- a/client/src/app/routes/nova/landing/Landing.tsx +++ b/client/src/app/routes/nova/landing/Landing.tsx @@ -8,7 +8,7 @@ import { useNetworkStats } from "~/helpers/nova/hooks/useNetworkStats"; import Hero from "~/app/components/Hero"; import { IStatDisplay } from "~/app/lib/interfaces"; import { StatDisplaySize } from "~/app/lib/enums"; -import { formatAmount } from "~/helpers/stardust/valueFormatHelper"; +import { formatAmountWithMetricUnit } from "~/helpers/nova/formatAmountWithMetricUnit"; import "./Landing.scss"; const Landing: React.FC = () => { @@ -52,12 +52,12 @@ const Landing: React.FC = () => { size: StatDisplaySize.Small, }, { - title: `${totalValidatorsStake !== undefined ? formatAmount(totalValidatorsStake, tokenInfo) : "--"}`, + title: `${totalValidatorsStake !== undefined ? formatAmountWithMetricUnit(totalValidatorsStake, tokenInfo) : "--"}`, subtitle: "Total Staked", size: StatDisplaySize.Small, }, { - title: `${totalDelegatedStake !== null ? formatAmount(totalDelegatedStake, tokenInfo) : "--"}`, + title: `${totalDelegatedStake !== null ? formatAmountWithMetricUnit(totalDelegatedStake, tokenInfo) : "--"}`, subtitle: "Total Delegated", size: StatDisplaySize.Small, }, diff --git a/client/src/helpers/nova/formatAmountWithMetricUnit.ts b/client/src/helpers/nova/formatAmountWithMetricUnit.ts new file mode 100644 index 000000000..3b318a389 --- /dev/null +++ b/client/src/helpers/nova/formatAmountWithMetricUnit.ts @@ -0,0 +1,27 @@ +import { INodeInfoBaseToken } from "@iota/sdk-wasm-stardust/web"; +import { UnitsHelper } from "@iota/iota.js"; + +/** + * Format token amount to metric unit. + * @param value The raw amount to format. + * @param tokenInfo The token info configuration to use. + * @returns The formatted string with the metric unit. + */ +export function formatAmountWithMetricUnit(value: number | bigint | string, tokenInfo: INodeInfoBaseToken): string { + const amount = Number(value); + + if (Number.isNaN(value)) { + return ""; + } + + const formattedValue = UnitsHelper.formatBest(amount, 0); + + // remove leading lowercase "i" from formatted value + const lastIIndex = formattedValue.lastIndexOf("i"); + if (lastIIndex === -1) return `${formattedValue} ${tokenInfo.unit}`; + + const formattedAmountWithToken = + formattedValue.substring(0, lastIIndex) + formattedValue.substring(lastIIndex + 1) + `${tokenInfo.unit}`; + + return formattedAmountWithToken; +} From e7a2385a3b161ef7d4973b8576a70bd888e1624e Mon Sep 17 00:00:00 2001 From: JCNoguera Date: Mon, 13 May 2024 12:30:56 +0200 Subject: [PATCH 2/3] chore: improve comment --- client/src/helpers/nova/formatAmountWithMetricUnit.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/src/helpers/nova/formatAmountWithMetricUnit.ts b/client/src/helpers/nova/formatAmountWithMetricUnit.ts index 3b318a389..e00793cd3 100644 --- a/client/src/helpers/nova/formatAmountWithMetricUnit.ts +++ b/client/src/helpers/nova/formatAmountWithMetricUnit.ts @@ -16,12 +16,12 @@ export function formatAmountWithMetricUnit(value: number | bigint | string, toke const formattedValue = UnitsHelper.formatBest(amount, 0); - // remove leading lowercase "i" from formatted value - const lastIIndex = formattedValue.lastIndexOf("i"); - if (lastIIndex === -1) return `${formattedValue} ${tokenInfo.unit}`; + // Remove iota unit if it exists + const lastIotaIndex = formattedValue.lastIndexOf("i"); + if (lastIotaIndex === -1) return `${formattedValue} ${tokenInfo.unit}`; const formattedAmountWithToken = - formattedValue.substring(0, lastIIndex) + formattedValue.substring(lastIIndex + 1) + `${tokenInfo.unit}`; + formattedValue.substring(0, lastIotaIndex) + formattedValue.substring(lastIotaIndex + 1) + `${tokenInfo.unit}`; return formattedAmountWithToken; } From cbaa804bd3a8de5bbc4618dfd13e9a561348c35f Mon Sep 17 00:00:00 2001 From: JCNoguera Date: Mon, 13 May 2024 14:16:02 +0200 Subject: [PATCH 3/3] chore: remove iota.js and do the conversion manually --- .../nova/landing/LandingEpochSection.tsx | 6 +-- .../src/app/routes/nova/landing/Landing.tsx | 6 +-- .../nova/formatAmountWithMetricUnit.ts | 27 ---------- .../nova/formatRawAmountWithMetricUnit.ts | 52 +++++++++++++++++++ 4 files changed, 58 insertions(+), 33 deletions(-) delete mode 100644 client/src/helpers/nova/formatAmountWithMetricUnit.ts create mode 100644 client/src/helpers/nova/formatRawAmountWithMetricUnit.ts diff --git a/client/src/app/components/nova/landing/LandingEpochSection.tsx b/client/src/app/components/nova/landing/LandingEpochSection.tsx index b1aa02708..1e1b8e62e 100644 --- a/client/src/app/components/nova/landing/LandingEpochSection.tsx +++ b/client/src/app/components/nova/landing/LandingEpochSection.tsx @@ -12,7 +12,7 @@ import StatDisplay from "../../StatDisplay"; import { clamp } from "~/helpers/clamp"; import { Link } from "react-router-dom"; import { getTimeRemaining } from "~/helpers/nova/novaTimeUtils"; -import { formatAmountWithMetricUnit } from "~/helpers/nova/formatAmountWithMetricUnit"; +import { formatRawAmountWithMetricUnit } from "~/helpers/nova/formatRawAmountWithMetricUnit"; import "./LandingEpochSection.scss"; const EPOCH_DATE_FORMAT = "DD MMM YYYY HH:mm:ss"; @@ -50,11 +50,11 @@ const LandingEpochSection: React.FC = () => { subtitle: "Validators", }, { - title: `${totalCommitteeStake !== undefined ? formatAmountWithMetricUnit(totalCommitteeStake, tokenInfo) : "--"}`, + title: `${totalCommitteeStake !== undefined ? formatRawAmountWithMetricUnit(totalCommitteeStake, tokenInfo) : "--"}`, subtitle: "Staked in committee", }, { - title: `${commiiteeDelegatorStake !== undefined ? formatAmountWithMetricUnit(commiiteeDelegatorStake, tokenInfo) : "--"}`, + title: `${commiiteeDelegatorStake !== undefined ? formatRawAmountWithMetricUnit(commiiteeDelegatorStake, tokenInfo) : "--"}`, subtitle: "Delegated in committee", }, { diff --git a/client/src/app/routes/nova/landing/Landing.tsx b/client/src/app/routes/nova/landing/Landing.tsx index 155c435e9..4c26e1538 100644 --- a/client/src/app/routes/nova/landing/Landing.tsx +++ b/client/src/app/routes/nova/landing/Landing.tsx @@ -8,7 +8,7 @@ import { useNetworkStats } from "~/helpers/nova/hooks/useNetworkStats"; import Hero from "~/app/components/Hero"; import { IStatDisplay } from "~/app/lib/interfaces"; import { StatDisplaySize } from "~/app/lib/enums"; -import { formatAmountWithMetricUnit } from "~/helpers/nova/formatAmountWithMetricUnit"; +import { formatRawAmountWithMetricUnit } from "~/helpers/nova/formatRawAmountWithMetricUnit"; import "./Landing.scss"; const Landing: React.FC = () => { @@ -52,12 +52,12 @@ const Landing: React.FC = () => { size: StatDisplaySize.Small, }, { - title: `${totalValidatorsStake !== undefined ? formatAmountWithMetricUnit(totalValidatorsStake, tokenInfo) : "--"}`, + title: `${totalValidatorsStake !== undefined ? formatRawAmountWithMetricUnit(totalValidatorsStake, tokenInfo) : "--"}`, subtitle: "Total Staked", size: StatDisplaySize.Small, }, { - title: `${totalDelegatedStake !== null ? formatAmountWithMetricUnit(totalDelegatedStake, tokenInfo) : "--"}`, + title: `${totalDelegatedStake !== null ? formatRawAmountWithMetricUnit(totalDelegatedStake, tokenInfo) : "--"}`, subtitle: "Total Delegated", size: StatDisplaySize.Small, }, diff --git a/client/src/helpers/nova/formatAmountWithMetricUnit.ts b/client/src/helpers/nova/formatAmountWithMetricUnit.ts deleted file mode 100644 index e00793cd3..000000000 --- a/client/src/helpers/nova/formatAmountWithMetricUnit.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { INodeInfoBaseToken } from "@iota/sdk-wasm-stardust/web"; -import { UnitsHelper } from "@iota/iota.js"; - -/** - * Format token amount to metric unit. - * @param value The raw amount to format. - * @param tokenInfo The token info configuration to use. - * @returns The formatted string with the metric unit. - */ -export function formatAmountWithMetricUnit(value: number | bigint | string, tokenInfo: INodeInfoBaseToken): string { - const amount = Number(value); - - if (Number.isNaN(value)) { - return ""; - } - - const formattedValue = UnitsHelper.formatBest(amount, 0); - - // Remove iota unit if it exists - const lastIotaIndex = formattedValue.lastIndexOf("i"); - if (lastIotaIndex === -1) return `${formattedValue} ${tokenInfo.unit}`; - - const formattedAmountWithToken = - formattedValue.substring(0, lastIotaIndex) + formattedValue.substring(lastIotaIndex + 1) + `${tokenInfo.unit}`; - - return formattedAmountWithToken; -} diff --git a/client/src/helpers/nova/formatRawAmountWithMetricUnit.ts b/client/src/helpers/nova/formatRawAmountWithMetricUnit.ts new file mode 100644 index 000000000..bcccf8f6b --- /dev/null +++ b/client/src/helpers/nova/formatRawAmountWithMetricUnit.ts @@ -0,0 +1,52 @@ +import { INodeInfoBaseToken } from "@iota/sdk-wasm-stardust/web"; +import { formatAmount } from "~/helpers/stardust/valueFormatHelper"; + +const UNIT_MAP: { threshold: number; unit: string }[] = [ + { unit: "", threshold: 1 }, + { unit: "K", threshold: 1_000 }, + { unit: "M", threshold: 1_000_000 }, + { unit: "G", threshold: 1_000_000_000 }, + { unit: "T", threshold: 1_000_000_000_000 }, + { unit: "P", threshold: 1_000_000_000_000_000 }, +]; + +const sortedUnits = UNIT_MAP.sort((a, b) => b.threshold - a.threshold); + +/** + * Format the best amount with metric unit. + * @param value The value to format. + * @returns The formatted string with the metric unit. + */ +function formatBestAmountWithMetric(value: number): string { + for (const { threshold, unit } of sortedUnits) { + if (value >= threshold) { + return `${Math.floor(value / threshold)}${unit}`; + } + } + + return `${Math.floor(value)}`; +} + +/** + * Format token amount to metric unit. + * @param value The raw amount to format. + * @param tokenInfo The token info configuration to use. + * @returns The formatted string with the metric unit. + */ +export function formatRawAmountWithMetricUnit(value: number | bigint | string, tokenInfo: INodeInfoBaseToken): string { + if (Number.isNaN(value)) { + return ""; + } + + const tokenAmount = parseInt(formatAmount(value, tokenInfo, false, 0).replace(tokenInfo.unit, "")); + const formattedValue = formatBestAmountWithMetric(tokenAmount); + + // Remove iota unit if it exists + const lastIotaIndex = formattedValue.lastIndexOf("i"); + if (lastIotaIndex === -1) return `${formattedValue} ${tokenInfo.unit}`; + + const formattedAmountWithToken = + formattedValue.substring(0, lastIotaIndex) + formattedValue.substring(lastIotaIndex + 1) + ` ${tokenInfo.unit}`; + + return formattedAmountWithToken; +}