Skip to content

Commit 1ed99b7

Browse files
authored
Merge pull request #115 from Devminjeong-eum/feat/DEV-155
[DEV-155] TTS 기능 구현, 메인 페이지 UI 수정 및 유저 디바이스 체크기능 추가
2 parents f9d9421 + c8f8180 commit 1ed99b7

File tree

11 files changed

+230
-79
lines changed

11 files changed

+230
-79
lines changed

public/sw.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/components/common/TTSPlayer.tsx

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
'use client';
2+
3+
import useDeviceType from '@/hooks/useDeviceType';
4+
import clsx from 'clsx';
5+
import SpeakerSvg from '../svg-component/SpeakerSvg';
6+
import useAudioPlayer from '@/hooks/useAudioPlayer';
7+
import useGetTTSUrl from '@/hooks/query/useGetTTSUrl';
8+
9+
type Props = {
10+
id: string;
11+
diacritic: string;
12+
};
13+
14+
export default function TTSPlayer({ diacritic, id }: Props) {
15+
const deviceType = useDeviceType();
16+
const { data: audioUrl } = useGetTTSUrl(id);
17+
const { audioRef, startAudio, isPlaying } = useAudioPlayer(id);
18+
19+
const handleClick = (e: React.MouseEvent<HTMLDivElement>) => {
20+
e.stopPropagation();
21+
22+
if (audioUrl) {
23+
startAudio(audioUrl);
24+
}
25+
};
26+
27+
return (
28+
<div
29+
onClick={handleClick}
30+
className={clsx(
31+
'flex items-center px-[6px] py-[3px] gap-1 bg-[#F6F8FA] hover:bg-[#EFF2F6] h-[19px] text-[#69699C] rounded-3xl',
32+
isPlaying && 'ring-main-gradient-full ring-1 animate-pulse',
33+
)}
34+
>
35+
<audio ref={audioRef} loop={false} />
36+
<div
37+
className={clsx(
38+
'w-[12px] h-[12px]',
39+
deviceType === 'PC' &&
40+
'hover:ring-[#EFF2F7] hover:bg-[#F1F4FA] hover:ring-2',
41+
)}
42+
>
43+
<SpeakerSvg />
44+
</div>
45+
<p className="leading-[13px] text-[11px]">{diacritic}</p>
46+
</div>
47+
);
48+
}

src/components/common/WordItem.tsx

+35-47
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import { useRouter } from 'next/navigation';
44
import HeartSvg from '@/components/svg-component/HeartSvg';
55
import clsx from 'clsx';
66
import { useMutationLike } from '@/hooks/useMutationLike';
7-
import { Dispatch, SetStateAction } from 'react';
7+
import { Dispatch, SetStateAction, useCallback } from 'react';
8+
import TTSPlayer from './TTSPlayer';
89

910
type Props = MainItemType & {
1011
setIsOpenModal: Dispatch<SetStateAction<boolean>>;
@@ -26,59 +27,46 @@ export default function WordItem({
2627
setIsOpenModal,
2728
});
2829

29-
const handleLikeButton = () => {
30-
isLike ? handleSubLike() : handleAddLike();
31-
};
30+
const handleLikeButton = useCallback(
31+
(e: React.MouseEvent<HTMLButtonElement>) => {
32+
e.stopPropagation();
33+
isLike ? handleSubLike() : handleAddLike();
34+
},
35+
[isLike, handleAddLike, handleSubLike],
36+
);
3237

3338
return (
34-
<article
39+
<div
3540
key={id}
36-
className="min-h-[98px] p-[18px] pb-[14px] w-full ring-1 bg-white ring-[#F2F4F9] hover:ring-[#EFF2F7] rounded-2xl hover:bg-[#F1F4FA] hover:ring-2 cursor-pointer"
41+
className={clsx(
42+
'h-[120px] px-[18px] py-[16px] w-full ring-1 bg-white ring-[#F2F4F9] rounded-2xl cursor-pointer',
43+
)}
3744
onClick={() => router.push(getWordDetailPath(name))}
3845
>
39-
<div className="flex flex-col relative gap-1">
40-
<div className="flex justify-between items-center ">
41-
{/* 영단어, 단어 뜻, 발음 기호 컨테이너 */}
42-
<div className="flex flex-wrap items-center gap-2">
43-
{/* 영단어 */}
44-
<h3 className="text-main-blue font-semibold text-[16px]">{name}</h3>
45-
46-
{/* 단어 뜻 */}
47-
<div className="text-main-charcoal break-words text-[14px] font-medium">
48-
<span className="mr-[2px] text-[#F2F4F9] font-extralight">|</span>{' '}
49-
{/* {pronunciation.join(', ')} */}
50-
{/* FIXME: 여름과 논의 필요 */}
51-
{pronunciation[0]}
52-
</div>
46+
<header className="flex justify-between leading-[16px] mb-[5px]">
47+
<h3 className="text-main-blue font-semibold text-[17px]">{name}</h3>
48+
<button
49+
onClick={handleLikeButton}
50+
className={clsx(
51+
isLike ? 'text-main-blue' : 'text-[#DEE3F1]',
52+
'ml-[2px] mb-[3px]',
53+
)}
54+
>
55+
<HeartSvg />
56+
</button>
57+
</header>
5358

54-
{/* 발음기호 */}
55-
<p className="text-[#858596] text-[11px] font-[300] -ml-[1px]">
56-
{/* {diacritic.join(', ')} */}
57-
{/* FIXME: 여름과 논의 필요 */}
58-
{diacritic[0]}
59-
</p>
60-
</div>
61-
62-
{/* 좋아요 버튼 */}
59+
<section className="flex gap-1 items-center leading-[19px] mb-[6px]">
60+
<p className="font-medium text-[14px] text-main-charcoal">
61+
{pronunciation[0]}
62+
</p>
6363

64-
<button
65-
onClick={(e) => {
66-
e.stopPropagation();
67-
handleLikeButton();
68-
}}
69-
className={clsx(isLike ? 'text-main-blue' : 'text-[#D3DAED]')}
70-
>
71-
<HeartSvg />
72-
</button>
73-
</div>
64+
<TTSPlayer diacritic={diacritic[0].slice(1, -1)} id={id} />
65+
</section>
7466

75-
{/* 설명 */}
76-
<p className="text-main-gray text-[14px] font-[300] leading-[19px] line-clamp-1 xs:line-clamp-2">
77-
{description}
78-
</p>
79-
</div>
80-
</article>
67+
<p className="text-[14px] leading-[20px] text-main-gray line-clamp-2 tracking-[-2%] mt-[4px]">
68+
{description}
69+
</p>
70+
</div>
8171
);
8272
}
83-
84-

