fix: график крипты открывать за всё время, лучший/худший рост — по выбранному периоду

График во вкладке «Криптовалюта» теперь по умолчанию показывает всю
историю, а не 30 дней — короткое окно у портфеля, растущего годами,
часто попадало на локальную просадку и выглядело демотивирующе.

Плитки «Лучший рост» и «Худшее падение» раньше всегда считались за
24 часа. Теперь они следуют за диапазоном, выбранным у графика
(7д/30д/90д/всё время): для 7д/30д используются закэшированные данные
CoinGecko, для «всё время» — метрика за 1 год (дальше бесплатный
тариф CoinGecko историю не отдаёт), а для 90д — отдельный запрос по
каждой монете, который выполняется только когда пользователь реально
выбрал этот период.
This commit is contained in:
ZProger
2026-09-04 02:18:31 +03:00
parent aefd6cc035
commit cd125e9e45
15 changed files with 404 additions and 33 deletions

View File

@@ -11,6 +11,7 @@ import { CryptoStatsRow } from "@/components/crypto/CryptoStatsRow";
import { CryptoTransactionHistoryModal } from "@/components/crypto/CryptoTransactionHistoryModal";
import { CryptoTransactionModal } from "@/components/crypto/CryptoTransactionModal";
import {
useCrypto90dPerformance,
useCryptoHistory,
useCryptoHoldings,
useCryptoPortfolios,
@@ -27,7 +28,9 @@ function formatSyncedAt(iso: string): string {
export function CryptoPage() {
const { t } = useTranslation();
const [range, setRange] = useState<CryptoRange>("30d");
// "all" by default — a 30-day window makes a portfolio that's actually
// grown steadily for years look demotivating whenever it's mid-dip.
const [range, setRange] = useState<CryptoRange>("all");
// Single "hide balance" switch for the whole tab — owned here so every
// money-displaying section (chart, stats, allocation, table, trade
// history) masks together instead of the toggle only affecting whichever
@@ -38,6 +41,7 @@ export function CryptoPage() {
const { data: portfolios } = useCryptoPortfolios();
const { data, isLoading } = useCryptoHoldings(portfolioFilter);
const { data: history, isLoading: isHistoryLoading } = useCryptoHistory(range, portfolioFilter);
const { data: performance90d, isLoading: isPerformance90dLoading } = useCrypto90dPerformance(range, portfolioFilter);
const refresh = useRefreshCryptoPrices();
const deleteHolding = useDeleteCryptoHolding();
@@ -55,6 +59,10 @@ export function CryptoPage() {
const holdings = data?.holdings ?? [];
const totalValue = holdings.reduce((sum, holding) => sum + (holding.value !== null ? Number(holding.value) : 0), 0);
const portfoliosById = useMemo(() => new Map((portfolios ?? []).map((p) => [p.id, p])), [portfolios]);
const performance90dByAsset = useMemo(
() => new Map((performance90d?.items ?? []).map((item) => [item.asset_id, item.price_change_percent])),
[performance90d]
);
function openAddPortfolioModal() {
setEditingPortfolio(null);
@@ -110,7 +118,14 @@ export function CryptoPage() {
onToggleHidden={() => setHidden((prev) => !prev)}
/>
<CryptoStatsRow holdings={holdings} isLoading={isLoading} hidden={hidden} />
<CryptoStatsRow
holdings={holdings}
isLoading={isLoading}
hidden={hidden}
range={range}
performance90d={performance90dByAsset}
isPerformance90dLoading={isPerformance90dLoading}
/>
<Card>
<CardHeader className="flex-col items-stretch gap-3 sm:flex-row sm:items-center">