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 {
useBrokerageOperations,
} from "@/entities/broker-operation";
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 useBondPageVm() {
...firstPage,
cursor: cursors.at(-1),
});
const sync = useScreenSync(
{
screen: "bond",
instrumentUid: uid,
candlePeriod: period,
accountId: "all",
categories: categories[filter],
limit: 20,
},
[bond, quote, candles, position, metrics, coupons, operations],
);
const setParam = (key: string, value: string) => {
const next = new URLSearchParams(params);
next.set(key, value);
@@ -70,7 +82,7 @@ function useBondPageVm() {
queryKey: brokerageOperationsQueryOptions("all", firstPage).queryKey,
exact: true,
});
} else void operations.refetch();
} else void sync.refresh();
};
const chartData =
candles.data?.candles.map((candle) => ({
@@ -79,31 +91,34 @@ function useBondPageVm() {
price: Number(candle.close),
})) ?? [];
return {
refresh: sync.refresh,
refreshing: sync.refreshing,
refreshError: sync.transportError || sync.sourceError,
canonicalTo:
bond.data?.slug && bond.data.slug !== bondId
? `/bonds/${encodeURIComponent(bond.data.slug)}${params.size ? `?${params.toString()}` : ""}`
: null,
bond: bond.data,
bondState: section(bond),
bondState: section(bond, uid ? sync.refresh : () => bond.refetch()),
code: apiErrorCode(bond.error),
logoSrc: instrumentLogoUrl(bond.data?.logoName),
metrics: metrics.data,
metricsState: section(metrics),
metricsState: section(metrics, sync.refresh),
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: null,
hasIncompleteCandle:
candles.data?.candles.some((candle) => !candle.isComplete) ?? false,
position: position.data,
positionState: section(position),
positionState: section(position, sync.refresh),
coupons: coupons.data,
couponsState: section(coupons),
couponsState: section(coupons, sync.refresh),
upcoming:
coupons.data?.coupons
.filter((event) => event.timing === "upcoming")
@@ -121,7 +136,10 @@ function useBondPageVm() {
),
) ?? [],
operations: operations.data,
operationsState: { ...section(operations), retry: retryOperations },
operationsState: {
...section(operations, sync.refresh),
retry: retryOperations,
},
eventRows:
operations.data?.items.map((item) => ({
...item,