update,
This commit is contained in:
@@ -100,6 +100,7 @@ function RouteConfig() {
|
||||
<ConnectiveRevisionSelectCategory />
|
||||
</Route>
|
||||
{/* */}
|
||||
{/* http://localhost:5173/matching_frenzy/r/000000000000001 */}
|
||||
<Route exact path={`${MATCHING_FRENZY_LINK}/r/:p_route`}>
|
||||
<MatchingFrenzyMatchRun />
|
||||
</Route>
|
||||
|
@@ -30,7 +30,9 @@ export const AppStateProvider: React.FC<{ children: ReactNode }> = ({ children }
|
||||
// user_config.json
|
||||
// 012_put_matching_frenzy_count_down_time_to_config_file
|
||||
const [user_config_json, setUserConfigJson] = useState<any>({});
|
||||
const [MATCHING_FRENZY_COUNT_DOWN_S, setMATCHING_FRENZY_COUNT_DOWN_S] = useState<number>(120);
|
||||
|
||||
// TODO: resume to 120
|
||||
const [MATCHING_FRENZY_COUNT_DOWN_S, setMATCHING_FRENZY_COUNT_DOWN_S] = useState<number>(300);
|
||||
useEffect(() => {
|
||||
fetch('/data/user_config.json')
|
||||
.then((res) => res.json())
|
||||
|
28
002_source/ionic_mobile/src/hooks/fetchMFQuestions.tsx
Normal file
28
002_source/ionic_mobile/src/hooks/fetchMFQuestions.tsx
Normal file
@@ -0,0 +1,28 @@
|
||||
import { QuizMFQuestion } from '../types/QuizMFQuestion';
|
||||
import { usePocketBase } from './usePocketBase';
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: Infinity,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const fetchMFQuestions = async (cat_id: string, pb: any) => {
|
||||
const response = await queryClient.fetchQuery({
|
||||
queryKey: ['fetchData'],
|
||||
staleTime: 60 * 1000,
|
||||
queryFn: async () => {
|
||||
return await pb.collection('QuizMFQuestions').getList<QuizMFQuestion>(1, 9999, {
|
||||
filter: `cat_id = "${cat_id}"`,
|
||||
$autoCancel: false,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
export default fetchMFQuestions;
|
@@ -0,0 +1,44 @@
|
||||
import { QuizMFQuestion } from '../types/QuizMFQuestion';
|
||||
import { usePocketBase } from './usePocketBase';
|
||||
import { QueryClient } from '@tanstack/react-query';
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
staleTime: Infinity,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const useListQuizMFQuestionsByCategoryId = (cat_id: string) => {
|
||||
const { user, pb } = usePocketBase();
|
||||
|
||||
const fetchData = async () => {
|
||||
const response = await queryClient.fetchQuery({
|
||||
queryKey: ['fetchData'],
|
||||
staleTime: 60 * 1000,
|
||||
queryFn: async () => {
|
||||
return await pb.collection('QuizMFQuestions').getList<QuizMFQuestion>(1, 9999, {
|
||||
filter: `cat_id = "${cat_id}"`,
|
||||
$autoCancel: false,
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
return response;
|
||||
};
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['useListQuizMFQuestionsByCategoryId'],
|
||||
staleTime: 60 * 1000,
|
||||
queryFn: async () => {
|
||||
return await pb.collection('QuizMFQuestions').getList<QuizMFQuestion>(1, 9999, {
|
||||
filter: `cat_id = "${cat_id}"`,
|
||||
$autoCancel: false,
|
||||
});
|
||||
},
|
||||
// enabled: !!user.id,
|
||||
});
|
||||
};
|
||||
|
||||
export default useListQuizMFQuestionsByCategoryId;
|
@@ -1,5 +1,4 @@
|
||||
import { useEffect } from 'react';
|
||||
// import Helloworld from './Helloworld';
|
||||
import React, { useEffect } from 'react';
|
||||
import './style.css';
|
||||
|
||||
function CountDown({
|
||||
|
@@ -0,0 +1,20 @@
|
||||
import React, { useState } from 'react';
|
||||
import { alarmOutline, arrowBackCircleOutline } from 'ionicons/icons';
|
||||
import { IonButton, IonContent, IonIcon, IonPage, useIonRouter } from '@ionic/react';
|
||||
import { useAppStateContext } from '../../../contexts/AppState';
|
||||
|
||||
function CountDownDisplay() {
|
||||
const { MATCHING_FRENZY_COUNT_DOWN_S } = useAppStateContext();
|
||||
|
||||
const [countdown_s, setCountDown_s] = useState<number>(MATCHING_FRENZY_COUNT_DOWN_S);
|
||||
const [show_time_left_s, setShowTimeLeft] = useState<string>('0:00');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<IonIcon icon={alarmOutline}></IonIcon>
|
||||
{countdown_s > 0 ? show_time_left_s : 'times up'}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default CountDownDisplay;
|
@@ -3,47 +3,48 @@ import { alarmOutline, arrowBackCircleOutline } from 'ionicons/icons';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
import { LoadingScreen } from '../../components/LoadingScreen';
|
||||
import { MATCHING_FRENZY_LINK, QUIZ_MAIN_MENU_LINK } from '../../constants';
|
||||
import { MATCHING_FRENZY_LINK } from '../../constants';
|
||||
import { useAppStateContext } from '../../contexts/AppState';
|
||||
import { useMyIonQuizContext } from '../../contexts/MyIonQuiz';
|
||||
import IMatchingFrenzyQuestion from '../../interfaces/IMatchingFrenzyQuestion';
|
||||
import { listMatchingFrenzyContent } from '../../public_data/listMatchingFrenzyContent';
|
||||
import { shuffleArray } from '../../utils/shuffleArray';
|
||||
import CountDown from './CountDown';
|
||||
import MatchingFrenzyCard from './MatchingFrenzyCard';
|
||||
import PressStartToBegin from './PressStartToBegin';
|
||||
import './style.css';
|
||||
import ConfirmUserQuitQuiz from '../../components/ConfirmUserQuitQuiz';
|
||||
import formatTimeDisplay from './formatTimeDisplay';
|
||||
import fetchMFQuestions from '../../hooks/fetchMFQuestions';
|
||||
import { usePocketBase } from '../../hooks/usePocketBase';
|
||||
|
||||
function MatchingFrenzyMatchRun() {
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
|
||||
const router = useIonRouter();
|
||||
const { p_route } = useParams<{ p_route: string }>();
|
||||
const i_p_route = parseInt(p_route);
|
||||
|
||||
const [question_list, setQuestionList] = useState<IMatchingFrenzyQuestion[] | []>([]);
|
||||
const { p_route: cat_id } = useParams<{ p_route: string }>();
|
||||
let [start_match, setStartMatch] = useState(false);
|
||||
let [test_random, setTestRandom] = useState(0);
|
||||
const [show_confirm_user_exit, setShowConfirmUserExit] = useState<boolean>(false);
|
||||
const { setMatchingFrenzyInProgress } = useAppStateContext();
|
||||
const [question_list, setQuestionList] = useState<IMatchingFrenzyQuestion[]>([]);
|
||||
const [current_question_meta, setCurrentQuestionMeta] = useState<IMatchingFrenzyQuestion | undefined>(undefined);
|
||||
|
||||
const [current_question, setCurrentQuestion] = useState(0);
|
||||
const [num_correct, setNumCorrect] = useState(0);
|
||||
|
||||
const { setTabActive } = useAppStateContext();
|
||||
|
||||
const [answer_list, setAnswerList] = useState<string[]>([]);
|
||||
|
||||
const { MATCHING_FRENZY_COUNT_DOWN_S } = useAppStateContext();
|
||||
const [countdown_s, setCountDown_s] = useState<number>(MATCHING_FRENZY_COUNT_DOWN_S);
|
||||
const [show_time_left_s, setShowTimeLeft] = useState<string>('0:00');
|
||||
const { setMatchingFrenzyInProgress } = useAppStateContext();
|
||||
const { setMatchingFrenzyCurrentTest, setMatchingFrenzyResult, saveMatchingFrenzyResultToScoreBoard } =
|
||||
useMyIonQuizContext();
|
||||
const [num_correct, setNumCorrect] = useState(0);
|
||||
const [answer_list, setAnswerList] = useState<string[]>([]);
|
||||
|
||||
let [start_match, setStartMatch] = useState(false);
|
||||
let [test_random, setTestRandom] = useState(0);
|
||||
// TODO: review useless
|
||||
const i_p_route = parseInt(p_route);
|
||||
|
||||
const formatTimeDisplay = (s: number) => {
|
||||
return `${Math.floor(s / 60)}:${(s % 60).toString().padStart(2, '0')}`;
|
||||
function processStartMatch() {
|
||||
setMatchingFrenzyInProgress(true);
|
||||
setStartMatch(true);
|
||||
}
|
||||
|
||||
const incNumCorrect = () => {
|
||||
setNumCorrect(num_correct + 1);
|
||||
};
|
||||
|
||||
const nextQuestion = () => {
|
||||
@@ -71,15 +72,8 @@ function MatchingFrenzyMatchRun() {
|
||||
setCurrentQuestionMeta(question_list[next_question_num]);
|
||||
}
|
||||
};
|
||||
|
||||
const incNumCorrect = () => {
|
||||
setNumCorrect(num_correct + 1);
|
||||
};
|
||||
|
||||
function processStartMatch() {
|
||||
setMatchingFrenzyInProgress(true);
|
||||
setStartMatch(true);
|
||||
}
|
||||
const { setMatchingFrenzyCurrentTest, setMatchingFrenzyResult, saveMatchingFrenzyResultToScoreBoard } =
|
||||
useMyIonQuizContext();
|
||||
|
||||
useEffect(() => {
|
||||
setShowTimeLeft(formatTimeDisplay(countdown_s));
|
||||
@@ -102,9 +96,9 @@ function MatchingFrenzyMatchRun() {
|
||||
}, [countdown_s]);
|
||||
|
||||
const [init_ans, setInitAns] = useState<string[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!current_question_meta) return;
|
||||
|
||||
let init_options = [...question_list.map((q) => q.word), ...answer_list, ...init_ans];
|
||||
|
||||
let all_answer_list = [...new Set(init_options)];
|
||||
@@ -115,83 +109,73 @@ function MatchingFrenzyMatchRun() {
|
||||
setAnswerList(shuffleArray([...sliced_shuffle_array, current_question_meta.word]));
|
||||
}, [current_question_meta]);
|
||||
|
||||
const { user, pb } = usePocketBase();
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const res_json = await listMatchingFrenzyContent();
|
||||
const cat_json = res_json[i_p_route];
|
||||
const init_answer = cat_json.init_answer;
|
||||
setInitAns(cat_json.init_answer);
|
||||
fetchMFQuestions(cat_id, pb)
|
||||
.then((result) => {
|
||||
console.log(result);
|
||||
const cat_json = result.items;
|
||||
const init_answer = result.items[0].init_answer;
|
||||
setInitAns(init_answer);
|
||||
|
||||
let temp = res_json[i_p_route].content;
|
||||
let shuffled_temp = shuffleArray(temp);
|
||||
let temp = result.items;
|
||||
let shuffled_temp = shuffleArray(temp);
|
||||
|
||||
setQuestionList(shuffled_temp);
|
||||
setCurrentQuestionMeta(cat_json.content[current_question]);
|
||||
})();
|
||||
setQuestionList(shuffled_temp);
|
||||
setCurrentQuestionMeta(result.items[current_question] as unknown as IMatchingFrenzyQuestion);
|
||||
|
||||
setTabActive(QUIZ_MAIN_MENU_LINK);
|
||||
setTestRandom(Math.random());
|
||||
}, []);
|
||||
|
||||
const [show_confirm_user_exit, setShowConfirmUserExit] = useState<boolean>(false);
|
||||
setLoading(false);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(error);
|
||||
});
|
||||
}, [cat_id]);
|
||||
|
||||
if (!current_question_meta) return <LoadingScreen />;
|
||||
if (loading) return <LoadingScreen />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<IonPage>
|
||||
<IonContent fullscreen>
|
||||
{start_match ? (
|
||||
<>
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: '1rem',
|
||||
marginRight: '1rem',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<div style={{ width: '33vw' }}>
|
||||
<IonButton color="dark" fill="clear" shape="round" onClick={() => setShowConfirmUserExit(true)}>
|
||||
<IonIcon slot="icon-only" size="large" icon={arrowBackCircleOutline}></IonIcon>
|
||||
</IonButton>
|
||||
</div>
|
||||
<div>
|
||||
<IonIcon icon={alarmOutline}></IonIcon>
|
||||
{countdown_s > 0 ? show_time_left_s : 'times up'}
|
||||
</div>
|
||||
<div style={{ width: '33vw', display: 'flex', justifyContent: 'flex-end' }}>
|
||||
Matches:{num_correct}
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
style={{
|
||||
marginLeft: '1rem',
|
||||
marginRight: '1rem',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<div style={{ width: '33vw' }}>
|
||||
<IonButton color="dark" fill="clear" shape="round" onClick={() => setShowConfirmUserExit(true)}>
|
||||
<IonIcon slot="icon-only" size="large" icon={arrowBackCircleOutline}></IonIcon>
|
||||
</IonButton>
|
||||
</div>
|
||||
<div style={{ margin: '1rem' }}></div>
|
||||
{/* */}
|
||||
{/*
|
||||
<div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', gap: '0.3rem' }}>
|
||||
<IonIcon icon={alarmOutline}></IonIcon>
|
||||
{countdown_s > 0 ? show_time_left_s : 'times up'}
|
||||
</div>
|
||||
<div>Matches:{num_correct}</div>
|
||||
<div>
|
||||
<IonIcon icon={alarmOutline}></IonIcon>
|
||||
{countdown_s > 0 ? show_time_left_s : 'times up'}
|
||||
</div>
|
||||
*/}
|
||||
|
||||
<MatchingFrenzyCard
|
||||
num_correct={num_correct}
|
||||
incNumCorrect={incNumCorrect}
|
||||
//
|
||||
nextQuestion={nextQuestion}
|
||||
question_meta={{
|
||||
//
|
||||
word_c: current_question_meta.word_c,
|
||||
modal_answer: current_question_meta.word,
|
||||
}}
|
||||
shuffle_word_list={answer_list}
|
||||
/>
|
||||
<div style={{ width: '33vw', display: 'flex', justifyContent: 'flex-end' }}>Matches:{num_correct}</div>
|
||||
</div>
|
||||
</>
|
||||
<div style={{ margin: '1rem' }}></div>
|
||||
|
||||
<MatchingFrenzyCard
|
||||
num_correct={num_correct}
|
||||
incNumCorrect={incNumCorrect}
|
||||
//
|
||||
nextQuestion={nextQuestion}
|
||||
question_meta={{
|
||||
//
|
||||
word_c: current_question_meta.word_c,
|
||||
modal_answer: current_question_meta.word,
|
||||
}}
|
||||
shuffle_word_list={answer_list}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<PressStartToBegin processStartMatch={processStartMatch} />
|
||||
)}
|
||||
@@ -210,6 +194,7 @@ function MatchingFrenzyMatchRun() {
|
||||
}
|
||||
|
||||
export default MatchingFrenzyMatchRun;
|
||||
|
||||
function isEndOfQuestionList(current_question: number, question_list: [] | IMatchingFrenzyQuestion[]) {
|
||||
return current_question >= question_list.length - 1;
|
||||
}
|
||||
|
@@ -0,0 +1,5 @@
|
||||
const formatTimeDisplay = (s: number) => {
|
||||
return `${Math.floor(s / 60)}:${(s % 60).toString().padStart(2, '0')}`;
|
||||
};
|
||||
|
||||
export default formatTimeDisplay;
|
19
002_source/ionic_mobile/src/types/QuizMFQuestion.ts
Normal file
19
002_source/ionic_mobile/src/types/QuizMFQuestion.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
export interface QuizMFQuestion {
|
||||
id: string;
|
||||
created: Date;
|
||||
updated: Date;
|
||||
word: string;
|
||||
word_c: string;
|
||||
cat_id: string;
|
||||
visible?: string;
|
||||
sound?: File;
|
||||
cat_image?: File;
|
||||
expand?: {
|
||||
cat_id?: {
|
||||
id: string;
|
||||
cat_name: string;
|
||||
cat_image?: File;
|
||||
};
|
||||
};
|
||||
init_answer: string[];
|
||||
}
|
Reference in New Issue
Block a user