210 lines
7.7 KiB
TypeScript
210 lines
7.7 KiB
TypeScript
import { useState } from 'react'
|
||
import './App.css'
|
||
import Form from 'react-bootstrap/Form';
|
||
import { Badge, Button, ListGroup } from 'react-bootstrap';
|
||
import { initials, finales, syllables, tones } from './data';
|
||
import { getRandomTones, isEnabled, toggle } from './utils';
|
||
import { Found, Syllable, Tone } from './types';
|
||
import { strings } from './strings';
|
||
|
||
const defaultFoundState:Found = {
|
||
allfinales: false,
|
||
allInitiales: false,
|
||
finales: [],
|
||
initiales: [],
|
||
syllables: [],
|
||
toneS: [],
|
||
randomTones: []
|
||
}
|
||
|
||
enum Status {params, plaing, plaied, showlist}
|
||
|
||
function App() {
|
||
|
||
const [ count, setCount ] = useState(10)
|
||
const [ pause, setPause ] = useState(3)
|
||
|
||
const [ found, setFound ] = useState(defaultFoundState)
|
||
|
||
const onchangepause = (e: React.ChangeEvent<HTMLInputElement> ) => setPause( Number(e.target.value) )
|
||
const onchangecount = (e: React.ChangeEvent<HTMLInputElement> ) => {
|
||
setCount( Number(e.target.value) )
|
||
refreshRandomTones()
|
||
}
|
||
//---------------- to found state
|
||
const toggleAllInitiales = () => {
|
||
let toggled = found.allInitiales ? [] : initials
|
||
let foundSyllables:Syllable[] = syllables.filter( syl =>
|
||
toggled.includes(syl.initiale) && found.finales.includes(syl.finale) )
|
||
let foundTones = tones.filter( t => foundSyllables.some( syl => syl.tones.some( st => st===t.tone) ) )
|
||
let foundRandomTones = getRandomTones( foundTones, count )
|
||
setFound({...found,
|
||
allInitiales: !found.allInitiales,
|
||
initiales: toggled,
|
||
syllables: foundSyllables,
|
||
toneS: foundTones,
|
||
randomTones: foundRandomTones
|
||
})
|
||
}
|
||
const toggleInitialsState = (caption: string) => {
|
||
let toggled = toggle(found.initiales,caption)
|
||
let foundSyllables:Syllable[] = syllables.filter( syl =>
|
||
toggled.includes(syl.initiale) && found.finales.includes(syl.finale) )
|
||
let foundTones = tones.filter( t => foundSyllables.some( syl => syl.tones.some( st => st===t.tone) ) )
|
||
let foundRandomTones = getRandomTones( foundTones, count )
|
||
setFound({...found,
|
||
initiales: toggled,
|
||
syllables: foundSyllables,
|
||
toneS: foundTones,
|
||
randomTones: foundRandomTones
|
||
})
|
||
}
|
||
|
||
const toggleAllFinales = () => {
|
||
let toggled = found.allfinales ? [] : finales.map( f => f.finale)
|
||
let foundSyllables:Syllable[] = syllables.filter ( syl =>
|
||
found.initiales.includes( syl.initiale) && toggled.includes( syl.finale ) )
|
||
let foundTones = tones.filter( t => foundSyllables.some( syl => syl.tones.some( st => st===t.tone) ) )
|
||
let foundRandomTones = getRandomTones( foundTones, count )
|
||
setFound({...found,
|
||
allfinales: !found.allfinales,
|
||
finales: toggled,
|
||
syllables: foundSyllables,
|
||
toneS: foundTones,
|
||
randomTones: foundRandomTones
|
||
})
|
||
}
|
||
const toggleFinalsState = (caption: string) => {
|
||
let toggled = toggle(found.finales,caption)
|
||
let foundSyllables:Syllable[] = syllables.filter ( syl =>
|
||
found.initiales.includes( syl.initiale) && toggled.includes( syl.finale ) )
|
||
let foundTones = tones.filter( t => foundSyllables.some( syl => syl.tones.some( st => st===t.tone) ) )
|
||
let foundRandomTones = getRandomTones( foundTones, count )
|
||
setFound({...found,
|
||
finales: toggled,
|
||
syllables: foundSyllables,
|
||
toneS: foundTones,
|
||
randomTones: foundRandomTones
|
||
})
|
||
}
|
||
|
||
const isFound = ():boolean => found.syllables.length > 0
|
||
const [plaingNo, setPlaingNo ] = useState(0)
|
||
|
||
// ---- опять по новой
|
||
const [ status, setStatus ] = useState(Status.params)
|
||
|
||
const beginDictation = (): void => {
|
||
refreshRandomTones()
|
||
console.debug(found.randomTones)
|
||
playDictation2(found.randomTones)
|
||
}
|
||
|
||
const playDictation2 = (randomTones: Tone[]) => {
|
||
if ( randomTones.length == 0 ) return
|
||
let audios:HTMLAudioElement[] = []
|
||
randomTones.forEach(element => {
|
||
audios = [...audios, new Audio(`/src/assets/audio/${element.tone}.mp3`) ]
|
||
});
|
||
console.debug(audios)
|
||
if ( audios.length == 0 ) return
|
||
for(let x=0; x<audios.length-1;x++)
|
||
{
|
||
audios[x].onended = () => setTimeout( () => {
|
||
let pno = x+2
|
||
setPlaingNo(pno)
|
||
audios[x+1].play()
|
||
}, 1000*pause ) ;
|
||
}
|
||
audios[audios.length-1].onended = () => setStatus(Status.plaied)
|
||
setStatus(Status.plaing)
|
||
setPlaingNo(1)
|
||
audios[0].play()
|
||
}
|
||
|
||
const renderRandomTones2 = () => {
|
||
return found.randomTones.map( (ton, i) => { return <span key={i}><Badge bg="success" pill>{ton.caption}</Badge>{' '}</span> })
|
||
}
|
||
|
||
const refresh = () => {
|
||
refreshRandomTones()
|
||
setStatus(Status.params)
|
||
}
|
||
|
||
const refreshRandomTones = () => {
|
||
let foundRandomTones = getRandomTones( found.toneS, count )
|
||
setFound({...found,
|
||
randomTones: foundRandomTones
|
||
})
|
||
}
|
||
|
||
return (
|
||
<>
|
||
<h1>Диктант pīnyīn</h1>
|
||
<ListGroup>
|
||
<ListGroup.Item disabled={status != Status.params}>
|
||
<h2>{strings.selectInitiales}</h2>
|
||
<Button variant={found.allInitiales ? "primary" : "outline-primary"}
|
||
onClick= {()=>toggleAllInitiales()}>
|
||
{found.allInitiales ? strings.unselectAll : strings.selectAll}
|
||
</Button>
|
||
{initials.map( (text, i) => <Button variant={isEnabled(found.initiales, text) ? "primary" : "outline-primary"}
|
||
key={i} onClick={()=>toggleInitialsState(text)}>
|
||
{text}
|
||
</Button>
|
||
)}
|
||
</ListGroup.Item>
|
||
<ListGroup.Item disabled={status != Status.params}>
|
||
<h2>{strings.selectFinales}</h2>
|
||
<Button variant={found.allfinales ? "success" : "outline-success"}
|
||
onClick= {toggleAllFinales}>
|
||
{found.allfinales ? strings.unselectAll : strings.selectAll}
|
||
</Button>
|
||
{finales.map( (fin, i) => <Button variant={isEnabled(found.finales, fin.finale) ? "success" : "outline-success"}
|
||
key={i} onClick={()=>toggleFinalsState(fin.finale)}>
|
||
{fin.caption}
|
||
</Button>
|
||
)}
|
||
</ListGroup.Item>
|
||
<ListGroup.Item disabled={status != Status.params}>
|
||
<h2>{strings.params}</h2>
|
||
<Form.Label>Количество слогов {count}</Form.Label>
|
||
<Form.Range value={count} min={5} max={50} step={5} onChange={onchangecount}/>
|
||
<Form.Label>Пауза между слогами {pause} секунд</Form.Label>
|
||
<Form.Range value={pause} min={1} max={10} step={1} onChange={onchangepause}/>
|
||
</ListGroup.Item>
|
||
<ListGroup.Item>
|
||
{ status == Status.params &&
|
||
<>
|
||
Выбрано {found.initiales.length} инициалей, {found.finales.length} финалей, найдено {found.syllables.length} слогов, { found.toneS.length } тонов ,
|
||
<br/>
|
||
<Button variant={isFound() ? "success" : "secondary"} size="lg" onClick={()=>beginDictation()} disabled={!isFound()}>
|
||
{isFound() ? "Начать диктант!" : "Выберите инициали и финали" }
|
||
</Button>
|
||
</>
|
||
}
|
||
{
|
||
status == Status.plaing &&
|
||
<h1>Воспроизводится...{plaingNo}</h1>
|
||
}
|
||
{
|
||
status == Status.plaied &&
|
||
<Button variant="success" onClick={()=>setStatus(Status.showlist)}>Показать слоги</Button>
|
||
}
|
||
{
|
||
status == Status.showlist &&
|
||
<>
|
||
<div>{renderRandomTones2()}</div>
|
||
<br/>
|
||
<Button variant="success" onClick={()=>refresh()}>В начало</Button>
|
||
</>
|
||
}
|
||
</ListGroup.Item>
|
||
</ListGroup>
|
||
</>
|
||
)
|
||
}
|
||
|
||
export default App
|
||
|