요구사항
1.강의 동영상을 보면서 퀴즈가 나타나야 합니다.
2.강의시간은 강의 정보가 담긴 배열에 담겨있으며 강의가 10% 이상 진행 된 뒤 나타나야 합니다.
3.퀴즈는 강의가 끝나기 이전에 등장해야하며, 강의 종료시간 10% 미만에은 등장하지 않아야 합니다.
4.퀴즈의 등장시간은 랜덤한 시간대로 출현하거나 또는 사용자가 정한 고정 시간대에 등장할 수 있어야 합니다.
5.퀴즈의 간격은 총 강의 시간의 10% 이상이어야 합니다. 10분 분량의 강의에 3분이 될 때 퀴즈가 출현했다면, 3분에서 4분 사이에는 퀴즈가 나타나지 않아야 합니다.
6.퀴즈는 총 '알람', '객관식', 'OX'가 있습니다.
7.정답을 제출한 퀴즈는 다시 강의를 볼 때 나타나지 않아야 합니다.
import { IQuiz, IQuizTime } from "@layouts/Lesson/LessonContentVideo";
export default function getRandomInteger(quiz: IQuiz[],currentLessonPlayTime: number) {
const quizTimeSet = new Set<number>();
function getRandomInt(currentLessonPlayTime: number) {
const quizTerm = Math.floor(currentLessonPlayTime * 0.1);
// currentLessonPlayTime의 10% 미만이어야 함. 추후 상수 quizTerm은 유저의 입력값으로 변경 필요.
const num = Math.floor( Math.random() * (currentLessonPlayTime - quizTerm));
let flag = true;
// currentLessonPlayTime의 10% 미만이어야 함. 추후 상수 quizTerm은 유저의 입력값으로 변경 필요.
for(let i = 0; i < quizTerm; i ++) {
if(quizTimeSet.has(num + i) || quizTimeSet.has(num - i)) {
flag = false;
break;
}
}
// currentLessonPlayTime의 10% 미만이어야 함. 추후 상수 quizTerm은 유저의 입력값으로 변경 필요.
if(flag && num > quizTerm){
quizTimeSet.add(num);
return num
} else {
return getRandomInt(currentLessonPlayTime)
}
}
const newQuiz = quiz.map((q: IQuiz): IQuizTime => {
if(q.randomTime === true){
const randomInt = getRandomInt(currentLessonPlayTime);
return {
...q,
quizOccurTime: randomInt,
isSolvedQuiz: false
}
}
else {
quizTimeSet.add(q.setTimeMin * 60 + q.setTimeSecond);
return {
...q,
quizOccurTime : q.setTimeMin * 60 + q.setTimeSecond,
isSolvedQuiz: false
};
}
});
getRandomInt(currentLessonPlayTime)
return newQuiz;
}
export default function LessonContentVideo(props: Props) {
const [occurTimeQuiz, setOccurTimeQuiz] = useState<IQuizTime[] | []>([]);
const [currentQuiz, setCurrentQuiz] = useState<IQuizTime | null>(null);
// 시간이 되면 퀴즈를 출력한 뒤 로컬스토리지에 담긴 퀴즈를 삭제한다.
useEffect(() => {
const flag = setInterval(() => {
occurTimeQuiz.forEach((quiz:IQuizTime) => {
if (!quiz.isSolvedQuiz && quiz.quizOccurTime === videoCurrentSeconds.current) {
videoPlayer.current?.pause();
setCurrentQuiz(quiz);
const quizOccurTime = JSON.parse(localStorage.getItem(lessonSeq + ''));
const filteredQuiz = quizOccurTime.filter((item:IQuizTime) => item.quizOccurTime !== quiz.quizOccurTime);
localStorage.setItem(lessonSeq as string, JSON.stringify(filteredQuiz));
setOccurTimeQuiz(filteredQuiz);
}
})
},1000)
return () => clearInterval(flag)
},[occurTimeQuiz])
// 페이지에 접속할 때 로컬에 담긴 퀴즈를 상태로 가져오거나 퀴즈출현시간을 생성한 뒤 상태로 담는다
useEffect(() => {
if(!quiz) return;
const quizTime = JSON.parse(localStorage.getItem(lessonSeq as string));
if(!quizTime){
const occureTimeAddedQuiz = getRandomInteger(quiz, props.currentLessonPlayTime)
localStorage.setItem(lessonSeq as string, JSON.stringify(occureTimeAddedQuiz));
setOccurTimeQuiz(occureTimeAddedQuiz);
} else {
setOccurTimeQuiz(quizTime);
}
},[lessonSeq,quiz])
return (
<>
<VideoContainer>
{
quiz?.length > 0 &&
<LessonContentVideoModal
open={openModal}
handleClose={handleCloseModal}
quiz={currentQuiz}
/>
}
<VideoPlayer />
</VideoContainer>
</>
export function ModalQuiz({open,onCloseModal,quiz,loading = false}: ModalProps) {
const [userSelectedAnswer, setUserSelectedAnswer] = useState('await');
const [quizItem, setQuizItem] = useState<string[]>([]);
const [quizAnswer, setQuizAnswer] = useState('');
const onChoiceAnswer = useCallback((answer: string) => {
setUserSelectedAnswer(answer);
},[])
useEffect(() => {
if(!quiz) return;
const { item1 = '',item2 = '',item3 = '',item4 = '',answer } = quiz;
const temp = [`1. ${item1}`,`2. ${item2}`,`3. ${item3}`,`4. ${item4}`];
const quizAnswerNumber = temp.find((item) => item.includes(answer) && item);
setQuizAnswer(quizAnswerNumber)
setQuizItem(temp)
},[quiz])
return (
<DialogBox aria-labelledby='modal-title' open={open}>
{loading || !quiz
? <Spinner />
: (
<ModalBox>
<Box>
<Box>
<Typography>
{quiz.alarmContent} {'\n'}
{quiz.quizContent}
</Typography>
{
(userSelectedAnswer === 'await' && quiz.lessonQuizTypeEnum === "MULTIPLE_CHOICE")
&& <Typography>
다음 중 맞는 보기를 고르세요.
</Typography>
}
</Box>
{ (quiz.lessonQuizTypeEnum === 'ALARM' && userSelectedAnswer === 'await')
&& <Alarm onClick={onCloseModal} />
}
{
(quiz.lessonQuizTypeEnum === "MULTIPLE_CHOICE" && userSelectedAnswer === 'await')
&& <MultipleChoiceQuiz
quizItem={quizItem}
onCloseModal={onCloseModal}
onChoiceAnswer={onChoiceAnswer} />
}
{
(quiz.lessonQuizTypeEnum === "MULTIPLE_CHOICE" && userSelectedAnswer !== 'await')
&& <QuizAnswerResult
quizAnswer={quizAnswer}
userSelectedAnswer={userSelectedAnswer}
quiz={quiz}
onCloseModal={onCloseModal}
onChoiceAnswer={onChoiceAnswer} />
}
{
(quiz.lessonQuizTypeEnum === "OX_QUIZ" && userSelectedAnswer === 'await')
&& <OXQuiz
quizAnswer={quiz.answer}
itemO={quiz.itemO}
itemX={quiz.itemX}
onChoiceAnswer={onChoiceAnswer} />
}
{
(quiz.lessonQuizTypeEnum === "OX_QUIZ" && userSelectedAnswer !== 'await')
&& <QuizAnswerResult
quizAnswer={quiz.answer}
userSelectedAnswer={userSelectedAnswer}
quiz={quiz}
onCloseModal={onCloseModal}
onChoiceAnswer={onChoiceAnswer} />
}
</Box>
</Box>
</ModalBox>
)}
</DialogBox>
);
}
onChoiceAnswer에 의해 await값이 선택한 보기로 바뀌며 <QuizAnswerResult> 컴포넌트가 출력된다.function MultipleChoiceQuiz({quizItem, onChoiceAnswer}: Props) {
return (
<Box>
{
quizItem.map((item) => (
<QuizItemButton
key={item}
item={item}
bgColor='rgb(194,51,51)'
onClick={onChoiceAnswer}
/>
))
}
</Box>
)
}
function QuizAnswerResult({onChoiceAnswer, userSelectedAnswer, quiz, quizAnswer, onCloseModal}: Props) {
const onDeleteSolvedQuiz = useCallback(() => {
onCloseModal();
onChoiceAnswer('await');
},[])
const isCorrectColor = userSelectedAnswer.split('. ')[1] === quiz.answer ? '#03dd03' : '#ff0000'
return (
<>
<Box
sx={{
boxShadow:`2px 2px 2px ${isCorrectColor}`,
backgroundColor: isCorrectColor,
}}
>
<Typography> {quizAnswer} </Typography>
</Box>
<Box>
<Typography color={isCorrectColor}>
{
userSelectedAnswer.split('. ')[1] === quiz.answer
? '(O) 정답입니다.'
: `(X) 틀렸습니다. \n
정답은 ${quizAnswer} 입니다.
`
}
</Typography>
<Typography marginTop='2rem'>
{quiz.feedback}
</Typography>
</Box>
<Button onClick={onDeleteSolvedQuiz}> 확인 </Button>
</>
)
}
export default QuizAnswerResult