From 1630ad399553b004ed724f52f4817f2f573ef776 Mon Sep 17 00:00:00 2001 From: gs0428 Date: Tue, 20 Feb 2024 04:08:18 +0900 Subject: [PATCH] =?UTF-8?q?feature-081:=20=EC=95=8C=EB=A6=BC=EC=B0=BD?= =?UTF-8?q?=EC=97=90=20=EC=98=81=EC=83=81=20=EB=B3=80=ED=99=98=20=EC=83=81?= =?UTF-8?q?=ED=83=9C=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/user.ts | 13 +++--- src/components/Home/SearchYoutube.tsx | 36 ++------------- .../ModelController/ModelController.tsx | 10 +++++ .../layout/header/alarm/AlarmItem.tsx | 43 +++++++++++++++++- .../layout/header/alarm/AlarmList.tsx | 1 + src/components/layout/header/alarm/index.tsx | 20 ++++++++- .../layout/sideBar/ConvertVideo.tsx | 41 +++-------------- src/hooks/useCreateVideo.ts | 44 +++++++++++++++++++ .../layout/header/alarm/AlarmItem.style.ts | 27 ++++++++++++ 9 files changed, 159 insertions(+), 76 deletions(-) create mode 100644 src/hooks/useCreateVideo.ts diff --git a/src/apis/user.ts b/src/apis/user.ts index 3c06989..626280a 100644 --- a/src/apis/user.ts +++ b/src/apis/user.ts @@ -15,7 +15,7 @@ import { FindEmailRequest, FindPasswordResponse, FindPasswordRequest, - CreateVideoAlarmRequest + CreateVideoAlarmRequest, } from '@/models/user'; import { AlarmResponse, @@ -93,11 +93,8 @@ export const createVideoAlarmAPI = ( PREFIX + `/videoAlarm/${videoId}/${status}`, data, ); -} +}; -export const findPasswordAPI = (data : FindPasswordRequest) => { - return axios.post( - PREFIX + '/findPassword', - data - ); -} +export const findPasswordAPI = (data: FindPasswordRequest) => { + return axios.post(PREFIX + '/findPassword', data); +}; diff --git a/src/components/Home/SearchYoutube.tsx b/src/components/Home/SearchYoutube.tsx index 779cb9b..f883f88 100644 --- a/src/components/Home/SearchYoutube.tsx +++ b/src/components/Home/SearchYoutube.tsx @@ -1,8 +1,5 @@ import React, { useState, FormEvent } from 'react'; -import { useNavigate } from 'react-router-dom'; -import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; - -import { createVideoAPI } from '@/apis/videos'; +import { useRecoilState, useSetRecoilState } from 'recoil'; import VideoIcon from '@/assets/icons/video.svg?react'; import WarningIcon from '@/assets/icons/warning.svg?react'; @@ -17,30 +14,26 @@ import { import { recommendationModalState } from '@/stores/modal'; import { - modelingDataState, modelingProgressState, modelingStatusState, videoLinkState, } from '@/stores/model-controller'; -import { userTokenState } from '@/stores/user'; import { validateYoutubeLink } from '@/utils/validation'; import ProgressBar from './ProgressBar'; +import useCreateVideo from '@/hooks/useCreateVideo'; type Props = { searchRef: React.RefObject; }; const SearchYoutube = ({ searchRef }: Props) => { - const navigate = useNavigate(); - - const userToken = useRecoilValue(userTokenState); const setIsOpenModal = useSetRecoilState(recommendationModalState); const setVideoLink = useSetRecoilState(videoLinkState); const setProgress = useSetRecoilState(modelingProgressState); const [status, setStatus] = useRecoilState(modelingStatusState); - const [modelingData, setModelingData] = useRecoilState(modelingDataState); + const { createVideo } = useCreateVideo(); const [inputLink, setInputLink] = useState(''); @@ -98,27 +91,6 @@ const SearchYoutube = ({ searchRef }: Props) => { setProgress(0); }; - const handleClickCreateVideoButton = async () => { - if (!modelingData) return; - - if (userToken) { - try { - const { video_id } = (await createVideoAPI(modelingData)).data.result; - - navigate(`/summary/${video_id}`); - setModelingData(null); - } catch (e) { - console.error(e); - } - } else { - navigate('/summary/guest'); - } - - setVideoLink(null); - setStatus('NONE'); - setProgress(0); - }; - return ( <> @@ -172,7 +144,7 @@ const SearchYoutube = ({ searchRef }: Props) => { color: theme.color.gray500, backgroundColor: theme.color.green400, }} - onClick={handleClickCreateVideoButton} + onClick={createVideo} > 영상 읽기 diff --git a/src/components/common/ModelController/ModelController.tsx b/src/components/common/ModelController/ModelController.tsx index 90de4ea..03c1ca3 100644 --- a/src/components/common/ModelController/ModelController.tsx +++ b/src/components/common/ModelController/ModelController.tsx @@ -49,6 +49,15 @@ const ModelController = () => { setVideoLink(null); }; + const handleUpdateAlarm = async (title: string) => { + await createVideoAlarmAPI(0, 'success', { + title: `[${title}]`, + content: + '영상이 모두 변환되었어요!\n이제 정리 된 영상을 확인하러 가볼까요?', + is_confirm: false, + }); + }; + useEffect(() => { if (!videoLink) return; @@ -89,6 +98,7 @@ const ModelController = () => { setModelingData(finalData); setModelingProgress(100); + handleUpdateAlarm(finalData.title); setModelingStatus('COMPLETE'); } catch (e) { console.error(e); diff --git a/src/components/layout/header/alarm/AlarmItem.tsx b/src/components/layout/header/alarm/AlarmItem.tsx index d026ac1..d7f8b06 100644 --- a/src/components/layout/header/alarm/AlarmItem.tsx +++ b/src/components/layout/header/alarm/AlarmItem.tsx @@ -11,12 +11,21 @@ import ErrorImage from '@/assets/Error.png'; import { Container } from '@/styles/layout/header/alarm/AlarmItem.style'; import { diffTime } from '@/utils/date'; +import { useRecoilValue } from 'recoil'; +import { + modelingProgressState, + modelingStatusState, +} from '@/stores/model-controller'; +import theme from '@/styles/theme'; +import useCreateVideo from '@/hooks/useCreateVideo'; +import { confirmSelectAlarmAPI } from '@/apis/user'; type Props = { alarm: IAlarm; selectIdList: number[]; onUpdateSelectIdList: (list: number[]) => void; onClose: () => void; + onRefresh: () => void; }; const AlarmItem = ({ @@ -24,8 +33,12 @@ const AlarmItem = ({ selectIdList, onUpdateSelectIdList, onClose, + onRefresh, }: Props) => { const navigate = useNavigate(); + const status = useRecoilValue(modelingStatusState); + const progress = useRecoilValue(modelingProgressState); + const { createVideo } = useCreateVideo(); const isSelected = selectIdList.indexOf(alarm.alarm_id) > -1; const type = () => { @@ -58,11 +71,22 @@ const AlarmItem = ({ return `${second}초`; }; - const handleClick = () => { + const handleClick = async () => { if (alarm.type === 'notice') { navigate('/guide'); onClose(); } + if (alarm.type === 'video' && !alarm.is_confirm) { + try { + await confirmSelectAlarmAPI({ alarms: [alarm.alarm_id] }); + + onRefresh(); + createVideo(); + onClose(); + } catch (e) { + console.error(e); + } + } }; const handleClickRemoveButton: React.MouseEventHandler = ( @@ -110,6 +134,23 @@ const AlarmItem = ({ {alarm.content} + {status !== 'NONE' && alarm.alarm_id === 999 && ( +
+
+
+
+ + + {status === 'ERROR' ? '변환 중 오류' : `${progress}%`} + +
+ )} ); }; diff --git a/src/components/layout/header/alarm/AlarmList.tsx b/src/components/layout/header/alarm/AlarmList.tsx index 72336a5..41b5a53 100644 --- a/src/components/layout/header/alarm/AlarmList.tsx +++ b/src/components/layout/header/alarm/AlarmList.tsx @@ -75,6 +75,7 @@ const AlarmList = ({ alarmList, onRefresh, onClose }: Props) => { selectIdList={selectIdList} onUpdateSelectIdList={setSelectIdList} onClose={onClose} + onRefresh={onRefresh} /> ))}
diff --git a/src/components/layout/header/alarm/index.tsx b/src/components/layout/header/alarm/index.tsx index 365177c..66ae127 100644 --- a/src/components/layout/header/alarm/index.tsx +++ b/src/components/layout/header/alarm/index.tsx @@ -40,9 +40,27 @@ const Alarm = ({ isDark }: Props) => { }, []); useEffect(() => { - if (status === 'ERROR') { + if (status === 'ERROR' || status === 'COMPLETE') { + console.log(status, 'dd'); callAPI(); } + + if (status === 'CONTINUE') { + setAlarmList([ + { + state: 'success', + type: 'video', + alarm_id: 999, + title: '열심히 영상을 변환하는 중이에요!', + content: '잠시후 멋진 글을 만날 수 있어요:)', + is_confirm: 0, + created_at: new Date().toString(), + updated_at: new Date().toString(), + }, + ...alarmList, + ]); + } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [status]); return ( diff --git a/src/components/layout/sideBar/ConvertVideo.tsx b/src/components/layout/sideBar/ConvertVideo.tsx index aa5de8d..aee3b06 100644 --- a/src/components/layout/sideBar/ConvertVideo.tsx +++ b/src/components/layout/sideBar/ConvertVideo.tsx @@ -1,8 +1,6 @@ import { useState } from 'react'; -import { useLocation, useNavigate } from 'react-router-dom'; -import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; - -import { createVideoAPI } from '@/apis/videos'; +import { useLocation } from 'react-router-dom'; +import { useRecoilValue, useSetRecoilState } from 'recoil'; import VideoSvg from '@/assets/icons/video.svg?react'; import DownSvg from '@/assets/icons/down.svg?react'; @@ -13,27 +11,23 @@ import { CommonTitle } from '@/styles/layout/sideBar/UserMode.style'; import { recommendationModalState } from '@/stores/modal'; import { - modelingDataState, modelingProgressState, modelingStatusState, videoLinkState, } from '@/stores/model-controller'; -import { userTokenState } from '@/stores/user'; import theme from '@/styles/theme'; import { validateYoutubeLink } from '@/utils/validation'; +import useCreateVideo from '@/hooks/useCreateVideo'; const ConvertVideo = () => { const { pathname } = useLocation(); - const navigate = useNavigate(); - - const userToken = useRecoilValue(userTokenState); const setIsOpenModal = useSetRecoilState(recommendationModalState); const setVideoLink = useSetRecoilState(videoLinkState); - const [status, setStatus] = useRecoilState(modelingStatusState); - const [progress, setProgress] = useRecoilState(modelingProgressState); - const [modelingData, setModelingData] = useRecoilState(modelingDataState); + const status = useRecoilValue(modelingStatusState); + const progress = useRecoilValue(modelingProgressState); + const { createVideo } = useCreateVideo(); const [isOpen, setIsOpen] = useState(false); const [isFocus, setIsFocus] = useState(false); @@ -41,27 +35,6 @@ const ConvertVideo = () => { const isValidate = validateYoutubeLink(url); - const handleClickCreateVideoButton = async () => { - if (!modelingData) return; - - if (userToken) { - try { - const { video_id } = (await createVideoAPI(modelingData)).data.result; - - navigate(`/summary/${video_id}`); - setModelingData(null); - } catch (e) { - console.error(e); - } - } else { - navigate('/summary/guest'); - } - - setVideoLink(null); - setStatus('NONE'); - setProgress(0); - }; - const handleClickStartConvertButton: React.MouseEventHandler< HTMLButtonElement > = (e) => { @@ -112,7 +85,7 @@ const ConvertVideo = () => { color: theme.color.gray500, backgroundColor: theme.color.green400, }} - onClick={handleClickCreateVideoButton} + onClick={createVideo} > start diff --git a/src/hooks/useCreateVideo.ts b/src/hooks/useCreateVideo.ts new file mode 100644 index 0000000..7c93c7b --- /dev/null +++ b/src/hooks/useCreateVideo.ts @@ -0,0 +1,44 @@ +import { createVideoAPI } from '@/apis/videos'; +import { + modelingDataState, + modelingProgressState, + modelingStatusState, + videoLinkState, +} from '@/stores/model-controller'; +import { userTokenState } from '@/stores/user'; +import { useNavigate } from 'react-router-dom'; +import { useRecoilState, useRecoilValue, useSetRecoilState } from 'recoil'; + +const useCreateVideo = () => { + const [modelingData, setModelingData] = useRecoilState(modelingDataState); + const userToken = useRecoilValue(userTokenState); + const setVideoLink = useSetRecoilState(videoLinkState); + const setStatus = useSetRecoilState(modelingStatusState); + const setProgress = useSetRecoilState(modelingProgressState); + const navigate = useNavigate(); + + const createVideo = async () => { + if (!modelingData) return; + + if (userToken) { + try { + const { video_id } = (await createVideoAPI(modelingData)).data.result; + + navigate(`/summary/${video_id}`); + setModelingData(null); + } catch (e) { + console.error(e); + } + } else { + navigate('/summary/guest'); + } + + setVideoLink(null); + setStatus('NONE'); + setProgress(0); + }; + + return { createVideo }; +}; + +export default useCreateVideo; diff --git a/src/styles/layout/header/alarm/AlarmItem.style.ts b/src/styles/layout/header/alarm/AlarmItem.style.ts index 6b9496d..b1eeb0d 100644 --- a/src/styles/layout/header/alarm/AlarmItem.style.ts +++ b/src/styles/layout/header/alarm/AlarmItem.style.ts @@ -1,3 +1,4 @@ +import theme from '@/styles/theme'; import styled from 'styled-components'; export const Container = styled.div` @@ -122,4 +123,30 @@ export const Container = styled.div` } } } + + & div.progress-wrap { + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 4px; + } + + & div.progress-bar { + width: 100%; + height: 8px; + border-radius: 100px; + background-color: ${(props) => props.theme.color.gray100}; + overflow: hidden; + + & > div { + height: 100%; + transition: 1s; + transition-delay: 0.5s; + } + } + + & span.progress-text { + ${theme.typography.Caption3} + color: ${theme.color.gray400}; + } `;