feat: show persisted investment data and sync state

Consume the provided resource-envelope API snapshot. Preserve financial snapshots during
refresh, observe synchronization without importing, and fence late responses across
account and connection changes.
This commit is contained in:
2026-09-26 21:22:49 +03:00
parent b987fd8e78
commit 4e5f564fa1
81 changed files with 4714 additions and 1420 deletions
+26 -8
View File
@@ -16,6 +16,7 @@ import {
useStockQuote,
} from "@/entities/stock";
import { section } from "@/features/instrument-details";
import { useScreenSync } from "@/features/tbank-connection";
import { apiErrorCode } from "@/shared/api";
import { formatDate, instrumentLogoUrl } from "@/shared/lib";
@@ -58,6 +59,17 @@ function useStockPageVm() {
...firstPage,
cursor: cursors.at(-1),
});
const sync = useScreenSync(
{
screen: "stock",
instrumentUid: uid,
candlePeriod: period,
accountId: "all",
categories: categories[filter],
limit: 20,
},
[stock, quote, candles, position, company, dividends, operations],
);
const setParam = (key: string, value: string) => {
const next = new URLSearchParams(params);
next.set(key, value);
@@ -70,7 +82,7 @@ function useStockPageVm() {
queryKey: brokerageOperationsQueryOptions("all", firstPage).queryKey,
exact: true,
});
} else void operations.refetch();
} else void sync.refresh();
};
const chartData =
candles.data?.candles.map((candle) => ({
@@ -85,20 +97,23 @@ function useStockPageVm() {
: null;
const companyData = company.data;
return {
refresh: sync.refresh,
refreshing: sync.refreshing,
refreshError: sync.transportError || sync.sourceError,
canonicalTo:
stock.data?.slug && stock.data.slug !== stockId
? `/stocks/${encodeURIComponent(stock.data.slug)}${params.size ? `?${params.toString()}` : ""}`
: null,
stock: stock.data,
stockState: section(stock),
stockState: section(stock, uid ? sync.refresh : () => stock.refetch()),
code: apiErrorCode(stock.error),
logoSrc: instrumentLogoUrl(stock.data?.logoName),
quote: quote.data,
quoteState: section(quote),
quoteState: section(quote, sync.refresh),
candles: candles.data,
chartData,
priceRows: chartData.toReversed(),
candlesState: section(candles),
candlesState: section(candles, sync.refresh),
period,
setPeriod: (value: string) => setParam("period", value),
averageLine:
@@ -106,11 +121,11 @@ function useStockPageVm() {
hasIncompleteCandle:
candles.data?.candles.some((candle) => !candle.isComplete) ?? false,
position: position.data,
positionState: section(position),
positionState: section(position, sync.refresh),
company: companyData,
companyState: section(company),
companyState: section(company, sync.refresh),
dividends: dividends.data,
dividendsState: section(dividends),
dividendsState: section(dividends, sync.refresh),
upcoming:
dividends.data?.dividends
.filter((event) => event.timing === "upcoming")
@@ -128,7 +143,10 @@ function useStockPageVm() {
),
) ?? [],
operations: operations.data,
operationsState: { ...section(operations), retry: retryOperations },
operationsState: {
...section(operations, sync.refresh),
retry: retryOperations,
},
eventRows:
operations.data?.items.map((item) => ({
...item,