update,
This commit is contained in:
@@ -1,82 +0,0 @@
|
||||
import { IonButton, useIonRouter } from '@ionic/react';
|
||||
import './Lesson.css';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { LoadingScreen } from '../../components/LoadingScreen';
|
||||
import { COLOR_TEXT } from '../../constants';
|
||||
import { useMyIonStore } from '../../contexts/MyIonStore';
|
||||
import { listLessonCategories } from '../../public_data/listLessonCategories';
|
||||
import { getLessonConnectivesLink } from './getLessonConnectivesLink';
|
||||
import { getLessonVocabularyLink } from './getLessonWordLink';
|
||||
|
||||
let lessonLinkProxy = [getLessonVocabularyLink, getLessonConnectivesLink];
|
||||
|
||||
interface ContainerProps {
|
||||
test_active_lesson_idx: number;
|
||||
}
|
||||
|
||||
const LessonContainer: React.FC<ContainerProps> = ({ test_active_lesson_idx = 1 }) => {
|
||||
const router = useIonRouter();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const { lesson_contents, setLessonContent } = useMyIonStore();
|
||||
const [active_lesson_idx, setActiveLessonIdx] = useState<number>(0);
|
||||
const [selected_content, setSelectedContent] = useState<any>([]);
|
||||
|
||||
useEffect(() => {
|
||||
listLessonCategories().then((cats: any) => {
|
||||
console.log({ cats });
|
||||
setLessonContent(cats);
|
||||
setActiveLessonIdx(test_active_lesson_idx);
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) return;
|
||||
console.log('active_category changed', active_lesson_idx);
|
||||
let selected_category = lesson_contents[test_active_lesson_idx];
|
||||
setSelectedContent(selected_category['content']);
|
||||
}, [active_lesson_idx, loading, test_active_lesson_idx]);
|
||||
|
||||
if (loading) return <LoadingScreen />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div style={{ display: 'flex', flexDirection: 'row', flexWrap: 'wrap', justifyContent: 'center' }}>
|
||||
{selected_content.map((content: any, cat_idx: number) => (
|
||||
<IonButton
|
||||
key={cat_idx}
|
||||
style={{ width: '45vw', height: '45vw' }}
|
||||
fill="clear"
|
||||
// href={lessonLinkProxy[test_active_lesson_idx](test_active_lesson_idx.toString(), cat_idx.toString(), '0')}
|
||||
onClick={() => {
|
||||
router.push(
|
||||
lessonLinkProxy[test_active_lesson_idx](test_active_lesson_idx.toString(), cat_idx.toString(), '0'),
|
||||
undefined,
|
||||
'replace',
|
||||
);
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
||||
<div
|
||||
key={cat_idx}
|
||||
style={{
|
||||
width: '100px',
|
||||
height: '100px',
|
||||
backgroundImage: `url(${content.cat_image})`,
|
||||
backgroundPosition: 'center',
|
||||
backgroundSize: 'cover',
|
||||
borderRadius: '0.5rem',
|
||||
margin: '.5rem',
|
||||
}}
|
||||
></div>
|
||||
<span style={{ color: COLOR_TEXT }}>{content.cat_name}</span>
|
||||
</div>
|
||||
</IonButton>
|
||||
))}
|
||||
</div>
|
||||
{/* <EndOfList /> */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LessonContainer;
|
@@ -0,0 +1,91 @@
|
||||
import { IonButton, useIonRouter } from '@ionic/react';
|
||||
import './Lesson.css';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { LoadingScreen } from '../../../components/LoadingScreen';
|
||||
import { COLOR_TEXT } from '../../../constants';
|
||||
import { useMyIonStore } from '../../../contexts/MyIonStore';
|
||||
import { listLessonCategories } from '../../../public_data/listLessonCategories';
|
||||
import { getLessonConnectivesLink } from '../getLessonConnectivesLink';
|
||||
import { getLessonVocabularyLink } from '../getLessonWordLink';
|
||||
|
||||
let lessonLinkProxy = [getLessonVocabularyLink, getLessonConnectivesLink];
|
||||
|
||||
interface ContainerProps {
|
||||
test_active_lesson_idx: number;
|
||||
lesson_type_id: string;
|
||||
}
|
||||
|
||||
const LessonContainer: React.FC<ContainerProps> = ({
|
||||
test_active_lesson_idx = 1,
|
||||
lesson_type_id = '000000000000001',
|
||||
}) => {
|
||||
const router = useIonRouter();
|
||||
const [loading, setLoading] = useState<boolean>(true);
|
||||
const { lesson_contents, setLessonContent } = useMyIonStore();
|
||||
const [active_lesson_idx, setActiveLessonIdx] = useState<number>(0);
|
||||
const [selected_content, setSelectedContent] = useState<any>([]);
|
||||
|
||||
useEffect(() => {
|
||||
listLessonCategories().then((cats: any) => {
|
||||
console.log({ cats });
|
||||
setLessonContent(cats);
|
||||
setActiveLessonIdx(test_active_lesson_idx);
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) return;
|
||||
console.log('active_category changed', active_lesson_idx);
|
||||
let selected_category = lesson_contents[test_active_lesson_idx];
|
||||
setSelectedContent(selected_category['content']);
|
||||
}, [active_lesson_idx, loading, test_active_lesson_idx]);
|
||||
|
||||
if (loading) return <LoadingScreen />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
flexWrap: 'wrap',
|
||||
justifyContent: 'center',
|
||||
//
|
||||
}}
|
||||
>
|
||||
{selected_content.map((content: any, cat_idx: number) => (
|
||||
<>
|
||||
<IonButton
|
||||
key={cat_idx}
|
||||
style={{ width: '45vw', height: '45vw' }}
|
||||
fill="clear"
|
||||
onClick={() => {
|
||||
router.push(`/lesson_word_page/v/${lesson_type_id}/${content.id}/0`, undefined, 'replace');
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
||||
<div
|
||||
key={cat_idx}
|
||||
style={{
|
||||
width: '100px',
|
||||
height: '100px',
|
||||
backgroundImage: `url(${content.cat_image})`,
|
||||
backgroundPosition: 'center',
|
||||
backgroundSize: 'cover',
|
||||
borderRadius: '0.5rem',
|
||||
margin: '.5rem',
|
||||
}}
|
||||
></div>
|
||||
<span style={{ color: COLOR_TEXT }}>{content.cat_name}</span>
|
||||
</div>
|
||||
</IonButton>
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
{/* <EndOfList /> */}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LessonContainer;
|
@@ -0,0 +1,28 @@
|
||||
import { FunctionComponent, useEffect } from 'react';
|
||||
import { useGlobalAudioPlayer } from 'react-use-audio-player';
|
||||
import { useListeningPracticeTimeSpent } from '../../../contexts/MyIonMetric/ListeningPracticeTimeSpent';
|
||||
|
||||
export const AudioControls: FunctionComponent<{ audio_src: string }> = ({ audio_src }) => {
|
||||
const { play, pause, playing, duration } = useGlobalAudioPlayer();
|
||||
const { load, src: loadedSrc } = useGlobalAudioPlayer();
|
||||
let { myIonMetricIncListeningPracticeTimeSpent } = useListeningPracticeTimeSpent();
|
||||
|
||||
useEffect(() => {
|
||||
if (audio_src) {
|
||||
load(audio_src);
|
||||
}
|
||||
}, [audio_src]);
|
||||
|
||||
useEffect(() => {
|
||||
if (loadedSrc) {
|
||||
}
|
||||
}, [loadedSrc]);
|
||||
|
||||
useEffect(() => {
|
||||
if (playing) {
|
||||
myIonMetricIncListeningPracticeTimeSpent(duration);
|
||||
}
|
||||
}, [playing]);
|
||||
|
||||
return <></>;
|
||||
};
|
@@ -0,0 +1,319 @@
|
||||
import { IonButton, IonButtons, IonContent, IonIcon, IonModal, IonPage, IonToolbar, useIonRouter } from '@ionic/react';
|
||||
import './style.css';
|
||||
import {
|
||||
arrowBackCircleOutline,
|
||||
chevronBack,
|
||||
chevronForward,
|
||||
close,
|
||||
heart,
|
||||
heartOutline,
|
||||
play,
|
||||
volumeHighOutline,
|
||||
} from 'ionicons/icons';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
//
|
||||
import Markdown from 'react-markdown';
|
||||
import { useParams } from 'react-router';
|
||||
import { useGlobalAudioPlayer } from 'react-use-audio-player';
|
||||
import remarkGfm from 'remark-gfm';
|
||||
import { LoadingScreen } from '../../../components/LoadingScreen';
|
||||
import RemoveFavoritePrompt from '../../../components/RemoveFavoritePrompt';
|
||||
import { LESSON_LINK } from '../../../constants';
|
||||
import { useMyIonFavorite } from '../../../contexts/MyIonFavorite';
|
||||
//
|
||||
import { useMyIonStore } from '../../../contexts/MyIonStore';
|
||||
import ILesson from '../../../interfaces/ILesson';
|
||||
import ILessonCategory from '../../../interfaces/ILessonCategory';
|
||||
import IWordCard from '../../../interfaces/IWordCard';
|
||||
import { getFavLessonVocabularyLink, getLessonVocabularyLink } from '../getLessonWordLink';
|
||||
import { AudioControls } from './AudioControls';
|
||||
|
||||
const LessonWordPageByDb: React.FC = () => {
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const router = useIonRouter();
|
||||
const modal = useRef<HTMLIonModalElement>(null);
|
||||
const { lesson_idx, cat_idx, word_idx } = useParams<{ lesson_idx: string; cat_idx: string; word_idx: string }>();
|
||||
|
||||
const [open_remove_modal, setOpenRemoveModal] = useState(false);
|
||||
const [lesson_info, setLessonInfo] = useState<ILesson | undefined>(undefined);
|
||||
const [cat_info, setCatInfo] = useState<ILessonCategory | undefined>(undefined);
|
||||
const [word_info, setWordInfo] = useState<IWordCard | undefined>(undefined);
|
||||
|
||||
// const { play: play_word, playing } = useGlobalAudioPlayer();
|
||||
const { myIonStoreAddFavoriteVocabulary, myIonStoreRemoveFavoriteVocabulary, myIonStoreFindInFavoriteVocabulary } =
|
||||
useMyIonFavorite();
|
||||
let [favorite_address, setFavoriteAddress] = useState(getFavLessonVocabularyLink(lesson_idx, cat_idx, word_idx));
|
||||
|
||||
useEffect(() => {
|
||||
setFavoriteAddress(getFavLessonVocabularyLink(lesson_idx, cat_idx, word_idx));
|
||||
|
||||
// if (lesson_contents.length > 0) {
|
||||
// let lesson_content: ILesson = lesson_contents[parseInt(lesson_idx)];
|
||||
// let category_content: ILessonCategory = lesson_content.content[parseInt(cat_idx)];
|
||||
// let word_content: IWordCard = category_content.content[parseInt(word_idx)];
|
||||
// setWordInfo(word_content);
|
||||
// }
|
||||
}, [lesson_idx, cat_idx, word_idx]);
|
||||
|
||||
function dismiss() {
|
||||
setOpenRemoveModal(false);
|
||||
}
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
let { lesson_contents } = useMyIonStore();
|
||||
|
||||
useEffect(() => {
|
||||
// NOTES: lesson_content == [] during loading
|
||||
if (lesson_contents.length > 0) {
|
||||
let lesson_content: ILesson = lesson_contents[parseInt(lesson_idx)];
|
||||
let category_content: ILessonCategory = lesson_content.content[parseInt(cat_idx)];
|
||||
let word_content: IWordCard = category_content.content[parseInt(word_idx)];
|
||||
|
||||
setLessonInfo(lesson_content);
|
||||
setCatInfo(category_content);
|
||||
setWordInfo(word_content);
|
||||
|
||||
setLoading(false);
|
||||
}
|
||||
}, [lesson_contents]);
|
||||
|
||||
let [in_fav, setInFav] = useState(false);
|
||||
const isInFavorite = async (string_to_search: string) => {
|
||||
let result = await myIonStoreFindInFavoriteVocabulary(string_to_search);
|
||||
setInFav(result);
|
||||
};
|
||||
|
||||
const addToFavorite = async (string_to_add: string) => {
|
||||
await myIonStoreAddFavoriteVocabulary(string_to_add);
|
||||
|
||||
await isInFavorite(string_to_add);
|
||||
setInFav(!in_fav);
|
||||
};
|
||||
|
||||
function handleUserRemoveFavorite() {
|
||||
setOpenRemoveModal(true);
|
||||
}
|
||||
|
||||
const removeFromFavorite = async (string_to_remove: string) => {
|
||||
await myIonStoreRemoveFavoriteVocabulary(string_to_remove);
|
||||
await isInFavorite(string_to_remove);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await isInFavorite(getFavLessonVocabularyLink(lesson_idx, cat_idx, word_idx));
|
||||
})();
|
||||
}, [lesson_idx, cat_idx, word_idx]);
|
||||
|
||||
return <>should not see me</>;
|
||||
|
||||
if (lesson_info == undefined) return <LoadingScreen />;
|
||||
if (!cat_info || !word_info) return <LoadingScreen />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<IonPage>
|
||||
<IonContent fullscreen>
|
||||
<div style={{ position: 'fixed' }}>
|
||||
<IonButton
|
||||
size="large"
|
||||
shape="round"
|
||||
fill="clear"
|
||||
color={'dark'}
|
||||
// href={`${LESSON_LINK}/a/${lesson_info.name}`}
|
||||
onClick={() => {
|
||||
router.push(`${LESSON_LINK}/a/${lesson_info.name}`);
|
||||
}}
|
||||
>
|
||||
<IonIcon size="large" icon={arrowBackCircleOutline} />
|
||||
</IonButton>
|
||||
</div>
|
||||
<div style={{ marginTop: '3rem' }}>
|
||||
<div style={{ textAlign: 'center', fontSize: '1.2rem' }}>{cat_info.cat_name}</div>
|
||||
</div>
|
||||
<div style={{ marginTop: '1rem', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
||||
{/* <LessonContainer name='Tab 1 page' /> */}
|
||||
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
|
||||
<div>
|
||||
<IonButton
|
||||
size="large"
|
||||
shape="round"
|
||||
fill="clear"
|
||||
color={parseInt(word_idx) === 0 ? 'medium' : 'dark'}
|
||||
disabled={parseInt(word_idx) === 0}
|
||||
// href={getLessonVocabularyLink(lesson_idx, cat_idx, Math.max(0, parseInt(word_idx) - 1).toString())}
|
||||
onClick={() => {
|
||||
router.push(
|
||||
getLessonVocabularyLink(lesson_idx, cat_idx, Math.max(0, parseInt(word_idx) - 1).toString()),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IonIcon slot="icon-only" size="large" icon={chevronBack}></IonIcon>
|
||||
</IonButton>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: '66vw',
|
||||
height: '66vw',
|
||||
backgroundImage: `url(${word_info.image_url})`,
|
||||
backgroundPosition: 'center',
|
||||
backgroundSize: 'cover',
|
||||
borderRadius: '0.5rem',
|
||||
margin: '.5rem',
|
||||
}}
|
||||
></div>
|
||||
<div>
|
||||
<IonButton
|
||||
size="large"
|
||||
shape="round"
|
||||
fill="clear"
|
||||
color={parseInt(word_idx) === cat_info.content.length - 1 ? 'medium' : 'dark'}
|
||||
disabled={parseInt(word_idx) === cat_info.content.length - 1}
|
||||
// href={getLessonVocabularyLink(
|
||||
// lesson_idx,
|
||||
// cat_idx,
|
||||
// Math.min(cat_info.content.length - 1, parseInt(word_idx) + 1).toString()
|
||||
// )}
|
||||
onClick={() => {
|
||||
router.push(
|
||||
getLessonVocabularyLink(
|
||||
lesson_idx,
|
||||
cat_idx,
|
||||
Math.min(cat_info.content.length - 1, parseInt(word_idx) + 1).toString(),
|
||||
),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IonIcon slot="icon-only" size="large" icon={chevronForward}></IonIcon>
|
||||
</IonButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ marginTop: '1rem' }}>
|
||||
<div
|
||||
style={{
|
||||
width: '2rem',
|
||||
height: '2rem',
|
||||
display: 'inline-flex',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
borderRadius: '1rem',
|
||||
backgroundColor: 'black',
|
||||
color: 'white',
|
||||
}}
|
||||
>
|
||||
{parseInt(word_idx) + 1}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div
|
||||
style={{ display: 'flex', flexDirection: 'row', gap: '1rem', alignItems: 'center', marginTop: '1rem' }}
|
||||
>
|
||||
<div>
|
||||
<AudioControls audio_src={getFile(word_info.id, word_info.sound)} />
|
||||
<IonButton
|
||||
size="large"
|
||||
color="dark"
|
||||
shape="round"
|
||||
fill="clear"
|
||||
disabled={playing}
|
||||
onClick={() => (playing ? null : play_word())}
|
||||
>
|
||||
<IonIcon
|
||||
size="large"
|
||||
color="dark"
|
||||
slot="icon-only"
|
||||
icon={playing ? play : volumeHighOutline}
|
||||
></IonIcon>
|
||||
</IonButton>
|
||||
</div>
|
||||
<div style={{ fontWeight: 'bold', fontSize: '1.5rem' }}>{word_info.word}</div>
|
||||
<div>
|
||||
<IonButton
|
||||
color="danger"
|
||||
shape="round"
|
||||
size="large"
|
||||
fill="clear"
|
||||
// id='open-modal'
|
||||
onClick={() => {
|
||||
in_fav ? handleUserRemoveFavorite() : addToFavorite(favorite_address);
|
||||
}}
|
||||
>
|
||||
<IonIcon
|
||||
size="large"
|
||||
color="danger"
|
||||
slot="icon-only"
|
||||
icon={in_fav ? heart : heartOutline}
|
||||
></IonIcon>
|
||||
</IonButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginTop: '1rem' }}>
|
||||
<div style={{ fontWeight: 'bold', fontSize: '1.3rem' }}>{word_info.word_c}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
gap: '0.5rem',
|
||||
alignItems: 'center',
|
||||
marginTop: '2rem',
|
||||
}}
|
||||
>
|
||||
<Markdown remarkPlugins={[remarkGfm]}>{word_info.sample_e}</Markdown>
|
||||
<Markdown remarkPlugins={[remarkGfm]}>{word_info.sample_c}</Markdown>
|
||||
</div>
|
||||
</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
{/* */}
|
||||
|
||||
<IonModal isOpen={open_remove_modal} id="example-modal" ref={modal}>
|
||||
<IonContent>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="end">
|
||||
<IonButton onClick={() => dismiss()} shape="round" fill="clear">
|
||||
<IonIcon size="large" slot="icon-only" icon={close}></IonIcon>
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
<div style={{ marginTop: '2rem' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
||||
<div>Are you sure to remove favorite ?</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'row', gap: '1rem', marginTop: '1rem' }}>
|
||||
<div>
|
||||
<IonButton color="dark" onClick={() => dismiss()} fill="outline">
|
||||
Cancel
|
||||
</IonButton>
|
||||
</div>
|
||||
<div>
|
||||
<IonButton
|
||||
onClick={() => {
|
||||
removeFromFavorite(getFavLessonVocabularyLink(lesson_idx, cat_idx, word_idx));
|
||||
setIsOpen(true);
|
||||
dismiss();
|
||||
}}
|
||||
fill="solid"
|
||||
color="danger"
|
||||
>
|
||||
Remove
|
||||
</IonButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</IonContent>
|
||||
</IonModal>
|
||||
|
||||
<RemoveFavoritePrompt open={isOpen} setIsOpen={setIsOpen} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LessonWordPageByDb;
|
@@ -0,0 +1,31 @@
|
||||
.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ion-modal#example-modal {
|
||||
--height: 33%;
|
||||
--width: 80%;
|
||||
--border-radius: 16px;
|
||||
--box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
|
||||
}
|
||||
|
||||
ion-modal#example-modal::part(backdrop) {
|
||||
/* background: rgba(209, 213, 219); */
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
ion-modal#example-modal ion-toolbar {
|
||||
/* --background: rgb(14 116 144); */
|
||||
/* --color: white; */
|
||||
--color: black;
|
||||
}
|
||||
|
||||
ion-toast.custom-toast::part(message) {
|
||||
text-align: center;
|
||||
font-size: 1.5rem;
|
||||
color: rgba(0, 0, 0, 0.9);
|
||||
}
|
||||
|
||||
ion-toast.custom-toast::part(container) {
|
||||
bottom: 100px;
|
||||
}
|
@@ -10,8 +10,7 @@ import {
|
||||
play,
|
||||
volumeHighOutline,
|
||||
} from 'ionicons/icons';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
// import { StoreContext, useMyIonStore } from '../../contexts/store';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
//
|
||||
import Markdown from 'react-markdown';
|
||||
import { useParams } from 'react-router';
|
||||
@@ -26,19 +25,25 @@ import { useMyIonStore } from '../../../contexts/MyIonStore';
|
||||
import ILesson from '../../../interfaces/ILesson';
|
||||
import ILessonCategory from '../../../interfaces/ILessonCategory';
|
||||
import IWordCard from '../../../interfaces/IWordCard';
|
||||
import { getFavLessonVocabularyLink, getLessonVocabularyLink } from '../../Lesson/getLessonWordLink';
|
||||
import {
|
||||
getFavLessonVocabularyLink,
|
||||
getLessonVocabularyLink,
|
||||
getLessonVocabularyLinkString,
|
||||
} from '../../Lesson/getLessonWordLink';
|
||||
import { AudioControls } from './AudioControls';
|
||||
import useGetVocabularyRoute from '../../../hooks/useGetVocabularyRoute';
|
||||
import { UseQueryResult } from '@tanstack/react-query';
|
||||
import { ListResult } from 'pocketbase';
|
||||
import LessonsTypes from '../../../types/LessonsTypes';
|
||||
|
||||
//
|
||||
|
||||
const LessonWordPage: React.FC = () => {
|
||||
const router = useIonRouter();
|
||||
function WordPage(): React.JSX.Element {
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [open_remove_modal, setOpenRemoveModal] = useState(false);
|
||||
|
||||
const router = useIonRouter();
|
||||
const modal = useRef<HTMLIonModalElement>(null);
|
||||
const { lesson_idx, cat_idx, word_idx } = useParams<{ lesson_idx: string; cat_idx: string; word_idx: string }>();
|
||||
|
||||
const [open_remove_modal, setOpenRemoveModal] = useState(false);
|
||||
const [lesson_info, setLessonInfo] = useState<ILesson | undefined>(undefined);
|
||||
const [cat_info, setCatInfo] = useState<ILessonCategory | undefined>(undefined);
|
||||
const [word_info, setWordInfo] = useState<IWordCard | undefined>(undefined);
|
||||
@@ -46,78 +51,64 @@ const LessonWordPage: React.FC = () => {
|
||||
const { play: play_word, playing } = useGlobalAudioPlayer();
|
||||
const { myIonStoreAddFavoriteVocabulary, myIonStoreRemoveFavoriteVocabulary, myIonStoreFindInFavoriteVocabulary } =
|
||||
useMyIonFavorite();
|
||||
//
|
||||
// const lesson_vocab_address = `/lesson_word_page/v/${lesson_idx}/${cat_idx}/${word_idx}`;
|
||||
let [favorite_address, setFavoriteAddress] = useState(getFavLessonVocabularyLink(lesson_idx, cat_idx, word_idx));
|
||||
useEffect(() => {
|
||||
setFavoriteAddress(getFavLessonVocabularyLink(lesson_idx, cat_idx, word_idx));
|
||||
let { status, data: tempResult } = useGetVocabularyRoute(lesson_idx, cat_idx);
|
||||
|
||||
if (lesson_contents.length > 0) {
|
||||
let lesson_content: ILesson = lesson_contents[parseInt(lesson_idx)];
|
||||
let category_content: ILessonCategory = lesson_content.content[parseInt(cat_idx)];
|
||||
let word_content: IWordCard = category_content.content[parseInt(word_idx)];
|
||||
setWordInfo(word_content);
|
||||
}
|
||||
}, [lesson_idx, cat_idx, word_idx]);
|
||||
//
|
||||
|
||||
function dismiss() {
|
||||
setOpenRemoveModal(false);
|
||||
}
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
let { lesson_contents } = useMyIonStore();
|
||||
|
||||
useEffect(() => {
|
||||
// NOTES: lesson_content == [] during loading
|
||||
if (lesson_contents.length > 0) {
|
||||
let lesson_content: ILesson = lesson_contents[parseInt(lesson_idx)];
|
||||
let category_content: ILessonCategory = lesson_content.content[parseInt(cat_idx)];
|
||||
let word_content: IWordCard = category_content.content[parseInt(word_idx)];
|
||||
|
||||
setLessonInfo(lesson_content);
|
||||
setCatInfo(category_content);
|
||||
setWordInfo(word_content);
|
||||
|
||||
setLoading(false);
|
||||
}
|
||||
}, [lesson_contents]);
|
||||
|
||||
let [in_fav, setInFav] = useState(false);
|
||||
const isInFavorite = async (string_to_search: string) => {
|
||||
let result = await myIonStoreFindInFavoriteVocabulary(string_to_search);
|
||||
setInFav(result);
|
||||
};
|
||||
|
||||
const addToFavorite = async (string_to_add: string) => {
|
||||
await myIonStoreAddFavoriteVocabulary(string_to_add);
|
||||
|
||||
await isInFavorite(string_to_add);
|
||||
setInFav(!in_fav);
|
||||
};
|
||||
|
||||
function handleUserRemoveFavorite() {
|
||||
setOpenRemoveModal(true);
|
||||
function getFile(recordId: string, fileName: string) {
|
||||
return `http://127.0.0.1:8090/api/files/Vocabularies/${recordId}/${fileName}`;
|
||||
}
|
||||
|
||||
const removeFromFavorite = async (string_to_remove: string) => {
|
||||
await myIonStoreRemoveFavoriteVocabulary(string_to_remove);
|
||||
await isInFavorite(string_to_remove);
|
||||
};
|
||||
useEffect(() => {
|
||||
setLoading(!(status === 'success'));
|
||||
}, [status]);
|
||||
|
||||
// useEffect(() => {
|
||||
// // console.log({ lesson_idx, cat_idx, word_idx });
|
||||
// if (tempResult) {
|
||||
// try {
|
||||
// setCatInfo(tempResult.items[parseInt(word_idx)].expand.cat_id as unknown as ILessonCategory);
|
||||
// //
|
||||
// setWordInfo(tempResult.items[parseInt(word_idx)] as unknown as IWordCard);
|
||||
// } catch (error) {
|
||||
// console.error(error);
|
||||
// }
|
||||
// }
|
||||
// }, [lesson_idx, cat_idx, word_idx]);
|
||||
let [lastWord, setLastWord] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
await isInFavorite(getFavLessonVocabularyLink(lesson_idx, cat_idx, word_idx));
|
||||
})();
|
||||
if (tempResult) {
|
||||
try {
|
||||
setCatInfo(tempResult.items[parseInt(word_idx)].expand.cat_id as unknown as ILessonCategory);
|
||||
//
|
||||
setWordInfo(tempResult.items[parseInt(word_idx)] as unknown as IWordCard);
|
||||
//
|
||||
setLastWord(parseInt(word_idx) === tempResult.items.length - 1);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}, [lesson_idx, cat_idx, word_idx]);
|
||||
|
||||
// if (loading) return <>loading</>;
|
||||
// if (!word_info) return <>loading</>;
|
||||
|
||||
if (lesson_info == undefined) return <LoadingScreen />;
|
||||
useEffect(() => {
|
||||
// console.log({ lesson_idx, cat_idx, word_idx });
|
||||
if (tempResult) {
|
||||
try {
|
||||
setCatInfo(tempResult.items[parseInt(word_idx)].expand.cat_id as unknown as ILessonCategory);
|
||||
//
|
||||
setWordInfo(tempResult.items[parseInt(word_idx)] as unknown as IWordCard);
|
||||
//
|
||||
setLastWord(parseInt(word_idx) === tempResult.items.length - 1);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
}, [tempResult]);
|
||||
|
||||
if (!cat_info || !word_info) return <LoadingScreen />;
|
||||
|
||||
if (!tempResult) return <LoadingScreen />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<IonPage>
|
||||
@@ -128,9 +119,8 @@ const LessonWordPage: React.FC = () => {
|
||||
shape="round"
|
||||
fill="clear"
|
||||
color={'dark'}
|
||||
// href={`${LESSON_LINK}/a/${lesson_info.name}`}
|
||||
onClick={() => {
|
||||
router.push(`${LESSON_LINK}/a/${lesson_info.name}`);
|
||||
router.push(`${LESSON_LINK}/a/Vocabulary`);
|
||||
}}
|
||||
>
|
||||
<IonIcon size="large" icon={arrowBackCircleOutline} />
|
||||
@@ -152,42 +142,67 @@ const LessonWordPage: React.FC = () => {
|
||||
// href={getLessonVocabularyLink(lesson_idx, cat_idx, Math.max(0, parseInt(word_idx) - 1).toString())}
|
||||
onClick={() => {
|
||||
router.push(
|
||||
getLessonVocabularyLink(lesson_idx, cat_idx, Math.max(0, parseInt(word_idx) - 1).toString()),
|
||||
getLessonVocabularyLinkString(
|
||||
lesson_idx,
|
||||
cat_idx,
|
||||
Math.max(0, parseInt(word_idx) - 1).toString(),
|
||||
),
|
||||
);
|
||||
}}
|
||||
>
|
||||
<IonIcon slot="icon-only" size="large" icon={chevronBack}></IonIcon>
|
||||
</IonButton>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
width: '66vw',
|
||||
height: '66vw',
|
||||
backgroundImage: `url(${word_info.image_url})`,
|
||||
backgroundPosition: 'center',
|
||||
backgroundSize: 'cover',
|
||||
borderRadius: '0.5rem',
|
||||
margin: '.5rem',
|
||||
}}
|
||||
></div>
|
||||
{word_info && word_info?.image != '' ? (
|
||||
<div
|
||||
style={{
|
||||
width: '66vw',
|
||||
height: '66vw',
|
||||
backgroundImage: `url(${getFile(word_info.id, word_info.image)})`,
|
||||
backgroundPosition: 'center',
|
||||
backgroundSize: 'cover',
|
||||
borderRadius: '0.5rem',
|
||||
margin: '.5rem',
|
||||
}}
|
||||
></div>
|
||||
) : (
|
||||
<div
|
||||
style={{
|
||||
width: '66vw',
|
||||
height: '66vw',
|
||||
backgroundPosition: 'center',
|
||||
backgroundSize: 'cover',
|
||||
borderRadius: '0.5rem',
|
||||
margin: '.5rem',
|
||||
//
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
fontSize: '1.5rem',
|
||||
}}
|
||||
>
|
||||
empty pic
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<IonButton
|
||||
size="large"
|
||||
shape="round"
|
||||
fill="clear"
|
||||
color={parseInt(word_idx) === cat_info.content.length - 1 ? 'medium' : 'dark'}
|
||||
disabled={parseInt(word_idx) === cat_info.content.length - 1}
|
||||
// href={getLessonVocabularyLink(
|
||||
color={lastWord ? 'medium' : 'dark'}
|
||||
disabled={lastWord}
|
||||
// href={getLessonVocabularyLinkString(
|
||||
// lesson_idx,
|
||||
// cat_idx,
|
||||
// Math.min(cat_info.content.length - 1, parseInt(word_idx) + 1).toString()
|
||||
// Math.min(tempResult.items.length - 1, parseInt(word_idx) + 1).toString(),
|
||||
// )}
|
||||
onClick={() => {
|
||||
router.push(
|
||||
getLessonVocabularyLink(
|
||||
getLessonVocabularyLinkString(
|
||||
lesson_idx,
|
||||
cat_idx,
|
||||
Math.min(cat_info.content.length - 1, parseInt(word_idx) + 1).toString(),
|
||||
Math.min(tempResult.items.length - 1, parseInt(word_idx) + 1).toString(),
|
||||
),
|
||||
);
|
||||
}}
|
||||
@@ -216,10 +231,20 @@ const LessonWordPage: React.FC = () => {
|
||||
|
||||
<div>
|
||||
<div
|
||||
style={{ display: 'flex', flexDirection: 'row', gap: '1rem', alignItems: 'center', marginTop: '1rem' }}
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'row',
|
||||
gap: '1rem',
|
||||
alignItems: 'center',
|
||||
marginTop: '1rem',
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<AudioControls audio_src={word_info.sound_url} />
|
||||
{word_info && word_info?.sound != '' ? (
|
||||
<AudioControls audio_src={getFile(word_info.id, word_info.sound)} />
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
<IonButton
|
||||
size="large"
|
||||
color="dark"
|
||||
@@ -236,6 +261,7 @@ const LessonWordPage: React.FC = () => {
|
||||
></IonIcon>
|
||||
</IonButton>
|
||||
</div>
|
||||
|
||||
<div style={{ fontWeight: 'bold', fontSize: '1.5rem' }}>{word_info.word}</div>
|
||||
<div>
|
||||
<IonButton
|
||||
@@ -244,21 +270,30 @@ const LessonWordPage: React.FC = () => {
|
||||
size="large"
|
||||
fill="clear"
|
||||
// id='open-modal'
|
||||
onClick={() => {
|
||||
in_fav ? handleUserRemoveFavorite() : addToFavorite(favorite_address);
|
||||
}}
|
||||
// onClick={() => {
|
||||
// in_fav ? handleUserRemoveFavorite() : addToFavorite(favorite_address);
|
||||
// }}
|
||||
>
|
||||
<IonIcon
|
||||
size="large"
|
||||
color="danger"
|
||||
slot="icon-only"
|
||||
icon={in_fav ? heart : heartOutline}
|
||||
icon={heartOutline}
|
||||
// icon={in_fav ? heart : heartOutline}
|
||||
></IonIcon>
|
||||
</IonButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginTop: '1rem' }}>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
marginTop: '1rem',
|
||||
//
|
||||
}}
|
||||
>
|
||||
<div style={{ fontWeight: 'bold', fontSize: '1.3rem' }}>{word_info.word_c}</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -272,55 +307,21 @@ const LessonWordPage: React.FC = () => {
|
||||
marginTop: '2rem',
|
||||
}}
|
||||
>
|
||||
<Markdown remarkPlugins={[remarkGfm]}>{word_info.sample_e}</Markdown>
|
||||
<Markdown remarkPlugins={[remarkGfm]}>{word_info.sample_c}</Markdown>
|
||||
<Markdown remarkPlugins={[remarkGfm]}>
|
||||
{word_info.sample_e}
|
||||
{/* */}
|
||||
</Markdown>
|
||||
<Markdown remarkPlugins={[remarkGfm]}>
|
||||
{word_info.sample_c}
|
||||
{/* */}
|
||||
</Markdown>
|
||||
</div>
|
||||
</div>
|
||||
</IonContent>
|
||||
</IonPage>
|
||||
{/* */}
|
||||
|
||||
<IonModal isOpen={open_remove_modal} id="example-modal" ref={modal}>
|
||||
<IonContent>
|
||||
<IonToolbar>
|
||||
<IonButtons slot="end">
|
||||
<IonButton onClick={() => dismiss()} shape="round" fill="clear">
|
||||
<IonIcon size="large" slot="icon-only" icon={close}></IonIcon>
|
||||
</IonButton>
|
||||
</IonButtons>
|
||||
</IonToolbar>
|
||||
<div style={{ marginTop: '2rem' }}>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
|
||||
<div>Are you sure to remove favorite ?</div>
|
||||
|
||||
<div style={{ display: 'flex', flexDirection: 'row', gap: '1rem', marginTop: '1rem' }}>
|
||||
<div>
|
||||
<IonButton color="dark" onClick={() => dismiss()} fill="outline">
|
||||
Cancel
|
||||
</IonButton>
|
||||
</div>
|
||||
<div>
|
||||
<IonButton
|
||||
onClick={() => {
|
||||
removeFromFavorite(getFavLessonVocabularyLink(lesson_idx, cat_idx, word_idx));
|
||||
setIsOpen(true);
|
||||
dismiss();
|
||||
}}
|
||||
fill="solid"
|
||||
color="danger"
|
||||
>
|
||||
Remove
|
||||
</IonButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</IonContent>
|
||||
</IonModal>
|
||||
|
||||
<RemoveFavoritePrompt open={isOpen} setIsOpen={setIsOpen} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export default LessonWordPage;
|
||||
export default WordPage;
|
||||
|
@@ -1,5 +1,13 @@
|
||||
import { FAVORITE_LINK } from '../../constants';
|
||||
|
||||
export function getLessonVocabularyLinkString(
|
||||
s_active_lesson_idx: string,
|
||||
s_cat_idx: string,
|
||||
s_word_idx: string,
|
||||
): string {
|
||||
return `/lesson_word_page/v/${s_active_lesson_idx}/${s_cat_idx}/${s_word_idx}`;
|
||||
}
|
||||
|
||||
export function getLessonVocabularyLink(i_active_lesson_idx: string, i_cat_idx: string, i_word_idx: string): string {
|
||||
let s_active_lesson_idx = parseInt(i_active_lesson_idx);
|
||||
let s_cat_idx = parseInt(i_cat_idx);
|
||||
|
@@ -25,6 +25,9 @@ import { LESSON_LINK } from '../../constants';
|
||||
import { useMyIonStore } from '../../contexts/MyIonStore';
|
||||
import { listLessonCategories } from '../../public_data/listLessonCategories';
|
||||
import LessonContainer from './LessonContainer';
|
||||
import useHelloworld from '../../hooks/useHelloworld';
|
||||
import useListAllLessonTypes from '../../hooks/useListAllLessonTypes';
|
||||
import LessonsTypes, { LessonsType } from '../../types/LessonsTypes';
|
||||
|
||||
const Lesson: React.FC = () => {
|
||||
const { act_category } = useParams<{ act_category: string }>();
|
||||
@@ -35,14 +38,14 @@ const Lesson: React.FC = () => {
|
||||
let [active_lesson_idx, setActiveLessonIdx] = useState<number>(0);
|
||||
let [selected_content, setSelectedContent] = useState<any>([]);
|
||||
|
||||
useEffect(() => {
|
||||
listLessonCategories().then((cats: any) => {
|
||||
console.log({ cats });
|
||||
setLessonContent(cats);
|
||||
setActiveLessonIdx(0);
|
||||
setLoading(false);
|
||||
});
|
||||
}, []);
|
||||
// useEffect(() => {
|
||||
// listLessonCategories().then((cats: any) => {
|
||||
// console.log({ cats });
|
||||
// setLessonContent(cats);
|
||||
// setActiveLessonIdx(0);
|
||||
// setLoading(false);
|
||||
// });
|
||||
// }, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (loading) return;
|
||||
@@ -65,7 +68,24 @@ const Lesson: React.FC = () => {
|
||||
}
|
||||
}, [act_category, lesson_content]);
|
||||
|
||||
const lessonTypesQuery = useListAllLessonTypes();
|
||||
const [lessonTypes, setLessonTypes] = useState<LessonsType[]>([]);
|
||||
useEffect(() => {
|
||||
if (lessonTypesQuery.status === 'success') {
|
||||
const { data: tempLessonTypes } = lessonTypesQuery;
|
||||
console.log({ tempLessonTypes });
|
||||
if (tempLessonTypes) {
|
||||
setLessonTypes(tempLessonTypes);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
}
|
||||
}, [lessonTypesQuery]);
|
||||
|
||||
if (loading) return <LoadingScreen />;
|
||||
if (lessonTypes.length === 0) return <LoadingScreen />;
|
||||
|
||||
// return <pre>{JSON.stringify({ t: lessonTypes }, null, 2)}</pre>;
|
||||
|
||||
return (
|
||||
<IonPage>
|
||||
@@ -98,6 +118,7 @@ const Lesson: React.FC = () => {
|
||||
</IonTitle>
|
||||
</IonToolbar>
|
||||
</IonHeader>
|
||||
{/* */}
|
||||
<div
|
||||
style={{
|
||||
width: '100%',
|
||||
@@ -109,9 +130,9 @@ const Lesson: React.FC = () => {
|
||||
<IonList lines="none">
|
||||
<IonItem>
|
||||
<IonSelect value={active_lesson_idx} onIonChange={(e) => setActiveLessonIdx(e.detail.value)}>
|
||||
{lesson_content.map((category: any, idx: number) => (
|
||||
{lessonTypes.map((lessonType: any, idx: number) => (
|
||||
<IonSelectOption key={idx} value={idx}>
|
||||
{category.name}
|
||||
{lessonType.name}
|
||||
</IonSelectOption>
|
||||
))}
|
||||
</IonSelect>
|
||||
@@ -119,7 +140,10 @@ const Lesson: React.FC = () => {
|
||||
</IonList>
|
||||
</div>
|
||||
{/* */}
|
||||
<LessonContainer test_active_lesson_idx={active_lesson_idx} />
|
||||
<LessonContainer
|
||||
test_active_lesson_idx={active_lesson_idx}
|
||||
lesson_type_id={lessonTypes[active_lesson_idx].id}
|
||||
/>
|
||||
{/* */}
|
||||
<CongratGenius />
|
||||
<CongratHardworker />
|
||||
|
Reference in New Issue
Block a user