src/components/pages/home/HomeSkeleton.tsx

+18-15
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,33 @@
11
import HeartSvg from '@/components/svg-component/HeartSvg';
2+
import clsx from 'clsx';
23

34
export default function HomeSkeleton({ limit }: { limit: number }) {
45
return (
56
<div className="flex flex-col gap-2 mt-4 px-5 bg-[#FBFCFE]">
67
{Array.from({ length: limit }, (_, idx) => (
78
<div
89
key={idx}
9-
className="animate-pulse min-h-[98px] p-[18px] pb-[14px] w-full ring-1 bg-white ring-[#F2F4F9] rounded-2xl"
10+
className={clsx(
11+
'h-[120px] px-[18px] py-[16px] w-full ring-1 bg-white ring-[#F2F4F9] rounded-2xl animate-pulse',
12+
)}
1013
>
11-
<div className="flex justify-between items-center">
12-
{/* 영문, 국문, 발음기호 */}
13-
<div className="flex flex-wrap items-center gap-2">
14-
<div className="w-[53px] h-[17px] bg-gray-200 rounded"></div>
15-
<div className="text-[#F2F4F9] font-extralight">|</div>
16-
<div className="w-[24px] h-[17px] bg-gray-200 rounded"></div>
17-
<div className="w-[42px] h-[17px] bg-gray-200 rounded"></div>
18-
</div>
19-
{/* 하트 */}
20-
<div className="text-[#D3DAED] h-6">
14+
<header className="flex justify-between leading-[16px] mb-[5px]">
15+
<div className="w-1/3 h-4 bg-[#EAECF3] rounded-[2px]"></div>
16+
<div className={clsx('text-[#F5F6FA]', 'ml-[2px] mb-[3px]')}>
2117
<HeartSvg />
2218
</div>
23-
</div>
19+
</header>
20+
21+
<section className="flex gap-1 items-center leading-[19px] mb-[6px]">
22+
<div className="w-12 h-4 bg-[#F3F4F9] rounded-[2px]"></div>
23+
<div className="flex items-center px-[6px] py-[3px] gap-1 bg-[#F3F4F9] h-[19px] text-gray-300 rounded-[2px]">
24+
<div className="w-[12px] h-[12px] bg-[#EAECF3] rounded-full"></div>
25+
<div className="w-8 h-3 bg-[#EAECF3] rounded-[2px]"></div>
26+
</div>
27+
</section>
2428

25-
{/* 설명 */}
26-
<div className="h-3 mt-3 bg-gray-200 rounded"></div>
27-
<div className="h-3 mt-2 bg-gray-200 rounded w-[70%]"></div>
29+
<div className="w-full h-4 bg-[#F3F4F9] rounded-[2px] mt-[4px]"></div>
30+
<div className="w-3/4 h-4 bg-[#F3F4F9] rounded-[2px] mt-[4px]"></div>
2831
</div>
2932
))}
3033
</div>

src/components/pages/home/index.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const HomeClientPage = () => {
2222
);
2323

2424
return (
25-
<main className="py-5 rounded-[24px] bg-[#FBFCFE] min-h-screen -mt-[20px] z-50 flex flex-col gap-[8px] px-5">
25+
<main className="py-5 rounded-t-[24px] bg-[#FBFCFE] min-h-screen -mt-[20px] z-50 flex flex-col gap-[16px] px-5">
2626
<HomeToggleZone handleToggle={handleToggle} isTrending={isTrending} />
2727
{isTrending === 'trend' ? (
2828
<TrendingPosts />

0 commit comments

Comments
 (0)