From 47fea81380832cbbe5cdd03ed9db22efc4535d37 Mon Sep 17 00:00:00 2001 From: "dev.daminik00" Date: Sun, 24 Aug 2025 01:41:12 +0200 Subject: [PATCH] add auto refill --- src/hooks/chats/useChatSocket.ts | 36 +++++++++++++++++++++++++++++++- src/services/socket/events.ts | 9 ++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/hooks/chats/useChatSocket.ts b/src/hooks/chats/useChatSocket.ts index 3853bc9..e67ef6e 100644 --- a/src/hooks/chats/useChatSocket.ts +++ b/src/hooks/chats/useChatSocket.ts @@ -4,6 +4,7 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { fetchChatMessages } from "@/entities/chats/actions"; import type { IChatMessage } from "@/entities/chats/types"; +import { useSingleCheckout } from "@/hooks/payment/useSingleCheckout"; import { useSocketEvent } from "@/hooks/socket/useSocketEvent"; import { useChatStore } from "@/providers/chat-store-provider"; import { @@ -112,6 +113,20 @@ export const useChatSocket = ( emit("fetch_balance", { chatId }); }, [emit, chatId]); + // Auto top-up: use existing single checkout flow + const { handleSingleCheckout, isLoading: isAutoTopUpLoading } = useSingleCheckout({ + onSuccess: fetchBalance, + onError: () => { + // eslint-disable-next-line no-console + console.error("Auto top-up payment failed"); + // Release in-flight lock on error so a future event can retry + autoTopUpInProgressRef.current = false; + }, + }); + + // Auto top-up: silent flow (no UI prompt) + const autoTopUpInProgressRef = useRef(false); + const balancePollId = useRef(null); const startBalancePolling = useCallback(() => { @@ -177,13 +192,32 @@ export const useChatSocket = ( options.onNewMessage(data[0]); } }); - useSocketEvent("current_balance", b => setBalance(b.data)); + useSocketEvent("current_balance", b => { + setBalance(b.data); + // If auto top-up was in-flight, release the lock only after balance became positive + if (autoTopUpInProgressRef.current && b?.data?.balance > 0) { + autoTopUpInProgressRef.current = false; + } + }); useSocketEvent("balance_updated", b => { setBalance(prev => (prev ? { ...prev, balance: b.data.balance } : null)); + if (autoTopUpInProgressRef.current && b?.data?.balance > 0) { + autoTopUpInProgressRef.current = false; + } }); useSocketEvent("session_started", s => setSession(s.data)); useSocketEvent("session_ended", () => setSession(null)); useSocketEvent("show_refill_modals", r => setRefillModals(r.data)); + useSocketEvent("auto_topup_request", r => { + if (!r?.data) return; + if (isAutoTopUpLoading) return; + // Prevent concurrent or rapid duplicate attempts + if (autoTopUpInProgressRef.current) return; + autoTopUpInProgressRef.current = true; + // Trigger checkout silently + handleSingleCheckout(r.data); + }); + useEffect(() => { if (!session?.maxFinishedAt) return; diff --git a/src/services/socket/events.ts b/src/services/socket/events.ts index 1fc5f2e..c9cfb11 100644 --- a/src/services/socket/events.ts +++ b/src/services/socket/events.ts @@ -44,6 +44,12 @@ export interface IRefillModals { products?: IRefillModalsProduct[]; } +export interface IAutoTopUpRequest { + productId: string; + key: string; + isAutoTopUp?: boolean; +} + export interface IUnreadMessagesCount { unreadCount: number; } @@ -81,6 +87,9 @@ export interface ServerToClientEvents { show_refill_modals: ( data: ServerToClientEventsBaseData ) => void; + auto_topup_request: ( + data: ServerToClientEventsBaseData + ) => void; chats_updated: (data: IGetChatsListResponse) => void; unread_messages_count: (data: IUnreadMessagesCount) => void; }