init commit,

This commit is contained in:
louiscklaw
2025-04-26 10:08:01 +08:00
parent 7d70b5826b
commit d0ea7e5452
473 changed files with 29989 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
import { FunctionComponent } from 'react';
import './style.css';
interface AudioSeekBarProps {
value: number;
range_list: number[];
}
const AttentiveEarsProgressBar: FunctionComponent<AudioSeekBarProps> = ({ value, range_list }) => {
let test_value = value;
let scale = [];
for (let i = 0; i < range_list.length; i++) {
let scale_full_range = range_list[i];
let scale_range = i > 0 ? scale_full_range - range_list[i - 1] : scale_full_range;
let fill = 0;
if (test_value > scale_full_range) {
fill = scale_full_range;
} else {
if (i > 0) {
fill = Math.max(0, test_value - range_list[i - 1]);
} else {
fill = test_value;
}
}
let fill_pct = Math.min(fill / scale_range, 1);
scale.push([fill_pct]);
}
return (
<>
<div style={{ display: 'flex', flexDirection: 'row' }}>
{scale.map((item: any, index: number) => {
return (
<div
style={{
width: `calc( 100% / ${range_list.length} )`,
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-end',
}}
>
<div style={{ fontSize: '0.5rem' }}>{range_list[index]}</div>
<div
className={`audioSeekBar playBar_t__seek`}
style={{
// border: '1px solid #000',
borderTop: '1px solid #000',
borderBottom: '1px solid #000',
borderLeft: index == 0 ? '1px solid #000' : '',
borderRight: index == scale.length - 1 ? '1px solid #000' : '',
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
}}
>
<div style={{ width: `${item * 100}%` }} className="audioSeekBar__tick" />
</div>
</div>
);
})}
</div>
</>
);
};
export default AttentiveEarsProgressBar;

View File

@@ -0,0 +1,29 @@
.audioSeekBar {
cursor: pointer;
background-color: white;
overflow: hidden;
}
.audioSeekBar__tick {
background-color: rgb(56, 182, 255);
height: 100%;
}
.playBar_t {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 100px;
background-color: #1c192f;
display: flex;
align-items: flex-start;
padding: 0px 50px;
}
.playBar_t__seek {
width: 100%;
height: 10px;
/* border-radius: 12px; */
margin-right: 0px;
}

View File

