All files / src/contexts QuickViewModalContext.tsx

87.67% Statements 64/73
72.72% Branches 16/22
100% Functions 2/2
87.67% Lines 64/73

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 731x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 26x 26x 2x 2x 23x 23x 1x 1x 22x 22x 22x 22x 22x 22x 22x 22x 22x 22x           22x 22x 22x 22x 22x 22x 22x 22x 7x 7x         7x 7x 22x 22x 22x 4x 4x 4x 4x 4x 4x 4x 4x 4x 22x 22x 22x 22x 22x 22x 22x 22x
"use client"
import React, { createContext, useContext, useState, useCallback, useMemo } from "react";
import { useSearchParams, usePathname, useRouter } from "next/navigation";
 
interface ModalContextType {
  isModalOpen: boolean;
  openModal: (productId?: number) => void;
  closeModal: () => void;
  previewProductId: number | null;
}
 
const ModalContext = createContext<ModalContextType | undefined>(undefined);
 
export const useModalContext = () => {
  const context = useContext(ModalContext);
  if (!context) {
    throw new Error("useModalContext must be used within a ModalProvider");
  }
  return context;
};
 
export const ModalProvider = ({ children }: { children: React.ReactNode }) => {
  // Track manual open/close state separately from URL-derived state
  const [manualModalState, setManualModalState] = useState<{ isOpen: boolean; productId: number | null } | null>(null);
  const searchParams = useSearchParams();
  const pathname = usePathname();
  const router = useRouter();
 
  // Derive modal state from URL params - this avoids setState in effect
  const urlDerivedState = useMemo(() => {
    const previewParam = searchParams.get('preview');
    if (previewParam) {
      const productId = parseInt(previewParam, 10);
      if (!isNaN(productId)) {
        return { isOpen: true, productId };
      }
    }
    return { isOpen: false, productId: null };
  }, [searchParams]);
 
  // Combine manual state with URL-derived state (manual takes precedence when set)
  const isModalOpen = manualModalState?.isOpen ?? urlDerivedState.isOpen;
  const previewProductId = manualModalState?.productId ?? urlDerivedState.productId;
 
  const openModal = useCallback((productId?: number) => {
    // Update URL with preview param if productId is provided
    if (productId && typeof window !== 'undefined') {
      const params = new URLSearchParams(searchParams.toString());
      params.set('preview', productId.toString());
      router.replace(`${pathname}?${params.toString()}`, { scroll: false });
    }
    // Set manual state for immediate UI update
    setManualModalState({ isOpen: true, productId: productId ?? null });
  }, [searchParams, pathname, router]);
 
  const closeModal = useCallback(() => {
    // Remove preview param from URL
    if (typeof window !== 'undefined') {
      const params = new URLSearchParams(searchParams.toString());
      params.delete('preview');
      const newUrl = params.toString() ? `${pathname}?${params.toString()}` : pathname;
      router.replace(newUrl, { scroll: false });
    }
    // Set manual state for immediate UI update
    setManualModalState({ isOpen: false, productId: null });
  }, [searchParams, pathname, router]);
 
  return (
    <ModalContext.Provider value={{ isModalOpen, openModal, closeModal, previewProductId }}>
      {children}
    </ModalContext.Provider>
  );
};