118 lines
4.2 KiB
TypeScript
118 lines
4.2 KiB
TypeScript
"use client";
|
||
|
||
import { useCallback, useState, useTransition } from "react";
|
||
import { useRouter } from "next/navigation";
|
||
|
||
import { checkVideoGuidePurchase } from "@/entities/dashboard/actions";
|
||
import { useSingleCheckout } from "@/hooks/payment/useSingleCheckout";
|
||
import { useToast } from "@/providers/toast-provider";
|
||
import { ROUTES } from "@/shared/constants/client-routes";
|
||
|
||
interface UseVideoGuidePurchaseOptions {
|
||
videoGuideId: string;
|
||
productId: string;
|
||
productKey: string;
|
||
}
|
||
|
||
export function useVideoGuidePurchase(options: UseVideoGuidePurchaseOptions) {
|
||
const { productId, productKey } = options;
|
||
const { addToast } = useToast();
|
||
const router = useRouter();
|
||
const [isProcessingPurchase, setIsProcessingPurchase] = useState(false);
|
||
const [isCheckingPurchase, setIsCheckingPurchase] = useState(false);
|
||
const [isPending, startTransition] = useTransition();
|
||
|
||
const { handleSingleCheckout, isLoading: isCheckoutLoading } = useSingleCheckout({
|
||
onSuccess: async () => {
|
||
// Показываем toast о успешной покупке
|
||
addToast({
|
||
variant: "success",
|
||
message: "Video guide purchased successfully!",
|
||
duration: 3000,
|
||
});
|
||
|
||
// Включаем лоадер на всей карточке
|
||
setIsProcessingPurchase(true);
|
||
|
||
// Ждем 4 секунды
|
||
await new Promise(resolve => setTimeout(resolve, 4000));
|
||
|
||
// Обновляем данные dashboard в transition
|
||
// isPending будет true пока данные загружаются
|
||
startTransition(() => {
|
||
router.refresh();
|
||
});
|
||
|
||
// Убираем наш флаг, но isPending продолжит показывать loader
|
||
setIsProcessingPurchase(false);
|
||
},
|
||
onError: error => {
|
||
addToast({
|
||
variant: "error",
|
||
message: error || "Purchase failed. Please try again.",
|
||
duration: 5000,
|
||
});
|
||
},
|
||
returnUrl: new URL(ROUTES.home(), process.env.NEXT_PUBLIC_APP_URL || "").toString(),
|
||
});
|
||
|
||
const handlePurchase = useCallback(async () => {
|
||
// Сначала проверяем, не куплен ли уже продукт
|
||
setIsCheckingPurchase(true);
|
||
|
||
try {
|
||
const result = await checkVideoGuidePurchase(productKey);
|
||
|
||
if (result.data && result.data.isPurchased) {
|
||
// Продукт уже куплен! Показываем сообщение и обновляем страницу
|
||
addToast({
|
||
variant: "success",
|
||
message: "You already own this video guide!",
|
||
duration: 3000,
|
||
});
|
||
|
||
setIsCheckingPurchase(false);
|
||
|
||
// Включаем лоадер на всей карточке
|
||
setIsProcessingPurchase(true);
|
||
|
||
// Даем небольшую задержку для плавного UX
|
||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||
|
||
// Обновляем данные dashboard в transition
|
||
// isPending будет true пока данные загружаются
|
||
startTransition(() => {
|
||
router.refresh();
|
||
});
|
||
|
||
// Убираем наш флаг, но isPending продолжит показывать loader
|
||
setIsProcessingPurchase(false);
|
||
return;
|
||
}
|
||
|
||
// Продукт не куплен, продолжаем с checkout
|
||
setIsCheckingPurchase(false);
|
||
handleSingleCheckout({
|
||
productId,
|
||
key: productKey,
|
||
});
|
||
} catch (error) {
|
||
console.error("Error checking purchase status:", error);
|
||
setIsCheckingPurchase(false);
|
||
|
||
// Даже если проверка не удалась, продолжаем с checkout
|
||
// чтобы не блокировать покупку
|
||
handleSingleCheckout({
|
||
productId,
|
||
key: productKey,
|
||
});
|
||
}
|
||
}, [handleSingleCheckout, productId, productKey, addToast, router]);
|
||
|
||
return {
|
||
handlePurchase,
|
||
isCheckoutLoading: isCheckoutLoading || isCheckingPurchase, // Загрузка на кнопке (во время checkout или проверки)
|
||
isProcessingPurchase: isProcessingPurchase || isPending, // Загрузка на всей карточке (включая transition)
|
||
};
|
||
}
|