Skip to content

Commit

Permalink
Feat: 스토리 상세 페이지 완료
Browse files Browse the repository at this point in the history
  • Loading branch information
D5ng committed Jun 22, 2024
1 parent bd848f0 commit aa8742b
Show file tree
Hide file tree
Showing 23 changed files with 403 additions and 84 deletions.
19 changes: 18 additions & 1 deletion api/story/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { axiosInstance } from '../index';
import { StoryData } from '../../types/story/index';
import { CreateCommentData, StoryDetailsData } from '@/types/story/details';
import { CreateCommentData, StoryDetailsData, UpdateCommentData } from '@/types/story/details';
import { get } from 'http';

export class StoryAPI {
async fetchStory(page = 0, size = 10) {
Expand All @@ -22,6 +23,22 @@ export class StoryAPI {
async createCommentStory({ diaryId, data }: CreateCommentData) {
return (await axiosInstance.post(`comments/${diaryId}`, data)).data;
}

async deleteCommentStory(commentId: number) {
return await axiosInstance.delete(`comments/parent/${commentId}`);
}

async updateCommentStory({ commentId, data }: UpdateCommentData) {
return await axiosInstance.put(`comments/${commentId}`, data);
}

async deleteReplyStroy(commentId: number) {
return await axiosInstance.delete(`comments/child/${commentId}`);
}

async deleteStory(diaryId: number) {
return await axiosInstance.delete(`stories/${diaryId}`);
}
}

const storyApiInstance = new StoryAPI();
Expand Down
30 changes: 27 additions & 3 deletions components/kebab/kebab.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import Image from 'next/image';
import { PropsWithChildren, createContext, useContext, useRef } from 'react';
import { DropdownMenuContextProps } from './kebab.type';
import useOutside from '@/hooks/use-outside';
Expand Down Expand Up @@ -47,17 +46,42 @@ function DropdownMenuItem(props: PropsWithChildren<{ onClick?: () => void }>) {
);
}

function Kebab() {
function Kebab(props: { color?: 'black' | 'gray' }) {
const dropdownContext = useContext(DropdownMenuContext);
const ref = useOutside<HTMLButtonElement, HTMLUListElement>({
isOpen: dropdownContext.isOpen,
onCloseToggle: dropdownContext.onCloseToggle,
modalRef: dropdownContext.modalRef!,
});

const defaultColor = props.color || 'black';
const svgColor = defaultColor === 'black' ? '#000' : '#9e9e9e';

return (
<button className={styles.kebab} onClick={dropdownContext.onOpenToggle} ref={ref}>
<Image src="/images/kebab-icon.svg" alt="모달 열기" fill />
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path
d="M12 13C12.5523 13 13 12.5523 13 12C13 11.4477 12.5523 11 12 11C11.4477 11 11 11.4477 11 12C11 12.5523 11.4477 13 12 13Z"
stroke={svgColor}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M19 13C19.5523 13 20 12.5523 20 12C20 11.4477 19.5523 11 19 11C18.4477 11 18 11.4477 18 12C18 12.5523 18.4477 13 19 13Z"
stroke={svgColor}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
<path
d="M5 13C5.55228 13 6 12.5523 6 12C6 11.4477 5.55228 11 5 11C4.44772 11 4 11.4477 4 12C4 12.5523 4.44772 13 5 13Z"
stroke={svgColor}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
);
}
Expand Down
Empty file.
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
.layout {
position: fixed;
bottom: 83px;
bottom: 0;
width: 380px;
height: 100px;
background-color: var(--white);
padding-top: 25px;
}

.reply {
color: var(--main-color);
font-size: 12px;
margin-bottom: 7px;
display: block;
position: absolute;
top: 0;
}

.form {
Expand All @@ -17,12 +29,17 @@
resize: none;
font-size: 16px;
line-height: 28px;
outline: none;

&::placeholder {
font-size: 16px;
color: #9e9e9e;
line-height: 28px;
}

&.active {
border: 1px solid var(--main-color);
}
}

.submit {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import React, { ChangeEventHandler, FormEventHandler, useRef, useState } from 'react';
import styles from './story-detail-add-comment.module.scss';
import React, { ChangeEventHandler, FormEvent, FormEventHandler, KeyboardEventHandler, useRef, useState } from 'react';
import { useCreateComment } from '@/hooks/queries/story/mutation';
import styles from './story-detail-add-comment.module.scss';

interface StoryDetailAddCommentProps {
storyId: number;
replyId: number | null;
replyOwner: { author: string; replyId: number } | null;
onReplyReset: () => void;
}

Expand All @@ -31,28 +31,51 @@ export default function StoryDetailAddComment(props: StoryDetailAddCommentProps)

let data: { content: string; parentCommentId: number | null };

if (props.replyId) {
data = { content: commentValue, parentCommentId: props.replyId };
if (commentValue.trim().length === 0) return;

if (props.replyOwner) {
data = { content: commentValue, parentCommentId: props.replyOwner.replyId };
} else {
data = { content: commentValue, parentCommentId: null };
}

console.log(data)

commentMutation.mutate({ diaryId: props.storyId, data });
handleResetComment();
handleResizeHeight();
props.onReplyReset();
};

const handleCommentKeyDown: KeyboardEventHandler<HTMLTextAreaElement> = (event) => {
if (event.nativeEvent.isComposing) return;

if (event.key === 'Enter' && event.shiftKey) {
return;
} else if (event.key === 'Enter') {
event.preventDefault();
handleSubmit(event as unknown as FormEvent<HTMLFormElement>);
handleResetComment();
}
};

const textareaClassName = `${isDisbaled ? '' : styles.active} ${styles.textarea}`;

return (
<div className={styles.layout}>
{props.replyOwner && (
<span className={styles.reply}>{props.replyOwner.author}님에게 댓글을 남기는 중입니다..</span>
)}
<form className={styles.form} onSubmit={handleSubmit}>
<textarea
className={styles.textarea}
className={textareaClassName}
placeholder="댓글을 입력하세요"
ref={textareaRef}
onInput={handleResizeHeight}
rows={1}
onChange={handleCommentChange}
value={commentValue}
onKeyDown={handleCommentKeyDown}
></textarea>
<button className={styles.submit} type="submit">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
Expand Down
61 changes: 61 additions & 0 deletions components/story/detail/comment-item/story-detail-comment-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import React from 'react';
import DropdownMenu from '@/components/kebab/kebab';
import { AvatarImage, AvatarName } from '@/components/avatar/avatar';
import StoryDetailReplyComments from '@/components/story/detail/reply/story-detail-reply-comments';
import useToggle from '@/hooks/use-toggle';
import { useDeleteComment } from '@/hooks/queries/story/mutation';
import { StoryComment } from '@/types/story/details';
import styles from './story-detail-comment-item.module.scss';

interface StoryDetailCommentItemProps extends StoryComment {
onReplyClick: (data: { author: string; replyId: number }) => void;
}

export default function StoryDetailCommentItem(props: StoryDetailCommentItemProps) {
const { isToggle: isOpen, handleCloseToggle: onCloseToggle, handleOpenToggle: onOpenToggle } = useToggle();

const deleteMutation = useDeleteComment();

const updateFn = () => {
console.log('Update');
onCloseToggle();
};

const deleteFn = () => {
deleteMutation.mutate(props.commentId);
onCloseToggle();
};

return (
<>
<li className={styles['comment-item']}>
<div className={styles['comment-item__info']}>
<AvatarImage image={props.commentsAuthorProfile} />
<div>
<AvatarName>{props.commentAuthor}</AvatarName>
<time className={styles['comment-item__date']}>2024-06-21</time>
</div>
</div>
<div className={styles['comment-item__contents']}>
<p>{props.comment}</p>
</div>
<button
className={styles['comment-item__reply']}
onClick={props.onReplyClick.bind(null, { author: props.commentAuthor, replyId: props.commentId })}
>
댓글
</button>
<div className={styles['comment-item__kebab']}>
<DropdownMenu value={{ isOpen, onOpenToggle, onCloseToggle }}>
<DropdownMenu.Kebab color="gray" />
<DropdownMenu.Content>
<DropdownMenu.Item onClick={updateFn}>수정</DropdownMenu.Item>
<DropdownMenu.Item onClick={deleteFn}>삭제</DropdownMenu.Item>
</DropdownMenu.Content>
</DropdownMenu>
</div>
<StoryDetailReplyComments replyList={props.children} />
</li>
</>
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
.wrapper {
margin-top: 12px;
padding: 0 24px 140px 24px;
padding: 0 0px 140px;
border-top: 1px solid #bdbdbd;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import React from 'react';
import { StoryComment } from '@/types/story/details';
import StoryDetailCommentItem from '@/components/story/detail/story-detail-comment-item';
import StoryDetailCommentItem from '@/components/story/detail/comment-item/story-detail-comment-item';
import styles from './story-detail-comments.module.scss';

interface StoryDetailCommentsProps {
comments: StoryComment[];
onReplyClick: (id: number) => void;
onReplyClick: (data: { author: string; replyId: number }) => void;
}

export default function StoryDetailComments(props: StoryDetailCommentsProps) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,30 @@ import CalendarInstance from '@/utils/date/date.utils';
import DropdownMenu from '@/components/kebab/kebab';
import useToggle from '@/hooks/use-toggle';
import styles from './story-detail-header.module.scss';
import { useRouter } from 'next/router';
import { useDeleteStory } from '@/hooks/queries/story/mutation';

interface StoryDetailHeaderProps {
isMyStory: boolean | undefined;
}

export default function StoryDetailHeader(props: StoryDetailHeaderProps) {
const router = useRouter();
const storyId = +router.query.storyId!;
const { isToggle: isOpen, handleCloseToggle: onCloseToggle, handleOpenToggle: onOpenToggle } = useToggle();
const today = CalendarInstance.getToday();

const deleteStoryMutation = useDeleteStory();

const updateFn = () => {
console.log('Update');
// 다이어리 수정 페이지로 이동해야함.
router.push(`/diaries/${storyId}`);
onCloseToggle();
};

const deleteFn = () => {
console.log('Delete');
deleteStoryMutation.mutate(storyId);
router.push(`/story`);
onCloseToggle();
};

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
.comment-item {
position: relative;
margin-top: 20px;
padding: 20px 0 20px 28px;
border-top: 1px solid #eee;

& + & {
margin-top: 0;
}
}

.comment-item__info {
display: flex;
gap: 0 8px;
}

.comment-item__date {
display: block;
font-size: 12px;
color: #d5d9dc;
margin-top: 5px;
}

.comment-item__contents {
margin-top: 12px;
font-size: 14px;
}

.comment-item__reply {
margin-top: 10px;
font-size: 12px;
color: #9e9e9e;
}

.comment-item__kebab {
position: absolute;
right: 0;
top: 20px;
}

.textarea {
width: 100%;
resize: none;
border-radius: 5px;
padding: 10px;
outline: none;
border: 1px solid var(--main-color);
}

.reply-form {
position: relative;

&__button {
font-size: 12px;
text-decoration: underline;
color: var(--main-color);
width: 100%;
text-align: end;
margin-top: 5px;
}
}
Loading

0 comments on commit aa8742b

Please sign in to comment.