@@ -0,0 +1,79 @@
import { IonButton, IonContent, IonModal, useIonRouter } from '@ionic/react';
import { useRef } from 'react';
import { COLOR_TEXT } from '../../constants';
import { useAppStateContext } from '../../contexts/AppState';
import { useMyIonQuizContext } from '../../contexts/MyIonQuiz';
function ConfirmUserExit() {
let {
setTabActive,
show_confirm_user_exit,
setShowConfirmUserExit,
url_push_after_user_confirm,
setListeningPracticeInProgress,
setConnectiveRevisionInProgress,
setMatchingFrenzyInProgress,
} = useAppStateContext();
const modal = useRef<HTMLIonModalElement>(null);
const router = useIonRouter();
const { resetListeningPracticeCorrectionList } = useMyIonQuizContext();
return (
<>
<IonModal
isOpen={show_confirm_user_exit}
ref={modal}
// trigger='open-modal'
initialBreakpoint={0.25}
breakpoints={[0, 0.25, 0.5, 0.75]}
onDidDismiss={() => setShowConfirmUserExit(false)}
>
<IonContent className="ion-padding">
<div style={{ margin: '2rem', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
<div style={{ color: COLOR_TEXT }}>Are you sure want to quit ?</div>
<div
style={{
margin: '2rem',
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
gap: '3rem',
}}
>
<IonButton
color="dark"
shape={'round'}
style={{ minWidth: '80px' }}
onClick={() => setShowConfirmUserExit(false)}
>
No
</IonButton>
<IonButton
fill="outline"
shape={'round'}
style={{ minWidth: '80px', color: COLOR_TEXT }}
color="dark"
onClick={() => {
setShowConfirmUserExit(false);
setListeningPracticeInProgress(false);
setMatchingFrenzyInProgress(false);
setConnectiveRevisionInProgress(false);
//
resetListeningPracticeCorrectionList();
//
setTabActive(url_push_after_user_confirm);
//
router.push(url_push_after_user_confirm, 'none', 'replace');
}}
>
Yes
</IonButton>
</div>
</div>
</IonContent>
</IonModal>
</>
);
}
export default ConfirmUserExit;

View File

@@ -0,0 +1,86 @@
import { IonButton, IonContent, IonModal, useIonRouter } from '@ionic/react';
import { Dispatch, FunctionComponent, SetStateAction, useRef } from 'react';
import { COLOR_TEXT } from '../../constants';
import { useAppStateContext } from '../../contexts/AppState';
import { useMyIonQuizContext } from '../../contexts/MyIonQuiz';
interface AudioSeekBarProps {
show_confirm_user_exit: boolean;
setShowConfirmUserExit: Dispatch<SetStateAction<boolean>>;
url_push_after_user_confirm: string;
}
const ConfirmUserQuitQuiz: FunctionComponent<AudioSeekBarProps> = ({
show_confirm_user_exit,
setShowConfirmUserExit,
//
url_push_after_user_confirm,
}) => {
let { setListeningPracticeInProgress, setConnectiveRevisionInProgress, setMatchingFrenzyInProgress } =
useAppStateContext();
let { setTabActive } = useAppStateContext();
const modal = useRef<HTMLIonModalElement>(null);
const router = useIonRouter();
const { resetListeningPracticeCorrectionList } = useMyIonQuizContext();
return (
<>
<IonModal
isOpen={show_confirm_user_exit}
ref={modal}
// trigger='open-modal'
initialBreakpoint={0.25}
breakpoints={[0, 0.25, 0.5, 0.75]}
onDidDismiss={() => setShowConfirmUserExit(false)}
>
<IonContent className="ion-padding">
<div style={{ margin: '2rem', display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
<div style={{ color: COLOR_TEXT }}>Are you sure want to quit ?</div>
<div
style={{
margin: '2rem',
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
gap: '3rem',
}}
>
<IonButton
color="dark"
shape={'round'}
style={{ minWidth: '80px' }}
onClick={() => setShowConfirmUserExit(false)}
>
No
</IonButton>
<IonButton
fill="outline"
shape={'round'}
style={{ minWidth: '80px', color: COLOR_TEXT }}
color="dark"
onClick={() => {
setShowConfirmUserExit(false);
setListeningPracticeInProgress(false);
setMatchingFrenzyInProgress(false);
setConnectiveRevisionInProgress(false);
//
resetListeningPracticeCorrectionList();
//
setTabActive(url_push_after_user_confirm);
//
router.push(url_push_after_user_confirm, 'none', 'replace');
}}
>
Yes
</IonButton>
</div>
</div>
</IonContent>
</IonModal>
</>
);
};
export default ConfirmUserQuitQuiz;

View File

@@ -0,0 +1,68 @@
import { FunctionComponent } from 'react';
import './style.css';
interface AudioSeekBarProps {
value: number;
range_list: number[];
}
const ConnectivesConquerorProgressBar: FunctionComponent<AudioSeekBarProps> = ({ value, range_list }) => {
let test_value = value;
let scale = [];
for (let i = 0; i < range_list.length; i++) {
let scale_full_range = range_list[i];
let scale_range = i > 0 ? scale_full_range - range_list[i - 1] : scale_full_range;
let fill = 0;
if (test_value > scale_full_range) {
fill = scale_full_range;
} else {
if (i > 0) {
fill = Math.max(0, test_value - range_list[i - 1]);
} else {
fill = test_value;
}
}
let fill_pct = Math.min(fill / scale_range, 1);
scale.push([fill_pct]);
}
return (
<>
<div style={{ display: 'flex', flexDirection: 'row' }}>
{scale.map((item: any, index: number) => {
return (
<div
style={{
width: `calc( 100% / ${range_list.length} )`,
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-end',
}}
>
<div style={{ fontSize: '0.5rem' }}>{range_list[index]}</div>
<div
className={`audioSeekBar playBar_t__seek`}
style={{
// border: '1px solid #000',
borderTop: '1px solid #000',
borderBottom: '1px solid #000',
borderLeft: index == 0 ? '1px solid #000' : '',
borderRight: index == scale.length - 1 ? '1px solid #000' : '',
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
}}
>
<div style={{ width: `${item * 100}%` }} className="audioSeekBar__tick" />
</div>
</div>
);
})}
</div>
</>
);
};
export default ConnectivesConquerorProgressBar;

View File

@@ -0,0 +1,29 @@
.audioSeekBar {
cursor: pointer;
background-color: white;
overflow: hidden;
}
.audioSeekBar__tick {
background-color: rgb(56, 182, 255);
height: 100%;
}
.playBar_t {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 100px;
background-color: #1c192f;
display: flex;
align-items: flex-start;
padding: 0px 50px;
}
.playBar_t__seek {
width: 100%;
height: 10px;
/* border-radius: 12px; */
margin-right: 0px;
}

View File

@@ -0,0 +1,28 @@
import { IonToast } from '@ionic/react';
import './style.css';
import { CORRECT_ANSWER_MESSAGE } from '../../constants';
import { useAppStateContext } from '../../contexts/AppState';
interface QuestionCardProps {
isOpen: boolean;
dismiss: () => void;
}
const CorrectAnswerToast: React.FC<QuestionCardProps> = ({ isOpen, dismiss }) => {
const { CORRECT_ANS_TOAST_APPEAR_TIMEOUT_S } = useAppStateContext();
return (
<>
<IonToast
className="correct-answer-toast"
isOpen={isOpen}
message={CORRECT_ANSWER_MESSAGE}
onDidDismiss={() => dismiss()}
duration={CORRECT_ANS_TOAST_APPEAR_TIMEOUT_S * 1000}
color="success"
></IonToast>
</>
);
};
export default CorrectAnswerToast;

View File

@@ -0,0 +1,9 @@
ion-toast.correct-answer-toast::part(message) {
text-align: center;
font-size: 1.5rem;
color: rgba(0, 0, 0, 0.9);
}
ion-toast.correct-answer-toast::part(container) {
bottom: 100px;
}

View File

@@ -0,0 +1,12 @@
import { IonBackdrop } from '@ionic/react';
import { useAppStateContext } from '../../contexts/AppState';
export function DisableUserTap({ children }: { children: React.ReactNode }) {
const { disable_user_tap } = useAppStateContext();
return (
<>
{disable_user_tap ? <IonBackdrop visible={true}></IonBackdrop> : null}
{children}
</>
);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,21 @@
import { App } from '@capacitor/app';
import { IonButton, IonIcon } from '@ionic/react';
import { FunctionComponent } from 'react';
import './style.css';
import { exitOutline } from 'ionicons/icons';
const ExitButton: FunctionComponent = () => {
const handleExitOnClick = () => {
App.exitApp();
};
return (
<>
<IonButton color="clear" fill="clear" shape="round" onClick={handleExitOnClick}>
<IonIcon slot={'icon-only'} color={'dark'} size={'large'} icon={exitOutline} />
</IonButton>
</>
);
};
export default ExitButton;

View File

@@ -0,0 +1,29 @@
.audioSeekBar {
cursor: pointer;
background-color: white;
overflow: hidden;
}
.audioSeekBar__tick {
background-color: rgb(56, 182, 255);
height: 100%;
}
.playBar_t {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 100px;
background-color: #1c192f;
display: flex;
align-items: flex-start;
padding: 0px 50px;
}
.playBar_t__seek {
width: 100%;
height: 10px;
/* border-radius: 12px; */
margin-right: 0px;
}

View File

@@ -0,0 +1,24 @@
.container {
text-align: center;
position: absolute;
left: 0;
right: 0;
top: 50%;
transform: translateY(-50%);
}
.container strong {
font-size: 20px;
line-height: 26px;
}
.container p {
font-size: 16px;
line-height: 22px;
color: #8c8c8c;
margin: 0;
}
.container a {
text-decoration: none;
}

View File

@@ -0,0 +1,43 @@
import './ExploreContainer.css';
import React from 'react';
import Markdown from 'react-markdown';
import remarkGfm from 'remark-gfm';
interface ContainerProps {
name: string;
}
const markdown = `
Just a link: www.nasa.gov.
- [ ] helloworld
__bold__
# h1
## h2
### h3
#### h4
##### h5
###### h6
| test | test | test |
| --- | --- | --- |
| 1 | 1 | 1 |
`;
const ExploreContainer: React.FC<ContainerProps> = ({ name }) => {
return (
<div className="container">
<strong>{name}</strong>
<p>
Explore{' '}
<a target="_blank" rel="noopener noreferrer" href="https://ionicframework.com/docs/components">
UI Components
</a>
</p>
<div>
<Markdown remarkPlugins={[remarkGfm]}>{markdown}</Markdown>
</div>
</div>
);
};
export default ExploreContainer;

View File

@@ -0,0 +1,109 @@
import { FunctionComponent } from 'react';
import './style.css';
interface AudioSeekBarProps {
value: number;
range_list: number[];
}
const GeniusProgressBar: FunctionComponent<AudioSeekBarProps> = ({ value, range_list }) => {
let test_value = value;
let scale = [];
for (let i = 0; i < range_list.length; i++) {
let scale_full_range = range_list[i];
let scale_range = i > 0 ? scale_full_range - range_list[i - 1] : scale_full_range;
let fill = 0;
if (test_value > scale_full_range) {
fill = scale_full_range;
} else {
if (i > 0) {
fill = Math.max(0, test_value - range_list[i - 1]);
} else {
fill = Math.max(0, test_value);
}
}
let fill_pct = Math.min(fill / scale_range, 1);
scale.push([fill_pct]);
}
return (
<>
<div style={{ display: 'flex', flexDirection: 'row', width: '100%' }}>
<div style={{ width: '100%', display: 'flex', flexDirection: 'row' }}>
{scale.map((item: any, index: number) => {
return (
<>
<div
style={{
width: `calc( 100% / ${range_list.length} )`,
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-end',
}}
>
<div style={{ fontSize: '0.5rem' }}>{range_list[index]}</div>
<div
className={`audioSeekBar playBar_t__seek`}
style={{
// border: '1px solid #000',
borderTop: '1px solid #000',
borderBottom: '1px solid #000',
borderLeft: index == 0 ? '1px solid #000' : '',
borderRight: index == scale.length - 1 ? '1px solid #000' : '',
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
}}
>
<div style={{ width: `${item * 100}%` }} className="audioSeekBar__tick" />
</div>
</div>
</>
);
})}
</div>
</div>
</>
);
return (
<>
<div style={{ backgroundColor: 'gold' }}>
<div>helloworld</div>
<div style={{ display: 'flex', flexDirection: 'row' }}>
{scale.map((item, index) => {
return (
<div
style={{
width: `calc( 100% / ${range_list.length} )`,
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-end',
}}
>
<div style={{ fontSize: '0.5rem' }}>{range_list[index]}</div>
<div
className={`audioSeekBar playBar_t__seek`}
style={{ borderTop: '1px solid #000', borderBottom: '1px solid #000' }}
>
<div
style={{
width: `${item * 100}%`,
borderLeft: index == 0 ? '1px solid #000' : '',
borderRight: index == 6 ? '1px solid #000' : '',
}}
className="audioSeekBar__tick"
/>
</div>
</div>
);
})}
</div>
</div>
</>
);
};
export default GeniusProgressBar;

View File

@@ -0,0 +1,29 @@
.audioSeekBar {
cursor: pointer;
background-color: white;
overflow: hidden;
}
.audioSeekBar__tick {
background-color: rgb(56, 182, 255);
height: 100%;
}
.playBar_t {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 100px;
background-color: #1c192f;
display: flex;
align-items: flex-start;
padding: 0px 50px;
}
.playBar_t__seek {
width: 100%;
height: 10px;
/* border-radius: 12px; */
margin-right: 0px;
}

View File

@@ -0,0 +1,72 @@
import { FunctionComponent } from 'react';
import './style.css';
interface AudioSeekBarProps {
value: number;
range_list: number[];
}
const HardWorkerProgressBar: FunctionComponent<AudioSeekBarProps> = ({ value, range_list }) => {
let test_value = value;
let scale = [];
for (let i = 0; i < range_list.length; i++) {
let scale_full_range = range_list[i];
let scale_range = i > 0 ? scale_full_range - range_list[i - 1] : scale_full_range;
let fill = 0;
if (test_value > scale_full_range) {
fill = scale_full_range;
} else {
if (i > 0) {
fill = Math.max(0, test_value - range_list[i - 1]);
} else {
fill = test_value;
}
}
let fill_pct = Math.min(fill / scale_range, 1);
scale.push([fill_pct]);
}
return (
<>
<div style={{ display: 'flex', flexDirection: 'row', width: '100%' }}>
<div style={{ width: '100%', display: 'flex', flexDirection: 'row' }}>
{scale.map((item: any, index: number) => {
return (
<>
<div
style={{
width: `calc( 100% / ${range_list.length} )`,
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-end',
}}
>
<div style={{ fontSize: '0.5rem' }}>{range_list[index]}</div>
<div
className={`audioSeekBar playBar_t__seek`}
style={{
// border: '1px solid #000',
borderTop: '1px solid #000',
borderBottom: '1px solid #000',
borderLeft: index == 0 ? '1px solid #000' : '',
borderRight: index == scale.length - 1 ? '1px solid #000' : '',
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
}}
>
<div style={{ width: `${item * 100}%` }} className="audioSeekBar__tick" />
</div>
</div>
</>
);
})}
</div>
</div>
</>
);
};
export default HardWorkerProgressBar;

View File

@@ -0,0 +1,29 @@
.audioSeekBar {
cursor: pointer;
background-color: white;
overflow: hidden;
}
.audioSeekBar__tick {
background-color: rgb(56, 182, 255);
height: 100%;
}
.playBar_t {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 100px;
background-color: #1c192f;
display: flex;
align-items: flex-start;
padding: 0px 50px;
}
.playBar_t__seek {
width: 100%;
height: 10px;
/* border-radius: 12px; */
margin-right: 0px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

View File

@@ -0,0 +1,9 @@
import ImageJpg from './image.jpg';
function HelloworldImage() {
return (
<div style={{ width: '100%', height: '100%', backgroundImage: `url(${ImageJpg})`, backgroundSize: 'cover' }}></div>
);
}
export { HelloworldImage };

View File

@@ -0,0 +1,26 @@
import { IonContent, IonPage, IonSpinner } from '@ionic/react';
import { useTranslation } from 'react-i18next';
export function LoadingScreen() {
const { t, i18n } = useTranslation();
return (
<IonPage>
<IonContent fullscreen>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
gap: '1rem',
//
marginTop: '33vh',
}}
>
<IonSpinner></IonSpinner>
<div>{t('Loading')}</div>
</div>
</IonContent>
</IonPage>
);
}

View File

@@ -0,0 +1,34 @@
import { IonIcon } from '@ionic/react';
import './style.css';
import { star, starOutline } from 'ionicons/icons';
interface ContainerProps {
num_rating: number;
max_rating?: number;
}
const MarkRating: React.FC<ContainerProps> = ({ num_rating, max_rating = 3 }) => {
return (
<>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: '0.5rem' }}>
{Array.from({ length: max_rating }, (_, index) => {
if (index < num_rating) {
return (
<div style={{ width: '10vw', height: '10vw' }}>
<IonIcon size="large" color="warning" icon={star}></IonIcon>
</div>
);
} else {
return (
<div style={{ width: '10vw', height: '10vw' }}>
<IonIcon size="large" color="warning" style={{ opacity: '0.3' }} icon={starOutline}></IonIcon>
</div>
);
}
})}
</div>
</>
);
};
export default MarkRating;

View File

@@ -0,0 +1,68 @@
import { FunctionComponent } from 'react';
import './style.css';
interface AudioSeekBarProps {
value: number;
range_list: number[];
}
const MatchmakingProgressBar: FunctionComponent<AudioSeekBarProps> = ({ value, range_list }) => {
let test_value = value;
let scale = [];
for (let i = 0; i < range_list.length; i++) {
let scale_full_range = range_list[i];
let scale_range = i > 0 ? scale_full_range - range_list[i - 1] : scale_full_range;
let fill = 0;
if (test_value > scale_full_range) {
fill = scale_full_range;
} else {
if (i > 0) {
fill = Math.max(0, test_value - range_list[i - 1]);
} else {
fill = test_value;
}
}
let fill_pct = Math.min(fill / scale_range, 1);
scale.push([fill_pct]);
}
return (
<>
<div style={{ display: 'flex', flexDirection: 'row' }}>
{scale.map((item: any, index: number) => {
return (
<div
style={{
width: `calc( 100% / ${range_list.length} )`,
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-end',
}}
>
<div style={{ fontSize: '0.5rem' }}>{range_list[index]}</div>
<div
className={`audioSeekBar playBar_t__seek`}
style={{
// border: '1px solid #000',
borderTop: '1px solid #000',
borderBottom: '1px solid #000',
borderLeft: index == 0 ? '1px solid #000' : '',
borderRight: index == scale.length - 1 ? '1px solid #000' : '',
display: 'flex',
flexDirection: 'column',
alignItems: 'flex-start',
}}
>
<div style={{ width: `${item * 100}%` }} className="audioSeekBar__tick" />
</div>
</div>
);
})}
</div>
</>
);
};
export default MatchmakingProgressBar;

View File

@@ -0,0 +1,29 @@
.audioSeekBar {
cursor: pointer;
background-color: white;
overflow: hidden;
}
.audioSeekBar__tick {
background-color: rgb(56, 182, 255);
height: 100%;
}
.playBar_t {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 100px;
background-color: #1c192f;
display: flex;
align-items: flex-start;
padding: 0px 50px;
}
.playBar_t__seek {
width: 100%;
height: 10px;
/* border-radius: 12px; */
margin-right: 0px;
}

View File

@@ -0,0 +1,124 @@
import { IonButton, IonIcon, IonModal } from '@ionic/react';
import { closeOutline } from 'ionicons/icons';
import { useEffect, useRef, useState } from 'react';
import { COLOR_TEXT, CONNECTIVE_CONQUEROR_STAGES, DEBUG } from '../../../../constants';
import './style.css';
import { useConnectivesRevisionCorrectCount } from '../../../../contexts/MyIonMetric/ConnectivesRevisionCorrectCount';
function CongratConnectiveConqueror({}: {}) {
const modal = useRef<HTMLIonModalElement>(null);
const [opened, setOpened] = useState(false);
// const message = '';
const [message, setMessage] = useState('');
const [openCongratListeningProgress, setOpenCongratListeningProgress] = useState(false);
const [progress, setProgress] = useState(0);
const [count_to_prompt, setCountToPrompt] = useState(0);
const {
myIonMetricGetConnectivesRevisionCorrectCount,
myIonMetricGetConnectivesRevisionCorrectCountIgnore,
myIonMetricSetConnectivesRevisionCorrectCountIgnore,
} = useConnectivesRevisionCorrectCount();
const dismiss_modal = () => {
myIonMetricSetConnectivesRevisionCorrectCountIgnore(count_to_prompt);
setOpened(false);
};
async function promptProgress(count_to_prompt: number) {
if (count_to_prompt == (await myIonMetricGetConnectivesRevisionCorrectCountIgnore()).count) return;
// setCountPrompted(count_to_prompt);
setMessage('You scored full marks ' + count_to_prompt + ' times.');
// setOpenedCongraFullmarkTimes(true);
setOpened(true);
}
useEffect(() => {
const prompt_list = CONNECTIVE_CONQUEROR_STAGES;
const b_prompt_list = Array(prompt_list.length).fill(false);
for (let i = 0; i < prompt_list.length; i++) {
if (progress > prompt_list[i]) {
b_prompt_list[i] = true;
}
}
let lastTrueIndex = -1;
for (let i = b_prompt_list.length - 1; i >= 0; i--) {
if (b_prompt_list[i]) {
lastTrueIndex = i;
break;
}
}
if (lastTrueIndex == -1) {
DEBUG ? console.log('prompt ignored') : '';
} else {
setCountToPrompt(prompt_list[lastTrueIndex]);
promptProgress(prompt_list[lastTrueIndex]);
}
}, [progress]);
useEffect(() => {
(async () => {
let temp = await myIonMetricGetConnectivesRevisionCorrectCount();
setProgress(temp.count);
})();
}, []);
return (
<>
<IonModal
id="congratulation-listening-progress-modal"
ref={modal}
trigger="open-custom-dialog"
onIonModalWillDismiss={() => dismiss_modal()}
// 008_remove_achievement_announcement, hide congratulation modal
isOpen={false}
>
<div
style={{
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '1rem',
color: COLOR_TEXT,
}}
>
{DEBUG ? <span className={'debug'}>congratulation connectives conqueror</span> : <></>}
<div>
<h1>Congratulation!</h1>
</div>
<div>{'Genius'}</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '0.25rem',
}}
>
<div>{message}</div>
</div>
</div>
<IonButton
color="light"
shape="round"
style={{ position: 'absolute', top: '5px', right: '5px' }}
onClick={() => dismiss_modal()}
>
<IonIcon slot="icon-only" icon={closeOutline}></IonIcon>
</IonButton>
</IonModal>
</>
);
}
export default CongratConnectiveConqueror;

View File

@@ -0,0 +1,21 @@
.bold {
font-weight: bold;
}
ion-modal#congratulation-listening-progress-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#congratulation-listening-progress-modal::part(backdrop) {
/* background: rgba(209, 213, 219); */
opacity: 1;
}
ion-modal#congratulation-listening-progress-modal ion-toolbar {
/* --background: rgb(14 116 144); */
/* --color: white; */
--color: black;
}

View File

