Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor : docs, docs-list #147

Merged
merged 7 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion app/(docs)/[classify]/DocsList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import * as styles from "./style.css";
const DocsList: FC<{ classify: string }> = ({ classify }) => {
const { formatDate } = useDate();
const { data: docsList } = useSuspenseQuery(docsQuery.list(classify));

const docsType = classify.toUpperCase();

return (
Expand Down
132 changes: 58 additions & 74 deletions app/(docs)/docs/[title]/Docs.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
"use client";

import { FC, Suspense } from "react";
import "dayjs/locale/ko";
import DOMPurify from "isomorphic-dompurify";
import Link from "next/link";
import {
useQueries,
useQuery,
useQueryClient,
useSuspenseQueries,
useSuspenseQuery,
Expand All @@ -20,31 +19,30 @@ import { toast } from "react-toastify";
import { useCreateLikeMutation, useDeleteLikeMutation } from "@/services/like/like.mutation";
import FrameEncoder from "@/components/FrameEncoder";
import { documentCompiler } from "@/utils";
import { CLASSIFY } from "@/record";
import { EditorType } from "@/enum";
import * as styles from "./style.css";

const Docs: FC<{ title: string; list: string[] }> = ({ title, list }) => {
const Docs: FC<{ title: string; frameNameList: Array<string> }> = ({ title, frameNameList }) => {
const frameQueryList = frameNameList.map((frame) => docsQuery.title(frame));
const frameList = useSuspenseQueries({ queries: frameQueryList }).map(({ data }) => data);
const { data: docs } = useSuspenseQuery(docsQuery.title(title));
const frameData = useSuspenseQueries({
queries: list.map((frame) => docsQuery.title(frame)),
}).map(({ data }) => data);
const { isLoggedIn } = useUser();
const [{ data: like }, { data: isILike }] = useQueries({
queries: [likeQuery.likeCount(title), likeQuery.isILike(docs.id)],
});
const queryClient = useQueryClient();

const { data: like } = useQuery(likeQuery.likeCount(title));
const { data: isILike } = useQuery(likeQuery.isILike(docs.id));
const { mutate: createLike } = useCreateLikeMutation();
const { mutate: cancelLike } = useDeleteLikeMutation();

const handleQueryInvalidate = () => {
queryClient.invalidateQueries(likeQuery.isILike(docs.id));
queryClient.invalidateQueries(likeQuery.likeCount(title));
};

const handleLikeToggleClick = () => {
const onSuccessToggleLike = () => {
queryClient.invalidateQueries(likeQuery.isILike(docs.id));
queryClient.invalidateQueries(likeQuery.likeCount(title));
};
if (!isLoggedIn) return toast(<Toastify content="로그인 후 이용해주세요!" />);

if (isILike) return cancelLike(docs.id, { onSuccess: handleQueryInvalidate });
createLike(docs.id, { onSuccess: handleQueryInvalidate });
if (isILike) return cancelLike(docs.id, { onSuccess: onSuccessToggleLike });
createLike(docs.id, { onSuccess: onSuccessToggleLike });
};

const sanitizeData = () => ({
Expand All @@ -54,63 +52,49 @@ const Docs: FC<{ title: string; list: string[] }> = ({ title, list }) => {
return (
<Suspense>
<Container {...docs}>
<div className={styles.container}>
<header className={styles.header}>
<span className={styles.warning}>
문의를 통해 본인 문서의 기재되길 원치않는 특정 내용을 즉시 삭제할 수 있습니다.
<br />
문서 기재로 발생한 이슈에 대해 부마위키 팀은 아무런 책임을 지지 않으며, 수사 기관에
편집 기록과 관련된 데이터를 제공할 수 있습니다.
</span>
<button onClick={handleLikeToggleClick} className={styles.likeButton}>
<LikeIcon isLike={isILike} width={16} height={16} />
<span>{like.thumbsUpsCount}</span>
</button>
</header>
{docs.docsType === "FRAME" ? (
<div>
<div className={styles.body}>
<FrameEncoder
title={docs.title}
contents={docs.contents}
docsType={docs.docsType}
mode="READ"
/>
</div>
</div>
) : (
<>
{frameData.map(
(frame) =>
frame !== null &&
frame.docsType === "FRAME" && (
<FrameEncoder
key={frame.id}
title={frame.title}
contents={frame.contents}
docsType={frame.docsType}
mode="READ"
/>
),
)}
<div className={styles.body} dangerouslySetInnerHTML={sanitizeData()} />
</>
)}
<div className={styles.contributorsBox}>
<h1 className={styles.contributorTitle}>문서 기여자</h1>
<div className={styles.contributorList}>
{docs.contributors.map((contributor) => (
<Link
key={contributor.id}
href={`/user/${contributor.id}`}
className={styles.contributor}
>
{contributor.nickName}
</Link>
))}
</div>
</div>
</div>
<header className={styles.header}>
<p className={styles.warning}>
문의를 통해 본인 문서의 기재되길 원치않는 특정 내용을 즉시 삭제할 수 있습니다.
<br />
문서 기재로 발생한 이슈에 대해 부마위키 팀은 아무런 책임을 지지 않으며, 수사 기관에 편집
기록과 관련된 데이터를 제공할 수 있습니다.
</p>
<button onClick={handleLikeToggleClick} className={styles.likeButton}>
<LikeIcon isLike={isILike} width={16} height={16} />
{like.thumbsUpsCount}
</button>
</header>
{/** 문서 분류가 틀이면 틀이 문서 자체이기에 보여주고, 아니라면 틀 리스트 + 문서 */}
{docs.docsType === CLASSIFY.틀 ? (
<FrameEncoder {...docs} mode={EditorType.READ} />
) : (
(function DocsComponent() {
return (
<>
{frameList
.filter((frame) => frame && frame.docsType === CLASSIFY.틀)
.map((frame) => (
<FrameEncoder key={frame.id} {...frame} mode={EditorType.READ} />
))}
<section className={styles.body} dangerouslySetInnerHTML={sanitizeData()} />
</>
);
})()
)}
<footer className={styles.contributorsBox}>
<h1 className={styles.contributorTitle}>문서 기여자</h1>
<ul className={styles.contributorList}>
{docs.contributors.map((contributor) => (
<Link
key={contributor.id}
href={`/user/${contributor.id}`}
className={styles.contributor}
>
{contributor.nickName}
</Link>
))}
</ul>
</footer>
</Container>
</Suspense>
);
Expand Down
27 changes: 11 additions & 16 deletions app/(docs)/docs/[title]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ export const generateMetadata = async ({ params: { title } }: PageProps): Promis
try {
const queryClient = getQueryClient();
const data = await queryClient.fetchQuery(docsQuery.title(title));

return generateOpenGraph({
title: data.title,
description: data.contents,
});
} catch {
// prefetch 단계에서 오류가 발생했다면 해당 문서는 Not Found
notFound();
}
};
Expand All @@ -33,24 +33,19 @@ const Page = async ({ params: { title } }: PageProps) => {
await queryClient.prefetchQuery(docsQuery.title(title)),
await queryClient.prefetchQuery(likeQuery.likeCount(title)),
]);

const data = await queryClient.fetchQuery(docsQuery.title(title));

const frameList: string[] = [];
const frames = Array.from(data.contents.matchAll(/include\((.+)\);/g));
frames.forEach((frame) => {
if (!frameList.includes(frame[1])) {
frameList.push(frame[1]);
}
});

await Promise.all(
frameList.map((frameTitle) => queryClient.prefetchQuery(docsQuery.title(frameTitle))),
);
/**
* contents에서 frame list를 얻기 위해 값을 반환하는 fetchQuery 사용
* include();와 match되는 글자 탐색
* "include();" 삭제 (틀 이름만 남을 수 있도록)
*/
const { contents } = await queryClient.fetchQuery(docsQuery.title(title));
const matchesFrameFormatList = contents.match(/include\((.*?)\);/g) || [];
const frameList = matchesFrameFormatList.map((match) => match.replace(/include\(|\);/g, ""));
await Promise.all(frameList.map((frame) => queryClient.prefetchQuery(docsQuery.title(frame))));

return (
<HydrationBoundary state={dehydrate(queryClient)}>
<Docs title={title} list={frameList} />
<Docs title={title} frameNameList={frameList} />
</HydrationBoundary>
);
};
Expand Down
1 change: 1 addition & 0 deletions app/(docs)/docs/[title]/style.css.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export const container = style({

export const header = style({
width: "100%",
marginBottom: "40px",
...flex.BETWEEN,
});

Expand Down
1 change: 1 addition & 0 deletions app/getQueryClient.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { QueryClient } from "@tanstack/react-query";
import { cache } from "react";

// 서버사이드에서 React Query prefetch를 진행하기 위함
const getQueryClient = cache(() => new QueryClient());
export default getQueryClient;
1 change: 1 addition & 0 deletions app/providers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { ToastContainer } from "react-toastify";

const Providers = ({ children }: PropsWithChildren) => {
const [queryClient] = useState(
// lazy initialization
() =>
new QueryClient({
defaultOptions: {
Expand Down
2 changes: 2 additions & 0 deletions enum/editorType.enum.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
enum EditorType {
EDIT = "EDIT",
CREATE = "CREATE",
READ = "READ",
WRITE = "WRITE",
}

export default EditorType;