import {Dispatch, SetStateAction, useEffect, useRef} from 'react'; import { getCartUpdates } from '../api/promoApi'; import { normalizeProductImage } from '../utils/product'; import type { Product } from '../types/cart'; import {useGlobalZustandState} from "./useGlobalZustandState"; interface UseCartPollingParams { enabled: boolean; onProductsChange: Dispatch>; onComplete: () => void; onLoadingChange: (isLoading: boolean) => void; setDisplayText: Dispatch>; } let missing_scenarios = [ [ [10_000, 'Ищем Брока...'], [10_000, 'Мы всё ещё пытаемся его найти...'], [10_000, 'Может быть он в колбасном отделе?'], [10_000, 'Или в своём любимом?'], [10_000, 'Какой у Брока любимый отдел?'], [10_000, 'Если это прятки, то я проиграл, выходи'], [10_000, 'Я сдаюсь, перезагрузите страницу?'], ], [ [10_000, 'Брок у полки с соком...'], [10_000, 'Он выбирает апельсиновый...'], [10_000, 'Вспомнил, что любит яблочный...'], [10_000, 'Теперь сравнивает состав...'], [10_000, 'Сока нет, есть нектар...'], [10_000, 'Брок возмущён...'], [10_000, 'Он уходит с водой...'], [10_000, 'Классика. Перезагрузите страницу?'] ], [ [10_000, 'Брок в молочном отделе...'], [10_000, 'Ищет кефир 1%...'], [10_000, 'Нашёл, но срок годности...'], [10_000, 'Завтра. Брок не рискует...'], [10_000, 'Берёт 2,5%...'], [10_000, 'Потом ставит обратно...'], [10_000, 'Пробует на ощупь ряженку...'], [10_000, 'Это надолго. Перезагрузите страницу?'] ], [ [10_000, 'Брок в отделе замороженных продуктов...'], [15_000, 'Брок застрял в отделе заморозки. Буквально...'], [10_000, 'Брок пытается открыть морозилку изнутри...'], [10_000, 'Кажется, два пломбира ему мешают...'], [10_000, 'Вызвали спасателей и сотрудника с ломом...'], [10_000, 'Брок заявил, что без пломбира не выйдет...'], [10_000, 'Переговоры вступили в финальную стадию...'], [10_000, 'У Брока коробка пломбира'], [10_000, 'Перезагрузите страницу?'], ], [ [10_000, 'Брок увидел акцию 1+1 и теперь не может выбрать...'], [10_000, 'Брок сказал, что ему нужно ещё пять минуточек...'], [10_000, 'Брок увлёкся бесплатными образцами...'], [10_000, 'Кажется, Брок уже на КСО'], [10_000, 'Брок забыл, что он собирает корзину?'], [10_000, 'Брок двигается к выходу с арбузом...'], [10_000, 'Брок отвлёкся на робот-пылесос...'], [10_000, 'Перезагрузите страницу?'], ], // [ // [10_000, 'Брок встал в очередь на кассу...'], // [10_000, 'У Брока в корзине один огурец и лотерейный билет...'], // [10_000, 'Брок завис на вопросе: «Пакет нужен?»'], // [10_000, 'Брок спрашивает: «А у вас есть пакет с динозаврами?»'], // [10_000, 'Брок утверждает, что видел такие пакеты в рекламе 2007 года...'], // [10_000, 'Менеджер ищет пакеты с динозаврами на складе...'], // [10_000, 'Это надолго, нужна перезагрузка'], // ], [ [10_000, 'Брок обнаружил тележку с крутящимся колесом...'], [10_000, 'Теперь он использует её как скейтборд...'], [10_000, 'Брок проехал мимо отдела напитков...'], [10_000, 'Сотрудник делает замечание...'], [10_000, 'Брок не слышит — он в наушниках...'], [10_000, 'Брок врезался в пирамиду из консервов...'], [10_000, 'Консервы разлетелись'], [10_000, 'Брок помогает собирать, напевая оперу...'], [10_000, 'Импровизированный концерт у полки с томатами.'], [10_000, 'Перезагрузите страницу?'], ], [ [10_000, 'Брок идёт к выходу, его останавливает охранник...'], [10_000, 'Охранник просит показать чек...'], [10_000, 'Брок показывает купон на скидку...'], [10_000, 'Охранник терпеливо объясняет разницу...'], [10_000, 'Брок делает вид, что не понимает по-русски...'], [10_000, 'Охранник переходит на английский, французский...'], [10_000, 'Брок отвечает на клингонском'], [10_000, 'Охранник оказывается фанатом «Звёздного пути»'], [10_000, 'Они уже пьют кофе и обсуждают спин-оффы.'] ], [ [10_000, 'Брок свернул не туда и оказался в отделе игрушек...'], [10_000, 'Брок застыл перед полкой с резиновыми уточками...'], [10_000, 'Брок нажимает на всех уточек по очереди...'], [10_000, 'Магазин наполняется хором пищащих уток...'], [10_000, 'Сотрудник пытается остановить, но Брок уже дирижирует...'], [10_000, 'Покупатели подхватывают, достают уточек из корзин...'], [10_000, 'Флешмоб пищащих уток выходит из-под контроля...'], [10_000, 'Брок покидает отдел под овации, оставив уточку в кармане.'], [10_000, 'Брока вернёт лишь перезагрузка страницы'], ] ] as const; export function useCartPolling({ enabled, onProductsChange, onComplete, onLoadingChange, setDisplayText, }: UseCartPollingParams) { const timeoutRef = useRef(null); const {updateFromResponse, markAsComplete, markAsIncomplete} = useGlobalZustandState((state) => state); useEffect(() => { if (!enabled) return; setDisplayText('Брок бегает по супермаркету...') let cancelled = false; let missing_timeout: ReturnType | null = null; let missing_scenario_index = Math.round(Math.random() * (missing_scenarios.length - 1)); let missing_scenario_element = -1; const timeoutFunction = () => { missing_scenario_element++; if (missing_scenario_element < missing_scenarios[missing_scenario_index].length) { setDisplayText(missing_scenarios[missing_scenario_index][missing_scenario_element][1]) missing_timeout = setTimeout(timeoutFunction, missing_scenarios[missing_scenario_index][missing_scenario_element][0]); } }; async function tick() { try { onLoadingChange(true); const update = await getCartUpdates(); if (cancelled) return; if (update.status === 'waiting') { if (!missing_timeout) { missing_timeout = setTimeout(() => { setDisplayText('Ищем Брока...') timeoutFunction(); }, 10_000); } timeoutRef.current = window.setTimeout(tick, 2000); return; } else if (missing_timeout) { clearTimeout(missing_timeout); } missing_scenario_element = -1; if (update.status === 'complete') { onLoadingChange(false); onComplete(); updateFromResponse(true, update.payload?.excess || 0, update.payload?.precision || 0); return; } if (update.status === 'clear') { const nextProducts = update.payload?.chunk?.map(product => normalizeProductImage(product)) ?? []; timeoutRef.current = window.setTimeout(tick, 3000); markAsIncomplete(); onProductsChange(nextProducts); } if (update.status === 'data') { const chunk = update.payload?.chunk?.map(product => normalizeProductImage(product)) ?? []; updateFromResponse(false, update.payload?.excess || 0, update.payload?.precision || 0); onProductsChange((current: Product[]) => { const next = [...current]; chunk.forEach(product => { const index = next.findIndex(item => item.name === product.name); if (index === -1) { next.push(product); } else { next[index] = { ...next[index], n: next[index].n + product.n, }; } }); return next; }); setDisplayText('Брок закинул пару товаров...') } timeoutRef.current = window.setTimeout(tick, 5000); } catch (error) { setDisplayText('Нет связи с супермаркетом...') timeoutRef.current = window.setTimeout(tick, 3000); } } tick(); return () => { cancelled = true; if (missing_timeout) { window.clearTimeout(missing_timeout); } if (timeoutRef.current) { window.clearTimeout(timeoutRef.current); } }; }, [enabled, onComplete, onLoadingChange, onProductsChange, updateFromResponse, markAsComplete, markAsIncomplete]); }