@@ -0,0 +1,122 @@
import { IonButton, IonIcon, IonModal } from '@ionic/react';
import { closeOutline } from 'ionicons/icons';
import { useEffect, useRef, useState } from 'react';
import { COLOR_TEXT, DEBUG, GENIUS_STAGES } from '../../../../constants';
import './style.css';
import { useAppStateContext } from '../../../../contexts/AppState';
import { useFullmarkCount } from '../../../../contexts/MyIonMetric/FullmarkCount';
function CongratGenius({}: {}) {
const modal = useRef<HTMLIonModalElement>(null);
const [opened, setOpened] = useState(false);
// const message = '';
const [message, setMessage] = useState('');
const [progress, setProgress] = useState(0);
const [count_to_prompt, setCountToPrompt] = useState(0);
const { myIonMetricGetFullMarkCount, myIonMetricGetFullMarkIgnore, myIonMetricSetFullMarkIgnore } =
useFullmarkCount();
const dismiss_modal = () => {
myIonMetricSetFullMarkIgnore(count_to_prompt);
setOpened(false);
};
async function promptGeniusProgress(count_to_prompt: number) {
if (count_to_prompt == (await myIonMetricGetFullMarkIgnore()).count) return;
// setCountPrompted(count_to_prompt);
setMessage('You scored full marks ' + count_to_prompt + ' times.');
// setOpenedCongraFullmarkTimes(true);
setOpened(true);
}
useEffect(() => {
const prompt_list = GENIUS_STAGES;
const b_prompt_list = Array(prompt_list.length).fill(false);
for (let i = 0; i < prompt_list.length; i++) {
if (progress >= prompt_list[i]) {
b_prompt_list[i] = true;
}
}
let lastTrueIndex = -1;
for (let i = b_prompt_list.length - 1; i >= 0; i--) {
if (b_prompt_list[i]) {
lastTrueIndex = i;
break;
}
}
if (lastTrueIndex == -1) {
DEBUG ? console.log('prompt ignored') : '';
} else {
console.log({ progress });
setCountToPrompt(prompt_list[lastTrueIndex]);
promptGeniusProgress(prompt_list[lastTrueIndex]);
}
}, [progress]);
let { tab_active } = useAppStateContext();
useEffect(() => {
(async () => {
let temp = await myIonMetricGetFullMarkCount();
setProgress(temp.count);
})();
}, []);
return (
<>
<IonModal
id="congratulation-listening-progress-modal"
ref={modal}
trigger="open-custom-dialog"
onIonModalWillDismiss={() => dismiss_modal()}
// 008_remove_achievement_announcement, hide congratulation modal
isOpen={false}
>
<div
style={{
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '1rem',
color: COLOR_TEXT,
}}
>
{DEBUG ? <span className={'debug'}>congratulation genius</span> : <></>}
<div>
<h1>Congratulation!</h1>
</div>
<div>{'Genius'}</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '0.25rem',
}}
>
<div>{message}</div>
</div>
</div>
<IonButton
color="light"
shape="round"
style={{ position: 'absolute', top: '5px', right: '5px' }}
onClick={() => dismiss_modal()}
>
<IonIcon slot="icon-only" icon={closeOutline}></IonIcon>
</IonButton>
</IonModal>
</>
);
}
export default CongratGenius;

View File

@@ -0,0 +1,21 @@
.bold {
font-weight: bold;
}
ion-modal#congratulation-listening-progress-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#congratulation-listening-progress-modal::part(backdrop) {
/* background: rgba(209, 213, 219); */
opacity: 1;
}
ion-modal#congratulation-listening-progress-modal ion-toolbar {
/* --background: rgb(14 116 144); */
/* --color: white; */
--color: black;
}

View File

@@ -0,0 +1,125 @@
import { IonButton, IonIcon, IonModal } from '@ionic/react';
import { closeOutline } from 'ionicons/icons';
import { useEffect, useRef, useState } from 'react';
import { COLOR_TEXT, DEBUG, HARDWORKER_STAGES } from '../../../../constants';
import './style.css';
import { useAppUseTime } from '../../../../contexts/MyIonMetric/AppUseTime';
function CongratHardworker({}: {}) {
const modal = useRef<HTMLIonModalElement>(null);
const [opened, setOpened] = useState(false);
// const message = '';
const [message, setMessage] = useState('');
const [progress, setProgress] = useState(0);
const [count_to_prompt, setCountToPrompt] = useState(0);
const {
//
myIonMetricGetAppUseTime,
myIonMetricSetAppUseTimeIgnore,
myIonMetricGetAppUseTimeIgnore,
} = useAppUseTime();
const dismiss_modal = () => {
myIonMetricSetAppUseTimeIgnore(count_to_prompt);
setOpened(false);
};
async function promptHardworkerProgress(count_to_prompt: number) {
if (count_to_prompt == (await myIonMetricGetAppUseTimeIgnore()).time_spent_s) return;
// setCountPrompted(count_to_prompt);
setMessage('You scored full marks ' + count_to_prompt + ' times.');
// setOpenedCongraFullmarkTimes(true);
setOpened(true);
}
useEffect(() => {
const prompt_list = HARDWORKER_STAGES;
const b_prompt_list = Array(prompt_list.length).fill(false);
for (let i = 0; i < prompt_list.length; i++) {
if (progress >= prompt_list[i]) {
b_prompt_list[i] = true;
}
}
let lastTrueIndex = -1;
for (let i = b_prompt_list.length - 1; i >= 0; i--) {
if (b_prompt_list[i]) {
lastTrueIndex = i;
break;
}
}
if (lastTrueIndex == -1) {
DEBUG ? console.log('prompt ignored') : '';
} else {
console.log({ progress });
setCountToPrompt(prompt_list[lastTrueIndex]);
promptHardworkerProgress(prompt_list[lastTrueIndex]);
}
}, [progress]);
useEffect(() => {
(async () => {
let temp = await myIonMetricGetAppUseTime();
setProgress(Math.floor(temp.time_spent_s / 60));
})();
}, []);
return (
<>
<IonModal
id="congratulation-listening-progress-modal"
ref={modal}
trigger="open-custom-dialog"
onIonModalWillDismiss={() => dismiss_modal()}
// 008_remove_achievement_announcement, hide congratulation modal
isOpen={false}
>
<div
style={{
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '1rem',
color: COLOR_TEXT,
}}
>
{DEBUG ? <span className={'debug'}>congratulation hardworker</span> : <></>}
<div>
<h1>Congratulation!</h1>
</div>
<div>{'Genius'}</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '0.25rem',
}}
>
<div>{message}</div>
</div>
</div>
<IonButton
color="light"
shape="round"
style={{ position: 'absolute', top: '5px', right: '5px' }}
onClick={() => dismiss_modal()}
>
<IonIcon slot="icon-only" icon={closeOutline}></IonIcon>
</IonButton>
</IonModal>
</>
);
}
export default CongratHardworker;

View File

@@ -0,0 +1,21 @@
.bold {
font-weight: bold;
}
ion-modal#congratulation-listening-progress-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#congratulation-listening-progress-modal::part(backdrop) {
/* background: rgba(209, 213, 219); */
opacity: 1;
}
ion-modal#congratulation-listening-progress-modal ion-toolbar {
/* --background: rgb(14 116 144); */
/* --color: white; */
--color: black;
}

View File

@@ -0,0 +1,78 @@
import { IonButton, IonIcon, IonModal } from '@ionic/react';
import { closeOutline } from 'ionicons/icons';
import { useEffect, useRef, useState } from 'react';
import { COLOR_TEXT } from '../../../../constants';
import './style.css';
function CongratHelloworld({}: {}) {
const modal = useRef<HTMLIonModalElement>(null);
const [opened, setOpened] = useState(false);
// const message = '';
const [message, setMessage] = useState('');
const [openCongratListeningProgress, setOpenCongratListeningProgress] = useState(false);
const [progress, setProgress] = useState(0);
const [count_to_prompt, setCountToPrompt] = useState(0);
const dismiss_modal = () => {
setOpened(false);
};
useEffect(() => {}, [progress]);
useEffect(() => {
(async () => {})();
}, []);
return (
<>
<IonModal
id="congratulation-listening-progress-modal"
ref={modal}
trigger="open-custom-dialog"
onIonModalWillDismiss={() => dismiss_modal()}
isOpen={opened}
>
<div
style={{
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '1rem',
color: COLOR_TEXT,
}}
>
<div>
<h1>Congratulation!</h1>
</div>
<div>{'Genius'}</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '0.25rem',
}}
>
<div>{message}</div>
</div>
</div>
<IonButton
color="light"
shape="round"
style={{ position: 'absolute', top: '5px', right: '5px' }}
onClick={() => dismiss_modal()}
>
<IonIcon slot="icon-only" icon={closeOutline}></IonIcon>
</IonButton>
</IonModal>
</>
);
}
export default CongratHelloworld;

View File

@@ -0,0 +1,21 @@
.bold {
font-weight: bold;
}
ion-modal#congratulation-listening-progress-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#congratulation-listening-progress-modal::part(backdrop) {
/* background: rgba(209, 213, 219); */
opacity: 1;
}
ion-modal#congratulation-listening-progress-modal ion-toolbar {
/* --background: rgb(14 116 144); */
/* --color: white; */
--color: black;
}

View File

@@ -0,0 +1,124 @@
import { IonButton, IonIcon, IonModal } from '@ionic/react';
import { closeOutline } from 'ionicons/icons';
import { useEffect, useRef, useState } from 'react';
import { ATTENTIVE_EARS_STAGES, COLOR_TEXT, DEBUG } from '../../../../constants';
import { useListeningPracticeTimeSpent } from '../../../../contexts/MyIonMetric/ListeningPracticeTimeSpent';
import './style.css';
function CongratListeningProgress({}: {}) {
const modal = useRef<HTMLIonModalElement>(null);
const [opened, setOpened] = useState(false);
// const message = '';
const [message, setMessage] = useState('');
const [openCongratListeningProgress, setOpenCongratListeningProgress] = useState(false);
const [progress, setProgress] = useState(0);
const [count_to_prompt, setCountToPrompt] = useState(0);
const {
myIonMetricGetListeningPracticeTimeSpent,
myIonMetricGetListeningPracticeProgressIgnore,
myIonMetricSetListeningPracticeProgressIgnore,
} = useListeningPracticeTimeSpent();
const dismiss_modal = () => {
myIonMetricSetListeningPracticeProgressIgnore(count_to_prompt);
setOpened(false);
};
async function promptListeningPracticeProgress(count_to_prompt: number) {
if (count_to_prompt == (await myIonMetricGetListeningPracticeProgressIgnore()).time_spent_s) return;
// setCountPrompted(count_to_prompt);
setMessage('You scored full marks ' + count_to_prompt + ' times.');
// setOpenedCongraFullmarkTimes(true);
setOpened(true);
}
useEffect(() => {
const prompt_list = ATTENTIVE_EARS_STAGES;
const b_prompt_list = Array(prompt_list.length).fill(false);
for (let i = 0; i < prompt_list.length; i++) {
if (progress > prompt_list[i]) {
b_prompt_list[i] = true;
}
}
let lastTrueIndex = -1;
for (let i = b_prompt_list.length - 1; i >= 0; i--) {
if (b_prompt_list[i]) {
lastTrueIndex = i;
break;
}
}
if (lastTrueIndex == -1) {
DEBUG ? console.log('prompt ignored') : '';
} else {
setCountToPrompt(prompt_list[lastTrueIndex]);
promptListeningPracticeProgress(prompt_list[lastTrueIndex]);
}
}, [progress]);
useEffect(() => {
(async () => {
let temp = await myIonMetricGetListeningPracticeTimeSpent();
setProgress(temp.time_spent_s / 60);
})();
}, []);
return (
<>
<IonModal
id="congratulation-listening-progress-modal"
ref={modal}
trigger="open-custom-dialog"
onIonModalWillDismiss={() => dismiss_modal()}
// 008_remove_achievement_announcement, hide congratulation modal
isOpen={false}
>
<div
style={{
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '1rem',
color: COLOR_TEXT,
}}
>
{DEBUG ? <span className={'debug'}>congratulation listening progress</span> : <></>}
<div>
<h1>Congratulation!</h1>
</div>
<div>{'Genius'}</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '0.25rem',
}}
>
<div>{message}</div>
</div>
</div>
<IonButton
color="light"
shape="round"
style={{ position: 'absolute', top: '5px', right: '5px' }}
onClick={() => dismiss_modal()}
>
<IonIcon slot="icon-only" icon={closeOutline}></IonIcon>
</IonButton>
</IonModal>
</>
);
}
export default CongratListeningProgress;

View File

@@ -0,0 +1,21 @@
.bold {
font-weight: bold;
}
ion-modal#congratulation-listening-progress-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#congratulation-listening-progress-modal::part(backdrop) {
/* background: rgba(209, 213, 219); */
opacity: 1;
}
ion-modal#congratulation-listening-progress-modal ion-toolbar {
/* --background: rgb(14 116 144); */
/* --color: white; */
--color: black;
}

View File

@@ -0,0 +1,124 @@
import { IonButton, IonIcon, IonModal } from '@ionic/react';
import { closeOutline } from 'ionicons/icons';
import { useEffect, useRef, useState } from 'react';
import { COLOR_TEXT, DEBUG, MATCHMAKING_STAGES } from '../../../../constants';
import './style.css';
import { useMatchingFrenzyCorrectCount } from '../../../../contexts/MyIonMetric/MatchingFrenzyCorrectCount';
function CongratMatchmaking({}: {}) {
const modal = useRef<HTMLIonModalElement>(null);
const [opened, setOpened] = useState(false);
// const message = '';
const [message, setMessage] = useState('');
const [openCongratListeningProgress, setOpenCongratListeningProgress] = useState(false);
const [progress, setProgress] = useState(0);
const [count_to_prompt, setCountToPrompt] = useState(0);
const {
myIonMetricGetMatchingFrenzyCorrectCount,
myIonMetricGetMatchingFrenzyCorrectCountIgnore,
myIonMetricSetMatchingFrenzyCorrectCountIgnore,
} = useMatchingFrenzyCorrectCount();
const dismiss_modal = () => {
myIonMetricSetMatchingFrenzyCorrectCountIgnore(count_to_prompt);
setOpened(false);
};
async function promptProgress(count_to_prompt: number) {
if (count_to_prompt == (await myIonMetricGetMatchingFrenzyCorrectCountIgnore()).count) return;
// setCountPrompted(count_to_prompt);
setMessage('You scored full marks ' + count_to_prompt + ' times.');
// setOpenedCongraFullmarkTimes(true);
setOpened(true);
}
useEffect(() => {
const prompt_list = MATCHMAKING_STAGES;
const b_prompt_list = Array(prompt_list.length).fill(false);
for (let i = 0; i < prompt_list.length; i++) {
if (progress > prompt_list[i]) {
b_prompt_list[i] = true;
}
}
let lastTrueIndex = -1;
for (let i = b_prompt_list.length - 1; i >= 0; i--) {
if (b_prompt_list[i]) {
lastTrueIndex = i;
break;
}
}
if (lastTrueIndex == -1) {
DEBUG ? console.log('prompt ignored') : '';
} else {
setCountToPrompt(prompt_list[lastTrueIndex]);
promptProgress(prompt_list[lastTrueIndex]);
}
}, [progress]);
useEffect(() => {
(async () => {
let temp = await myIonMetricGetMatchingFrenzyCorrectCount();
setProgress(temp.count);
})();
}, []);
return (
<>
<IonModal
id="congratulation-listening-progress-modal"
ref={modal}
trigger="open-custom-dialog"
onIonModalWillDismiss={() => dismiss_modal()}
// 008_remove_achievement_announcement, hide congratulation modal
isOpen={false}
>
<div
style={{
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '1rem',
color: COLOR_TEXT,
}}
>
{DEBUG ? <span className={'debug'}>congratulation matchmaking</span> : <></>}
<div>
<h1>Congratulation!</h1>
</div>
<div>{'Genius'}</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '0.25rem',
}}
>
<div>{message}</div>
</div>
</div>
<IonButton
color="light"
shape="round"
style={{ position: 'absolute', top: '5px', right: '5px' }}
onClick={() => dismiss_modal()}
>
<IonIcon slot="icon-only" icon={closeOutline}></IonIcon>
</IonButton>
</IonModal>
</>
);
}
export default CongratMatchmaking;

