Advertisement
justfrenzy

CrackFlix - frontend /src/components/App/Search.tsx

May 12th, 2024
124
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
TypeScript 6.18 KB | Source Code | 0 0
  1. import React, { useContext, useEffect, useState } from 'react';
  2. import { AppContext, AppContextTypes } from '../../utils/AppContext';
  3. import { useNavigate } from 'react-router-dom';
  4. import { mediaType } from './props.interface';
  5. import toast, { Toaster } from 'react-hot-toast';
  6. import SearchForm from './SearchForm';
  7. import { QuickSearch, ToggleLikedFunc } from './handleFunctions';
  8. import SearchSuggestions from './SearchSuggestions';
  9.  
  10. export interface SearchI {
  11.     search: string;
  12.     setSearch: React.Dispatch<React.SetStateAction<string>>;
  13. }
  14.  
  15. export interface mediaTypeCheck extends mediaType {
  16.     check: boolean;
  17. }
  18.  
  19. const SearchComp: React.FC<SearchI> = (props) => {
  20.     const { search, setSearch } = props;
  21.  
  22.     const { defaultLanguage, theme, systemTheme, isMobile, accessToken } =
  23.         React.useContext<AppContextTypes>(AppContext);
  24.  
  25.     const navigate = useNavigate();
  26.  
  27.     const [selected, setSelected] = useState(false);
  28.     const [data, setData] = useState([] as mediaType[] | []);
  29.  
  30.     const [typingTimeout, setTypingTimeout] = useState<NodeJS.Timeout | null>(
  31.         null
  32.     );
  33.     const [isLoading, setLoading] = useState<boolean>(false);
  34.  
  35.     const { searchRef } = useContext<AppContextTypes>(AppContext);
  36.     const [isDisabled, setDisabled] = useState(false);
  37.  
  38.     const {
  39.         filterTxt,
  40.         placeholderTxt,
  41.     }: { filterTxt: string; placeholderTxt: string } =
  42.         defaultLanguage === 'en-US' ? SearchText.en : SearchText.bg;
  43.  
  44.     useEffect(() => {
  45.         if (typingTimeout) {
  46.             clearTimeout(typingTimeout);
  47.         }
  48.  
  49.         if (search.length > 3) {
  50.             const handleKeyword = async () => {
  51.                 const response = await QuickSearch(search, defaultLanguage);
  52.                 if (response.status === 200) {
  53.                     setData(response.media as unknown as mediaTypeCheck[]);
  54.                 }
  55.                 console.log(`response`, response);
  56.             };
  57.  
  58.             const timeoutId = setTimeout(() => {
  59.                 setLoading(true);
  60.                 console.log(`true`);
  61.                 console.log('Stopped typing:', search);
  62.                 handleKeyword();
  63.                 console.log(`idk if its not working`);
  64.                 setLoading(false);
  65.             }, 500);
  66.  
  67.             // Storing the timeout ID for possible cancellation
  68.             setTypingTimeout(timeoutId);
  69.  
  70.             // Event listener to handle clicks outside of the search component
  71.             const handleClickOutside = (e: { target: unknown }) => {
  72.                 if (
  73.                     searchRef.current &&
  74.                     !searchRef.current.contains(e.target)
  75.                 ) {
  76.                     setSelected(false);
  77.                 } else {
  78.                     setSelected(true);
  79.                 }
  80.             };
  81.             // Adding the event listener to the document body
  82.             document.body.addEventListener(`click`, handleClickOutside);
  83.         } else {
  84.             // If the search term is not long enough, clear the data
  85.             setData([]);
  86.         }
  87.         // eslint-disable-next-line react-hooks/exhaustive-deps
  88.     }, [search, defaultLanguage, searchRef, accessToken]);
  89.  
  90.     useEffect(() => {
  91.         const handleEscape = (e: KeyboardEvent) => {
  92.             if (e.key === 'Escape' && selected) {
  93.                 setSelected(false);
  94.             }
  95.         };
  96.  
  97.         // Add event listener when component mounts
  98.         document.addEventListener('keydown', handleEscape);
  99.  
  100.         // Clean up the event listener when component unmounts
  101.         return () => {
  102.             document.removeEventListener('keydown', handleEscape);
  103.         };
  104.     }, [selected]);
  105.  
  106.     // Function to handle the submission of the search form
  107.     const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
  108.         const form = e.target as HTMLFormElement;
  109.         const searchVal = form.elements[1] as HTMLInputElement;
  110.         if (searchVal?.value !== '' && searchVal?.value?.length > 3) {
  111.             navigate(
  112.                 `/filter?keyword=${search.split(' ').join('+')}&sort=popularity&type=multi`
  113.             );
  114.         }
  115.     };
  116.  
  117.     // eslint-disable-next-line @typescript-eslint/no-explicit-any
  118.     type fnType = (...args: any[]) => void;
  119.  
  120.     const debounce = (fn: fnType, delay: number): fnType => {
  121.         // eslint-disable-next-line @typescript-eslint/no-explicit-any
  122.         return (...args: any[]) => {
  123.             if (!isDisabled) {
  124.                 setDisabled(true);
  125.                 fn(...args);
  126.                 setTimeout(() => {
  127.                     setDisabled(false);
  128.                 }, delay);
  129.             }
  130.         };
  131.     };
  132.  
  133.     const toggleLiked = async (mediaId: number, media: 'movie' | 'tv') => {
  134.         if (media === 'movie' || media === 'tv') {
  135.             const response = await ToggleLikedFunc(
  136.                 mediaId,
  137.                 media,
  138.                 defaultLanguage
  139.             );
  140.             const themeKey: AppContextTypes['theme'] =
  141.                 theme === 'system' ? (systemTheme ? 'dark' : 'light') : theme;
  142.             if (response.status === 200) {
  143.                 const index = data.findIndex((item) => item.id === mediaId);
  144.  
  145.                 if (index !== -1) {
  146.                     const newData = [...data];
  147.                     newData[index] = {
  148.                         ...newData[index],
  149.                         check: !newData[index].check,
  150.                     };
  151.                     setData(newData);
  152.                 }
  153.                 toast.success(`${response.message}`, {
  154.                     style: {
  155.                         background: `${themeKey ? '#2a2a2a5d' : '#ffffffe9'} `,
  156.                         borderRadius: '30px',
  157.                         color: `${themeKey === `dark` ? '#fff' : '#fff'}`,
  158.                     },
  159.                     iconTheme: {
  160.                         primary: `#452fde`,
  161.                         secondary: `#fff`,
  162.                     },
  163.                 });
  164.             } else {
  165.                 toast.success(`${response.message}`, {
  166.                     style: {
  167.                         background: `${themeKey ? '#2a2a2a5d' : '#ffffffe9'} `,
  168.                         borderRadius: '30px',
  169.                         color: `${themeKey === `dark` ? '#fff' : '#fff'}`,
  170.                     },
  171.                     iconTheme: {
  172.                         primary: `#452fde`,
  173.                         secondary: `#fff`,
  174.                     },
  175.                 });
  176.             }
  177.         }
  178.     };
  179.  
  180.     const handleClicked = debounce(toggleLiked, 2000);
  181.  
  182.     // The render method returns the JSX for the search component
  183.     return (
  184.         <form
  185.             ref={searchRef}
  186.             onSubmit={(e) => {
  187.                 e.preventDefault();
  188.                 handleSubmit(e);
  189.             }}
  190.         >
  191.             {/* The search component container */}
  192.             <SearchForm
  193.                 filterTxt={filterTxt}
  194.                 placeholderTxt={placeholderTxt}
  195.                 isLoading={isLoading}
  196.                 isMobile={isMobile}
  197.                 search={search}
  198.                 setSearch={setSearch}
  199.                 //searchRef={searchRef}
  200.                 setSelected={setSelected}
  201.             />
  202.             <SearchSuggestions
  203.                 data={data}
  204.                 selected={selected}
  205.                 search={search}
  206.                 handleClicked={handleClicked}
  207.                 isLoading={isLoading}
  208.             />
  209.  
  210.             <Toaster position="top-right" />
  211.  
  212.             <></>
  213.         </form>
  214.     );
  215. };
  216.  
  217. export default SearchComp;
  218.  
  219. const SearchText = {
  220.     bg: {
  221.         filterTxt: 'Филтър',
  222.         placeholderTxt: 'Търси',
  223.     } as { filterTxt: string; placeholderTxt: string },
  224.     en: {
  225.         filterTxt: 'Filter',
  226.         placeholderTxt: 'Search',
  227.     } as { filterTxt: string; placeholderTxt: string },
  228. };
  229.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement