Skip to content

Commit

Permalink
[#447] [모임 상세] 모임 참여 여부, 모임장 여부에 따른 UI 구현 (#450)
Browse files Browse the repository at this point in the history
* style: BookGroupNavigation a모임제목 말줄임 적용

* feat: 모임상세 뒤로가기 구현

* feat: Avatar fallback image 설정

* feat: Loading 컴포넌트 작성

* feat: context provider에 Suspense 추가

* fix: axios host 설정

* feat: suspens query 적용

* fix: next/image sizes 관련 경고 해결

* feat: 모임 상세 skeleton 적용

* fix: context provider에서 suspense 제거

* style: TitleSkeleton pulse animation 추가

* fix: avatar next/image 사이즈 경고 해결

* feat: 게시글 없는 경우 메세지 노출

* style: toast 가운데 정렬

* chore: groupAPI 컨벤션에 맞게 네이밍 수정

* refactor: BookGroupNavigation 컴포넌트 분리

- 합성 컴포넌트로 리팩토링

* feat: 모임 가입 버튼 구현

* fix: 비공개 모임인 경우 댓글 보이지 않게 수정

* chore: 모임 가입문제 페이지로 라우팅되지 않도록 수정

* feat: BookInfoCard 클릭 시 책 상세 페이지로 이동

* feat: book query key factory 객체 추가

* refactor: getSize 함수가 객체를 반환하도록 수정

* refactor: AnimationStep 타입 추가, LoadigDot export 제거

* fix: BookGroupNavigation 내부에 SSRSafeSuspense 적용

* fix: 비공개 모임이지만 멤버인 경우 게시글 보이도록 수정

- comment divider style 제거

* feat: w-app css class 추가

- 모임 상세페이지 여백 style 조정
  • Loading branch information
gxxrxn committed Jun 17, 2024
1 parent 1f389e4 commit 61ada97
Show file tree
Hide file tree
Showing 24 changed files with 550 additions and 177 deletions.
9 changes: 5 additions & 4 deletions public/icons/avatar.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/apis/core/axios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import isClient from '@/utils/isClient';
import webStorage from '@/utils/storage';

const storage = webStorage(ACCESS_TOKEN_STORAGE_KEY);

const options: CreateAxiosDefaults = {
baseURL: process.env.NEXT_HOST,
headers: {
Accept: '*/*',
'Content-Type': 'application/json',
Expand Down
4 changes: 2 additions & 2 deletions src/apis/group/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
} from '@/types/group';
import { publicApi } from '../core/axios';

const GroupAPI = {
const groupAPI = {
getEntireGroups: (pageParam: string) =>
publicApi.get<APIGroupPagination>(
`/service-api/book-groups?pageSize=10&groupCursorId=` + pageParam
Expand Down Expand Up @@ -109,4 +109,4 @@ const GroupAPI = {
),
};

export default GroupAPI;
export default groupAPI;
64 changes: 34 additions & 30 deletions src/app/group/[groupId]/page.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
'use client';

import TopNavigation from '@/ui/Base/TopNavigation';
import SSRSafeSuspense from '@/components/SSRSafeSuspense';
import BookGroupInfo from '@/v1/bookGroup/detail/BookGroupInfo';
import { IconArrowLeft, IconHamburger, IconPost } from '@public/icons';

import { useBookGroupTitle } from '@/queries/group/useBookGroupQuery';
import CommentList from '@/v1/bookGroup/detail/CommentList';
import BookGroupNavigation from '@/v1/bookGroup/BookGroupNavigation';
import JoinBookGroupButton from '@/v1/bookGroup/detail/JoinBookGroupButton';

const DetailBookGroupPage = ({
params: { groupId },
Expand All @@ -14,39 +13,44 @@ const DetailBookGroupPage = ({
}) => {
return (
<>
<BookGroupNavigation groupId={groupId} />
<div className="flex flex-col gap-[2rem]">
<BookGroupInfo groupId={groupId} />
<div className="flex flex-col gap-[1rem]">
<Heading text="게시글" />
<CommentList groupId={groupId} />
<BookGroupNavigation groupId={groupId}>
<BookGroupNavigation.BackButton />
<BookGroupNavigation.Title />
<BookGroupNavigation.WriteButton />
<BookGroupNavigation.MenuButton />
</BookGroupNavigation>

<SSRSafeSuspense fallback={<PageSkeleton />}>
<div className="flex flex-col gap-[2rem]">
<BookGroupInfo groupId={groupId} />
<Divider />
<div className="flex flex-col gap-[1rem]">
<Heading text="게시글" />
<CommentList groupId={groupId} />
</div>
</div>
</div>
<JoinBookGroupButton groupId={groupId} />
</SSRSafeSuspense>
</>
);
};

export default DetailBookGroupPage;

const BookGroupNavigation = ({ groupId }: { groupId: number }) => {
const { data: title } = useBookGroupTitle(groupId);

return (
<TopNavigation>
<TopNavigation.LeftItem>
<IconArrowLeft />
</TopNavigation.LeftItem>
<TopNavigation.CenterItem textAlign="left">
{title}
</TopNavigation.CenterItem>
<TopNavigation.RightItem>
<IconPost />
<IconHamburger />
</TopNavigation.RightItem>
</TopNavigation>
);
};

const Heading = ({ text }: { text: string }) => (
<p className=" text-xl font-bold">{text}</p>
);

const PageSkeleton = () => (
<div className="flex w-full animate-pulse flex-col gap-[1rem] py-[2rem]">
<div className="h-[1.3rem] w-[6rem] bg-black-400"></div>
<div className="flex items-center gap-[1rem]">
<div className="h-[3.2rem] w-[3.2rem] rounded-full bg-black-400"></div>
<div className="h-[1.3rem] w-[8rem] bg-black-400"></div>
</div>
<div className="h-[1.8rem] w-[60%] bg-black-400"></div>
<div className="h-[18rem] w-full bg-black-400"></div>
</div>
);

const Divider = () => <p className="w-app h-[0.5rem] bg-background"></p>;
5 changes: 5 additions & 0 deletions src/app/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import Loading from '@/ui/Base/Loading';

export default function RootLoading() {
return <Loading fullpage />;
}
15 changes: 15 additions & 0 deletions src/components/SSRSafeSuspense.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { ComponentPropsWithoutRef, Suspense } from 'react';

import useMounted from '@/hooks/useMounted';

const SSRSafeSuspense = (props: ComponentPropsWithoutRef<typeof Suspense>) => {
const isMounted = useMounted();

if (isMounted) {
return <Suspense {...props} />;
}

return <>{props.fallback}</>;
};

export default SSRSafeSuspense;
10 changes: 10 additions & 0 deletions src/queries/book/key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { APIBook } from '@/types/book';

const bookKeys = {
all: ['book'],
details: () => [...bookKeys.all, 'detail'] as const,
detail: (bookId: APIBook['bookId']) =>
[...bookKeys.details(), bookId] as const,
};

export default bookKeys;
17 changes: 8 additions & 9 deletions src/queries/book/useBookInfoQuery.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
import { UseQueryOptions, useQuery } from '@tanstack/react-query';

import type { APIBook, APIBookDetail } from '@/types/book';
import useQueryWithSuspense, {
UseQueryOptionWithoutSuspense,
} from '@/hooks/useQueryWithSuspense';
import bookAPI from '@/apis/book';
import type { APIBook } from '@/types/book';
import bookKeys from './key';

const useBookInfoQuery = (
bookId: APIBook['bookId'],
options?: Pick<
UseQueryOptions<Awaited<ReturnType<typeof bookAPI.getBookInfo>>['data']>,
'onSuccess' | 'onError'
>
options?: UseQueryOptionWithoutSuspense<APIBookDetail>
) =>
useQuery(
['bookInfo', bookId],
useQueryWithSuspense(
bookKeys.detail(bookId),
() => bookAPI.getBookInfo(bookId).then(({ data }) => data),
options
);
Expand Down
25 changes: 16 additions & 9 deletions src/queries/group/useBookGroupCommentsQuery.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import { useQuery } from '@tanstack/react-query';
import { QueryOptions } from '@/types/query';
import {
APIGroupCommentPagination,
APIGroupDetail,
Expand All @@ -8,6 +6,9 @@ import {

import GroupAPI from '@/apis/group';
import bookGroupKeys from './key';
import useQueryWithSuspense, {
UseQueryOptionWithoutSuspense,
} from '@/hooks/useQueryWithSuspense';

const transformComments = ({ bookGroupComments }: APIGroupCommentPagination) =>
bookGroupComments.map<BookGroupComment>(comment => ({
Expand All @@ -23,18 +24,24 @@ const transformComments = ({ bookGroupComments }: APIGroupCommentPagination) =>

const useBookGroupCommentsQuery = <TData = APIGroupCommentPagination>(
groupId: APIGroupDetail['bookGroupId'],
select?: QueryOptions<APIGroupCommentPagination, TData>['select']
options?: UseQueryOptionWithoutSuspense<
APIGroupCommentPagination,
unknown,
TData
>
) =>
useQuery({
queryKey: bookGroupKeys.comments(groupId),
queryFn: () =>
useQueryWithSuspense(
bookGroupKeys.comments(groupId),
() =>
GroupAPI.getGroupComments({ bookGroupId: groupId }).then(
({ data }) => data
),
select,
});
options
);

export default useBookGroupCommentsQuery;

export const useBookGroupComments = (groupId: APIGroupDetail['bookGroupId']) =>
useBookGroupCommentsQuery(groupId, transformComments);
useBookGroupCommentsQuery(groupId, {
select: transformComments,
});
40 changes: 29 additions & 11 deletions src/queries/group/useBookGroupQuery.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useQuery } from '@tanstack/react-query';
import { UseQueryOptions } from '@tanstack/react-query';

import { APIGroupDetail, BookGroupDetail } from '@/types/group';
import { QueryOptions } from '@/types/query';

import { isExpired } from '@/utils/date';
import GroupAPI from '@/apis/group';
import useQueryWithSuspense from '@/hooks/useQueryWithSuspense';

import bookGroupKeys from './key';

const transformBookGroupDetail = (data: APIGroupDetail) =>
Expand All @@ -20,21 +21,38 @@ const transformBookGroupDetail = (data: APIGroupDetail) =>

export const useBookGroupQuery = <TData = APIGroupDetail>(
groupId: APIGroupDetail['bookGroupId'],
select: QueryOptions<APIGroupDetail, TData>['select']
options?: UseQueryOptions<APIGroupDetail, unknown, TData>
) =>
useQuery({
queryKey: bookGroupKeys.detail(groupId),
queryFn: () =>
useQueryWithSuspense(
bookGroupKeys.detail(groupId),
() =>
GroupAPI.getGroupDetailInfo({ bookGroupId: groupId }).then(
({ data }) => data
),
select,
});
options
);

export default useBookGroupQuery;

export const useBookGroup = (groupId: APIGroupDetail['bookGroupId']) =>
useBookGroupQuery(groupId, transformBookGroupDetail);
useBookGroupQuery(groupId, {
select: transformBookGroupDetail,
});

export const useBookGroupTitle = (groupId: APIGroupDetail['bookGroupId']) =>
useBookGroupQuery(groupId, data => data.title);
useBookGroupQuery(groupId, { select: data => data.title });

export const useBookGroupOwner = (groupId: APIGroupDetail['bookGroupId']) =>
useBookGroupQuery(groupId, {
select: data => ({ isMe: data.isOwner, id: data.owner.id }),
});

export const useBookGroupJoinInfo = (groupId: APIGroupDetail['bookGroupId']) =>
useBookGroupQuery(groupId, {
select: data => ({
isExpired: isExpired(data.endDate),
isMember: data.isGroupMember,
hasPassword: data.hasJoinPasswd,
question: data.joinQuestion,
}),
});
13 changes: 11 additions & 2 deletions src/queries/user/useMyProfileQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,20 @@ import useQueryWithSuspense, {
} from '@/hooks/useQueryWithSuspense';
import userKeys from './key';

const useMyProfileQuery = (options?: UseQueryOptionWithoutSuspense<APIUser>) =>
const useMyProfileQuery = <TData = APIUser>(
options?: UseQueryOptionWithoutSuspense<APIUser, unknown, TData>
) =>
useQueryWithSuspense(
userKeys.me(),
() => userAPI.getMyProfile().then(({ data }) => data),
options
{ ...options, staleTime: Infinity }
);

export default useMyProfileQuery;

export const useMyProfileId = (
options?: Omit<
UseQueryOptionWithoutSuspense<APIUser, unknown, unknown>,
'select'
>
) => useMyProfileQuery({ ...options, select: data => data.userId });
24 changes: 24 additions & 0 deletions src/stories/Base/Loading.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { Meta, StoryObj } from '@storybook/react';
import Loading from '@/ui/Base/Loading';

const meta: Meta<typeof Loading> = {
title: 'Base/Loading',
component: Loading,
tags: ['autodocs'],
};

export default meta;

type Story = StoryObj<typeof Loading>;

export const Default: Story = {
args: {},
};

export const Main: Story = {
args: { color: 'main' },
};

export const Grey: Story = {
args: { color: 'grey' },
};
6 changes: 6 additions & 0 deletions src/styles/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,9 @@
margin: 0 auto !important;
}
}

@layer utilities {
.w-app {
@apply relative -left-[2rem] w-[calc(100%+4rem)];
}
}
Loading

0 comments on commit 61ada97

Please sign in to comment.