View File

@@ -0,0 +1,21 @@
.bold {
font-weight: bold;
}
ion-modal#congratulation-listening-progress-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#congratulation-listening-progress-modal::part(backdrop) {
/* background: rgba(209, 213, 219); */
opacity: 1;
}
ion-modal#congratulation-listening-progress-modal ion-toolbar {
/* --background: rgb(14 116 144); */
/* --color: white; */
--color: black;
}

View File

@@ -0,0 +1,78 @@
import { IonButton, IonIcon, IonModal } from '@ionic/react';
import { closeOutline } from 'ionicons/icons';
import { useRef } from 'react';
import { COLOR_TEXT } from '../../../constants';
import { useFullmarkCount } from '../../../contexts/MyIonMetric/FullmarkCount';
function CongratulationAchievementModal({
opened,
setOpened,
count_prompted,
message = 'hello message',
}: {
message?: string;
opened: boolean;
count_prompted: number;
setOpened: (opened: boolean) => void;
}) {
const modal = useRef<HTMLIonModalElement>(null);
const { myIonMetricSetFullMarkIgnore } = useFullmarkCount();
const dismiss_modal = () => {
myIonMetricSetFullMarkIgnore(count_prompted);
setOpened(false);
};
return (
<>
<IonModal
id="example-modal"
ref={modal}
trigger="open-custom-dialog"
onIonModalWillDismiss={() => dismiss_modal()}
isOpen={opened}
>
<div
style={{
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '1rem',
color: COLOR_TEXT,
}}
>
<div>
<h1>Congratulation!</h1>
</div>
<div>{'Genius'}</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '0.25rem',
}}
>
<div>{message}</div>
</div>
</div>
<IonButton
color="light"
shape="round"
style={{ position: 'absolute', top: '5px', right: '5px' }}
onClick={() => dismiss_modal()}
>
<IonIcon slot="icon-only" icon={closeOutline}></IonIcon>
</IonButton>
</IonModal>
</>
);
}
export default CongratulationAchievementModal;

View File

@@ -0,0 +1,58 @@
import { IonButton, IonIcon, IonModal } from '@ionic/react';
import { closeOutline } from 'ionicons/icons';
import { useRef, useState } from 'react';
import { COLOR_TEXT } from '../../../constants';
function HelloworldModal() {
const modal = useRef<HTMLIonModalElement>(null);
const [opened, setOpened] = useState(true);
return (
<>
<IonButton onClick={() => setOpened(true)}>Open</IonButton>
<IonModal isOpen={opened} id="example-modal" ref={modal} trigger="open-custom-dialog">
<div
style={{
height: '100%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '1rem',
color: COLOR_TEXT,
}}
>
<div>
<h1>Congratulation!</h1>
</div>
<div>{'Genius'}</div>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '0.25rem',
}}
>
<div>You scored full</div>
<div>marks {'1000'} times!</div>
</div>
</div>
<IonButton
color="light"
shape="round"
style={{ position: 'absolute', top: '5px', right: '5px' }}
onClick={() => setOpened(false)}
>
<IonIcon slot="icon-only" icon={closeOutline}></IonIcon>
</IonButton>
</IonModal>
</>
);
}
export default HelloworldModal;

View File

@@ -0,0 +1,87 @@
import { FunctionComponent } from 'react';
import './style.css';
interface AudioSeekBarProps {
value: number;
full_range: number;
}
const MyProgressBar: FunctionComponent<AudioSeekBarProps> = ({ value, full_range }) => {
let test_value = value;
let first_scale_full_range = 10;
let first_scale = test_value > first_scale_full_range ? first_scale_full_range : test_value;
//
let second_scale_full_range = 50;
let second_scale_range = second_scale_full_range - first_scale_full_range;
let second_scale =
test_value >= second_scale_full_range ? second_scale_full_range : Math.max(0, test_value - first_scale_full_range);
let third_scale_full_range = 100;
let third_scale_range = third_scale_full_range - second_scale_full_range;
let third_scale =
test_value >= third_scale_full_range ? third_scale_full_range : Math.max(0, test_value - second_scale_full_range);
let forth_scale_full_range = 300;
let forth_scale_range = forth_scale_full_range - third_scale_full_range;
let forth_scale =
test_value >= forth_scale_full_range ? forth_scale_full_range : Math.max(0, test_value - third_scale_full_range);
let fifth_scale_full_range = 700;
let fifth_scale_range = fifth_scale_full_range - forth_scale_full_range;
let fifth_scale =
test_value >= fifth_scale_full_range ? fifth_scale_full_range : Math.max(0, test_value - forth_scale_full_range);
let sixth_scale_full_range = 1000;
let sixth_scale_range = sixth_scale_full_range - fifth_scale_full_range;
let sixth_scale =
test_value >= sixth_scale_full_range ? sixth_scale_full_range : Math.max(0, test_value - fifth_scale_full_range);
return (
<>
<div style={{ display: 'flex', flexDirection: 'row' }}>
<div style={{ width: 'calc( 100% / 6 )', display: 'flex', flexDirection: 'column', alignItems: 'flex-end' }}>
<div style={{ fontSize: '0.5rem' }}>{first_scale_full_range}</div>
<div className={`audioSeekBar playBar_t__seek`}>
<div style={{ width: `${(first_scale / first_scale_full_range) * 100}%` }} className="audioSeekBar__tick" />
</div>
</div>
<div style={{ width: 'calc( 100% / 6 )', display: 'flex', flexDirection: 'column', alignItems: 'flex-end' }}>
<div style={{ fontSize: '0.5rem' }}>{second_scale_full_range}</div>
<div className={`audioSeekBar playBar_t__seek`}>
<div style={{ width: `${(second_scale / second_scale_range) * 100}%` }} className="audioSeekBar__tick" />
</div>
</div>
<div style={{ width: 'calc( 100% / 6 )', display: 'flex', flexDirection: 'column', alignItems: 'flex-end' }}>
<div style={{ fontSize: '0.5rem' }}>{third_scale_full_range}</div>
<div className={`audioSeekBar playBar_t__seek`}>
<div style={{ width: `${(third_scale / third_scale_range) * 100}%` }} className="audioSeekBar__tick" />
</div>
</div>
<div style={{ width: 'calc( 100% / 6 )', display: 'flex', flexDirection: 'column', alignItems: 'flex-end' }}>
<div style={{ fontSize: '0.5rem' }}>{forth_scale_full_range}</div>
<div className={`audioSeekBar playBar_t__seek`}>
<div style={{ width: `${(forth_scale / forth_scale_range) * 100}%` }} className="audioSeekBar__tick" />
</div>
</div>
<div style={{ width: 'calc( 100% / 6 )', display: 'flex', flexDirection: 'column', alignItems: 'flex-end' }}>
<div style={{ fontSize: '0.5rem' }}>{fifth_scale_full_range}</div>
<div className={`audioSeekBar playBar_t__seek`}>
<div style={{ width: `${(fifth_scale / fifth_scale_range) * 100}%` }} className="audioSeekBar__tick" />
</div>
</div>
<div style={{ width: 'calc( 100% / 6 )', display: 'flex', flexDirection: 'column', alignItems: 'flex-end' }}>
<div style={{ fontSize: '0.5rem' }}>{sixth_scale_full_range}</div>
<div className={`audioSeekBar playBar_t__seek`}>
<div style={{ width: `${(sixth_scale / sixth_scale_range) * 100}%` }} className="audioSeekBar__tick" />
</div>
</div>
</div>
</>
);
};
export default MyProgressBar;

View File

@@ -0,0 +1,29 @@
.audioSeekBar {
cursor: pointer;
background-color: white;
overflow: hidden;
}
.audioSeekBar__tick {
background-color: rgb(56, 182, 255);
height: 100%;
}
.playBar_t {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 100px;
background-color: #1c192f;
display: flex;
align-items: flex-start;
padding: 0px 50px;
}
.playBar_t__seek {
width: 100%;
height: 10px;
/* border-radius: 12px; */
margin-right: 0px;
}

View File

@@ -0,0 +1,19 @@
import { FunctionComponent } from 'react';
import './style.css';
interface AudioSeekBarProps {
value: number;
full_range: number;
}
const MyProgressBar: FunctionComponent<AudioSeekBarProps> = ({ value, full_range }) => {
return (
<>
<div className={`audioSeekBar playBar_t__seek`}>
<div style={{ width: `${(value / full_range) * 100}%` }} className="audioSeekBar__tick" />
</div>
</>
);
};
export default MyProgressBar;

View File

@@ -0,0 +1,29 @@
.audioSeekBar {
cursor: pointer;
background-color: white;
overflow: hidden;
}
.audioSeekBar__tick {
background-color: #999900;
height: 100%;
}
.playBar_t {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 100px;
background-color: #1c192f;
display: flex;
align-items: flex-start;
padding: 25px 50px;
}
.playBar_t__seek {
width: 100%;
height: 10px;
border-radius: 12px;
margin-right: 10px;
}

View File

