Skip to content

Commit

Permalink
Merge pull request #154 from IT-Cotato/feat/#152
Browse files Browse the repository at this point in the history
[FEAT] 댓글 번역 추가 및 번역 로직 수정
  • Loading branch information
Ki-Tak authored Feb 24, 2025
2 parents 6782cec + d2f903b commit 3e4e1a7
Show file tree
Hide file tree
Showing 12 changed files with 203 additions and 121 deletions.
2 changes: 1 addition & 1 deletion src/apis/translate/translateText.api.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { authApi } from '../axios-instance';
import { API_DOMAINS } from '@/constants/api';
export const translateText = async ({ content }) => {
export const translateText = async (content) => {
const response = await authApi.post(API_DOMAINS.TRANSLATE_TEXT, content);
return response.data.data
}
2 changes: 1 addition & 1 deletion src/assets/imgs/translate.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/components/board/BoardListBox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const BoardListBox = ({ list, listKey, togglePin }) => {
{list.map((data, index) => (
<BoardList
data={data}
key={index}
key={data.order}
listKey={listKey}
index={index}
togglePin={togglePin}
Expand Down
68 changes: 55 additions & 13 deletions src/components/board/PostComment.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,21 @@ import anonymous from "@/assets/imgs/anonymous.svg"
import Like from "@/assets/imgs/like.svg?react"
import FillLike from "@/assets/imgs/fillLike.svg?react"
import Comment from "@/assets/imgs/comment.svg?react"
import Translate from "@/assets/imgs/translate.svg?react"
import { formatTime } from "@/utils/formatTime"
import { useRef } from "react"
import { cn } from "@/utils/cn"
import { TranslateButton } from "../common/TranslateButton"
import { Translating } from "../common/Translating"
import { useCommentTranslate } from "@/hooks/useCommentTranslate"
export const PostComment = ({ data, setInputFocus, focusedComment, setFocusedComment, handleCommentLike }) => {
const commentRef = useRef(null);
const {
translateState,
setTranslateState,
translatedContent,
translatePending,
handleTranslate
} = useCommentTranslate(data.commentId)
const handleComment = (ref, commentId, parentId) => {
ref.current?.scrollIntoView({
behavior: "smooth",
Expand All @@ -19,7 +28,6 @@ export const PostComment = ({ data, setInputFocus, focusedComment, setFocusedCom
})
setInputFocus(true)
}

return (
<>
<div ref={commentRef}
Expand All @@ -31,7 +39,7 @@ export const PostComment = ({ data, setInputFocus, focusedComment, setFocusedCom
<div className="flex justify-between">
<div className="flex items-center gap-2">
<img src={anonymous} className="w-[1.375rem] h-[1.375rem]" />
<p>{data.author === "Author" ? <span className="text-[#2768FF]">Anonimity(Author)</span> : data.author}</p>
<p>{data.author === "Author" ? <span className="text-[#2768FF]">Anonymity(Author)</span> : data.author}</p>
<p className="text-neutral-border-50">{formatTime(data.createdTime)}</p>
</div>
<div className="flex gap-1 text-neutral-base">
Expand All @@ -50,8 +58,19 @@ export const PostComment = ({ data, setInputFocus, focusedComment, setFocusedCom
</button>
</div>
</div>
<p className="text-neutral-base leading-normal whitespace-pre-line">
{data.content}
<p className="relative text-neutral-base leading-normal whitespace-pre-line">
<span
className={cn({
'opacity-0': translatePending,
})}
>
{translateState ? translatedContent : data?.content}
</span>
{translatePending && (
<div className="absolute left-0 -translate-y-1/2 top-1/2">
<Translating width={'1.75rem'} height={'1.75rem'} />
</div>
)}
</p>
<div className="flex justify-between">
<div className="text-neutral-border-50"
Expand All @@ -61,12 +80,14 @@ export const PostComment = ({ data, setInputFocus, focusedComment, setFocusedCom
}}>
Reply
</div>
<Translate className="w-[1.125rem] h-[1.125rem] text-neutral-base" />
<TranslateButton size="small" color="base" state={translateState} setState={setTranslateState}
handleTranslate={() => handleTranslate(data.content)} />
</div>

</div>
{data.replies &&
data.replies.map((item, index) => (
<ReplyComment reply={data.author} data={item} key={index} focusedComment={focusedComment} handleComment={handleComment}
data.replies.map((item) => (
<ReplyComment reply={data.author} data={item} key={item.commentId} focusedComment={focusedComment} handleComment={handleComment}
handleCommentLike={handleCommentLike} />
))
}
Expand All @@ -75,6 +96,13 @@ export const PostComment = ({ data, setInputFocus, focusedComment, setFocusedCom
}
const ReplyComment = ({ reply, data, focusedComment, handleComment, handleCommentLike }) => {
const commentRef = useRef(null);
const {
translateState,
setTranslateState,
translatedContent,
translatePending,
handleTranslate
} = useCommentTranslate(data.commentId)
return (
<div ref={commentRef}
className={cn("flex flex-col gap-2 pl-[2.8125rem] pr-4 py-[0.9375rem] text-base",
Expand All @@ -85,7 +113,7 @@ const ReplyComment = ({ reply, data, focusedComment, handleComment, handleCommen
<div className="flex justify-between" >
<div className="flex items-center gap-2">
<img src={anonymous} className="w-[1.375rem] h-[1.375rem]" />
<p>{data.author === "Author" ? <span className="text-[#2768FF]">Anonimity(Author)</span> : data.author}</p>
<p>{data.author === "Author" ? <span className="text-[#2768FF]">Anonymity(Author)</span> : data.author}</p>
<p className="text-neutral-border-50">{formatTime(data.createdTime)}</p>
</div>
<button className="flex gap-[0.125rem] cursor-pointer"
Expand All @@ -98,9 +126,22 @@ const ReplyComment = ({ reply, data, focusedComment, handleComment, handleCommen
<p className="min-w-[.625rem] text-neutral-base">{data.likes}</p>
</button>
</div>
<p className="text-neutral-base leading-normal whitespace-pre-line">
<span className="text-primary-base">@{reply === "Author" ? "Anontmity(Author)" : reply}&nbsp;</span>
{data.content}
<p className="relative text-neutral-base leading-normal whitespace-pre-line">
<span
className={cn({
'opacity-0': translatePending,
})}
>
<span className="text-primary-base">
@{reply === "Author" ? "Anonymity(Author)" : reply}&nbsp;
</span>
{translateState ? translatedContent : data?.content}
</span>
{translatePending && (
<div className="absolute left-0 -translate-y-1/2 top-1/2">
<Translating width={'1.75rem'} height={'1.75rem'} />
</div>
)}
</p>
<div className="flex justify-between">
<div className="text-neutral-border-50"
Expand All @@ -110,7 +151,8 @@ const ReplyComment = ({ reply, data, focusedComment, handleComment, handleCommen
}}>
Reply
</div>
<Translate className="w-[1.125rem] h-[1.125rem] text-neutral-base" />
<TranslateButton size="small" color="base" state={translateState} setState={setTranslateState}
handleTranslate={() => handleTranslate(data.content)} />
</div>
</div >
)
Expand Down
51 changes: 14 additions & 37 deletions src/components/board/PostList.jsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,20 @@
import Like from '../../assets/imgs/like.svg?react';
import Comment from '../../assets/imgs/comment.svg?react';
import Translate from '../../assets/imgs/translate.svg?react';
import { useNavigate } from 'react-router-dom';
import { formatTime } from '@/utils/formatTime';
import { useState } from 'react';
import { Translating } from '../common/Translating';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { translatePost } from '@/apis/translate/translatePost.api';
import { QUERY_KEYS } from '@/constants/api';
import { cn } from '@/utils/cn';
import { TranslateButton } from '../common/TranslateButton';
import { usePostTranslate } from '@/hooks/usePostTranslate';
export const PostList = ({ data, isActive, ...props }) => {
const navigate = useNavigate();
const queryClient = useQueryClient();
const [translateState, setTranslateState] = useState(false);
const [translatedPost, setTranslatedPost] = useState(null);
const { mutate: postTranslate, isPending: translatePending } = useMutation({
mutationFn: async (postId) => {
return await translatePost({ postId: postId });
},
onSuccess: (response, postId) => {
queryClient.invalidateQueries({
queryKey: [QUERY_KEYS.GET_TRANSLATE_POST_LIST, postId],
});
setTranslatedPost(response);
setTranslateState(true);
},
});
const handleTranslate = () => {
if (translatedPost) {
setTranslateState(true);
} else {
postTranslate(data.id);
}
};
const {
translateState,
setTranslateState,
translatedPost,
translatePending,
handleTranslate
} = usePostTranslate(data.id)

const handleOnClick = (data) => {
if (props.onClick) {
Expand Down Expand Up @@ -65,7 +47,7 @@ export const PostList = ({ data, isActive, ...props }) => {
</span>
</h1>
<h2 className="flex w-full line-clamp-2 text-neutral-base">
<span
<span
className={cn('line-clamp-2', {
'opacity-0': translatePending,
})}
Expand Down Expand Up @@ -105,15 +87,10 @@ export const PostList = ({ data, isActive, ...props }) => {
{formatTime(data.createdTime)}
</p>
</div>
<button
onClick={(e) => {
e.stopPropagation();
translateState ? setTranslateState(false) : handleTranslate();
}}
>
<Translate className="text-neutral-title" />
</button>
<TranslateButton handleTranslate={handleTranslate}
state={translateState} setState={setTranslateState}
size='small' color='title' />
</div>
</div>
</div >
);
};
45 changes: 20 additions & 25 deletions src/components/common/TranslateButton.jsx
Original file line number Diff line number Diff line change
@@ -1,32 +1,27 @@
import TranslateImg from '@/assets/imgs/translate.svg';

import TranslateImg from '@/assets/imgs/translate.svg?react';
import { cn } from '@/utils/cn';
/** 번역 컴포넌트
* @param {Object} props - 컴포넌트의 props
* @param {'post' | 'content' | 'text'} props.translateType - 번역 타입 (게시된 post, 작성 중인 content, 단순 text)
* @param {number | string} props.value - post 번역 시 number, 작성 중인 글 본문이나 단순 text 번역 시 string
* @param {function} props.setValue - 해당 함수로 번역된 텍스트 전달
* @param {function} props.handleTranslate
* @param {boolean} props.state - 해당 함수로 번역된 텍스트 전달
* @param {function} props.setState - 해당 함수로 번역된 텍스트 전달
* @param {'small' | 'large'} props.size - 크기
* @param {'title' | 'base'} props.color - 색상
*/
export const TranslateButton = ({ translateType, value, setValue }) => {
const translate = () => {
if (value || value === 0) {
if (translateType === 'post') {
// postId 전달 시 게시글 번역 - value는 number
console.log(value);
} else if (translateType === 'content') {
// 작성 중인 게시글 본문 번역 - value는 string
// 모달 구현해야함
// 사용자가 선택한 언어 있으면 백에 알려주기
setValue(value); // 현재는 번역 안된 상태로 전달
} else if (translateType === 'text') {
// 영어로 번역 - value는 string
setValue(value); // 현재는 번역 안된 상태로 전달
}
}
};

export const TranslateButton = ({ handleTranslate, state, setState, size, color }) => {
return (
<button type="button" onClick={translate}>
<img src={TranslateImg} alt="" className="h-[1.375rem] w-[1.375rem]" />
<button type="button"
onClick={(e) => {
e.stopPropagation();
state ? setState(false) : handleTranslate();
}}>
<TranslateImg alt="Translate"
className={cn({
"w-[1.125rem] h-[1.125rem]": size === "small",
"w-6 h-6": size === "large",
"text-neutral-base": color === "base",
"text-neutral-title": color === "title"
})} />
</button>
);
};
3 changes: 1 addition & 2 deletions src/constants/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export const API_DOMAINS = {
ADMIN_BOARD_DETAIL: '/admin/boards/:boardId',
ADMIN_ACTIVE_BOARD: '/admin/boards/:boardId/active',
ADMIN_INACTIVE_BOARD: '/admin/boards/:boardId/inactive',

ADMIN_CARDNEWS: '/admin/cardNews',
ADMIN_CARDNEWS_DETAIL: '/admin/cardNews/:postId',

Expand Down Expand Up @@ -100,7 +100,6 @@ export const QUERY_KEYS = {

GET_COMMENT_LIST: 'commentList',
GET_TRANSLATE_POST: 'postTranslated',
GET_TRANSLATE_POST_LIST: 'postTranslatedList',
GET_TRANSLATE_TEXT: 'textTranslated',

GET_SEARCH_KEYWORD: 'searchKeyword',
Expand Down
37 changes: 37 additions & 0 deletions src/hooks/useCommentTranslate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useState } from "react";
import { translateText } from "@/apis/translate/translateText.api";
import { QUERY_KEYS } from "@/constants/api";

export const useCommentTranslate = (commentId) => {
const [translateState, setTranslateState] = useState(false);
const [translatedContent, setTranslatedContent] = useState(null);
const queryClient = useQueryClient();
const { mutate: commentTranslate, isPending: translatePending } = useMutation({
mutationFn: async (content) => {
return await translateText({ content });
},
onSuccess: (translatedContent) => {
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.GET_TRANSLATE_TEXT, commentId] });
setTranslateState(true);
setTranslatedContent(translatedContent.content);
},
});

const handleTranslate = (content) => {
if (translatedContent) {
setTranslateState(true);
} else {
commentTranslate(content);
}
};

return {
translateState,
setTranslateState,
translatedContent,
setTranslatedContent,
translatePending,
handleTranslate,
};
};
36 changes: 36 additions & 0 deletions src/hooks/usePostTranslate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { translatePost } from "@/apis/translate/translatePost.api";
import { QUERY_KEYS } from "@/constants/api";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { useState } from "react";

export const usePostTranslate = (postId) => {
const queryClient = useQueryClient();
const [translateState, setTranslateState] = useState(false);
const [translatedPost, setTranslatedPost] = useState(null);
const handleTranslate = () => {
if (translatedPost) {
setTranslateState(true);
}
else {
postTranslate(postId);
}
}
const { mutate: postTranslate, isPending: translatePending } = useMutation({
mutationFn: async () => {
return await translatePost({ postId: postId })
},
onSuccess: (translatedPost) => {
queryClient.invalidateQueries({ queryKey: [QUERY_KEYS.GET_TRANSLATE_POST, postId] });
setTranslateState(true);
setTranslatedPost(translatedPost)
}
})
return {
translateState,
setTranslateState,
translatedPost,
setTranslatedPost,
translatePending,
handleTranslate
}
}
Loading

0 comments on commit 3e4e1a7

Please sign in to comment.