diff --git a/components/balanceHistory/BalanceHistoryChart.tsx b/components/balanceHistory/BalanceHistoryChart.tsx index d526507ffb98df1f0104a66eca4c0566b71fe844..dd10214ac5f4301d1b1af8670cc930725a8cb585 100644 --- a/components/balanceHistory/BalanceHistoryChart.tsx +++ b/components/balanceHistory/BalanceHistoryChart.tsx @@ -23,8 +23,11 @@ import { ArrowDown, ArrowUp, Minus } from "lucide-react"; import { useI18n } from "@/i18n/i18n"; import useDynamicGlobal from "@/hooks/api/homePage/useDynamicGlobal"; import { useHiveChainContext } from "@/contexts/HiveChainContext"; -import { convertVestsToHive } from "@/utils/Calculations"; +import { convertVestsToHive, convertVestsToHP } from "@/utils/Calculations"; import { grabNumericValue } from "@/utils/StringUtils"; +import { Switch } from "@/components/ui/switch"; +import { Label } from "@/components/ui/label"; +import { useSettings } from "@/contexts/SettingsContext"; interface BalanceHistoryChartProps { aggregatedAccountBalanceHistory?: { @@ -63,22 +66,26 @@ const BalanceHistoryChart: React.FC = ({ const { t, dir, locale } = useI18n(); const { hiveChain } = useHiveChainContext(); const { dynamicGlobalData } = useDynamicGlobal(); + const { settings } = useSettings(); const isRTL = dir === "rtl"; const [isMobile, setIsMobile] = useState(window.innerWidth < 480); const [hiddenDataKeys, setHiddenDataKeys] = useState([]); + const [unit, setUnit] = useState<"vests" | "hp">( + settings.displayVestHpMode === "hp" ? "hp" : "vests" + ); + + useEffect(() => { + setUnit(settings.displayVestHpMode === "hp" ? "hp" : "vests"); + }, [settings.displayVestHpMode]); - // State to store available coins const availableCoins = ["HIVE", "VESTS", "HBD"]; - const [zoomedDomain, setZoomedDomain] = useState<[number, number] | null>( - null - ); + const [zoomedDomain, setZoomedDomain] = useState<[number, number] | null>(null); useEffect(() => { const handleResize = () => { setIsMobile(window.innerWidth < 480); }; - window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []); @@ -93,23 +100,32 @@ const BalanceHistoryChart: React.FC = ({ if (type === "VESTS") { const vests = item.balance?.toString() || "0"; - const hiveAmount = grabNumericValue( - convertVestsToHive( - hiveChain, - vests, - dynamicGlobalData.headBlockDetails.rawTotalVestingFundHive, - dynamicGlobalData.headBlockDetails.rawTotalVestingShares - ) - ); + const convertedValue = unit === "hp" + ? parseFloat( + convertVestsToHP( + hiveChain, + vests, + dynamicGlobalData.headBlockDetails.rawTotalVestingFundHive, + dynamicGlobalData.headBlockDetails.rawTotalVestingShares + ) + ) + : parseFloat( + convertVestsToHive( + hiveChain, + vests, + dynamicGlobalData.headBlockDetails.rawTotalVestingFundHive, + dynamicGlobalData.headBlockDetails.rawTotalVestingShares + ) + ); const dollarValueFull = - !isNaN(hiveAmount) && !isNaN(hivePrice) - ? hiveAmount * hivePrice + !isNaN(convertedValue) && !isNaN(hivePrice) + ? convertedValue * hivePrice : 0; return { ...item, - convertedHive: hiveAmount, + convertedHive: convertedValue, dollarValue: dollarValueFull, }; } @@ -120,23 +136,17 @@ const BalanceHistoryChart: React.FC = ({ ? item.balance * hivePrice : 0; - return { - ...item, - dollarValue, - }; + return { ...item, dollarValue }; } if (type === "HBD") { - return { - ...item, - dollarValue: !isNaN(item.balance) ? item.balance : 0, - }; + return { ...item, dollarValue: !isNaN(item.balance) ? item.balance : 0 }; } return item; }); }, - [dynamicGlobalData, hiveChain] + [dynamicGlobalData, hiveChain, unit] ); const dataMap: Record< @@ -162,20 +172,10 @@ const BalanceHistoryChart: React.FC = ({ setSelectedCoinType(coinType); }; - const displayData = useMemo(() => { - return dataMap[selectedCoinType]; - //eslint-disable-next-line react-hooks/exhaustive-deps - }, [selectedCoinType]); - - const CustomTooltip = ({ - active, - payload, - label, - }: { - active?: boolean; - payload?: any[]; - label?: string; - }) => { + const displayData = useMemo(() => dataMap[selectedCoinType], [selectedCoinType]); + + // ---------------- Tooltip ---------------- + const CustomTooltip = ({ active, payload, label }: { active?: boolean; payload?: any[]; label?: string; }) => { const { dir: tooltipDir } = useI18n(); if (quickView || !active || !payload || payload.length === 0) return null; const isTooltipRTL = tooltipDir === "rtl"; @@ -198,213 +198,128 @@ const BalanceHistoryChart: React.FC = ({ return (

{`${t("common.date")}: ${label}`}

-
-
+
+
{isPositiveChange ? ( - + ) : isZeroChange ? ( - + ) : ( - + )} - {` ${formatNumber(balanceChange, selectedCoinType === "VESTS")}`} + {` ${formatNumber(balanceChange, selectedCoinType === "VESTS" ? unit === "vests" : false)}`}
-
{`${t( - "common.balance" - )}: ${formatNumber( - actualBalance, - selectedCoinType === "VESTS" - )}`}
- +
{`${t("common.balance")}: ${formatNumber(actualBalance, selectedCoinType === "VESTS" ? unit === "vests" : false)}`}
{dollarValue ? (
- Dollar Value: $ - {formatNumber(dollarValue, false, selectedCoinType === "VESTS")} + Dollar Value: ${formatNumber(dollarValue, false, selectedCoinType === "VESTS")}
) : null}
- {showSavingsBalance === "yes" && - savingsBalance !== undefined && - selectedCoinType !== "VESTS" && ( -
-
- {isSavingsPositiveChange ? ( - - ) : isSavingsZeroChange ? ( - - ) : ( - - )} - {` ${formatNumber( - savingsBalanceChange, - selectedCoinType === "VESTS" - )}`} -
-
- {`${t("balanceHistoryChart.savingsBalance")}: ${formatNumber( - savingsBalance, - selectedCoinType === "VESTS" - )}`} -
+ {showSavingsBalance === "yes" && savingsBalance !== undefined && selectedCoinType !== "VESTS" && ( +
+
+ {isSavingsPositiveChange ? ( + + ) : isSavingsZeroChange ? ( + + ) : ( + + )} + {` ${formatNumber(savingsBalanceChange, selectedCoinType === "VESTS" ? unit === "vests" : false)}`}
- )} +
+ {`${t("balanceHistoryChart.savingsBalance")}: ${formatNumber(savingsBalance, selectedCoinType === "VESTS" ? unit === "vests" : false)}`} +
+
+ )}
); }; - const renderCoinButtons = () => { - return availableCoins.map((coinType) => ( - - )); - }; - const getMinMax = ( - data: { - balance: number; - savings_balance?: number; - dollarValue?: number; - convertedHive?: number; - }[] - ): [number, number] => { - if (!data || data.length === 0) { - return [0, 1]; - } + )}>{coinType} + ))} + + {selectedCoinType === "VESTS" && ( +
+ + setUnit(checked ? "hp" : "vests")} /> + +
+ )} +
+ ); + + // ---------------- Min/Max for Y-axis ---------------- + const getMinMax = (data: any[]): [number, number] => { + if (!data || data.length === 0) return [0, 1]; let allValues: number[] = []; if (selectedCoinType === "VESTS") { - const dollarValues = data - .map((item) => item.dollarValue) - .filter((v): v is number => typeof v === "number" && !Number.isNaN(v)); - + allValues = data.map((item) => item.convertedHive || 0); + const dollarValues = data.map((item) => item.dollarValue).filter((v): v is number => typeof v === "number" && !Number.isNaN(v)); allValues = allValues.concat(dollarValues); } else { allValues = data.map((item) => item.balance); - - const dollarValues = data - .map((item) => item.dollarValue) - .filter((v): v is number => typeof v === "number" && !Number.isNaN(v)); + const dollarValues = data.map((item) => item.dollarValue).filter((v): v is number => typeof v === "number" && !Number.isNaN(v)); allValues = allValues.concat(dollarValues); - if (showSavingsBalance === "yes") { - const savingsValues = data - .map((item) => item.savings_balance) - .filter((v): v is number => typeof v === "number"); + const savingsValues = data.map((item) => item.savings_balance).filter((v): v is number => typeof v === "number"); allValues = allValues.concat(savingsValues); } } - const minValue = Math.min(...allValues); - const maxValue = Math.max(...allValues); - - return [minValue, maxValue]; + return [Math.min(...allValues), Math.max(...allValues)]; }; const [fullDataMin, fullDataMax] = getMinMax(displayData); const [minValue, maxValue] = zoomedDomain || [fullDataMin, fullDataMax]; - const handleBrushAreaChange = (domain: { - startIndex?: number; - endIndex?: number; - }) => { - if ( - !domain || - domain.startIndex === undefined || - domain.endIndex === undefined - ) { - // Reset zoom if brush is cleared or start/end index is undefined + const handleBrushAreaChange = (domain: { startIndex?: number; endIndex?: number; }) => { + if (!domain || domain.startIndex === undefined || domain.endIndex === undefined) { setZoomedDomain([fullDataMin, fullDataMax]); return; } - - const { startIndex, endIndex } = domain; - const visibleData = (displayData || []).slice(startIndex, endIndex + 1); - + const visibleData = (displayData || []).slice(domain.startIndex, domain.endIndex + 1); if (visibleData.length > 0) { const [min, max] = getMinMax(visibleData); setZoomedDomain([min, max]); } }; - if (!displayData || !displayData.length) return; + if (!displayData || !displayData.length) return null; const isDualAxis = selectedCoinType === "VESTS"; const primaryAxisId = isRTL ? "right" : "left"; const secondaryAxisId = isRTL ? "left" : "right"; + const leftMargin = isMobile + ? 10 + : selectedCoinType === "VESTS" && unit === "hp" + ? 50 + : 30; + return (
- {quickView && ( -
- {renderCoinButtons()} -
- )} - - + {renderCoinButtons()} + @@ -426,13 +341,13 @@ const BalanceHistoryChart: React.FC = ({ tickCount={6} tickFormatter={(tick) => { if (selectedCoinType === "VESTS") { + if (unit === "hp") return `${formatNumber(tick, false, false)}`; const valueInK = tick / 1_000; return `${formatNumber(valueInK, true, false).split(".")[0]} K`; } return formatNumber(tick, false, false); }} /> - {isDualAxis && ( = ({ tickFormatter={(tick) => `$${Math.round(tick)}`} /> )} - } /> - = ({ dot={false} hide={hiddenDataKeys.includes("dollarValue")} /> - {showSavingsBalance === "yes" && selectedCoinType !== "VESTS" && ( = ({ hide={hiddenDataKeys.includes("savings_balance")} /> )} - {!quickView && ( = ({ wrapperStyle={{ paddingTop: isMobile ? "20px" : "0px" }} onClick={(event) => { const dataKey = event.dataKey as string; - const isHidden = hiddenDataKeys.includes(dataKey as string); - if (isHidden) { - setHiddenDataKeys( - hiddenDataKeys.filter((key) => key !== dataKey) - ); - } else { - setHiddenDataKeys([...hiddenDataKeys, dataKey]); - } + const isHidden = hiddenDataKeys.includes(dataKey); + if (isHidden) setHiddenDataKeys(hiddenDataKeys.filter((key) => key !== dataKey)); + else setHiddenDataKeys([...hiddenDataKeys, dataKey]); }} />