@@ -0,0 +1,140 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="649.3779"
height="727.77798"
viewBox="0 0 649.3779 727.77798"
xmlns:xlink="http://www.w3.org/1999/xlink"
role="img"
artist="Katerina Limpitsouni"
source="https://undraw.co/"
><path
d="M648.20033,259.05824h-3.99878V149.51291A63.40187,63.40187,0,0,0,580.79976,86.111H348.713a63.40184,63.40184,0,0,0-63.402,63.4017V750.48713A63.40181,63.40181,0,0,0,348.71284,813.889H580.79945a63.40185,63.40185,0,0,0,63.402-63.40167V337.0345h3.99884Z"
transform="translate(-285.31105 -86.11101)"
fill="#e6e6e6"
/><path
d="M583.3578,102.606h-30.295a22.49485,22.49485,0,0,1-20.82715,30.99053H399.2762a22.49484,22.49484,0,0,1-20.82715-30.99061H350.15346a47.34781,47.34781,0,0,0-47.34784,47.34774V750.04628a47.34781,47.34781,0,0,0,47.34777,47.34784H583.3578a47.34781,47.34781,0,0,0,47.34784-47.34778h0V149.95371A47.34777,47.34777,0,0,0,583.3578,102.606Z"
transform="translate(-285.31105 -86.11101)"
fill="#fff"
/><path
id="f3818c68-126c-4685-b4e0-2450731ccc2a-761"
data-name="a2804879-ded6-4045-b20f-1f1dde9b938b"
d="M611.46248,573.92982h-279.619a5.184,5.184,0,0,1-5.178-5.178v-69.361a5.184,5.184,0,0,1,5.178-5.178h279.619a5.184,5.184,0,0,1,5.178,5.178v69.362A5.184,5.184,0,0,1,611.46248,573.92982Zm-279.619-77.646a3.11,3.11,0,0,0-3.107,3.107v69.362a3.11,3.11,0,0,0,3.107,3.107h279.619a3.11,3.11,0,0,0,3.107-3.107v-69.362a3.11,3.11,0,0,0-3.107-3.107Z"
transform="translate(-285.31105 -86.11101)"
fill="#e6e6e6"
/><circle
id="abdb74b7-e321-430b-89c2-b563f66442fc"
data-name="b9ad11c9-d8a0-4df6-8741-900b9ec46a35"
cx="82.77841"
cy="447.95985"
r="21.74799"
fill="#e6e6e6"
/><path
id="addd02b1-b85b-481a-baea-1b0ba5ed9f4a-762"
data-name="bd261eec-7ae0-49b0-bf26-57ff03972605"
d="M418.31746,519.57286a3.625,3.625,0,0,0,0,7.249h170.878a3.625,3.625,0,0,0,.13989-7.24866l-.02087-.00033q-.05943-.001-.119,0Z"
transform="translate(-285.31105 -86.11101)"
fill="#e6e6e6"
/><path
id="a42dc2a4-5fb2-4ea8-b2b3-ce81bc256782-763"
data-name="e80b4447-8c34-435b-ba6c-5300a190ab24"
d="M418.31746,541.32087a3.625,3.625,0,0,0,0,7.249h170.878a3.625,3.625,0,0,0,.119-7.249q-.05943-.00092-.119,0Z"
transform="translate(-285.31105 -86.11101)"
fill="#e6e6e6"
/><path
id="b84687c4-e3b4-4975-8361-bf73c33c9ee5-764"
data-name="e55fcb7d-3a3b-45d8-b167-72fb2263dd92"
d="M611.46248,689.91981h-279.619a5.184,5.184,0,0,1-5.178-5.178v-69.361a5.184,5.184,0,0,1,5.178-5.178h279.619a5.184,5.184,0,0,1,5.178,5.178v69.362A5.184,5.184,0,0,1,611.46248,689.91981Zm-279.619-77.646a3.11,3.11,0,0,0-3.107,3.107v69.362a3.11,3.11,0,0,0,3.107,3.107h279.619a3.11,3.11,0,0,0,3.107-3.107v-69.362a3.11,3.11,0,0,0-3.107-3.107Z"
transform="translate(-285.31105 -86.11101)"
fill="#e6e6e6"
/><circle
id="fe3713e7-4e14-41f8-af1d-48b338e5371c"
data-name="a50d232f-7710-43e4-8fa9-6ef0443fc454"
cx="82.77841"
cy="563.94987"
r="21.74799"
fill="#e6e6e6"
/><path
id="e34cf46a-1d6c-4c41-b5e1-331fa5bf8d4c-765"
data-name="e49b4965-a9e9-4371-9134-194e44e65c31"
d="M418.31746,635.56288a3.625,3.625,0,0,0,0,7.249h170.878a3.625,3.625,0,0,0,.119-7.249q-.05943-.001-.119,0Z"
transform="translate(-285.31105 -86.11101)"
fill="#e6e6e6"
/><path
id="e105039f-b7a6-49c8-8f81-87505f1b0ae5-766"
data-name="abc4586a-ac92-4255-aae6-84f53baad571"
d="M418.31746,657.31086a3.625,3.625,0,0,0,0,7.249h170.878a3.625,3.625,0,0,0,.119-7.249q-.05943-.00092-.119,0Z"
transform="translate(-285.31105 -86.11101)"
fill="#e6e6e6"
/><path
id="ad9187ec-89e0-4b9d-a4fb-dc654c09bafe-767"
data-name="a9e593af-a319-4e97-9065-f0c2c04624d5"
d="M465.76845,429.99485a98.343,98.343,0,0,1-98.384-98.30194v-.08206c0-.206,0-.423.012-.629.3-53.879,44.432-97.756,98.372-97.756a98.384,98.384,0,0,1,.0224,196.768h-.0224Zm0-194.7a96.519,96.519,0,0,0-96.3,95.749c-.011.22-.011.4-.011.564a96.325,96.325,0,1,0,96.337-96.313h-.026Z"
transform="translate(-285.31105 -86.11101)"
fill="#e6e6e6"
/><circle cx="315.11393" cy="422.84174" r="40" fill="#fff" /><path
d="M586.53265,526.94884c-.06861,0-.13721-.00049-.20606-.00195h-21.5a10.39761,10.39761,0,0,1-.0083-20.79248h21.51807c.10547-.00195.22021-.00195.334,0a10.39771,10.39771,0,0,1-.13769,20.79443Z"
transform="translate(-285.31105 -86.11101)"
fill="#6c63ff"
/><rect x="325.3453" y="725.34374" width="324.03261" height="2.24072" fill="#3f3d56" /><path
d="M630.6207,621.842a6.56111,6.56111,0,0,1-.62973-1.259l-5.16339-13.82657a6.50779,6.50779,0,0,1,3.81532-8.36324l115.24228-43.03187a6.50621,6.50621,0,0,1,8.363,3.81491l5.16283,13.82751a6.49951,6.49951,0,0,1-3.81558,8.36283l-115.2412,43.03174a6.5067,6.5067,0,0,1-7.73351-2.55634Z"
transform="translate(-285.31105 -86.11101)"
fill="#6c63ff"
/><path
d="M743.51834,551.78826l-46.86323,18.68723a6,6,0,0,0-4.093,7.1996l5.43852,21.77515a6,6,0,0,0,8.37642,3.97481l46.99241-22.1188a6.0109,6.0109,0,0,0,3.52446-7.717l-5.64883-18.27558A6.01072,6.01072,0,0,0,743.51834,551.78826Z"
transform="translate(-285.31105 -86.11101)"
fill="#2f2e41"
/><path
d="M829.28044,687.45a6.50671,6.50671,0,0,1-6.89226-4.34039l-40.114-116.289a6.49951,6.49951,0,0,1,4.02482-8.26416l13.95295-4.81352a6.50408,6.50408,0,0,1,8.26373,4.02536l40.11465,116.2899a6.50779,6.50779,0,0,1-4.02531,8.26422l-13.95257,4.8125A6.56115,6.56115,0,0,1,829.28044,687.45Z"
transform="translate(-285.31105 -86.11101)"
fill="#6c63ff"
/><path
d="M811.5058,555.77814l15.39577,48.04524a6,6,0,0,1-2.95088,7.73816l-20.26024,9.657a6,6,0,0,1-8.39566-3.934l-12.83035-50.328a6.0109,6.0109,0,0,1,3.71178-7.62871l17.694-7.26835A6.01071,6.01071,0,0,1,811.5058,555.77814Z"
transform="translate(-285.31105 -86.11101)"
fill="#2f2e41"
/><path
d="M764.89068,812.11413H750.13141a6.50753,6.50753,0,0,1-6.5-6.5V682.59948a6.50753,6.50753,0,0,1,6.5-6.5h14.75927a6.50753,6.50753,0,0,1,6.5,6.5V805.61413A6.50753,6.50753,0,0,1,764.89068,812.11413Z"
transform="translate(-285.31105 -86.11101)"
fill="#2f2e41"
/><path
d="M794.08307,812.11413H779.3233a6.50753,6.50753,0,0,1-6.5-6.5V682.59948a6.50753,6.50753,0,0,1,6.5-6.5h14.75977a6.50753,6.50753,0,0,1,6.5,6.5V805.61413A6.50753,6.50753,0,0,1,794.08307,812.11413Z"
transform="translate(-285.31105 -86.11101)"
fill="#2f2e41"
/><path
d="M806.2467,697.06158H739.1842a7.00787,7.00787,0,0,1-7-7V581.11725a40.53125,40.53125,0,0,1,81.0625,0V690.06158A7.00786,7.00786,0,0,1,806.2467,697.06158Z"
transform="translate(-285.31105 -86.11101)"
fill="#ccc"
/><path
d="M816.34118,723.711h-34V531.55621l.6316.17236a45.38181,45.38181,0,0,1,33.3684,43.6875Z"
transform="translate(-285.31105 -86.11101)"
fill="#2f2e41"
/><path
d="M760.32629,723.711h-34V575.41607a45.38226,45.38226,0,0,1,33.36841-43.6875l.63159-.17236Z"
transform="translate(-285.31105 -86.11101)"
fill="#2f2e41"
/><circle cx="486.89704" cy="378.42061" r="53.51916" fill="#6c63ff" /><path
d="M832.71545,452.21s3-83-36-56c0,0-22.5-75.5-77.5,37.5l-16,16s72-10,139,21Z"
transform="translate(-285.31105 -86.11101)"
fill="#2f2e41"
/><path
d="M773.02436,491.03636c-3.30591-.09179-7.42029-.20654-10.59-2.522a8.13272,8.13272,0,0,1-3.20008-6.07275,5.47091,5.47091,0,0,1,1.86035-4.49317c1.65552-1.39892,4.073-1.727,6.67823-.96142l-2.69922-19.72559,1.98144-.27148,3.17322,23.18994-1.65466-.75928c-1.91834-.87988-4.55164-1.32764-6.188.05518a3.51513,3.51513,0,0,0-1.15271,2.8955,6.14685,6.14685,0,0,0,2.38123,4.52783c2.46667,1.80176,5.74621,2.03418,9.46582,2.13819Z"
transform="translate(-285.31105 -86.11101)"
fill="#2f2e41"
/><rect x="459.19183" y="372.85162" width="10.77161" height="2" fill="#2f2e41" /><rect
x="493.19183"
y="372.85162"
width="10.77161"
height="2"
fill="#2f2e41"
/><path
d="M652.88209,626.94393a6.00014,6.00014,0,0,1-7.96794-2.91753l-34.511-74.38406a6,6,0,0,1,10.88547-5.0504l34.511,74.38406A6.00012,6.00012,0,0,1,652.88209,626.94393Z"
transform="translate(-285.31105 -86.11101)"
fill="#3f3d56"
/><path
d="M617.425,546.90538a4,4,0,0,1-4-4V491.95275h-50a4,4,0,0,1,0-8h54a4,4,0,0,1,4,4v54.95263A4,4,0,0,1,617.425,546.90538Z"
transform="translate(-285.31105 -86.11101)"
fill="#6c63ff"
/><path
d="M618.371,552.55988a48,48,0,1,1,23.34031-63.74348A48.05437,48.05437,0,0,1,618.371,552.55988Zm-35.35277-76.19831a36,36,0,1,0,47.80761,17.50523A36.04072,36.04072,0,0,0,583.01827,476.36157Z"
transform="translate(-285.31105 -86.11101)"
fill="#3f3d56"
/></svg>

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

@@ -0,0 +1,94 @@
import { IonButton, IonIcon, IonModal } from '@ionic/react';
import { closeOutline } from 'ionicons/icons';
import { useRef } from 'react';
import MissingSvg from './image.svg';
import './style.css';
import { COLOR_TEXT } from '../../constants';
export interface NoFavoriteVocabModalProps {
open: boolean;
setOpen: (open: boolean) => void;
}
function NoFavoriteVocabModal({ open, setOpen }: NoFavoriteVocabModalProps) {
const modal = useRef<HTMLIonModalElement>(null);
function dismiss() {
modal.current?.dismiss();
}
return (
<IonModal
id="no-favorite-connectives-modal"
ref={modal}
trigger="open-custom-dialog"
isOpen={open}
onIonModalDidDismiss={() => setOpen(false)}
>
<div
style={{
padding: '1rem',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '1rem',
height: '100%',
}}
>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '1rem',
color: COLOR_TEXT,
}}
>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '0.25rem',
}}
>
<div
style={{
height: '33vw',
width: '33vw',
backgroundImage: `url(${MissingSvg})`,
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
}}
></div>
<div style={{ marginTop: '1rem' }}>You have currently no favorite</div>
<div> Vocabulary</div>
</div>
</div>
<IonButton
color="light"
shape="round"
style={{
width: '1rem',
height: '1rem',
position: 'absolute',
top: '0.5rem',
right: '0.5rem',
}}
onClick={() => setOpen(false)}
>
<IonIcon slot={'icon-only'} icon={closeOutline}></IonIcon>
</IonButton>
</div>
</IonModal>
);
}
export default NoFavoriteVocabModal;

View File

@@ -0,0 +1,21 @@
.bold {
font-weight: bold;
}
ion-modal#no-favorite-connectives-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#no-favorite-connectives-modal::part(backdrop) {
/* background: rgba(209, 213, 219); */
opacity: 1;
}
ion-modal#no-favorite-connectives-modal ion-toolbar {
/* --background: rgb(14 116 144); */
/* --color: white; */
--color: black;
}

View File

