98 lines
3.8 KiB
TypeScript
98 lines
3.8 KiB
TypeScript
import { useEffect, useRef } from 'react';
|
|
import type { Dispatch, MutableRefObject } from 'react';
|
|
import type { SetURLSearchParams } from 'react-router-dom';
|
|
import type { ManualEntryPayload } from '../lib/manualEntriesApi';
|
|
import type { TradePlansUiAction } from './tradePlansState';
|
|
|
|
type SimpleHolding = {
|
|
symbol: string;
|
|
size: number;
|
|
entryPrice: number;
|
|
profileId?: string;
|
|
tradeId?: string;
|
|
};
|
|
|
|
type UseTradePlansNavigationStateInput = {
|
|
searchParams: URLSearchParams;
|
|
setSearchParams: SetURLSearchParams;
|
|
savedSetups: ManualEntryPayload[];
|
|
availableSellHoldings: SimpleHolding[];
|
|
applyHoldingToDraft: (holding: SimpleHolding) => void;
|
|
dispatch: Dispatch<TradePlansUiAction>;
|
|
setupCardRefs: MutableRefObject<Record<string, HTMLDivElement | null>>;
|
|
};
|
|
|
|
export function useTradePlansNavigationState({
|
|
searchParams,
|
|
setSearchParams,
|
|
savedSetups,
|
|
availableSellHoldings,
|
|
applyHoldingToDraft,
|
|
dispatch,
|
|
setupCardRefs,
|
|
}: UseTradePlansNavigationStateInput) {
|
|
const consumedPrefillKeyRef = useRef<string>('');
|
|
const consumedSetupFocusKeyRef = useRef<string>('');
|
|
const focusedSetupTimerRef = useRef<number | null>(null);
|
|
|
|
useEffect(() => {
|
|
const requestedMode = String(searchParams.get('mode') || '').trim().toLowerCase();
|
|
const requestedSymbol = String(searchParams.get('symbol') || '').trim().toUpperCase();
|
|
const requestedTradeId = String(searchParams.get('tradeId') || '').trim();
|
|
const prefillKey = `${requestedMode}|${requestedSymbol}|${requestedTradeId}`;
|
|
if (!requestedMode || consumedPrefillKeyRef.current === prefillKey) return;
|
|
if (requestedMode !== 'sell') return;
|
|
if (availableSellHoldings.length === 0) return;
|
|
|
|
const selected = (requestedTradeId
|
|
? availableSellHoldings.find((holding) => holding.tradeId === requestedTradeId)
|
|
: null)
|
|
|| (requestedSymbol
|
|
? availableSellHoldings.find((holding) => holding.symbol === requestedSymbol)
|
|
: null)
|
|
|| availableSellHoldings[0];
|
|
|
|
if (selected) {
|
|
applyHoldingToDraft(selected);
|
|
dispatch({ type: 'set-message', value: `Loaded ${selected.symbol} from Portfolio. Configure the profit target and save the plan.` });
|
|
dispatch({ type: 'set-error', value: null });
|
|
}
|
|
consumedPrefillKeyRef.current = prefillKey;
|
|
setSearchParams({}, { replace: true });
|
|
}, [applyHoldingToDraft, availableSellHoldings, dispatch, searchParams, setSearchParams]);
|
|
|
|
useEffect(() => {
|
|
const requestedSetupId = String(searchParams.get('setupId') || '').trim();
|
|
if (!requestedSetupId || consumedSetupFocusKeyRef.current === requestedSetupId) return;
|
|
if (savedSetups.length === 0) return;
|
|
|
|
const targetEntry = savedSetups.find((entry) => String(entry.stock_instance_id || '') === requestedSetupId) || null;
|
|
consumedSetupFocusKeyRef.current = requestedSetupId;
|
|
setSearchParams({}, { replace: true });
|
|
|
|
if (!targetEntry) return;
|
|
|
|
dispatch({ type: 'set-focused-setup-id', value: requestedSetupId });
|
|
dispatch({ type: 'set-message', value: `Focused saved plan for ${targetEntry.symbol}.` });
|
|
dispatch({ type: 'set-error', value: null });
|
|
if (focusedSetupTimerRef.current !== null) {
|
|
window.clearTimeout(focusedSetupTimerRef.current);
|
|
}
|
|
focusedSetupTimerRef.current = window.setTimeout(() => {
|
|
dispatch({ type: 'set-focused-setup-id', value: null });
|
|
focusedSetupTimerRef.current = null;
|
|
}, 2200);
|
|
window.requestAnimationFrame(() => {
|
|
setupCardRefs.current[requestedSetupId]?.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
|
});
|
|
}, [dispatch, savedSetups, searchParams, setSearchParams, setupCardRefs]);
|
|
|
|
useEffect(() => {
|
|
return () => {
|
|
if (focusedSetupTimerRef.current !== null) {
|
|
window.clearTimeout(focusedSetupTimerRef.current);
|
|
}
|
|
};
|
|
}, []);
|
|
}
|