feat: add quiet reading with rare confirmations

Part of the quiet-user-feedback change; docs and specs land in their own repositories.
This commit is contained in:
2026-10-06 07:01:58 +03:00
parent c096fc70ef
commit 5b30bb802a
77 changed files with 1352 additions and 1259 deletions
@@ -6,25 +6,20 @@ import {
useBrokerageEvents,
} from "@/entities/brokerage-event";
import { useScreenSync } from "@/features/tbank-connection";
import { isValidPeriod, useUrlPeriod } from "@/shared/lib";
import { isValidPeriod, useInitialAvailability, useUrlPeriod } from "@/shared/lib";
type AccountEventsPageViewModel
= | {
canApply: boolean;
draftPeriod: [null | string, null | string];
events: BrokerageEvent[];
lastSuccessfulAt?: null | string;
onApply: () => void;
onPeriodChange: (value: [null | string, null | string]) => void;
onReset: () => void;
partial?: boolean;
refreshError: boolean;
refreshing: boolean;
retry: () => void;
sourceError?: boolean;
state: "loaded";
}
| { onReset: () => void; retry: () => void; state: "error" }
| { onReset: () => void; state: "error" }
| { state: "loading" };
function useAccountEventsPageViewModel(): AccountEventsPageViewModel {
@@ -56,7 +51,7 @@ function useAccountEventsPageViewModel(): AccountEventsPageViewModel {
...(appliedTo && { to: appliedTo }),
});
const sync = useScreenSync(
useScreenSync(
{
accountId,
screen: "events",
@@ -66,7 +61,15 @@ function useAccountEventsPageViewModel(): AccountEventsPageViewModel {
[events],
);
if (events.isLoading) return { state: "loading" };
const availability = useInitialAvailability({
hasContent: !!events.data,
identity: scope,
isError: events.isError || events.meta?.sync.state === "failed",
isFetching: events.isFetching,
});
if (availability === "loading" && events.isLoading)
return { state: "loading" };
const onReset = () => {
setDraftPeriod([null, null]);
@@ -76,12 +79,12 @@ function useAccountEventsPageViewModel(): AccountEventsPageViewModel {
if ((events.isError || events.meta?.sync.state === "failed") && !events.data)
return {
onReset,
retry: () => {
void sync.retry();
},
state: "error",
};
if (!events.data) return { state: "loading" };
if (!events.data)
return availability === "unavailable"
? { onReset, state: "error" }
: { state: "loading" };
return {
canApply:
@@ -89,7 +92,6 @@ function useAccountEventsPageViewModel(): AccountEventsPageViewModel {
&& isValidPeriod(draftFrom, draftTo),
draftPeriod,
events: events.data.events,
lastSuccessfulAt: events.meta?.lastSuccessfulAt,
onApply: () => {
if (!isValidPeriod(draftFrom, draftTo)) return;
setSearchParameters({
@@ -100,12 +102,6 @@ function useAccountEventsPageViewModel(): AccountEventsPageViewModel {
onPeriodChange: setDraftPeriod,
onReset,
partial: events.meta?.availability !== "complete",
refreshError: events.isError || sync.sourceError || sync.transportError,
refreshing: events.isFetching || sync.refreshing,
retry: () => {
void sync.retry();
},
sourceError: sync.sourceError,
state: "loaded",
};
}