@@ -0,0 +1,140 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="649.3779"
height="727.77798"
viewBox="0 0 649.3779 727.77798"
xmlns:xlink="http://www.w3.org/1999/xlink"
role="img"
artist="Katerina Limpitsouni"
source="https://undraw.co/"
><path
d="M648.20033,259.05824h-3.99878V149.51291A63.40187,63.40187,0,0,0,580.79976,86.111H348.713a63.40184,63.40184,0,0,0-63.402,63.4017V750.48713A63.40181,63.40181,0,0,0,348.71284,813.889H580.79945a63.40185,63.40185,0,0,0,63.402-63.40167V337.0345h3.99884Z"
transform="translate(-285.31105 -86.11101)"
fill="#e6e6e6"
/><path
d="M583.3578,102.606h-30.295a22.49485,22.49485,0,0,1-20.82715,30.99053H399.2762a22.49484,22.49484,0,0,1-20.82715-30.99061H350.15346a47.34781,47.34781,0,0,0-47.34784,47.34774V750.04628a47.34781,47.34781,0,0,0,47.34777,47.34784H583.3578a47.34781,47.34781,0,0,0,47.34784-47.34778h0V149.95371A47.34777,47.34777,0,0,0,583.3578,102.606Z"
transform="translate(-285.31105 -86.11101)"
fill="#fff"
/><path
id="f3818c68-126c-4685-b4e0-2450731ccc2a-761"
data-name="a2804879-ded6-4045-b20f-1f1dde9b938b"
d="M611.46248,573.92982h-279.619a5.184,5.184,0,0,1-5.178-5.178v-69.361a5.184,5.184,0,0,1,5.178-5.178h279.619a5.184,5.184,0,0,1,5.178,5.178v69.362A5.184,5.184,0,0,1,611.46248,573.92982Zm-279.619-77.646a3.11,3.11,0,0,0-3.107,3.107v69.362a3.11,3.11,0,0,0,3.107,3.107h279.619a3.11,3.11,0,0,0,3.107-3.107v-69.362a3.11,3.11,0,0,0-3.107-3.107Z"
transform="translate(-285.31105 -86.11101)"
fill="#e6e6e6"
/><circle
id="abdb74b7-e321-430b-89c2-b563f66442fc"
data-name="b9ad11c9-d8a0-4df6-8741-900b9ec46a35"
cx="82.77841"
cy="447.95985"
r="21.74799"
fill="#e6e6e6"
/><path
id="addd02b1-b85b-481a-baea-1b0ba5ed9f4a-762"
data-name="bd261eec-7ae0-49b0-bf26-57ff03972605"
d="M418.31746,519.57286a3.625,3.625,0,0,0,0,7.249h170.878a3.625,3.625,0,0,0,.13989-7.24866l-.02087-.00033q-.05943-.001-.119,0Z"
transform="translate(-285.31105 -86.11101)"
fill="#e6e6e6"
/><path
id="a42dc2a4-5fb2-4ea8-b2b3-ce81bc256782-763"
data-name="e80b4447-8c34-435b-ba6c-5300a190ab24"
d="M418.31746,541.32087a3.625,3.625,0,0,0,0,7.249h170.878a3.625,3.625,0,0,0,.119-7.249q-.05943-.00092-.119,0Z"
transform="translate(-285.31105 -86.11101)"
fill="#e6e6e6"
/><path
id="b84687c4-e3b4-4975-8361-bf73c33c9ee5-764"
data-name="e55fcb7d-3a3b-45d8-b167-72fb2263dd92"
d="M611.46248,689.91981h-279.619a5.184,5.184,0,0,1-5.178-5.178v-69.361a5.184,5.184,0,0,1,5.178-5.178h279.619a5.184,5.184,0,0,1,5.178,5.178v69.362A5.184,5.184,0,0,1,611.46248,689.91981Zm-279.619-77.646a3.11,3.11,0,0,0-3.107,3.107v69.362a3.11,3.11,0,0,0,3.107,3.107h279.619a3.11,3.11,0,0,0,3.107-3.107v-69.362a3.11,3.11,0,0,0-3.107-3.107Z"
transform="translate(-285.31105 -86.11101)"
fill="#e6e6e6"
/><circle
id="fe3713e7-4e14-41f8-af1d-48b338e5371c"
data-name="a50d232f-7710-43e4-8fa9-6ef0443fc454"
cx="82.77841"
cy="563.94987"
r="21.74799"
fill="#e6e6e6"
/><path
id="e34cf46a-1d6c-4c41-b5e1-331fa5bf8d4c-765"
data-name="e49b4965-a9e9-4371-9134-194e44e65c31"
d="M418.31746,635.56288a3.625,3.625,0,0,0,0,7.249h170.878a3.625,3.625,0,0,0,.119-7.249q-.05943-.001-.119,0Z"
transform="translate(-285.31105 -86.11101)"
fill="#e6e6e6"
/><path
id="e105039f-b7a6-49c8-8f81-87505f1b0ae5-766"
data-name="abc4586a-ac92-4255-aae6-84f53baad571"
d="M418.31746,657.31086a3.625,3.625,0,0,0,0,7.249h170.878a3.625,3.625,0,0,0,.119-7.249q-.05943-.00092-.119,0Z"
transform="translate(-285.31105 -86.11101)"
fill="#e6e6e6"
/><path
id="ad9187ec-89e0-4b9d-a4fb-dc654c09bafe-767"
data-name="a9e593af-a319-4e97-9065-f0c2c04624d5"
d="M465.76845,429.99485a98.343,98.343,0,0,1-98.384-98.30194v-.08206c0-.206,0-.423.012-.629.3-53.879,44.432-97.756,98.372-97.756a98.384,98.384,0,0,1,.0224,196.768h-.0224Zm0-194.7a96.519,96.519,0,0,0-96.3,95.749c-.011.22-.011.4-.011.564a96.325,96.325,0,1,0,96.337-96.313h-.026Z"
transform="translate(-285.31105 -86.11101)"
fill="#e6e6e6"
/><circle cx="315.11393" cy="422.84174" r="40" fill="#fff" /><path
d="M586.53265,526.94884c-.06861,0-.13721-.00049-.20606-.00195h-21.5a10.39761,10.39761,0,0,1-.0083-20.79248h21.51807c.10547-.00195.22021-.00195.334,0a10.39771,10.39771,0,0,1-.13769,20.79443Z"
transform="translate(-285.31105 -86.11101)"
fill="#6c63ff"
/><rect x="325.3453" y="725.34374" width="324.03261" height="2.24072" fill="#3f3d56" /><path
d="M630.6207,621.842a6.56111,6.56111,0,0,1-.62973-1.259l-5.16339-13.82657a6.50779,6.50779,0,0,1,3.81532-8.36324l115.24228-43.03187a6.50621,6.50621,0,0,1,8.363,3.81491l5.16283,13.82751a6.49951,6.49951,0,0,1-3.81558,8.36283l-115.2412,43.03174a6.5067,6.5067,0,0,1-7.73351-2.55634Z"
transform="translate(-285.31105 -86.11101)"
fill="#6c63ff"
/><path
d="M743.51834,551.78826l-46.86323,18.68723a6,6,0,0,0-4.093,7.1996l5.43852,21.77515a6,6,0,0,0,8.37642,3.97481l46.99241-22.1188a6.0109,6.0109,0,0,0,3.52446-7.717l-5.64883-18.27558A6.01072,6.01072,0,0,0,743.51834,551.78826Z"
transform="translate(-285.31105 -86.11101)"
fill="#2f2e41"
/><path
d="M829.28044,687.45a6.50671,6.50671,0,0,1-6.89226-4.34039l-40.114-116.289a6.49951,6.49951,0,0,1,4.02482-8.26416l13.95295-4.81352a6.50408,6.50408,0,0,1,8.26373,4.02536l40.11465,116.2899a6.50779,6.50779,0,0,1-4.02531,8.26422l-13.95257,4.8125A6.56115,6.56115,0,0,1,829.28044,687.45Z"
transform="translate(-285.31105 -86.11101)"
fill="#6c63ff"
/><path
d="M811.5058,555.77814l15.39577,48.04524a6,6,0,0,1-2.95088,7.73816l-20.26024,9.657a6,6,0,0,1-8.39566-3.934l-12.83035-50.328a6.0109,6.0109,0,0,1,3.71178-7.62871l17.694-7.26835A6.01071,6.01071,0,0,1,811.5058,555.77814Z"
transform="translate(-285.31105 -86.11101)"
fill="#2f2e41"
/><path
d="M764.89068,812.11413H750.13141a6.50753,6.50753,0,0,1-6.5-6.5V682.59948a6.50753,6.50753,0,0,1,6.5-6.5h14.75927a6.50753,6.50753,0,0,1,6.5,6.5V805.61413A6.50753,6.50753,0,0,1,764.89068,812.11413Z"
transform="translate(-285.31105 -86.11101)"
fill="#2f2e41"
/><path
d="M794.08307,812.11413H779.3233a6.50753,6.50753,0,0,1-6.5-6.5V682.59948a6.50753,6.50753,0,0,1,6.5-6.5h14.75977a6.50753,6.50753,0,0,1,6.5,6.5V805.61413A6.50753,6.50753,0,0,1,794.08307,812.11413Z"
transform="translate(-285.31105 -86.11101)"
fill="#2f2e41"
/><path
d="M806.2467,697.06158H739.1842a7.00787,7.00787,0,0,1-7-7V581.11725a40.53125,40.53125,0,0,1,81.0625,0V690.06158A7.00786,7.00786,0,0,1,806.2467,697.06158Z"
transform="translate(-285.31105 -86.11101)"
fill="#ccc"
/><path
d="M816.34118,723.711h-34V531.55621l.6316.17236a45.38181,45.38181,0,0,1,33.3684,43.6875Z"
transform="translate(-285.31105 -86.11101)"
fill="#2f2e41"
/><path
d="M760.32629,723.711h-34V575.41607a45.38226,45.38226,0,0,1,33.36841-43.6875l.63159-.17236Z"
transform="translate(-285.31105 -86.11101)"
fill="#2f2e41"
/><circle cx="486.89704" cy="378.42061" r="53.51916" fill="#6c63ff" /><path
d="M832.71545,452.21s3-83-36-56c0,0-22.5-75.5-77.5,37.5l-16,16s72-10,139,21Z"
transform="translate(-285.31105 -86.11101)"
fill="#2f2e41"
/><path
d="M773.02436,491.03636c-3.30591-.09179-7.42029-.20654-10.59-2.522a8.13272,8.13272,0,0,1-3.20008-6.07275,5.47091,5.47091,0,0,1,1.86035-4.49317c1.65552-1.39892,4.073-1.727,6.67823-.96142l-2.69922-19.72559,1.98144-.27148,3.17322,23.18994-1.65466-.75928c-1.91834-.87988-4.55164-1.32764-6.188.05518a3.51513,3.51513,0,0,0-1.15271,2.8955,6.14685,6.14685,0,0,0,2.38123,4.52783c2.46667,1.80176,5.74621,2.03418,9.46582,2.13819Z"
transform="translate(-285.31105 -86.11101)"
fill="#2f2e41"
/><rect x="459.19183" y="372.85162" width="10.77161" height="2" fill="#2f2e41" /><rect
x="493.19183"
y="372.85162"
width="10.77161"
height="2"
fill="#2f2e41"
/><path
d="M652.88209,626.94393a6.00014,6.00014,0,0,1-7.96794-2.91753l-34.511-74.38406a6,6,0,0,1,10.88547-5.0504l34.511,74.38406A6.00012,6.00012,0,0,1,652.88209,626.94393Z"
transform="translate(-285.31105 -86.11101)"
fill="#3f3d56"
/><path
d="M617.425,546.90538a4,4,0,0,1-4-4V491.95275h-50a4,4,0,0,1,0-8h54a4,4,0,0,1,4,4v54.95263A4,4,0,0,1,617.425,546.90538Z"
transform="translate(-285.31105 -86.11101)"
fill="#6c63ff"
/><path
d="M618.371,552.55988a48,48,0,1,1,23.34031-63.74348A48.05437,48.05437,0,0,1,618.371,552.55988Zm-35.35277-76.19831a36,36,0,1,0,47.80761,17.50523A36.04072,36.04072,0,0,0,583.01827,476.36157Z"
transform="translate(-285.31105 -86.11101)"
fill="#3f3d56"
/></svg>

After

Width:  |  Height:  |  Size: 8.4 KiB

View File

@@ -0,0 +1,95 @@
import { IonButton, IonIcon, IonModal } from '@ionic/react';
import { closeOutline } from 'ionicons/icons';
import { useRef } from 'react';
import MissingSvg from './image.svg';
import './style.css';
import { COLOR_TEXT } from '../../constants';
export interface NoFavoriteVocabModalProps {
open: boolean;
setOpen: (open: boolean) => void;
}
function NoFavoriteConnectivesModal({ open, setOpen }: NoFavoriteVocabModalProps) {
const modal = useRef<HTMLIonModalElement>(null);
function dismiss() {
modal.current?.dismiss();
}
return (
<IonModal
id="no-favorite-vocalbulary-modal"
ref={modal}
trigger="open-custom-dialog"
onWillDismiss={() => setOpen(false)}
onIonModalDidDismiss={() => setOpen(false)}
isOpen={open}
>
<div
style={{
padding: '1rem',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '1rem',
height: '100%',
}}
>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '1rem',
color: COLOR_TEXT,
}}
>
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
gap: '0.25rem',
}}
>
<div
style={{
height: '33vw',
width: '33vw',
backgroundImage: `url(${MissingSvg})`,
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
}}
></div>
<div style={{ marginTop: '1rem' }}>You have currently no favorite</div>
<div>Connectives</div>
</div>
</div>
<IonButton
color="light"
shape="round"
style={{
width: '1rem',
height: '1rem',
position: 'absolute',
top: '0.5rem',
right: '0.5rem',
}}
onClick={() => setOpen(false)}
>
<IonIcon slot={'icon-only'} icon={closeOutline}></IonIcon>
</IonButton>
</div>
</IonModal>
);
}
export default NoFavoriteConnectivesModal;

View File

@@ -0,0 +1,21 @@
.bold {
font-weight: bold;
}
ion-modal#no-favorite-vocalbulary-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#no-favorite-vocalbulary-modal::part(backdrop) {
/* background: rgba(209, 213, 219); */
opacity: 1;
}
ion-modal#no-favorite-vocalbulary-modal ion-toolbar {
/* --background: rgb(14 116 144); */
/* --color: white; */
--color: black;
}

View File

@@ -0,0 +1,29 @@
import { checkmarkOutline } from 'ionicons/icons';
import './style.css';
import { IonButton, IonIcon } from '@ionic/react';
interface ContainerProps {
num_rating: number;
num_full_rating: number;
}
const QuestionProgress: React.FC<ContainerProps> = ({ num_rating = 0, num_full_rating = 2 }) => {
return (
<>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: '0.15rem' }}>
{Array.from({ length: num_rating }, (_, index) => (
<IonButton shape="round" fill="outline" size="small" color="success">
<IonIcon slot="icon-only" icon={checkmarkOutline}></IonIcon>
</IonButton>
))}
{Array.from({ length: num_full_rating - num_rating }, (_, index) => (
<IonButton shape="round" fill="outline" size="small" color="medium">
<IonIcon slot="icon-only"></IonIcon>
</IonButton>
))}
</div>
</>
);
};
export default QuestionProgress;

View File

@@ -0,0 +1,3 @@
.quizzes_main_menu_container {
text-align: center;
}

View File

