From 2beb0f2eed25dd7076288d75765be890e0eca2fb Mon Sep 17 00:00:00 2001 From: kyuran kim <57716832+gxxrxn@users.noreply.github.com> Date: Wed, 22 May 2024 15:57:39 +0900 Subject: [PATCH] =?UTF-8?q?[#591]=203D=20=EC=B1=85=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8,=20=EC=B1=85=EC=9E=A5=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EA=B0=9C=EC=84=A0=20(#594)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 3d 책 컴포넌트 placeholder 적용 - global css에 bg-blur 클래스 추가 * feat: 책장 상세정보 query suspense 적용 - 책장페이지 ssr 되도록 컴포넌트 분리 * style: 책장 padding 수정 * style: BookShelf.Book 컴포넌트 패딩 1 -> 1.5rem으로 수정 * chore: 불필요한 prop, 태그 제거 --- src/app/bookshelf/[bookshelfId]/page.tsx | 165 ++++++++++-------- .../bookshelf/useBookShelfInfoQuery.ts | 23 ++- src/styles/global.css | 5 + src/v1/bookShelf/BookShelf.tsx | 58 +++--- src/v1/bookShelf/BookShelfCard.tsx | 2 +- src/v1/bookShelf/BookShelfRow.tsx | 2 +- 6 files changed, 156 insertions(+), 99 deletions(-) diff --git a/src/app/bookshelf/[bookshelfId]/page.tsx b/src/app/bookshelf/[bookshelfId]/page.tsx index 0d4c693f8..fbd85b001 100644 --- a/src/app/bookshelf/[bookshelfId]/page.tsx +++ b/src/app/bookshelf/[bookshelfId]/page.tsx @@ -1,23 +1,26 @@ 'use client'; import { useEffect } from 'react'; -import { useInView } from 'react-intersection-observer'; import Link from 'next/link'; +import { useInView } from 'react-intersection-observer'; + +import type { APIBookshelf } from '@/types/bookshelf'; + import useBookShelfBooksQuery from '@/queries/bookshelf/useBookShelfBookListQuery'; import useBookShelfInfoQuery from '@/queries/bookshelf/useBookShelfInfoQuery'; import useMutateBookshelfLikeQuery from '@/queries/bookshelf/useMutateBookshelfLikeQuery'; -import useToast from '@/v1/base/Toast/useToast'; +import { useMyProfileId } from '@/queries/user/useMyProfileQuery'; import { checkAuthentication } from '@/utils/helpers'; import { IconKakao } from '@public/icons'; +import { KAKAO_LOGIN_URL } from '@/constants/url'; + +import useToast from '@/v1/base/Toast/useToast'; import TopNavigation from '@/v1/base/TopNavigation'; import BookShelfRow from '@/v1/bookShelf/BookShelfRow'; import Button from '@/v1/base/Button'; import LikeButton from '@/v1/base/LikeButton'; import BackButton from '@/v1/base/BackButton'; import ShareButton from '@/v1/base/ShareButton'; -import type { APIBookshelf, APIBookshelfInfo } from '@/types/bookshelf'; - -const KAKAO_OAUTH_LOGIN_URL = `${process.env.NEXT_PUBLIC_API_URL}/oauth2/authorize/kakao?redirect_uri=${process.env.NEXT_PUBLIC_CLIENT_REDIRECT_URI}`; export default function UserBookShelfPage({ params: { bookshelfId }, @@ -26,13 +29,34 @@ export default function UserBookShelfPage({ bookshelfId: APIBookshelf['bookshelfId']; }; }) { + return ( +
+ + + + + + + + + + + +
+ ); +} + +const BookShelfInfo = ({ bookshelfId }: { bookshelfId: number }) => { const isAuthenticated = checkAuthentication(); - const { data, isSuccess } = useBookShelfInfoQuery({ bookshelfId }); + const { show: showToast } = useToast(); + + const { data } = useBookShelfInfoQuery(bookshelfId); + const { isLiked, likeCount, userId, userNickname, job } = data; + const { mutate: mutateBookshelfLike } = useMutateBookshelfLikeQuery(bookshelfId); - const { show: showToast } = useToast(); - if (!isSuccess) return null; + const { data: myId } = useMyProfileId({ enabled: isAuthenticated }); const handleClickLikeButton = () => { if (!isAuthenticated) { @@ -40,50 +64,41 @@ export default function UserBookShelfPage({ return; } - mutateBookshelfLike(data.isLiked); + if (userId === myId) { + showToast({ + message: '내 책장에는 좋아요를 누를 수 없어요.', + type: 'normal', + }); + return; + } + + mutateBookshelfLike(isLiked); }; return ( -
- - - - - - - - -
-

- {data.userNickname} - 님의 책장 -

-
- - {`${data.job.jobGroupKoreanName} • ${data.job.jobNameKoreanName}`} - - -
+
+

+ {userNickname} + 님의 책장 +

+
+ + {`${job.jobGroupKoreanName} • ${job.jobNameKoreanName}`} + +
- -
); -} +}; const BookShelfContent = ({ bookshelfId, - userNickname, }: { bookshelfId: APIBookshelf['bookshelfId']; - userNickname: APIBookshelfInfo['userNickname']; }) => { const isAuthenticated = checkAuthentication(); const { ref, inView } = useInView(); @@ -93,7 +108,6 @@ const BookShelfContent = ({ fetchNextPage, hasNextPage, isSuccess, - isFetching, isFetchingNextPage, } = useBookShelfBooksQuery({ bookshelfId }); @@ -113,39 +127,54 @@ const BookShelfContent = ({ )) )} - - {isFetching && !isFetchingNextPage ? null :
} + {!isFetchingNextPage &&
} ) : ( <> -
- -
-
-

- 지금 로그인하면 -
- 책장에 담긴 모든 책을 볼 수 있어요! -

-

- {userNickname}님의 책장에서 - 다양한 -
- 인사이트를 얻을 수 있어요. -

- - - -
+ + ); }; +const DummyBookShelfRow = () => ( +
+ +
+); + +const BookShelfLoginBox = ({ + bookshelfId, +}: { + bookshelfId: APIBookshelf['bookshelfId']; +}) => { + const { data } = useBookShelfInfoQuery(bookshelfId); + const { userNickname } = data; + + return ( +
+

+ 지금 로그인하면 +
+ 책장에 담긴 모든 책을 볼 수 있어요! +

+

+ {userNickname}님의 책장에서 + 다양한 +
+ 인사이트를 얻을 수 있어요. +

+ + + +
+ ); +}; const initialBookImageUrl = [ { bookId: 1, title: 'book1', imageUrl: '/images/book-cover/book1.jpeg' }, diff --git a/src/queries/bookshelf/useBookShelfInfoQuery.ts b/src/queries/bookshelf/useBookShelfInfoQuery.ts index 80b151b6f..c6230a80d 100644 --- a/src/queries/bookshelf/useBookShelfInfoQuery.ts +++ b/src/queries/bookshelf/useBookShelfInfoQuery.ts @@ -1,15 +1,20 @@ -import bookshelfAPI from '@/apis/bookshelf'; +import { UseQueryOptions } from '@tanstack/react-query'; import { APIBookshelfInfo } from '@/types/bookshelf'; -import { useQuery } from '@tanstack/react-query'; +import useQueryWithSuspense from '@/hooks/useQueryWithSuspense'; +import bookshelfAPI from '@/apis/bookshelf'; import bookShelfKeys from './key'; -const useBookShelfInfoQuery = ({ - bookshelfId, -}: { - bookshelfId: APIBookshelfInfo['bookshelfId']; -}) => - useQuery(bookShelfKeys.info(bookshelfId), () => - bookshelfAPI.getBookshelfInfo(bookshelfId).then(response => response.data) +const useBookShelfInfoQuery = ( + bookshelfId: APIBookshelfInfo['bookshelfId'], + options?: UseQueryOptions +) => + useQueryWithSuspense( + bookShelfKeys.info(bookshelfId), + () => + bookshelfAPI + .getBookshelfInfo(bookshelfId) + .then(response => response.data), + options ); export default useBookShelfInfoQuery; diff --git a/src/styles/global.css b/src/styles/global.css index 87d0472b6..5241fac06 100644 --- a/src/styles/global.css +++ b/src/styles/global.css @@ -75,4 +75,9 @@ .w-app { @apply relative -left-[2rem] w-[calc(100%+4rem)]; } + + .bg-blur { + box-shadow: inset 0 0 3rem #dddddd; + @apply bg-placeholder; + } } diff --git a/src/v1/bookShelf/BookShelf.tsx b/src/v1/bookShelf/BookShelf.tsx index f628108d4..1f1bc0ffa 100644 --- a/src/v1/bookShelf/BookShelf.tsx +++ b/src/v1/bookShelf/BookShelf.tsx @@ -1,12 +1,17 @@ -import { APIBookshelf } from '@/types/bookshelf'; -import { IconArrowRight, IconHeart } from '@public/icons'; +'use client'; + +import { ReactNode, useState } from 'react'; import Link from 'next/link'; -import Badge from '@/v1/base/Badge'; import Image from 'next/image'; -import { APIBook } from '@/types/book'; -import { ReactNode, useState } from 'react'; + import ColorThief from 'colorthief'; +import { APIBook } from '@/types/book'; +import { APIBookshelf } from '@/types/bookshelf'; +import { IconArrowRight, IconHeart } from '@public/icons'; + +import Badge from '@/v1/base/Badge'; + const BookShelf = ({ children }: { children: ReactNode }) => { return <>{children}; }; @@ -24,7 +29,7 @@ type InfoProps = Omit; const Info = ({ bookshelfName, bookshelfId, likeCount }: InfoProps) => { return ( -
+
{bookshelfName}
@@ -45,7 +50,7 @@ type BooksProps = Pick; const Books = ({ books }: BooksProps) => { return ( -
    +
      {books.map(book => (
    • @@ -58,8 +63,10 @@ const Books = ({ books }: BooksProps) => { const Book = ({ imageUrl, bookId, + title, }: Pick) => { const [bookSpineColor, setBookSpineColor] = useState(); + const placeholderClassName = bookSpineColor ? '' : 'bg-blur'; const handleOnLoadImage = (image: HTMLImageElement) => { const colorThief = new ColorThief(); @@ -71,44 +78,55 @@ const Book = ({ }; return ( -
      + {/** 책 옆면 (책등) */}
      + > + {/** 옆면과 표지 사이 여백을 메꾸기 위해 추가 */} +
      +
      + + {/** 책 하단 그림자 */}
      - book cover - -
      +
      + ); }; diff --git a/src/v1/bookShelf/BookShelfCard.tsx b/src/v1/bookShelf/BookShelfCard.tsx index a60d197ee..88f58efa1 100644 --- a/src/v1/bookShelf/BookShelfCard.tsx +++ b/src/v1/bookShelf/BookShelfCard.tsx @@ -11,7 +11,7 @@ const BookShelfCard = ({
      -
      +
      ) => { return ( -
      +