React

useInfiniteQuery 사용하기

Mori_FEDev 2023. 10. 11. 13:57
import React, { useContext, useEffect, useRef, useState } from 'react';
import { LocalStateContext } from '../../api/LocalStateContext';
import { DownChevronSvg } from '../svgs/ButtonSvg';
import { useInfiniteQuery } from '@tanstack/react-query';
import { useAxiosAuth } from '../../api';
import { Tooltip as ReactTooltip } from 'react-tooltip';

const ImgSlide = ({ openState, setOpenState }: any) => {
  const { imgState } = useContext(LocalStateContext);

  const [isFullScreen, setIsFullScreen] = useState(false);
  const [fullScreenData, setFullScreenData] = useState(null);

  const axiosAuth = useAxiosAuth();

  const fetchThumbData = async (pageParam: number) => {
    const response = await axiosAuth.get(
      `/aidelete/files/thumbURL?rootNodeId=${imgState?.rootNodeId}&selectedNodeId=${imgState?.structureId}&ancestorId=${imgState?.ancestorId}&depth=${imgState?.depth}&division=${imgState?.category}&page=${pageParam}`,
    );
    //return response해야 올바른값 옴
    return response;
  };

  enum ThumbData {
    ThumbData = 'thumbData',
  }

  // useInfiniteQuery를 사용하여 데이터를 가져오고 페이지네이션을 설정합니다.
  const {
    data,
    isLoading: isThumbLoading,
    refetch,
    fetchNextPage,
    hasNextPage,
    isFetchingNextPage,
  } = useInfiniteQuery([ThumbData, ThumbData], ({ pageParam = 1 }) => fetchThumbData(pageParam), {
    getNextPageParam: (lastPage, allPages) => {
      // @ts-ignore
      return lastPage.currentPage < lastPage.totalPage ? allPages.length + 1 : undefined;
    },
  });

  useEffect(() => {
    refetch();
  }, [imgState]);

  const [i, setI] = useState(2);

  const scrollContainerRef = useRef<HTMLDivElement | null>(null);
  const originImgScrollContainerRef = useRef<HTMLDivElement | null>(null);
  const anonyImgScrollContainerRef = useRef<HTMLDivElement | null>(null);

  // imgState가 변경될 때 스크롤 위치를 초기화
  const resetScrollPosition = () => {
    if (scrollContainerRef.current) {
      scrollContainerRef.current.scrollTo({ left: 0, behavior: 'auto' });
    }
    if (originImgScrollContainerRef.current) {
      originImgScrollContainerRef.current.scrollTo({ left: 0, behavior: 'auto' });
    }
    if (anonyImgScrollContainerRef.current) {
      anonyImgScrollContainerRef.current.scrollTo({ left: 0, behavior: 'auto' });
    }
    setI(2);
  };

  useEffect(() => {
    // imgState가 변경될 때 스크롤 위치를 초기화
    resetScrollPosition();
  }, [imgState]);

  const handleScroll = () => {
    const scrollContainer = scrollContainerRef.current;
    if (scrollContainer) {
      const scrollLeft = scrollContainer.scrollLeft;
      const scrollWidth = scrollContainer.scrollWidth;

      // 다른 컴포넌트에 스크롤 위치 설정
      if (originImgScrollContainerRef.current) {
        // 원본 이미지 컴포넌트의 스크롤 위치 설정
        originImgScrollContainerRef.current.scrollLeft = scrollLeft;
      }

      if (anonyImgScrollContainerRef.current) {
        // 익명화 이미지 컴포넌트의 스크롤 위치 설정
        anonyImgScrollContainerRef.current.scrollLeft = scrollLeft;
      }

      if (scrollLeft + scrollContainer.clientWidth >= scrollWidth) {
        // 스크롤이 오른쪽 끝에 도달하면 다음 페이지를 가져옵니다.
        setI((prevI) => prevI + 1);
        fetchNextPage({ pageParam: i });
      }
    }
  };

  useEffect(() => {
    const scrollContainer = scrollContainerRef.current;
    if (scrollContainer) {
      //
      scrollContainer.addEventListener('scroll', handleScroll);

      return () => {
        //
        scrollContainer.removeEventListener('scroll', handleScroll);
      };
    }
  }, [openState, data, isThumbLoading]);

  const GoFullScreen = (data: any) => {
    setIsFullScreen(true);
    setFullScreenData(data);
  };

  const [selectedSection, setSelectedSection] = useState<number | null>(null);

  const handleSectionClick = (e: any, index: number) => {
    // 이전에 선택한 요소를 가져옵니다.
    const prevSelectedSection = document.querySelector(`.${selectedSectionClass}`);
    if (prevSelectedSection) {
      // 이전에 선택한 요소로부터 클래스를 제거합니다.
      prevSelectedSection.classList.remove(selectedSectionClass);
    }

    // 현재 클릭한 요소에 클래스를 추가합니다.
    const currentSection = e.currentTarget;
    currentSection.classList.add(selectedSectionClass);
    setSelectedSection(index);
  };

  const selectedSectionClass = 'selected-section'; // 선택된 요소에 적용할 CSS 클래스 이름

  const scrollToSelectedSection = (index: number) => {
    const scrollContainer = scrollContainerRef.current;
    if (scrollContainer) {
      const selectedSection = scrollContainer.querySelector(`section:nth-child(${index + 1})`) as HTMLElement;
      if (selectedSection) {
        // 선택한 섹션으로 스크롤 이동

        scrollContainer.scrollTo({
          left: selectedSection.offsetLeft - scrollContainer.offsetLeft,
          behavior: 'smooth',
        });
      }
    }
  };

  // 원본
  const OriginImgComponent = ({ data }: any) => {
    return (
      <div className="flex">
        {data?.map((item: any, index: number) => (
          <section key={index}>
            <div
              className={`
                        rootNodeId:${item?.rootNodeId}, structureId:${item?.structureId}, depth:${item?.depth}, ancestorId:${item?.ancestorId}}
            relative mr-[12px] h-[140px] w-[140px] cursor-pointer`}
            >
              <ReactTooltip
                style={{ zIndex: 99 }}
                className="reacttooltip"
                id="my-tooltip-1"
                place="right"
                content={`rootNodeId:${item?.rootNodeId}, structureId:${item?.structureId}, depth:${item?.depth}, ancestorId:${item?.ancestorId}`}
              />

              <img
                data-tooltip-id="my-tooltip-1"
                onClick={(e) => {
                  handleSectionClick(e, index);
                  scrollToSelectedSection(index); // 클릭 시 스크롤 이동 함수 호출
                }}
                className={`
              ${selectedSection === index ? selectedSectionClass : ''}
              border-box reacttooltip block rounded-[6px]`}
                src={item?.url}
                alt={`rootNodeId:${item?.rootNodeId}, structureId:${item?.structureId}, depth:${item?.depth}, ancestorId:${item?.ancestorId}`}
              />
              <div className="absolute top-0 left-[5px] text-[3px] font-light text-white">
                <span className="text-white">{index + 1}</span>
              </div>

              <h3 className="absolute bottom-[5px] left-[50%] w-[100px] translate-x-[-50%] truncate text-center text-[10px] font-light text-white">
                {item?.name}
              </h3>
              <div
                onClick={async (e) => {
                  e.preventDefault();
                  setIsFullScreen(true);
                  await GoFullScreen(item?.url);
                }}
                className="absolute bottom-[5px] right-[5px] h-[14px] w-[14px] cursor-pointer bg-[url('/images/icons/Fullscreen.png')]"
              />
            </div>
          </section>
        ))}
      </div>
    );
  };

  // 익명화
  const AnonyImgComponent = ({ data, selectedSection }: any) => {
    return (
      <div className="flex">
        {data?.map((item: any, index: number) => (
          <section key={index}>
            <div
              className={`
                        rootNodeId:${item?.rootNodeId}, structureId:${item?.structureId}, depth:${item?.depth}, ancestorId:${item?.ancestorId}}
            relative mr-[12px] h-[140px] w-[140px] cursor-pointer`}
            >
              <ReactTooltip
                style={{ zIndex: 99 }}
                className="reacttooltip"
                id="my-tooltip-2"
                place="right"
                content={`rootNodeId:${item?.rootNodeId}, structureId:${item?.structureId}, depth:${item?.depth}, ancestorId:${item?.ancestorId}`}
              />
              <img
                data-tooltip-id="my-tooltip-2"
                onClick={(e) => {
                  handleSectionClick(e, index);
                  scrollToSelectedSection(index); // 클릭 시 스크롤 이동 함수 호출
                }}
                className={`
            ${selectedSection === index ? selectedSectionClass : ''}
                block rounded-[6px]`}
                src={item?.url}
                alt={`rootNodeId:${item?.rootNodeId}, structureId:${item?.structureId}, depth:${item?.depth}, ancestorId:${item?.ancestorId}`}
              />
              <div className="absolute top-0 left-[5px] text-[3px] font-light text-white">
                <span className="text-white">{index + 1}</span>
              </div>
              <h3
                className="className={`ml-[10px] ${ absolute bottom-0  left-[50%] block w-[100px] w-full translate-x-[-50%] cursor-pointer
 truncate text-[10px] font-light text-white"
              >
                {item?.name}
              </h3>
              <div
                onClick={async (e) => {
                  e.preventDefault();
                  setIsFullScreen(true);
                  await GoFullScreen(item.url);
                }}
                className="absolute bottom-[5px] right-[5px] h-[14px] w-[14px] cursor-pointer bg-[url('/images/icons/Fullscreen.png')]"
              />
            </div>
          </section>
        ))}
      </div>
    );
  };

  // function Length({ data }: any) {
  //   return <div className="text-[12px] font-light text-[#1FA1FF]">Total {data?.length}</div>;
  // }

  return (
    <>
      {openState ? (
        <section
          className={`absolute bottom-0 z-10 flex w-full items-center justify-center bg-black bg-opacity-70 py-6 transition-all duration-1000 ease-in-out`}
        >
          {isFullScreen && fullScreenData && (
            <div className="fixed top-0 left-0 z-30 flex h-full w-full items-center justify-center">
              <div className="absolute inset-0 bg-black opacity-90"></div>
              <div className="relative flex h-[80%] w-[80%] items-center justify-center">
                <div
                  onClick={() => setIsFullScreen(false)}
                  className="absolute top-4 right-4 h-8 w-8 cursor-pointer text-center text-white"
                >
                  Close&nbsp;✕
                </div>
                <img className="block h-full " src={fullScreenData} alt="DICOM" />
              </div>
            </div>
          )}
          <button
            onClick={() => setOpenState((prev: boolean) => !prev)}
            className="inner-shadow absolute top-[-20px] left-0 z-20 flex h-[20px] w-[85px] cursor-pointer justify-center rounded-t-[10px] bg-sub03 bg-no-repeat"
          >
            <div className={`${openState ? '' : 'scale-y-[-1] transform'}`}>
              <DownChevronSvg color={'black'} className={'h-[20px] w-[20px]'} />
            </div>
          </button>

          <article className={`${openState === true ? 'opacity-100' : 'opacity-0'} mr-[30px] w-[35%] `}>
            <div className="flex w-full items-center justify-between">
              <h2
                className={`
          mb-[5px] text-[12px] font-bold text-sub04`}
              >
                Original File
              </h2>
              {/*<Length data={data?.pages?.[0]?.data?.data?.originThumbList} />*/}
            </div>
            {isThumbLoading ? (
              <div
                className={`${
                  openState === true ? 'h-[170px]' : 'bottom-0 h-0'
                } x-scrollbar-style flex w-full items-center justify-center overflow-y-clip overflow-x-scroll rounded-[10px] bg-base pl-[12px] pt-[15px]`}
              >
                <h1 className="text-[24px] font-bold text-stone-400">Loading...</h1>
                {/*TODO:Loading처리하거나 file missing문구 띄우기*/}
                {/*data?.pages[0].data.data.originThumbList*/}
              </div>
            ) : (
              <div
                ref={(ref) => {
                  scrollContainerRef.current = ref; // 공유 ref 설정
                  originImgScrollContainerRef.current = ref; // 원본 이미지 컴포넌트의 ref 설정
                }}
                //  ref={scrollContainerRef}
                className={`${
                  openState === true ? 'h-[170px]' : 'bottom-0 h-0'
                } x-scrollbar-style flex w-full items-center justify-start overflow-y-clip overflow-x-scroll rounded-[10px] bg-base pl-[12px] pt-[15px]`}
              >
                {data?.pages && (
                  <OriginImgComponent
                    data={data?.pages.flatMap((page) => page?.data?.data?.originThumbList)}
                    selectedSection={selectedSection}
                  />
                )}
              </div>
            )}
          </article>

          <article className={`${openState === true ? 'opacity-100' : 'opacity-0'} w-[50%]`}>
            <div className="flex w-full items-center justify-between">
              <h2
                className={`${
                  openState === true ? 'opacity-100' : 'opacity-0'
                } mb-[5px] text-[12px] font-bold text-sub04`}
              >
                Anonymized file
              </h2>
              {/*<Length data={data?.pages?.[0]?.data?.data?.anonyThumbList} />*/}
            </div>

            {data?.pages[0]?.data?.data?.anonyThumbList.length === 0 ? (
              <div
                className={` ${openState === true ? 'h-[170px] ' : 'bottom-0 h-0'}
                 font18Bol flex w-full  items-center justify-center overflow-hidden rounded-[10px] bg-base`}
              >
                <div className="flex items-center justify-center text-black">
                  <div className="mr-[10px] h-[60px] w-[60px]">
                    <img className="block " src="/images/icons/none.png" alt="no anonymize img" />
                  </div>
                  Anonymized file missing
                </div>
              </div>
            ) : (
              <div
                ref={(ref) => {
                  anonyImgScrollContainerRef.current = ref; // 익명화 이미지 컴포넌트의 ref 설정
                }}
                //ref={scrollContainerRef}
                className={`
                   ${openState === true ? 'h-[170px] ' : 'bottom-0 h-0'}
          x-scrollbar-style flex w-full items-center justify-start overflow-x-scroll rounded-[10px] bg-base pl-[12px] pt-[15px]`}
              >
                <AnonyImgComponent
                  data={data?.pages.flatMap((page) => page?.data?.data?.anonyThumbList)}
                  selectedSection={selectedSection}
                />
              </div>
            )}
          </article>
        </section>
      ) : (
        <button
          onClick={() => setOpenState((prev: boolean) => !prev)}
          className="inner-shadow absolute top-[-20px] left-0 z-20 flex h-[20px] w-[85px] cursor-pointer justify-center rounded-t-[10px] bg-sub03 bg-no-repeat"
        >
          <div className={`${openState ? '' : 'scale-y-[-1] transform'}`}>
            <DownChevronSvg color={'black'} className={'h-[20px] w-[20px]'} />
          </div>
        </button>
      )}
    </>
  );
};

export default ImgSlide;