@@ -0,0 +1,95 @@
import { IonButton, useIonRouter } from '@ionic/react';
import './QuizzesMainMenuContainer.css';
import { COLOR_TEXT, CONNECTIVE_REVISION_LINK, LISTENING_PRACTICE_LINK, MATCHING_FRENZY_LINK } from '../constants';
interface ContainerProps {
name: string;
}
const QuizzesMainMenuContainer: React.FC<ContainerProps> = ({ name }) => {
const router = useIonRouter();
return (
<div
className="quizzes_main_menu_container"
style={{
display: 'column',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
}}
>
<div style={{ margin: '1rem 1rem' }}>
<IonButton
expand="block"
// href={LISTENING_PRACTICE_LINK}
onClick={() => {
router.push(LISTENING_PRACTICE_LINK, undefined, 'replace');
}}
fill="clear"
>
<div style={{ padding: '1rem' }}>
<div
style={{
width: '60vw',
height: '30vw',
backgroundImage: `url('/data/Lesson/images/quiz_listening_practice.jpg')`,
backgroundSize: 'cover',
borderRadius: '1rem',
}}
></div>
<div style={{ marginTop: '2rem', color: COLOR_TEXT }}>Listening Practice</div>
</div>
</IonButton>
</div>
<div style={{ margin: '1rem 1rem' }}>
<IonButton
expand="block"
// href={MATCHING_FRENZY_LINK}
onClick={() => {
router.push(MATCHING_FRENZY_LINK, undefined, 'replace');
}}
fill="clear"
>
<div style={{ padding: '1rem' }}>
<div
style={{
width: '60vw',
height: '30vw',
backgroundImage: `url('/data/Lesson/images/quiz_matching_frenzy.jpg')`,
backgroundSize: 'cover',
borderRadius: '1rem',
}}
></div>
<div style={{ marginTop: '0.5rem', color: COLOR_TEXT }}>Matching Frenzy</div>
</div>
</IonButton>
</div>
<div style={{ margin: '1rem 1rem' }}>
<IonButton
expand="block"
// href={CONNECTIVE_REVISION_LINK}
onClick={() => {
router.push(CONNECTIVE_REVISION_LINK, undefined, 'replace');
}}
fill="clear"
>
<div style={{ padding: '1rem' }}>
<div
style={{
width: '60vw',
height: '30vw',
backgroundImage: `url('/data/Lesson/images/quiz_connectives_revision.jpg')`,
backgroundSize: 'cover',
borderRadius: '1rem',
}}
></div>
<div style={{ marginTop: '0.5rem', color: COLOR_TEXT }}>Connectives Revision</div>
</div>
</IonButton>
</div>
</div>
);
};
export default QuizzesMainMenuContainer;

View File

@@ -0,0 +1,24 @@
import { IonToast } from '@ionic/react';
import React from 'react';
import './style.css';
import { THE_WORD_REMOVED, THE_WORD_REMOVED_DISMISS_TIMEOUT } from '../../constants';
const RemoveFavoritePrompt: React.FC<{ open: boolean; setIsOpen: (isOpen: boolean) => void }> = ({
open,
setIsOpen,
}) => {
return (
<>
<IonToast
className="remove-favorite-prompt-toast"
isOpen={open}
message={THE_WORD_REMOVED}
onDidDismiss={() => setIsOpen(false)}
color="success"
duration={THE_WORD_REMOVED_DISMISS_TIMEOUT}
/>
</>
);
};
export default RemoveFavoritePrompt;

View File

@@ -0,0 +1,11 @@
ion-toast.remove-favorite-prompt-toast::part(message) {
text-align: center;
font-size: 1.5rem;
color: rgba(0, 0, 0, 0.9);
}
/*
ion-toast.remove-favorite-prompt-toast::part(container) {
bottom: 100px;
}
*/

View File

@@ -0,0 +1,24 @@
.container {
text-align: center;
position: absolute;
left: 0;
right: 0;
top: 50%;
transform: translateY(-50%);
}
.container strong {
font-size: 20px;
line-height: 26px;
}
.container p {
font-size: 16px;
line-height: 22px;
color: #8c8c8c;
margin: 0;
}
.container a {
text-decoration: none;
}

View File

@@ -0,0 +1,135 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="845.87074"
height="569.98927"
viewBox="0 0 845.87074 569.98927"
xmlns:xlink="http://www.w3.org/1999/xlink"
role="img"
artist="Katerina Limpitsouni"
source="https://undraw.co/"
><polygon
points="760.114 287.832 752.997 294.857 762.452 319.527 775.643 292.177 797.249 168.007 764.02 167.081 760.114 287.832"
fill="#9f616a"
/><polygon
points="705.966 547.714 687.966 547.714 673.966 388.714 708.966 388.714 705.966 547.714"
fill="#9f616a"
/><polygon
points="773.966 547.714 755.966 547.714 741.966 388.714 776.966 388.714 773.966 547.714"
fill="#9f616a"
/><path
d="M937.09032,733.461l-26.27966-.13983a10.60246,10.60246,0,0,1-5.18583-13.20591h0a10.60249,10.60249,0,0,1,8.18437-6.73544l4.96424-.82737,4.32555-5.50525a8.19029,8.19029,0,0,1,10.758-2.00034c4.14944,2.63357,9.90449,3.511,17,2.92223l5.52291,13.4861c1.09439,7.29591-3.90074,12.10883-11.188,13.25945h0A13.43132,13.43132,0,0,1,937.09032,733.461Z"
transform="translate(-177.06463 -165.00536)"
fill="#2f2e41"
/><path
d="M869.09032,733.461l-26.27966-.13983a10.60246,10.60246,0,0,1-5.18583-13.20591h0a10.60249,10.60249,0,0,1,8.18437-6.73544l4.96424-.82737,4.80952-6.12121a7.45937,7.45937,0,0,1,10.01669-1.55131c4.16748,2.7633,10.01393,3.69017,17.25728,3.08916l5.52291,13.4861c1.09439,7.29591-3.90074,12.10883-11.188,13.25945h0A13.43132,13.43132,0,0,1,869.09032,733.461Z"
transform="translate(-177.06463 -165.00536)"
fill="#2f2e41"
/><circle cx="710.9656" cy="55.71363" r="27" fill="#9f616a" /><path
d="M927.03023,272.719l-44,3c.15576-14.2391-.46558-27.13579-4-35l31-10C910.3279,243.25667,916.90486,257.50461,927.03023,272.719Z"
transform="translate(-177.06463 -165.00536)"
fill="#9f616a"
/><path
d="M954.03023,467.719l-113-9c15.0769-50.26016,10.35876-116.07391-5-165.41577a28.4717,28.4717,0,0,1,18.28887-26.58848L883.03023,255.719l36,4,30.356,15.178a27.71585,27.71585,0,0,1,15.31608,25.30937C953.29231,345.83844,951.91774,404.51285,954.03023,467.719Z"
transform="translate(-177.06463 -165.00536)"
fill="#6c63ff"
/><path
d="M869.03023,344.719l-51-9L822.42,301.69848a42.20258,42.20258,0,0,1,30.84455-35.34011l9.76571-2.63938Z"
transform="translate(-177.06463 -165.00536)"
fill="#6c63ff"
/><polygon
points="661.966 292.714 667.966 300.714 658.966 325.714 645.966 294.714 642.966 168.714 675.966 172.714 661.966 292.714"
fill="#9f616a"
/><path
d="M925.03023,346.719l51-9-4.38975-34.02051a42.20256,42.20256,0,0,0-30.84455-35.34011l-9.7657-2.63938Z"
transform="translate(-177.06463 -165.00536)"
fill="#6c63ff"
/><path
d="M957.03023,558.719l-41.83858,2-12.16142-59-14.76772,55-44.23228,1-8.8417-71.06584a41.09716,41.09716,0,0,1,8.84169-30.93414v0l110,5Z"
transform="translate(-177.06463 -165.00536)"
fill="#2f2e41"
/><path
d="M890.53023,250.219l20.19008-3.82345,12.0114-20.51949a27.28013,27.28013,0,0,0-3.19387-31.95038l0,0a27.28016,27.28016,0,0,0-24.2073-8.837l-26.16686,3.73812a17.27884,17.27884,0,0,0-14.64408,19.66833l17.62513,2.99868Z"
transform="translate(-177.06463 -165.00536)"
fill="#2f2e41"
/><ellipse cx="701.4656" cy="63.21363" rx="5" ry="6" fill="#9f616a" /><path
d="M303.53023,177.219a11.01245,11.01245,0,0,0-11,11v201a11.01245,11.01245,0,0,0,11,11h5a11.01245,11.01245,0,0,0,11-11v-201a11.01245,11.01245,0,0,0-11-11Z"
transform="translate(-177.06463 -165.00536)"
fill="#e6e6e6"
/><path
d="M303.53023,181.719a6.50753,6.50753,0,0,0-6.5,6.5v201a6.50752,6.50752,0,0,0,6.5,6.5h5a6.50753,6.50753,0,0,0,6.5-6.5v-201a6.50753,6.50753,0,0,0-6.5-6.5Z"
transform="translate(-177.06463 -165.00536)"
fill="#fff"
/><path
d="M385.53023,177.219a11.01245,11.01245,0,0,0-11,11v201a11.01245,11.01245,0,0,0,11,11h5a11.01245,11.01245,0,0,0,11-11v-201a11.01245,11.01245,0,0,0-11-11Z"
transform="translate(-177.06463 -165.00536)"
fill="#e6e6e6"
/><path
d="M385.53023,181.719a6.50753,6.50753,0,0,0-6.5,6.5v201a6.50752,6.50752,0,0,0,6.5,6.5h5a6.50753,6.50753,0,0,0,6.5-6.5v-201a6.50753,6.50753,0,0,0-6.5-6.5Z"
transform="translate(-177.06463 -165.00536)"
fill="#fff"
/><path
d="M467.53023,177.219a11.01245,11.01245,0,0,0-11,11v201a11.01245,11.01245,0,0,0,11,11h5a11.01245,11.01245,0,0,0,11-11v-201a11.01245,11.01245,0,0,0-11-11Z"
transform="translate(-177.06463 -165.00536)"
fill="#e6e6e6"
/><path
d="M467.53023,181.719a6.50753,6.50753,0,0,0-6.5,6.5v201a6.50752,6.50752,0,0,0,6.5,6.5h5a6.50753,6.50753,0,0,0,6.5-6.5v-201a6.50753,6.50753,0,0,0-6.5-6.5Z"
transform="translate(-177.06463 -165.00536)"
fill="#fff"
/><path
d="M549.53023,177.219a11.01245,11.01245,0,0,0-11,11v201a11.01245,11.01245,0,0,0,11,11h5a11.01245,11.01245,0,0,0,11-11v-201a11.01245,11.01245,0,0,0-11-11Z"
transform="translate(-177.06463 -165.00536)"
fill="#e6e6e6"
/><path
d="M549.53023,181.719a6.50753,6.50753,0,0,0-6.5,6.5v201a6.50752,6.50752,0,0,0,6.5,6.5h5a6.50753,6.50753,0,0,0,6.5-6.5v-201a6.50753,6.50753,0,0,0-6.5-6.5Z"
transform="translate(-177.06463 -165.00536)"
fill="#fff"
/><path
d="M631.53023,177.219a11.01245,11.01245,0,0,0-11,11v201a11.01245,11.01245,0,0,0,11,11h5a11.01245,11.01245,0,0,0,11-11v-201a11.01245,11.01245,0,0,0-11-11Z"
transform="translate(-177.06463 -165.00536)"
fill="#e6e6e6"
/><path
d="M631.53023,181.719a6.50753,6.50753,0,0,0-6.5,6.5v201a6.50752,6.50752,0,0,0,6.5,6.5h5a6.50753,6.50753,0,0,0,6.5-6.5v-201a6.50753,6.50753,0,0,0-6.5-6.5Z"
transform="translate(-177.06463 -165.00536)"
fill="#fff"
/><path
d="M322.2878,242.2493H290.77265a10.25379,10.25379,0,0,1-10.24242-10.24243v-1.57576a10.25379,10.25379,0,0,1,10.24242-10.24242H322.2878a10.25379,10.25379,0,0,1,10.24243,10.24242v1.57576A10.25379,10.25379,0,0,1,322.2878,242.2493Z"
transform="translate(-177.06463 -165.00536)"
fill="#6c63ff"
/><path
d="M406.2878,275.2493H374.77265a10.25379,10.25379,0,0,1-10.24242-10.24243v-1.57576a10.25379,10.25379,0,0,1,10.24242-10.24242H406.2878a10.25379,10.25379,0,0,1,10.24243,10.24242v1.57576A10.25379,10.25379,0,0,1,406.2878,275.2493Z"
transform="translate(-177.06463 -165.00536)"
fill="#6c63ff"
/><path
d="M486.2878,340.2493H454.77265a10.25379,10.25379,0,0,1-10.24242-10.24243v-1.57576a10.25379,10.25379,0,0,1,10.24242-10.24242H486.2878a10.25379,10.25379,0,0,1,10.24243,10.24242v1.57576A10.25379,10.25379,0,0,1,486.2878,340.2493Z"
transform="translate(-177.06463 -165.00536)"
fill="#6c63ff"
/><path
d="M568.2878,222.2493H536.77265a10.25379,10.25379,0,0,1-10.24242-10.24243v-1.57576a10.25379,10.25379,0,0,1,10.24242-10.24242H568.2878a10.25379,10.25379,0,0,1,10.24243,10.24242v1.57576A10.25379,10.25379,0,0,1,568.2878,222.2493Z"
transform="translate(-177.06463 -165.00536)"
fill="#6c63ff"
/><path
d="M650.2878,300.2493H618.77265a10.25379,10.25379,0,0,1-10.24242-10.24243v-1.57576a10.25379,10.25379,0,0,1,10.24242-10.24242H650.2878a10.25379,10.25379,0,0,1,10.24243,10.24242v1.57576A10.25379,10.25379,0,0,1,650.2878,300.2493Z"
transform="translate(-177.06463 -165.00536)"
fill="#6c63ff"
/><polygon
points="845.475 569.989 578.074 569.989 578.074 567.804 845.871 567.804 845.475 569.989"
fill="#3f3d56"
/><circle cx="129.4656" cy="275.21363" r="13" fill="#e6e6e6" /><circle
cx="211.4656"
cy="275.21363"
r="13"
fill="#e6e6e6"
/><circle cx="293.4656" cy="275.21363" r="13" fill="#e6e6e6" /><circle
cx="375.4656"
cy="275.21363"
r="13"
fill="#e6e6e6"
/><circle cx="457.4656" cy="275.21363" r="13" fill="#e6e6e6" /><path
d="M205.86092,165.00536a28.79631,28.79631,0,1,0,28.79632,28.79629A28.79629,28.79629,0,0,0,205.86092,165.00536Zm0,53.33839a24.54208,24.54208,0,1,1,24.5421-24.5421A24.54208,24.54208,0,0,1,205.86092,218.34375Z"
transform="translate(-177.06463 -165.00536)"
fill="#e6e6e6"
/><path
d="M204.26992,203.64975a1.51846,1.51846,0,0,1-1.07424-.44513L194.99111,195a1.5192,1.5192,0,0,1,2.14848-2.14847l6.95228,6.95228,12.32319-16.80442a1.51945,1.51945,0,0,1,2.45057,1.79712l-13.37042,18.23239a1.52072,1.52072,0,0,1-1.10955.61665C204.34708,203.64856,204.3082,203.64975,204.26992,203.64975Z"
transform="translate(-177.06463 -165.00536)"
fill="#6c63ff"
/></svg>

