From 66d42fb5bb8f89c89302863b4bbfed53c548db85 Mon Sep 17 00:00:00 2001 From: eunhak Date: Tue, 30 Apr 2024 19:23:54 +0900 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20BottomNav=20UI=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/icons/Board.tsx | 30 +++++++++++ src/components/icons/Channel.tsx | 34 ++++++++++--- src/components/icons/CloseEys.tsx | 7 ++- src/components/icons/Favorite.tsx | 20 ++++++-- src/components/icons/FilledBoard.tsx | 29 +++++++++++ src/components/icons/FilledChannel.tsx | 34 +++++++++++++ src/components/icons/FilledFavorite.tsx | 25 ++++++++-- src/components/icons/FilledHome.tsx | 26 ++++++++-- src/components/icons/Home.tsx | 27 ++++++++-- src/components/icons/Map.tsx | 25 ++++++++-- .../shared/bottom-nav/BottomNav.tsx | 50 ++++++++++++++++--- 11 files changed, 277 insertions(+), 30 deletions(-) create mode 100644 src/components/icons/Board.tsx create mode 100644 src/components/icons/FilledBoard.tsx create mode 100644 src/components/icons/FilledChannel.tsx diff --git a/src/components/icons/Board.tsx b/src/components/icons/Board.tsx new file mode 100644 index 00000000..2241e55d --- /dev/null +++ b/src/components/icons/Board.tsx @@ -0,0 +1,30 @@ +interface FilledFavoriteProps { + width?: number + height?: number +} + +function FilledFavorite({ width = 21, height = 19 }: FilledFavoriteProps) { + return ( + + + + + + + ); +} + +export default FilledFavorite; diff --git a/src/components/icons/Channel.tsx b/src/components/icons/Channel.tsx index 25669aa7..2e97eba8 100644 --- a/src/components/icons/Channel.tsx +++ b/src/components/icons/Channel.tsx @@ -1,18 +1,38 @@ -import { Colors, colors } from '@styles/colorPalette'; - interface ChannelProps { width?: number height?: number - color?: Colors } -function Channel({ width = 19, height = 18, color = 'black' }: ChannelProps) { +function Channel({ width = 19, height = 18 }: ChannelProps) { return ( - - + + + + + + + - ); + + // return ( + // + // + // + + // ); } export default Channel; diff --git a/src/components/icons/CloseEys.tsx b/src/components/icons/CloseEys.tsx index 75716af9..81142cd9 100644 --- a/src/components/icons/CloseEys.tsx +++ b/src/components/icons/CloseEys.tsx @@ -1,3 +1,4 @@ +/* eslint-disable max-len */ import { colors } from '@styles/colorPalette'; interface CloseEyeProps { @@ -14,7 +15,11 @@ function CloseEys({ size, onClick }: CloseEyeProps) { xmlns="http://www.w3.org/2000/svg" onClick={onClick} > - + ); } diff --git a/src/components/icons/Favorite.tsx b/src/components/icons/Favorite.tsx index 3589c7f9..784543aa 100644 --- a/src/components/icons/Favorite.tsx +++ b/src/components/icons/Favorite.tsx @@ -5,11 +5,25 @@ interface FavoriteProps { function Favorite({ width = 21, height = 19 }: FavoriteProps) { return ( - - + + + + - ); + // return ( + // + // + // + + // ); } export default Favorite; diff --git a/src/components/icons/FilledBoard.tsx b/src/components/icons/FilledBoard.tsx new file mode 100644 index 00000000..f80ebdc0 --- /dev/null +++ b/src/components/icons/FilledBoard.tsx @@ -0,0 +1,29 @@ +/* eslint-disable max-len */ +interface FilledFavoriteProps { + width?: number + height?: number +} + +function FilledFavorite({ width = 21, height = 19 }: FilledFavoriteProps) { + return ( + + + + + + ); +} + +export default FilledFavorite; diff --git a/src/components/icons/FilledChannel.tsx b/src/components/icons/FilledChannel.tsx new file mode 100644 index 00000000..b1142df0 --- /dev/null +++ b/src/components/icons/FilledChannel.tsx @@ -0,0 +1,34 @@ +/* eslint-disable max-len */ +interface FilledFavoriteProps { + width?: number + height?: number +} + +function FilledFavorite({ width = 21, height = 19 }: FilledFavoriteProps) { + return ( + + + + + + + + + ); +} + +export default FilledFavorite; diff --git a/src/components/icons/FilledFavorite.tsx b/src/components/icons/FilledFavorite.tsx index ecad9ad3..541ab8f0 100644 --- a/src/components/icons/FilledFavorite.tsx +++ b/src/components/icons/FilledFavorite.tsx @@ -5,11 +5,30 @@ interface FilledFavoriteProps { function FilledFavorite({ width = 21, height = 19 }: FilledFavoriteProps) { return ( - - + + + + - ); + // return ( + // + // + // + + // ); } export default FilledFavorite; diff --git a/src/components/icons/FilledHome.tsx b/src/components/icons/FilledHome.tsx index 0db1c178..ffe9cecd 100644 --- a/src/components/icons/FilledHome.tsx +++ b/src/components/icons/FilledHome.tsx @@ -5,11 +5,31 @@ interface HomeProps { function FilledHome({ width = 16, height = 20 }: HomeProps) { return ( - - + + + + - ); + + // return ( + // + // + // + + // ); } export default FilledHome; diff --git a/src/components/icons/Home.tsx b/src/components/icons/Home.tsx index 543fc88b..cf0c2ece 100644 --- a/src/components/icons/Home.tsx +++ b/src/components/icons/Home.tsx @@ -5,11 +5,32 @@ interface HomeProps { function Home({ width = 16, height = 20 }: HomeProps) { return ( - - + + + + - ); + + // return ( + // + // + // + + // ); } export default Home; diff --git a/src/components/icons/Map.tsx b/src/components/icons/Map.tsx index 442997fb..bcdf70b3 100644 --- a/src/components/icons/Map.tsx +++ b/src/components/icons/Map.tsx @@ -7,11 +7,30 @@ interface MapProps { } function Map({ width = 17, height = 20, color = 'black' }: MapProps) { + // return ( + // + // + // + + // ); return ( - - + + + + - ); } diff --git a/src/components/shared/bottom-nav/BottomNav.tsx b/src/components/shared/bottom-nav/BottomNav.tsx index 44eb0dd2..671c52ac 100644 --- a/src/components/shared/bottom-nav/BottomNav.tsx +++ b/src/components/shared/bottom-nav/BottomNav.tsx @@ -1,9 +1,13 @@ 'use client'; +/* eslint-disable @typescript-eslint/no-unused-vars */ import classNames from 'classnames/bind'; import Link from 'next/link'; import { usePathname } from 'next/navigation'; +import Board from '@/components/icons/Board'; +import FilledBoard from '@/components/icons/FilledBoard'; +import FilledChannel from '@/components/icons/FilledChannel'; import Channel from '@components/icons/Channel'; import Favorite from '@components/icons/Favorite'; import FilledFavorite from '@components/icons/FilledFavorite'; @@ -28,31 +32,63 @@ function BottomNav() {
  • - 지도 + + 지도 +
  • {filteredPathName.startsWith('favorite') ? : } - 즐겨찾기 + + 즐겨찾기 +
  • {filteredPathName === '' ? : } - + + 홈 +
  • - - 채널 + {filteredPathName === 'channel' ? : } + {/* */} + + 채널 +
  • - - 프로필 + {filteredPathName === 'my-page' ? : } + {/* */} + + 프로필 +
  • From 7510ff3305e7e6913825b316ab7ef322e59e382c Mon Sep 17 00:00:00 2001 From: eunhak Date: Tue, 30 Apr 2024 19:25:09 +0900 Subject: [PATCH 2/5] =?UTF-8?q?chore:=20eslint=20=EA=B7=9C=EC=B9=99=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.eslintrc.json b/.eslintrc.json index 3dbcca1c..6995777a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -45,7 +45,9 @@ "*.ts", "*.tsx" ], - "rules": {} + "rules": { + "max-len": ["error", { "code": 2000 }] + } }, { "files": [ From a16be5c143e23ec27662949e0bad440469a73e65 Mon Sep 17 00:00:00 2001 From: eunhak Date: Wed, 8 May 2024 18:32:53 +0900 Subject: [PATCH 3/5] =?UTF-8?q?fix:=20=EC=84=A4=EC=A0=95=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .eslintrc.json | 73 +++++++++++++++++++++++++------------------------- .prettierrc | 13 +++++++++ next.config.js | 17 +++++++----- 3 files changed, 60 insertions(+), 43 deletions(-) create mode 100644 .prettierrc diff --git a/.eslintrc.json b/.eslintrc.json index 6995777a..b08f2bca 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -119,42 +119,43 @@ "ts": "never", "tsx": "never" } - ], // import 구문에서 파일 확장자를 생략해야합니다. 패키지를 import할 때는 이 규칙을 무시합니다. - "import/order": [ // import 순서를 지정합니다. - "error", - { - "groups": [ // 먼저 해당 groups의 순서를 갖습니다. - "type", - "builtin", - "external", - "internal", - "parent", - "sibling", - "index", - "unknown" - ], - "pathGroups": [ // 기본 groups 외 특정 순서를 지정할 수 있습니다. - { - "pattern": "react*", - "group": "external", - "position": "before" - }, - { - "pattern": "@/public/images/*", - "group": "unknown", - "position": "after" - } - ], - "pathGroupsExcludedImportTypes": [ - "type" - ], // type 종류는 pathGroups를 적용하지 않습니다. - "newlines-between": "always", // import grouping 간에 한 줄을 추가합니다. - "alphabetize": { // 같은 grouping 내에선 알파벳 순서를 따릅니다. - "order": "asc", - "caseInsensitive": true - } - } - ], + ], + // // import 구문에서 파일 확장자를 생략해야합니다. 패키지를 import할 때는 이 규칙을 무시합니다. + // "import/order": [ // import 순서를 지정합니다. + // "error", + // { + // "groups": [ // 먼저 해당 groups의 순서를 갖습니다. + // "type", + // "builtin", + // "external", + // "internal", + // "parent", + // "sibling", + // "index", + // "unknown" + // ], + // "pathGroups": [ // 기본 groups 외 특정 순서를 지정할 수 있습니다. + // { + // "pattern": "react*", + // "group": "external", + // "position": "before" + // }, + // { + // "pattern": "@/public/images/*", + // "group": "unknown", + // "position": "after" + // } + // ], + // "pathGroupsExcludedImportTypes": [ + // "type" + // ], // type 종류는 pathGroups를 적용하지 않습니다. + // "newlines-between": "always", // import grouping 간에 한 줄을 추가합니다. + // "alphabetize": { // 같은 grouping 내에선 알파벳 순서를 따릅니다. + // "order": "asc", + // "caseInsensitive": true + // } + // } + // ], "import/no-extraneous-dependencies": [ // 아래 devDependencies 내 파일들은 해당 룰에 영향을 받지 않습니다. "error", { diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..12093224 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,13 @@ +{ + "semi": true, + "trailingComma": "all", + "singleQuote": true, + "printWidth": 90, + "tabWidth": 2, + "jsxBracketSameLine": false, + "objectCurlySpacing": true, + "endOfLine": "auto", + "bracketSpacing": true, + "arrowParens": "avoid", + "proseWrap": "always" +} diff --git a/next.config.js b/next.config.js index fd0832a5..e841dcd2 100644 --- a/next.config.js +++ b/next.config.js @@ -1,11 +1,14 @@ /** @type {import('next').NextConfig} */ -const path = require('path') +const path = require('path'); module.exports = { output: 'standalone', sassOptions: { includePaths: [path.join(__dirname, 'styles')], }, + images: { + domains: ['i.ytimg.com', 'yt3.ggpht.com'], + }, // async rewrites() { // return [ // { @@ -17,17 +20,17 @@ module.exports = { webpack: (config, context) => { if (context?.isServer) { if (Array.isArray(config.resolve.alias)) { - config.resolve.alias.push({ name: "msw/browser", alias: false }) + config.resolve.alias.push({ name: 'msw/browser', alias: false }); } else { - config.resolve.alias["msw/browser"] = false + config.resolve.alias['msw/browser'] = false; } } else { if (Array.isArray(config.resolve.alias)) { - config.resolve.alias.push({ name: "msw/node", alias: false }) + config.resolve.alias.push({ name: 'msw/node', alias: false }); } else { - config.resolve.alias["msw/node"] = false + config.resolve.alias['msw/node'] = false; } } - return config + return config; }, -} \ No newline at end of file +}; From 26045a69f96cd7d1ae36d66b966f6cad0988349f Mon Sep 17 00:00:00 2001 From: eunhak Date: Fri, 10 May 2024 14:13:42 +0900 Subject: [PATCH 4/5] =?UTF-8?q?fix:=20=EC=9C=A0=ED=8A=9C=EB=B8=8C=20?= =?UTF-8?q?=EC=B1=84=EB=84=90=20UI=20+=20=EA=B8=B0=EB=8A=A5=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/channel/page.module.scss | 61 ++++++++-- src/app/channel/page.tsx | 114 ++++++++++++++---- .../ChannelArticle.module.scss | 18 ++- .../shared/channel-article/ChannelArticle.tsx | 69 +++++++---- .../channel/channelprofile.get.api.ts | 20 +++ src/remote/api/types/channel.ts | 50 ++++---- src/remote/queries/channel/useChannel.ts | 39 ++++++ src/stores/hooks.ts | 2 +- 8 files changed, 290 insertions(+), 83 deletions(-) create mode 100644 src/remote/api/requests/channel/channelprofile.get.api.ts create mode 100644 src/remote/queries/channel/useChannel.ts diff --git a/src/app/channel/page.module.scss b/src/app/channel/page.module.scss index ccabf9a5..d4137015 100644 --- a/src/app/channel/page.module.scss +++ b/src/app/channel/page.module.scss @@ -1,16 +1,32 @@ .headerTitleWrapper { + display: flex; box-sizing: border-box; - padding: 23px var(--default-padding) 12px; + flex-direction: row; + padding: 23px var(--default-padding) 2px; + + .left { + margin: 7px 10px 0 0; + } } .mainContainer { + position: relative; margin-bottom: 76px; + .headerFix { + position: sticky; + z-index: 100; + top: 0; + right: 0; + left: 0; + background-color: white; + } + .channelList { display: flex; box-sizing: border-box; margin: 12px 0; - padding: 0 var(--default-padding); + padding: 0 4px; overflow: auto; white-space: nowrap; gap: 5px; @@ -19,18 +35,45 @@ display: none; } + .channelImage { + display: block; + width: 50px; + height: 50px; + border-radius: 50%; + } + + .imageNone { + display: none; + } + .channelButton { padding: 5px 10px; + padding-bottom: 10px; transition: 0.15s all ease-out; - border: 1px solid var(--primary200); - border-radius: 15px; - color: var(--primary200); - font-size: 12px; + + // border: 1px solid var(--primary200); + // border-radius: 15px; + color: var(--gray500); + font-size: 16px; &.selected { - border-color: var(--primary400); - background-color: var(--primary400); - color: var(--white); + position: relative; + + // border-bottom: 2px solid var(--primary500); + &::after { + content: ""; + position: absolute; + bottom: 0; + left: 50%; // 가운데 정렬 + width: 80%; // 테두리의 길이를 조정 + height: 2px; + transform: translateX(-50%); // 가운데 정렬 + background-color: var(--primary500); + } + + // border-color: var(--primary400); + // background-color: var(--primary400); + color: var(--primary500); } } } diff --git a/src/app/channel/page.tsx b/src/app/channel/page.tsx index 24198e80..0ea1cc43 100644 --- a/src/app/channel/page.tsx +++ b/src/app/channel/page.tsx @@ -1,21 +1,27 @@ /* eslint-disable react/no-array-index-key */ +/* eslint-disable jsx-a11y/click-events-have-key-events */ +/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */ + 'use client'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import classNames from 'classnames/bind'; +import LeftIcon from '@/components/shared/header/headerItems/LeftIcon'; import Title from '@components/shared/title/Title'; import useYoutubeList from '@remote/queries/channel/useYoutubeList'; import BottomNav from '@shared/bottom-nav/BottomNav'; import ChannelArticle from '@shared/channel-article/ChannelArticle'; - +import useChannel from '@/remote/queries/channel/useChannel'; +import Image from 'next/image'; +import Flex from '@/components/shared/flex/Flex'; import styles from './page.module.scss'; const cx = classNames.bind(styles); -const CHANNEL_LIST = { +export const CHANNEL_LIST = { DetailWizard: 'UCJM63e_MydEL2o6dMuJ_teQ', ShineFreak: 'UCoqiH2Ce3qc8wr_t2GvIWvw', autogrm: 'UCKUHhKTlNTHRlwbjoFDKOfA', @@ -27,34 +33,98 @@ function ChannelPage() { const [selectedChannel, setSelectedChannel] = useState(Object.values(CHANNEL_LIST)[0]); const { data: dataList, isError } = useYoutubeList(selectedChannel); + const [scroll, setScroll] = useState(true); const handleChannel = (channelId: string) => { setSelectedChannel(channelId); }; + const { data: channelList1 } = useChannel('UCJM63e_MydEL2o6dMuJ_teQ'); + const { data: channelList2 } = useChannel('UCoqiH2Ce3qc8wr_t2GvIWvw'); + const { data: channelList3 } = useChannel('UCKUHhKTlNTHRlwbjoFDKOfA'); + const { data: channelList4 } = useChannel('UCHVO8oWoMCIIdQHXUB32X1w'); + const { data: channelList5 } = useChannel('UCB22mXLQeRlCPn-H000OxYg'); + + const channelList = [ + channelList1, + channelList2, + channelList3, + channelList4, + channelList5, + ]; + + // console.log(channelList); + const [lastScroll, setLastScroll] = useState(0); + + useEffect(() => { + const checkScrollTop = () => { + const currentScroll = document.documentElement.scrollTop; + if (currentScroll > 100 && lastScroll <= 100) { + setScroll(false); + setLastScroll(currentScroll); + } else if (currentScroll <= 100 && lastScroll > 100) { + setScroll(true); + setLastScroll(currentScroll); + } + }; + + const intervalId = setInterval(checkScrollTop, 100); // 1초에 한 번씩 checkScrollTop 함수를 호출 + + return () => { + clearInterval(intervalId); // 컴포넌트가 언마운트될 때 타이머를 정리 + }; + }, [lastScroll]); return ( <> -
    - - </div> <main className={cx('mainContainer')}> - <ul className={cx('channelList')}> - {Object.entries(CHANNEL_LIST).map(([channelName, channelId], idx) => { - return ( - <li key={idx}> - <button aria-label="채널 버튼" className={cx('channelButton', selectedChannel === channelId ? 'selected' : '')} onClick={() => { return handleChannel(channelId); }}> - {channelName} - </button> - </li> - ); - })} - </ul> - - {isError && (<div className={cx('error')}>서버요청을 초과하였습니다.</div>)} + <div className={cx('headerFix')}> + <div className={cx('headerTitleWrapper')}> + <LeftIcon className={cx('left')} type="search" /> + <Title title="추천 페이지" titleSize="t4" /> + </div> + <ul className={cx('channelList')}> + {Object.entries(CHANNEL_LIST).map(([channelName, channelId], idx) => { + return ( + <li + onClick={() => { + return handleChannel(channelId); + }} + key={idx} + > + <Flex direction="column" align="center" justify="center"> + <Image + className={cx(scroll ? 'channelImage' : 'imageNone')} + src={ + channelList[idx]?.items[0]?.snippet.thumbnails.high.url as string + } + alt="유튜브 채널 썸네일" + width={320} + height={180} + layout="cover" + objectFit="responsive" + /> + <button + aria-label="채널 버튼" + className={cx( + 'channelButton', + selectedChannel === channelId ? 'selected' : '', + )} + onClick={() => { + return handleChannel(channelId); + }} + > + {channelName} + </button> + </Flex> + </li> + ); + })} + </ul> + </div> + <div>{/* {channelList?.items?.thumbnails.high.url} */}</div> + {isError && <div className={cx('error')}>서버요청을 초과하였습니다.</div>} {dataList?.items?.map((data, idx) => { - return ( - <ChannelArticle key={idx} data={data} /> - ); + return <ChannelArticle key={idx} data={data} />; })} </main> <BottomNav /> diff --git a/src/components/shared/channel-article/ChannelArticle.module.scss b/src/components/shared/channel-article/ChannelArticle.module.scss index 70ef07ed..aab2b6e4 100644 --- a/src/components/shared/channel-article/ChannelArticle.module.scss +++ b/src/components/shared/channel-article/ChannelArticle.module.scss @@ -1,17 +1,29 @@ $img-size: 39px; .article { + // display: flex; + // flex-direction: column; + // align-items: center; + // justify-self: center; + width: 100%; height: 100%; + margin-bottom: 20px; } .videoContainer { - aspect-ratio: 16 / 9; + // aspect-ratio: 16 / 9; + width: 100%; + height: 20vh; .video { - width: 100%; + width: 90%; height: 100%; - vertical-align: top; + margin-left: 20px; + border-radius: 16px; + object-fit: cover; + + // vertical-align: top; } } diff --git a/src/components/shared/channel-article/ChannelArticle.tsx b/src/components/shared/channel-article/ChannelArticle.tsx index e6d122d5..e98015e5 100644 --- a/src/components/shared/channel-article/ChannelArticle.tsx +++ b/src/components/shared/channel-article/ChannelArticle.tsx @@ -1,7 +1,8 @@ -import YouTube from 'react-youtube'; +// import YouTube from 'react-youtube'; import classNames from 'classnames/bind'; -// import Image from 'next/image'; +import Image from 'next/image'; +import Link from 'next/link'; import { IVideo } from '@remote/api/types/channel'; import Text from '@shared/text/Text'; @@ -15,23 +16,39 @@ interface ChannelArticleProps { const cx = classNames.bind(styles); -const VIDEO_OPTS = { - width: '100%', - height: '100%', - playerVars: { - autoplay: 0, - }, -}; +// const VIDEO_OPTS = { +// width: '100%', +// height: '100%', +// playerVars: { +// autoplay: 0, +// }, +// }; function ChannelArticle({ data }: ChannelArticleProps) { + // console.log(data.id.videoId); return ( <article className={cx('article')}> - <div className={cx('videoContainer')}> - <YouTube className={cx('video')} videoId={data.id.videoId} opts={VIDEO_OPTS} /> - </div> - <div className={cx('videoInfoContainer')}> - {/* <div className={cx('imgBox')}> - <Image + <div> + <Link + className={cx('videoContainer')} + href={`https://www.youtube.com/watch?v=${data.id.videoId}`} + > + <div className={cx('videoContainer')}> + <Image + className={cx('video')} + src={data.snippet.thumbnails.high.url} + alt="유튜브 동영상" + width={320} + height={150} + layout="cover" + objectFit="responsive" + /> + {/* <YouTube className={cx('video')} videoId={data.id.videoId} opts={VIDEO_OPTS} /> */} + </div> + </Link> + <div className={cx('videoInfoContainer')}> + {/* <div className={cx('imgBox')}> + <img src={data.profile} alt="유튜버 프로필 이미지" width={0} @@ -39,15 +56,21 @@ function ChannelArticle({ data }: ChannelArticleProps) { layout="responsive" /> </div> */} - <div className={cx('infoBox')}> - <Text className="ellipsis" typography="t6" whiteSpace="nowrap"> - {data.snippet.title} - </Text> - <Text className="ellipsis" typography="t7" color="gray300" whiteSpace="nowrap"> - {data.snippet.description} - </Text> + <div className={cx('infoBox')}> + <Text className="ellipsis" typography="t6" whiteSpace="nowrap"> + {data.snippet.title} + </Text> + <Text + className="ellipsis" + typography="t7" + color="gray300" + whiteSpace="nowrap" + > + {data.snippet.description} + </Text> + </div> + <ChannelMoreBtn /> </div> - <ChannelMoreBtn /> </div> </article> ); diff --git a/src/remote/api/requests/channel/channelprofile.get.api.ts b/src/remote/api/requests/channel/channelprofile.get.api.ts new file mode 100644 index 00000000..f9102cd7 --- /dev/null +++ b/src/remote/api/requests/channel/channelprofile.get.api.ts @@ -0,0 +1,20 @@ +import axios from 'axios'; +/* eslint-disable @typescript-eslint/no-unsafe-return */ + +import { IVideoList } from '../../types/channel'; + +const getChannelProfile = async (id: string, part = 'snippet') => { + const response = await axios.get<IVideoList>( + `${process.env.NEXT_PUBLIC_YOUTUBE_BASE_URL}/channels`, + { + params: { + id, + part, + key: process.env.NEXT_PUBLIC_YOUTUBE_API_KEY, + }, + }, + ); + return response.data; +}; + +export default getChannelProfile; diff --git a/src/remote/api/types/channel.ts b/src/remote/api/types/channel.ts index 3b9010d2..3c392f8a 100644 --- a/src/remote/api/types/channel.ts +++ b/src/remote/api/types/channel.ts @@ -1,38 +1,38 @@ export interface IVideoList { - kind: string - etag: string - nextPageToken: string - regionCode: string + kind: string; + etag: string; + nextPageToken: string; + regionCode: string; pageInfo: { - totalResults: number - resultsPerPage: number - } - items: IVideo[] + totalResults: number; + resultsPerPage: number; + }; + items: IVideo[]; } export interface IVideo { - kind: string - etag: string + kind: string; + etag: string; id: { - kind: string - videoId: string - } - snippet: IVideoInfo + kind: string; + videoId: string; + }; + snippet: IVideoInfo; } export interface IVideoInfo { - publishedAt: string - channelId: string - title: string - description: string + publishedAt: string; + channelId: string; + title: string; + description: string; thumbnails: { - default: IThumbnail - medium: IThumbnail - high: IThumbnail - } - channelTitle: string - liveBroadcastContent: string - publishTime: string + default: IThumbnail; + medium: IThumbnail; + high: IThumbnail; + }; + channelTitle: string; + liveBroadcastContent: string; + publishTime: string; } export interface IThumbnail { diff --git a/src/remote/queries/channel/useChannel.ts b/src/remote/queries/channel/useChannel.ts new file mode 100644 index 00000000..23e0c913 --- /dev/null +++ b/src/remote/queries/channel/useChannel.ts @@ -0,0 +1,39 @@ +import { useQuery } from '@tanstack/react-query'; + +import getChannelProfile from '@/remote/api/requests/channel/channelprofile.get.api'; +import { IVideoList } from '@/remote/api/types/channel'; + +const useChannel = (id: string) => { + return useQuery<IVideoList>({ + queryKey: ['channelList', id], + queryFn: () => { + return getChannelProfile(id); + }, + staleTime: 86400000, + cacheTime: 86400000, + }); +}; + +export default useChannel; + +// // import { useQuery } from '@tanstack/react-query'; +// import { useQueries } from '@tanstack/react-query'; +// import getChannelProfile from '@/remote/api/requests/channel/channelprofile.get.api'; +// import { CHANNEL_LIST } from '@/app/channel/page'; + +// const useChannel = () => { +// return useQueries({ +// queries: Object.values(CHANNEL_LIST).map((value, index) => { +// return { +// queryKey: ['channel', index], +// queryFn: () => { +// return getChannelProfile(value); +// }, +// staleTime: 86400000, +// cacheTime: 86400000, +// }; +// }), +// }); +// }; + +// export default useChannel; diff --git a/src/stores/hooks.ts b/src/stores/hooks.ts index f6ed3e7f..add93280 100644 --- a/src/stores/hooks.ts +++ b/src/stores/hooks.ts @@ -1,7 +1,7 @@ -import type { RootState, AppDispatch, AppStore } from './store'; import type { TypedUseSelectorHook } from 'react-redux'; import { useDispatch, useSelector, useStore } from 'react-redux'; +import type { RootState, AppDispatch, AppStore } from './store'; // Use throughout your app instead of plain `useDispatch` and `useSelector` export const useAppDispatch: () => AppDispatch = useDispatch; From 213bf7abc382255f430e3469a3b5ee5f0fcb435a Mon Sep 17 00:00:00 2001 From: eunhak <akfmzh1207@naver.com> Date: Fri, 10 May 2024 14:17:24 +0900 Subject: [PATCH 5/5] =?UTF-8?q?style:=20=EC=98=A4=ED=83=80=20=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/shared/header/headerItems/LeftIcon.tsx | 4 ++-- src/components/shared/home-search-bar/HomeSearchBar.tsx | 7 ------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/components/shared/header/headerItems/LeftIcon.tsx b/src/components/shared/header/headerItems/LeftIcon.tsx index 5994ffd7..9085d888 100644 --- a/src/components/shared/header/headerItems/LeftIcon.tsx +++ b/src/components/shared/header/headerItems/LeftIcon.tsx @@ -2,7 +2,7 @@ import Link from 'next/link'; -import Inquire from '@/components/icons/Inquire'; +// import Inquire from '@/components/icons/Inquire'; import BackArrow from '@components/icons/BackArrow'; // import Inquire from '@components/icons/Inquire'; import Logo from '@components/icons/Logo'; @@ -30,7 +30,7 @@ function LeftIcon({ href="https://docs.google.com/forms/d/e/1FAIpQLSd96McWLLj3bR-3OEEIx656x3I_9-u7ifjwViQKlveIRb5QiA/viewform" target="_blank" > - <Inquire /> + {/* <Inquire /> */} </Link> </li> </Flex> diff --git a/src/components/shared/home-search-bar/HomeSearchBar.tsx b/src/components/shared/home-search-bar/HomeSearchBar.tsx index 1b1a789f..f2267c6f 100644 --- a/src/components/shared/home-search-bar/HomeSearchBar.tsx +++ b/src/components/shared/home-search-bar/HomeSearchBar.tsx @@ -51,13 +51,6 @@ const HomeSearchBar = forwardRef<HTMLInputElement, SearchBarProps>( } else { console.error('Invalid file'); } - // const file = FileRef?.current?.files?.[0]; - // if (file instanceof File) { - // const text = await NodeImg({ file }); - // console.log(text); - // } - - // console.log(typeof FileRef?.current?.files?.[0]); } catch (error) { console.error(error); alert('이미지를 불러오는데 실패했습니다.');