diff --git a/index.html b/index.html index 8769ef3..5b45a52 100644 --- a/index.html +++ b/index.html @@ -17,6 +17,11 @@
- + + diff --git a/package.json b/package.json index 0a19650..70b7584 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "@types/jest": "^29.5.11", "@types/node": "^20.11.17", "@types/react-modal": "^3.16.3", + "@types/youtube": "^0.0.50", "axios": "^1.6.4", "lodash": "^4.17.21", "react": "^18.2.0", diff --git a/src/assets/icons/pause.svg b/src/assets/icons/pause.svg new file mode 100644 index 0000000..d30f950 --- /dev/null +++ b/src/assets/icons/pause.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/Home/SearchYoutube.tsx b/src/components/Home/SearchYoutube.tsx index 0ef4b36..779cb9b 100644 --- a/src/components/Home/SearchYoutube.tsx +++ b/src/components/Home/SearchYoutube.tsx @@ -91,6 +91,13 @@ const SearchYoutube = ({ searchRef }: Props) => { } }; + const handleChangeInput: React.ChangeEventHandler = (e) => { + setInputLink(e.target.value); + setVideoLink(null); + setStatus('NONE'); + setProgress(0); + }; + const handleClickCreateVideoButton = async () => { if (!modelingData) return; @@ -153,7 +160,7 @@ const SearchYoutube = ({ searchRef }: Props) => { type="text" value={inputLink} disabled={status === 'CONTINUE'} - onChange={(e) => setInputLink(e.target.value)} + onChange={handleChangeInput} placeholder="https://youtube.com/..." /> diff --git a/src/components/SummaryPage/SummaryDetailBox/SummaryDetailBox.tsx b/src/components/SummaryPage/SummaryDetailBox/SummaryDetailBox.tsx index 4714388..8358d15 100644 --- a/src/components/SummaryPage/SummaryDetailBox/SummaryDetailBox.tsx +++ b/src/components/SummaryPage/SummaryDetailBox/SummaryDetailBox.tsx @@ -1,4 +1,5 @@ -import { useRecoilState, useRecoilValue } from 'recoil'; +import { useEffect, useRef } from 'react'; +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; import { updateVideoCategoryIdAPI } from '@/apis/videos'; @@ -6,8 +7,10 @@ import { IVideo } from '@/models/video'; import { summaryIsEditingViewState, + summaryPlaySubHeadingIdState, summaryUpdateVideoState, summaryVideoState, + summaryVideoTimeState, } from '@/stores/summary'; import { toastListState } from '@/stores/toast'; @@ -24,9 +27,15 @@ type Props = { }; const SummaryDetailBox = ({ onRefresh }: Props) => { + const player = useRef(); + const summaryVideo = useRecoilValue(summaryVideoState) as IVideo; const summaryUpdateVideo = useRecoilValue(summaryUpdateVideoState); + const setSummaryVideoTime = useSetRecoilState(summaryVideoTimeState); const isEditingView = useRecoilValue(summaryIsEditingViewState); + const [playSubHeadingId, setPlaySubHeadingId] = useRecoilState( + summaryPlaySubHeadingIdState, + ); const [toastList, setToastList] = useRecoilState(toastListState); const subHeading = isEditingView @@ -50,6 +59,75 @@ const SummaryDetailBox = ({ onRefresh }: Props) => { } }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const handleMessage = (e: any) => { + if (e.origin === 'https://www.youtube.com') { + try { + const { info } = JSON.parse(e.data); + + if (!info) return; + + setPlaySubHeadingId((id) => { + const item = subHeading.find((s) => s.id === id); + + // END or PAUSE + if ([0, 2].includes(info.playerState)) { + return -1; + } + + if (item) { + if ( + item.start_time > info.currentTime || + info.currentTime > item.end_time + ) + return -1; + } + + return id; + }); + + setSummaryVideoTime(info.currentTime); + } catch (e) { + console.error(e); + } + } + }; + + useEffect(() => { + if (player.current) return; + + player.current = new YT.Player('player', { + videoId: summaryVideo.youtube_id, + }); + + window.onmessage = handleMessage; + + return () => { + setSummaryVideoTime(0); + setPlaySubHeadingId(-1); + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [summaryVideo]); + + useEffect(() => { + if (player.current) { + if (playSubHeadingId > -1) { + const item = subHeading.find((s) => s.id === playSubHeadingId); + + if (item) { + player.current.seekTo(item.start_time, true); + player.current.playVideo(); + } + } else if (playSubHeadingId === -2) { + // 영상 멈추기 + player.current.pauseVideo(); + + setPlaySubHeadingId(-1); + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [playSubHeadingId]); + return (
{ ))}
-