After

Width:  |  Height:  |  Size: 8.1 KiB

View File

@@ -0,0 +1,50 @@
import { IonButton, IonIcon, useIonRouter } from '@ionic/react';
import { arrowBack } from 'ionicons/icons';
import { LESSON_LINK, VERSIONS } from '../../constants';
import SettingSvg from './image.svg';
interface ContainerProps {
name: string;
}
const SettingContainer: React.FC<ContainerProps> = ({ name }) => {
const router = useIonRouter();
return (
<div
style={{
width: '100%',
height: '85%',
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
}}
>
<div
style={{
backgroundImage: `url(${SettingSvg})`,
height: '33vh',
width: '33vh',
backgroundSize: 'contain',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
}}
></div>
<div>
<p>T.B.A.</p>
</div>
<div>{VERSIONS}</div>
<IonButton
onClick={() => {
router.push(LESSON_LINK, undefined, 'replace');
}}
style={{ marginTop: '1rem' }}
>
<IonIcon slot="start" icon={arrowBack}></IonIcon>
Back
</IonButton>
</div>
);
};
export default SettingContainer;

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -0,0 +1,90 @@
<svg
xmlns="http://www.w3.org/2000/svg"
width="863.91732"
height="364.20537"
viewBox="0 0 863.91732 364.20537"
xmlns:xlink="http://www.w3.org/1999/xlink"
role="img"
artist="Katerina Limpitsouni"
source="https://undraw.co/"
><polygon
points="311.959 119.745 0 119.745 0 222.156 11.817 222.156 11.817 248.941 38.601 222.156 311.959 222.156 311.959 119.745"
fill="#f0f0f0"
/><rect x="8.66553" y="129.71814" width="294.62811" height="81.92868" fill="#fff" /><rect
x="34.72148"
y="154.42552"
width="141.85589"
height="4.30497"
fill="#f0f0f0"
/><rect x="34.72148" y="169.31777" width="247.24292" height="4.30497" fill="#f0f0f0" /><rect
x="34.72148"
y="184.21003"
width="247.00081"
height="4.30497"
fill="#f0f0f0"
/><path
d="M692.07274,618.96676s1.487-31.15875,31.97119-27.537"
transform="translate(-168.04134 -267.89732)"
fill="#f0f0f0"
/><circle cx="515.41796" cy="306.16087" r="15.2571" fill="#f0f0f0" /><rect
x="512.9354"
y="331.85294"
width="4.30672"
height="30.14703"
fill="#f0f0f0"
/><circle cx="666.92952" cy="180.07338" r="123.29665" fill="#3f3d56" /><path
d="M757.348,457.86815a32.62688,32.62688,0,0,1,50.081,0,36.26372,36.26372,0,1,0-51.27085-1.18987Q756.73918,457.28694,757.348,457.86815Z"
transform="translate(-168.04134 -267.89732)"
fill="#fff"
/><path
d="M855.26,457.86815a32.627,32.627,0,0,1,50.08092,0,36.26371,36.26371,0,1,0-51.2708-1.18987Q854.65117,457.28694,855.26,457.86815Z"
transform="translate(-168.04134 -267.89732)"
fill="#fff"
/><circle cx="601.97649" cy="151.39215" r="12.47434" fill="#3f3d56" /><circle
cx="699.88499"
cy="151.39215"
r="12.47434"
fill="#3f3d56"
/><circle cx="578.08341" cy="210.89752" r="14.50548" fill="#6c63ff" /><circle
cx="744.8965"
cy="210.89752"
r="14.5055"
fill="#6c63ff"
/><polygon points="661.49 181.886 650.611 229.029 668.742 210.898 661.49 181.886" fill="#6c63ff" /><polygon
points="717.39 363.205 705.038 352.839 705.326 363.205 701.49 363.205 701.183 352.244 684.507 363.205 677.526 363.205 701.059 347.737 700.147 315.258 699.466 290.728 703.293 290.623 703.984 315.258 704.894 347.708 723.354 363.205 717.39 363.205"
fill="#3f3d56"
/><polygon
points="659.363 363.205 647.012 352.839 647.3 363.205 643.474 363.205 643.167 352.244 626.49 363.205 619.509 363.205 643.033 347.737 642.122 315.258 641.441 290.728 645.276 290.623 645.967 315.258 646.868 347.708 665.328 363.205 659.363 363.205"
fill="#3f3d56"
/><path
d="M836.784,315.60813c-3.3831,0-6.36764,2.628-8.36294,6.66445-1.75872-6.06969-5.45374-10.29078-9.7689-10.29078a6.56326,6.56326,0,0,0-.87094.1463c-1.65871-6.4805-5.51368-11.02542-10.00816-11.02542-6.00841,0-10.8791,8.118-10.8791,18.13187s4.87073,18.13187,10.8791,18.13187a6.56119,6.56119,0,0,0,.87093-.14629c1.65871,6.4805,5.51369,11.02541,10.00817,11.02541,3.3831,0,6.36764-2.62795,8.36294-6.66444,1.75876,6.06971,5.45374,10.29077,9.7689,10.29077,6.00841,0,10.8791-8.118,10.8791-18.13187S842.79244,315.60813,836.784,315.60813Z"
transform="translate(-168.04134 -267.89732)"
fill="#3f3d56"
/><path
d="M718.72328,451.807l-67.92039-11.01653c-3.42269-.55515-6.90789-1.11141-10.34147-.6282s-6.87069,2.1737-8.62107,5.16688a8.651,8.651,0,0,0,9.14985,12.853c-3.70741-.12023-7.60411-.19978-10.894,1.51369s-5.61946,5.87559-4.01553,9.22024a8.27667,8.27667,0,0,0,1.91922,2.4289,17.60582,17.60582,0,0,0,18.52289,3.14128c-2.50047,3.58582-7.46212,4.11838-11.7541,4.94866s-9.25362,3.258-9.41312,7.62664c-.17922,4.90869,5.66264,7.51763,10.47189,8.51687A137.41687,137.41687,0,0,0,712.648,489.3171a30.98,30.98,0,0,0,7.737-3.95049,17.43266,17.43266,0,0,0-7.05356-30.96345"
transform="translate(-168.04134 -267.89732)"
fill="#3f3d56"
/><path
d="M1011.89005,507.47917a137.41884,137.41884,0,0,0-51.17256-57.63676,30.97519,30.97519,0,0,0-7.80737-3.80966,17.43272,17.43272,0,0,0-20.50879,24.24615l-5.31525-2.74921Q943.09323,497.98314,959.1,528.43656c1.61312,3.06929,3.26318,6.18918,5.71292,8.64309s5.86648,4.18514,9.31075,3.78525a8.6006,8.6006,0,0,0,6.77916-12.2999,16.64264,16.64264,0,0,0,5.752,5.05979c3.34648,1.59972,8.07321.9603,9.7823-2.33177a8.27455,8.27455,0,0,0,.78809-2.99368,17.60592,17.60592,0,0,0-8.62117-16.69248c4.36853-.1565,7.77622,3.48909,11.01912,6.42052,3.24327,2.93143,8.1652,5.43808,11.75289,2.94008C1015.40712,518.16062,1013.98161,511.92354,1011.89005,507.47917Z"
transform="translate(-168.04134 -267.89732)"
fill="#3f3d56"
/><polygon
points="55.757 0 506 0 506 147.807 488.945 147.807 488.945 186.463 450.289 147.807 55.757 147.807 55.757 0"
fill="#cacaca"
/><rect x="68.26381" y="14.39335" width="425.22943" height="118.24561" fill="#fff" /><rect
x="102.45877"
y="48.91591"
width="204.73707"
height="6.21326"
fill="#6c63ff"
/><rect x="102.45877" y="70.40954" width="356.83952" height="6.21326" fill="#6c63ff" /><rect
x="102.45877"
y="91.90316"
width="356.49009"
height="6.21326"
fill="#6c63ff"
/><path
d="M1030.95866,632.10268h-381a1,1,0,0,1,0-2h381a1,1,0,0,1,0,2Z"
transform="translate(-168.04134 -267.89732)"
fill="#cacaca"
/></svg>

After

Width:  |  Height:  |  Size: 5.1 KiB

View File

@@ -0,0 +1,18 @@
import ImageSvg from './image.svg';
function SpeakerImage() {
return (
<div
style={{
width: '100%',
height: '100%',
backgroundImage: `url(${ImageSvg})`,
backgroundSize: 'contain',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
}}
></div>
);
}
export { SpeakerImage };

View File

@@ -0,0 +1,44 @@
import { IonToast } from '@ionic/react';
import { WRONG_ANSWER_MESSAGE } from '../../constants';
import './style.css';
import { useAppStateContext } from '../../contexts/AppState';
interface QuestionCardProps {
isOpen: boolean;
dismiss: () => void;
correct_answer?: string;
}
const WrongAnswerToast: React.FC<QuestionCardProps> = ({ isOpen, dismiss, correct_answer = '' }) => {
const { WRONG_ANS_TOAST_APPEAR_TIMEOUT_S } = useAppStateContext();
if (correct_answer != '') {
return (
<>
<IonToast
className="wrong-answer-toast"
isOpen={isOpen}
message={`${WRONG_ANSWER_MESSAGE}. The correct answer is "${correct_answer}"`}
onDidDismiss={() => dismiss()}
duration={WRONG_ANS_TOAST_APPEAR_TIMEOUT_S * 1000}
color="danger"
></IonToast>
</>
);
} else {
return (
<>
<IonToast
className="wrong-answer-toast"
isOpen={isOpen}
message={`${WRONG_ANSWER_MESSAGE}`}
onDidDismiss={() => dismiss()}
duration={WRONG_ANS_TOAST_APPEAR_TIMEOUT_S * 1000}
color="danger"
></IonToast>
</>
);
}
};
export default WrongAnswerToast;

View File

@@ -0,0 +1,10 @@
ion-toast.wrong-answer-toast::part(message) {
/* text-align: center; */
font-size: 1.5rem;
color: rgba(0, 0, 0, 0.9);
line-height: 2rem;
}
ion-toast.wrong-answer-toast::part(container) {
bottom: 100px;
}

View File

@@ -0,0 +1,48 @@
import { IonToast } from '@ionic/react';
import { WRONG_ANSWER_MESSAGE } from '../../constants';
import './style.css';
import { useAppStateContext } from '../../contexts/AppState';
interface QuestionCardProps {
isOpen: boolean;
dismiss: () => void;
correct_answer: string;
}
const WrongAnswerToastWithoutCorrectAnswer: React.FC<QuestionCardProps> = ({
isOpen,
dismiss,
correct_answer = '',
}) => {
const { WRONG_ANS_TOAST_APPEAR_TIMEOUT_S } = useAppStateContext();
if (correct_answer != '') {
return (
<>
<IonToast
className="wrong-answer-toast"
isOpen={isOpen}
message={`${WRONG_ANSWER_MESSAGE}. The correct answer is "${correct_answer}"`}
onDidDismiss={() => dismiss()}
duration={WRONG_ANS_TOAST_APPEAR_TIMEOUT_S * 1000}
color="danger"
></IonToast>
</>
);
} else {
return (
<>
<IonToast
className="wrong-answer-toast"
isOpen={isOpen}
message={`${WRONG_ANSWER_MESSAGE}`}
onDidDismiss={() => dismiss()}
duration={WRONG_ANS_TOAST_APPEAR_TIMEOUT_S * 1000}
color="danger"
></IonToast>
</>
);
}
};
export default WrongAnswerToastWithoutCorrectAnswer;

View File

@@ -0,0 +1,10 @@
ion-toast.wrong-answer-toast::part(message) {
/* text-align: center; */
font-size: 1.5rem;
color: rgba(0, 0, 0, 0.9);
line-height: 2rem;
}
ion-toast.wrong-answer-toast::part(container) {
bottom: 100px;
}