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.
98 lines
2.8 KiB
TypeScript
98 lines
2.8 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
import { useParams, useSearchParams } from "react-router";
|
|
import {
|
|
type BrokerageEvent,
|
|
useBrokerageEvents,
|
|
} from "@/entities/brokerage-event";
|
|
import { useScreenSync } from "@/features/tbank-connection";
|
|
|
|
type AccountEventsPageViewModel =
|
|
| { state: "loading" }
|
|
| { state: "error"; retry: () => void }
|
|
| {
|
|
state: "loaded";
|
|
events: BrokerageEvent[];
|
|
sourceError?: boolean;
|
|
lastSuccessfulAt?: string | null;
|
|
partial?: boolean;
|
|
refreshing: boolean;
|
|
refreshError: boolean;
|
|
retry: () => void;
|
|
draftFrom: string | null;
|
|
draftTo: string | null;
|
|
canApply: boolean;
|
|
onDraftFromChange: (value: string | null) => void;
|
|
onDraftToChange: (value: string | null) => void;
|
|
onApply: () => void;
|
|
onReset: () => void;
|
|
};
|
|
|
|
function useAccountEventsPageViewModel(
|
|
scopeId?: string,
|
|
): AccountEventsPageViewModel {
|
|
const { accountId: routeAccountId } = useParams();
|
|
const accountId = scopeId ?? routeAccountId;
|
|
const [searchParams, setSearchParams] = useSearchParams();
|
|
const appliedFrom = searchParams.get("from");
|
|
const appliedTo = searchParams.get("to");
|
|
const [draftFrom, setDraftFrom] = useState<string | null>(appliedFrom);
|
|
const [draftTo, setDraftTo] = useState<string | null>(appliedTo);
|
|
useEffect(() => {
|
|
setDraftFrom(appliedFrom);
|
|
setDraftTo(appliedTo);
|
|
}, [appliedFrom, appliedTo]);
|
|
const events = useBrokerageEvents(accountId, {
|
|
...(appliedFrom ? { from: appliedFrom } : {}),
|
|
...(appliedTo ? { to: appliedTo } : {}),
|
|
});
|
|
const sync = useScreenSync(
|
|
{
|
|
screen: "events",
|
|
accountId,
|
|
...(appliedFrom ? { from: appliedFrom } : {}),
|
|
...(appliedTo ? { to: appliedTo } : {}),
|
|
},
|
|
[events],
|
|
);
|
|
if (events.isLoading) return { state: "loading" };
|
|
if ((events.isError || events.meta?.sync.state === "failed") && !events.data)
|
|
return {
|
|
state: "error",
|
|
retry: () => {
|
|
void sync.refresh();
|
|
},
|
|
};
|
|
if (!events.data) return { state: "loading" };
|
|
return {
|
|
state: "loaded",
|
|
events: events.data.events,
|
|
sourceError: sync.sourceError,
|
|
lastSuccessfulAt: events.meta?.lastSuccessfulAt,
|
|
partial: events.meta?.availability !== "complete",
|
|
refreshing: events.isFetching || sync.refreshing,
|
|
refreshError: events.isError || sync.sourceError || sync.transportError,
|
|
retry: () => {
|
|
void sync.refresh();
|
|
},
|
|
draftFrom,
|
|
draftTo,
|
|
canApply:
|
|
(draftFrom !== appliedFrom || draftTo !== appliedTo) &&
|
|
(!draftFrom || !draftTo || draftFrom <= draftTo),
|
|
onDraftFromChange: setDraftFrom,
|
|
onDraftToChange: setDraftTo,
|
|
onApply: () =>
|
|
setSearchParams({
|
|
...(draftFrom ? { from: draftFrom } : {}),
|
|
...(draftTo ? { to: draftTo } : {}),
|
|
}),
|
|
onReset: () => {
|
|
setDraftFrom(null);
|
|
setDraftTo(null);
|
|
setSearchParams({});
|
|
},
|
|
};
|
|
}
|
|
|
|
export { type AccountEventsPageViewModel, useAccountEventsPageViewModel };
|