소스 검색

Merge branch 'free' of http://git.daboryhost.com:10880/meta-rare/metarare_front-prod into free

tuyetnhi 1 년 전
부모
커밋
e80b80d2e8

+ 31 - 0
assets/css/_scroll.scss

@@ -0,0 +1,31 @@
+@mixin scroll {
+    &::-webkit-scrollbar {
+        width: 5px;
+        height: 5px;
+    }
+
+    @media screen and (max-width: 1024px) {
+        &::-webkit-scrollbar {
+            width: 3px;
+            height: 3px;
+        }
+    }
+
+
+    /* Track */
+    &::-webkit-scrollbar-track {
+        background: #f1f1f1;
+        border-radius: 10px;
+    }
+
+    /* Handle */
+    &::-webkit-scrollbar-thumb {
+        background: #888;
+        border-radius: 10px;
+    }
+
+    /* Handle on hover */
+    &::-webkit-scrollbar-thumb:hover {
+        background: #555;
+    }
+}

+ 1 - 1
src/api/APIs.ts

@@ -77,7 +77,7 @@ const fetchExploreHint = (filter: IFilter) => {
 const fetchExploreKeyword = (collectionName: string) => {
   const url = `${host}/v1/explore/keyword`;
 
-  return post({ url, body: { collection_name: collectionName } });
+  return post({ url, body: { collection_name: collectionName, limit: 50 } });
 };
 
 // home

+ 10 - 10
src/components/common/NFTCard.tsx

@@ -116,16 +116,16 @@ const NFTCard: React.FC<IOwnProps> = ({
       onClick: () => { },
       isDisabled: true
     },
-    {
-      title: '공유하기',
-      onClick: () => {
-        const url = status === ESaleStatus.ON_GOING ? `${host}/sale/${saleId}` : `${host}/token/${tokenId}`;
-        navigator.clipboard.writeText(url).then(() => {
-          alert('복사되었습니다.');
-        });
-      },
-      isDisabled: false
-    },
+    // {
+    //   title: '공유하기',
+    //   onClick: () => {
+    //     const url = status === ESaleStatus.ON_GOING ? `${host}/sale/${saleId}` : `${host}/token/${tokenId}`;
+    //     navigator.clipboard.writeText(url).then(() => {
+    //       alert('복사되었습니다.');
+    //     });
+    //   },
+    //   isDisabled: false
+    // },
   ];
   const isMulti = totalCount > 1;
   const isEnd = status !== ESaleStatus.ON_GOING;

+ 3 - 3
src/components/common/layerFilter/CategoryLayerFilter.scss

@@ -1,4 +1,5 @@
 @import "/assets/css/_lib";
+@import "/assets/css/scroll";
 
 .ly_filter {
     width: 200px;
@@ -32,9 +33,8 @@
     overflow: auto;
     max-height: 168px;
 }
+
 .ico_check {
     position: absolute;
 }
-.detail_list {
-    max-height: 168;
-}
+

+ 3 - 3
src/components/common/layerFilter/CollectionLayerFilter.scss

@@ -1,4 +1,5 @@
 @import "/assets/css/_lib";
+@import '/assets/css/_scroll';
 
 .ly_filter {
     overflow: hidden;
@@ -65,6 +66,7 @@
 .detail_list {
     overflow: auto;
     max-height: 168px;
+    @include scroll;
 }
 .btn_area {
     padding: 20px 16px;
@@ -134,6 +136,4 @@
 .ico_check {
     position: absolute;
 }
-.detail_list {
-    max-height: 168;
-}
+

+ 10 - 5
src/components/common/layerFilter/CollectionLayerFilter.tsx

@@ -7,6 +7,7 @@ import { ReactComponent as IconSearch } from '@assets/img/svg/ico_search_gray.sv
 import useSearch from '@src/hooks/useSearch';
 import { useDispatch, useSelector } from 'react-redux';
 import { fetchExploreKeyword } from '@src/store/reducers/ExploreReducer';
+import { useDebounce } from 'usehooks-ts';
 
 const cx = classNames.bind(styles);
 
@@ -22,7 +23,7 @@ const CollectionLayerFilter: React.FC<IOwnProps> = ({
   collection,
   setCollection,
   update,
-  hide,
+  hide = () => {},
   handler
 }) => {
   const dispatch = useDispatch();
@@ -30,14 +31,14 @@ const CollectionLayerFilter: React.FC<IOwnProps> = ({
   const [selected, setSelected] = useState<string>(collection);
   const { keyword, setKeyword, onKeyPress } = useSearch();
   const { collectionList } = useSelector(store => store.explore);
-
+  const debounceKeyword = useDebounce(keyword, 1000);
   useEffect(() => {
     setSelected(collection);
   }, [update]);
 
   useEffect(() => {
-    dispatch(fetchExploreKeyword(keyword));
-  }, [keyword]);
+    dispatch(fetchExploreKeyword(debounceKeyword));
+  }, [debounceKeyword]);
 
   const handleClick = (title: string) => {
     if (title === selected) {
@@ -54,10 +55,14 @@ const CollectionLayerFilter: React.FC<IOwnProps> = ({
   const handleSubmit = () => {
     setCollection(selected);
     hide();
-    setKeyword('');
+    // setKeyword('');
     handler?.();
   };
 
+  useEffect(() => {
+    setCollection(selected);
+    handler?.();
+  }, [selected])
   return (
     <div className={cx('ly_filter')}>
       <div className={cx('search_wrap')}>

+ 18 - 18
src/components/common/sale/SaleButtonContainer.tsx

@@ -127,9 +127,9 @@ const ButtonContainer: React.FC<IButtonContainer> = ({ saleType, isOwner, isTime
       if (saleType === ESaleType.FIXED) {
         return (
           <div className={cx('btn_area')}>
-            <div className={cx('btn_wrap')}>
+            {/* <div className={cx('btn_wrap')}>
               <Button text='공유하기' onClick={handleShare} />
-            </div>
+            </div> */}
             <div className={cx('btn_wrap')}>
               <Button text='판매 취소' useColor={true} onClick={closeSell} />
             </div>
@@ -141,9 +141,9 @@ const ButtonContainer: React.FC<IButtonContainer> = ({ saleType, isOwner, isTime
       if (saleType === ESaleType.AUCTION && highestPrice) {
         return (
           <div className={cx('btn_area')}>
-            <div className={cx('btn_wrap')}>
+            {/* <div className={cx('btn_wrap')}>
               <Button text='공유하기' onClick={handleShare} />
-            </div>
+            </div> */}
             {!!highestPrice && (
               <div className={cx('btn_wrap')}>
                 <Button text='최고가에 판매하기' useColor={true} onClick={saleHighestPrice} />
@@ -157,9 +157,9 @@ const ButtonContainer: React.FC<IButtonContainer> = ({ saleType, isOwner, isTime
       if (saleType === ESaleType.TIME && isTimeout && highestPrice) {
         return (
           <div className={cx('btn_area')}>
-            <div className={cx('btn_wrap')}>
+            {/* <div className={cx('btn_wrap')}>
               <Button text='공유하기' onClick={handleShare} />
-            </div>
+            </div> */}
             <div className={cx('btn_wrap')}>
               <Button text='최고가에 판매하기' onClick={saleHighestPrice} />
             </div>
@@ -171,9 +171,9 @@ const ButtonContainer: React.FC<IButtonContainer> = ({ saleType, isOwner, isTime
       if (saleType === ESaleType.TIME && !highestPrice) {
         return (
           <div className={cx('btn_area')}>
-            <div className={cx('btn_wrap')}>
+            {/* <div className={cx('btn_wrap')}>
               <Button text='공유하기' onClick={handleShare} />
-            </div>
+            </div> */}
             <div className={cx('btn_wrap')}>
               <Button text='판매 취소' useColor={true} onClick={closeSell} />
             </div>
@@ -183,18 +183,18 @@ const ButtonContainer: React.FC<IButtonContainer> = ({ saleType, isOwner, isTime
 
       return (
         <div className={cx('btn_area')}>
-          <div className={cx('btn_wrap')}>
+          {/* <div className={cx('btn_wrap')}>
             <Button text='공유하기' onClick={handleShare} />
-          </div>
+          </div> */}
         </div>
       );
     } else {
       // 판매 종료
       return (
         <div className={cx('btn_area')}>
-          <div className={cx('btn_wrap')}>
+          {/* <div className={cx('btn_wrap')}>
             <Button text='공유하기' onClick={handleShare} />
-          </div>
+          </div> */}
           <div className={cx('btn_wrap')}>
             <Button text='판매 재등록' useColor={true} onClick={handleResale} />
           </div>
@@ -207,9 +207,9 @@ const ButtonContainer: React.FC<IButtonContainer> = ({ saleType, isOwner, isTime
     if (!isOnGoing) {
       return (
         <div className={cx('btn_area')}>
-          <div className={cx('btn_wrap')}>
+          {/* <div className={cx('btn_wrap')}>
             <Button text='공유하기' onClick={handleShare} />
-          </div>
+          </div> */}
         </div>
       );
     }
@@ -231,9 +231,9 @@ const ButtonContainer: React.FC<IButtonContainer> = ({ saleType, isOwner, isTime
     if (saleType === ESaleType.AUCTION && !myBiddingPrice) {
       return (
         <div className={cx('btn_area')}>
-          <div className={cx('btn_wrap')}>
+          {/* <div className={cx('btn_wrap')}>
             <Button text='공유하기' onClick={handleShare} />
-          </div>
+          </div> */}
           <div className={cx('btn_wrap')}>
             <Button text='입찰하기' useColor={true} onClick={handleBid} />
           </div>
@@ -282,9 +282,9 @@ const ButtonContainer: React.FC<IButtonContainer> = ({ saleType, isOwner, isTime
 
     return (
       <div className={cx('btn_area')}>
-        <div className={cx('btn_wrap')}>
+        {/* <div className={cx('btn_wrap')}>
           <Button text='공유하기' onClick={handleShare} />
-        </div>
+        </div> */}
       </div>
     );
   }

+ 36 - 5
src/components/mobile/Search.tsx

@@ -1,15 +1,16 @@
 import classNames from "classnames/bind";
-import React, { useRef, useState } from "react";
+import React, { useEffect, useRef, useState } from "react";
 import styles from "./Search.scss";
 
 import { ReactComponent as IconClose } from "@assets/img/svg/ico_close.svg";
 import { ReactComponent as IconSearchGray } from "@assets/img/svg/ico_search_gray.svg";
 import URLInfo from "@src/constants/URLInfo";
 import useSearch from "@src/hooks/useSearch";
-import { useSelector } from "react-redux";
-import { useHistory } from "react-router";
-import { useOnClickOutside } from "usehooks-ts";
+import { useDispatch, useSelector } from "react-redux";
+import { useHistory, useLocation } from "react-router";
+import { useDebounce, useOnClickOutside } from "usehooks-ts";
 import AutoCompleteSearch from "../common/AutoCompleteSearch";
+import { fetchExploreHint } from "@src/store/reducers/ExploreReducer";
 
 const cx = classNames.bind(styles);
 
@@ -20,6 +21,7 @@ interface IOwnProps {
 const Search: React.FC<IOwnProps> = ({ onClose }) => {
   const history = useHistory();
   const searchRef = useRef(null);
+  const dispatch = useDispatch();
   const { hint } = useSelector((state) => state.explore);
   const [showAutoComplete, setShowAutoComplete] = useState<boolean>(false);
   const onSearch = () => {
@@ -34,11 +36,40 @@ const Search: React.FC<IOwnProps> = ({ onClose }) => {
   const { keyword, setKeyword, onKeyPress, handleSearch } = useSearch({
     onSearch,
   });
-
+  const debouncedValue = useDebounce<string>(keyword, 1000);
+  const location = useLocation();
+  const searchText = new URLSearchParams(location.search).get(
+    URLInfo.SEARCH_PARAM.SEARCH_TEXT
+  );
   useOnClickOutside(searchRef, () => {
     setShowAutoComplete(false);
   });
 
+  const dispatchHint = (token_name: string) => {
+    dispatch(
+      fetchExploreHint({
+        token_name,
+      })
+    );
+  };
+
+  useEffect(() => {
+    dispatchHint(keyword);
+    if (URLInfo.isExplorePage(location)) {
+      history.push(URLInfo.getSearchResultUrl(keyword));
+    }
+  }, [debouncedValue]);
+
+  useEffect(() => {
+    if (!URLInfo.isExplorePage(location)) {
+      setKeyword("");
+    } else {
+      if (searchText) {
+        setKeyword(searchText);
+      }
+    }
+  }, [location]);
+
   return (
     <div className={cx("ly_search")} ref={searchRef}>
       <div className={cx("input_box")}>

+ 34 - 24
src/components/pc/Header.tsx

@@ -1,38 +1,38 @@
-import React, { useEffect, useState, useRef } from "react";
-import classNames from "classnames/bind";
-import styles from "./Header.scss";
-import { Link } from "react-router-dom";
-import { ReactComponent as IconLogo } from "@assets/img/svg/logo_metarare.svg";
-import { ReactComponent as IconSearchGray } from "@assets/img/svg/ico_search_gray.svg";
-import { ReactComponent as IconArrow } from "@assets/img/svg/ico_arr_gray.svg";
 import { ReactComponent as IconActivity } from "@assets/img/svg/ico_activity.svg";
-import { ReactComponent as IconTwitter } from "@assets/img/svg/ico_twitter.svg";
-import { ReactComponent as IconInstagram } from "@assets/img/svg/ico_insta.svg";
+import { ReactComponent as IconArrow } from "@assets/img/svg/ico_arr_gray.svg";
 import { ReactComponent as IconFacebook } from "@assets/img/svg/ico_facebook.svg";
+import { ReactComponent as IconInstagram } from "@assets/img/svg/ico_insta.svg";
+import { ReactComponent as IconSearchGray } from "@assets/img/svg/ico_search_gray.svg";
+import { ReactComponent as IconTwitter } from "@assets/img/svg/ico_twitter.svg";
+import { ReactComponent as IconLogo } from "@assets/img/svg/logo_metarare.svg";
 import URLInfo from "@src/constants/URLInfo";
-import useSearch from "@src/hooks/useSearch";
-import ProfileModal from "../common/ProfileModal";
 import LayerContainer from "@src/containers/common/LayerContainer";
-import { useDispatch, useSelector } from "react-redux";
 import useLogin from "@src/hooks/useLogin";
-import { useHistory, useLocation } from "react-router";
+import useSearch from "@src/hooks/useSearch";
+import { fetchExploreHint } from "@src/store/reducers/ExploreReducer";
 import { fetchUserInfo } from "@src/store/reducers/UserReducer";
-import { YoutubeIcon } from "../common/icon/youtube";
-import { useDebounce, useOnClickOutside} from "usehooks-ts";
+import classNames from "classnames/bind";
+import React, { useEffect, useRef, useState } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { useHistory, useLocation } from "react-router";
+import { Link } from "react-router-dom";
+import { useDebounce, useOnClickOutside } from "usehooks-ts";
 import AutoCompleteSearch from "../common/AutoCompleteSearch";
-import { fetchExploreHint } from "@src/store/reducers/ExploreReducer";
+import ProfileModal from "../common/ProfileModal";
+import { YoutubeIcon } from "../common/icon/youtube";
+import styles from "./Header.scss";
 
 const cx = classNames.bind(styles);
 
 const Header: React.FC = () => {
-  const searchRef = useRef(null)
+  const searchRef = useRef(null);
   const history = useHistory();
   const dispatch = useDispatch();
   const [showAutoComplete, setShowAutoComplete] = useState<boolean>(false);
   const { userInfo, isUserInfoLoaded } = useSelector((store) => store.user);
   const onSearch = () => {
     history.push(URLInfo.getSearchResultUrl(keyword));
-    setShowAutoComplete(false)
+    setShowAutoComplete(false);
   };
   const SelectTextComplete = (text: string) => {
     history.push(URLInfo.getSearchResultUrl(text));
@@ -40,12 +40,15 @@ const Header: React.FC = () => {
   const { keyword, setKeyword, onKeyPress, handleSearch } = useSearch({
     onSearch,
   });
-  const debouncedValue = useDebounce<string>(keyword, 1000)
+  const debouncedValue = useDebounce<string>(keyword, 1000);
   const [showCommunity, setShowCommunity] = useState(false);
   const [showProfile, setShowProfile] = useState<boolean>(false);
   const location = useLocation();
   const { handleClickLoginButton, loginText } = useLogin();
-  const { hint } = useSelector((state) => state.explore)
+  const searchText = new URLSearchParams(location.search).get(
+    URLInfo.SEARCH_PARAM.SEARCH_TEXT
+  );
+  const { hint } = useSelector((state) => state.explore);
   useEffect(() => {
     if (showProfile) {
       dispatch(fetchUserInfo());
@@ -71,14 +74,21 @@ const Header: React.FC = () => {
   });
 
   useEffect(() => {
-      dispatchHint(keyword)
+    dispatchHint(keyword);
+    if (URLInfo.isExplorePage(location)) {
+      history.push(URLInfo.getSearchResultUrl(keyword));
+    }
   }, [debouncedValue]);
 
   useEffect(() => {
-    if(!URLInfo.isExplorePage(location)){
-      setKeyword('')
+    if (!URLInfo.isExplorePage(location)) {
+      setKeyword("");
+    } else {
+      if (searchText) {
+        setKeyword(searchText);
+      }
     }
-  }, [location])
+  }, [location]);
 
   return (
     <div className={cx("header_wrap")}>

+ 28 - 28
src/index.scss

@@ -3,38 +3,38 @@
 
 body {
   -ms-overflow-style: none; /* IE and Edge */
-  scrollbar-width: none; /* Firefox */
-}
-body::-webkit-scrollbar {
-  display: none; /* Chrome, Safari, Opera*/
+  // scrollbar-width: none; /* Firefox */
 }
+// body::-webkit-scrollbar {
+//   display: none; /* Chrome, Safari, Opera*/
+// }
 
-::-webkit-scrollbar {
-  width: 5px;
-  height: 5px;
-}
+// ::-webkit-scrollbar {
+//   width: 5px;
+//   height: 5px;
+// }
 
-@media screen and (max-width: 1024px) {
-  ::-webkit-scrollbar {
-    width: 3px;
-    height: 3px;
-  }
-}
+// @media screen and (max-width: 1024px) {
+//   ::-webkit-scrollbar {
+//     width: 3px;
+//     height: 3px;
+//   }
+// }
   
 
-/* Track */
-::-webkit-scrollbar-track {
-  background: #f1f1f1;
-  border-radius: 10px; 
-}
+// /* Track */
+// ::-webkit-scrollbar-track {
+//   background: #f1f1f1;
+//   border-radius: 10px; 
+// }
  
-/* Handle */
-::-webkit-scrollbar-thumb {
-  background: #888; 
-  border-radius: 10px;
-}
+// /* Handle */
+// ::-webkit-scrollbar-thumb {
+//   background: #888; 
+//   border-radius: 10px;
+// }
 
-/* Handle on hover */
-::-webkit-scrollbar-thumb:hover {
-  background: #555; 
-}
+// /* Handle on hover */
+// ::-webkit-scrollbar-thumb:hover {
+//   background: #555; 
+// }

+ 8 - 7
src/pages/Explore.tsx

@@ -7,22 +7,23 @@ import { useDispatch, useSelector } from "react-redux";
 import {
   fetchExploreHome,
   fetchExploreSearch,
-  resetExploreHome
+  resetExploreHome,
 } from "@src/store/reducers/ExploreReducer";
 import useExploreFilter from "@src/hooks/useExploreFilter";
 import { useCallbackAwaitStateSync } from "use-callback-await-state-sync";
 import useMore from "@src/hooks/useMore";
 import URLInfo from "@src/constants/URLInfo";
 import { IFilter } from "metarare";
+import { useIsFirstRender } from "usehooks-ts";
 
 const cx = classNames.bind(styles);
 
 interface IOwnProps {}
 
 const Explore: React.FC<IOwnProps> = ({}) => {
+  const isFirstRender = useIsFirstRender();
   const dispatch = useDispatch();
   const { list, isExploreListLoaded } = useSelector((store) => store.explore);
-
   const searchText = new URLSearchParams(location.search).get(
     URLInfo.SEARCH_PARAM.SEARCH_TEXT
   );
@@ -40,14 +41,13 @@ const Explore: React.FC<IOwnProps> = ({}) => {
       currency_amount_end: filterProps.maxPrice,
       currency_amount_start: filterProps.minPrice,
       network: filterProps.networkType,
-      sale_type: filterProps.saleType
+      sale_type: filterProps.saleType,
     };
-
     if (searchText) {
       dispatch(
         fetchExploreSearch({
           ...commonBody,
-          token_name: searchText
+          token_name: searchText,
         })
       );
     } else {
@@ -59,7 +59,7 @@ const Explore: React.FC<IOwnProps> = ({}) => {
     {
       list,
       dispatchFnc: dispatchExploreHome,
-      setSelectedFilterType: filterProps.setSelectedFilterType
+      setSelectedFilterType: filterProps.setSelectedFilterType,
     }
   );
 
@@ -74,8 +74,9 @@ const Explore: React.FC<IOwnProps> = ({}) => {
   });
 
   useEffect(() => {
+    if (isFirstRender) return;
     fetchInit();
-  }, [location.search]);
+  }, [searchText]);
 
   return (
     <div className={cx("explore")}>

+ 1 - 0
src/pages/Sale.scss

@@ -129,6 +129,7 @@
       line-height: 1.5;
     }
     & > .share{
+      cursor: pointer;
       display: none;
       align-items: center;
       justify-content: center;