Skip to content

Commit

Permalink
Merge branch 'main' into feat/#456
Browse files Browse the repository at this point in the history
  • Loading branch information
minjongbaek authored Dec 18, 2023
2 parents 9bc7ed5 + 1610cfd commit 543ad20
Show file tree
Hide file tree
Showing 12 changed files with 399 additions and 164 deletions.
42 changes: 22 additions & 20 deletions src/app/bookshelf/[bookshelfId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,25 @@ import TopNavigation from '@/v1/base/TopNavigation';
import BookShelfRow from '@/v1/bookShelf/BookShelfRow';
import { isAuthed } from '@/utils/helpers';
import Link from 'next/link';

import type { APIBookshelf, APIBookshelfInfo } from '@/types/bookshelf';
import BackButton from '@/v1/base/BackButton';
import ShareButton from '@/v1/base/ShareButton';

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 { isAuthed } from '@/utils/helpers';

import { IconArrowLeft, IconShare, IconKakao } from '@public/icons';

import TopNavigation from '@/v1/base/TopNavigation';
import BookShelfRow from '@/v1/bookShelf/BookShelfRow';
import Button from '@/v1/base/Button';
import LikeButton from '@/v1/base/LikeButton';

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({
Expand All @@ -30,8 +45,8 @@ export default function UserBookShelfPage({
};
}) {
const { data, isSuccess } = useBookShelfInfoQuery({ bookshelfId });
const { mutate: likeBookshelf } = useBookshelfLike(bookshelfId);
const { mutate: unlikeBookshelf } = useBookshelfUnlike(bookshelfId);
const { mutate: mutateBookshelfLike } =
useMutateBookshelfLikeQuery(bookshelfId);
const { show: showToast } = useToast();

if (!isSuccess) return null;
Expand All @@ -42,7 +57,7 @@ export default function UserBookShelfPage({
return;
}

!data.isLiked ? likeBookshelf() : unlikeBookshelf();
mutateBookshelfLike(data.isLiked);
};

return (
Expand All @@ -64,24 +79,11 @@ export default function UserBookShelfPage({
<span className="text-[1.4rem] text-[#939393]">
{`${data.job.jobGroupKoreanName}${data.job.jobNameKoreanName}`}
</span>
<Button
size="small"
colorScheme="warning"
fullRadius
fill={data.isLiked ? true : false}
<LikeButton
isLiked={data.isLiked}
likeCount={data.likeCount}
onClick={handleClickLikeButton}
>
<div className="bold flex items-center gap-[0.3rem] text-xs">
<IconHeart
fill={data.isLiked ? '#F56565' : 'white'}
stroke={!data.isLiked ? '#F56565' : 'white'}
stroke-width={1.5}
height="1.3rem"
w="1.3rem"
/>
{data.likeCount}
</div>
</Button>
/>
</div>
</div>

Expand Down
104 changes: 104 additions & 0 deletions src/app/group/[groupId]/join/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
'use client';

import { notFound, useRouter } from 'next/navigation';
import { SubmitHandler, useForm } from 'react-hook-form';

import useJoinBookGroup from '@/hooks/group/useJoinBookGroup';

import SSRSafeSuspense from '@/components/SSRSafeSuspense';
import Loading from '@/v1/base/Loading';
import Input from '@/v1/base/Input';
import InputLength from '@/v1/base/InputLength';
import ErrorMessage from '@/v1/base/ErrorMessage';
import BottomActionButton from '@/v1/base/BottomActionButton';
import BookGroupNavigation from '@/v1/bookGroup/BookGroupNavigation';

type JoinFormValues = {
answer: string;
};

const JoinBookGroupPage = ({
params: { groupId },
}: {
params: { groupId: number };
}) => {
return (
<SSRSafeSuspense fallback={<Loading fullpage />}>
<BookGroupNavigation groupId={groupId}>
<BookGroupNavigation.BackButton
href={`/group/${groupId}`}
routeOption="replace"
/>
<BookGroupNavigation.Title />
</BookGroupNavigation>
<BookGroupJoinForm groupId={groupId} />
</SSRSafeSuspense>
);
};

const BookGroupJoinForm = ({ groupId }: { groupId: number }) => {
const router = useRouter();
const { isMember, hasPassword, question, joinBookGroup } =
useJoinBookGroup(groupId);

if (isMember || !hasPassword) {
notFound();
}

const {
register,
watch,
handleSubmit,
formState: { errors },
} = useForm<JoinFormValues>({ mode: 'all' });

const submitJoinForm: SubmitHandler<JoinFormValues> = ({ answer }) => {
joinBookGroup({
answer,
onSuccess: () => router.replace(`/group/${groupId}`),
});
};

return (
<form
className="mt-[2.5rem] flex flex-col gap-[2.5rem]"
onSubmit={handleSubmit(submitJoinForm)}
>
<p className="whitespace-pre-line text-2xl font-bold leading-snug">
{`문제를 맞추면
모임에 가입할 수 있어요`}
</p>
<div className="flex flex-col gap-[1.5rem]">
<p className="text-sm">{question}</p>
<div className="flex flex-col gap-[0.5rem]">
<Input
{...register('answer', {
required: '정답을 입력해주세요',
pattern: {
value: /^\S*$/g,
message: '띄어쓰기 없이 정답을 입력해주세요.',
},
minLength: { value: 1, message: '1자 이상 입력해주세요.' },
maxLength: { value: 10, message: '10자 이하 입력해주세요.' },
})}
placeholder="띄어쓰기 없이 정답을 입력해주세요"
error={!!errors.answer}
/>
<div className="flex flex-row-reverse justify-between gap-[0.4rem]">
<InputLength
isError={!!errors.answer}
currentLength={watch('answer')?.length}
maxLength={10}
/>
{errors.answer && (
<ErrorMessage>{errors.answer.message}</ErrorMessage>
)}
</div>
</div>
</div>
<BottomActionButton type="submit">제출하기</BottomActionButton>
</form>
);
};

export default JoinBookGroupPage;
56 changes: 56 additions & 0 deletions src/hooks/group/useJoinBookGroup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { isAxiosErrorWithCustomCode } from '@/utils/helpers';
import { SERVICE_ERROR_MESSAGE } from '@/constants';
import groupAPI from '@/apis/group';
import useToast from '@/v1/base/Toast/useToast';
import { useBookGroupJoinInfo } from '@/queries/group/useBookGroupQuery';

const useJoinBookGroup = (groupId: number) => {
const { data: bookGroupJoinData, refetch } = useBookGroupJoinInfo(groupId);
const { isExpired, isMember, hasPassword, question } = bookGroupJoinData;

const toast = useToast();

const joinBookGroup = async ({
answer,
onSuccess,
}: {
answer?: string;
onSuccess?: () => void;
}) => {
try {
await groupAPI.joinGroup({ bookGroupId: groupId, password: answer });
toast.show({ message: '🎉 모임에 가입되었어요! 🎉', type: 'success' });
onSuccess && onSuccess();
} catch (error) {
if (!isAxiosErrorWithCustomCode(error)) {
toast.show({ message: '잠시 후 다시 시도해주세요', type: 'error' });
return;
}

const { code } = error.response.data;
const message = SERVICE_ERROR_MESSAGE[code];
const isWrongAnswerErrorCode = code === 'BG3';

if (isWrongAnswerErrorCode) {
toast.show({
message: '정답이 아니에요. 다시 시도해주세요!',
type: 'error',
});
return;
}

toast.show({ message, type: 'error' });
}
};

return {
isExpired,
isMember,
hasPassword,
question,
refetch,
joinBookGroup,
};
};

export default useJoinBookGroup;
86 changes: 0 additions & 86 deletions src/queries/bookshelf/useBookShelfLikeMutation.ts

This file was deleted.

52 changes: 52 additions & 0 deletions src/queries/bookshelf/useMutateBookshelfLikeQuery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import type { APIBookshelfInfo } from '@/types/bookshelf';
import bookshelfAPI from '@/apis/bookshelf';
import bookShelfKeys from './key';

const useMutateBookshelfLikeQuery = (
bookshelfId: APIBookshelfInfo['bookshelfId']
) => {
const queryClient = useQueryClient();

return useMutation({
mutationFn: async (isLiked: APIBookshelfInfo['isLiked']) =>
!isLiked
? bookshelfAPI.likeBookshelf(bookshelfId)
: bookshelfAPI.unlikeBookshelf(bookshelfId),
onMutate: async () => {
await queryClient.cancelQueries(bookShelfKeys.info(bookshelfId));

const prevData = queryClient.getQueryData<APIBookshelfInfo>(
bookShelfKeys.info(bookshelfId)
);

if (prevData) {
const newData: APIBookshelfInfo = {
...prevData,
isLiked: !prevData.isLiked,
likeCount: prevData.isLiked
? prevData.likeCount - 1
: prevData.likeCount + 1,
};

queryClient.setQueryData<APIBookshelfInfo>(
bookShelfKeys.info(bookshelfId),
newData
);
}

return { prevData };
},
onError: (_error, _value, context) => {
queryClient.setQueryData(
bookShelfKeys.info(bookshelfId),
context?.prevData
);
},
onSettled: () => {
queryClient.invalidateQueries(bookShelfKeys.info(bookshelfId));
},
});
};

export default useMutateBookshelfLikeQuery;
Loading

0 comments on commit 543ad20

Please sign in to comment.