Compare commits

..

No commits in common. "50e99a08bf5be5be828af3e99a566c25c56b718b" and "dff64780f5b5a8b0b1253a13e792a8ac15c6bb4f" have entirely different histories.

1285 changed files with 1867 additions and 2088 deletions

1
.env
View File

@ -1 +0,0 @@
VITE_REACT_APP_VERSION=$npm_package_version

View File

@ -1,8 +1,7 @@
{ {
"name": "pinyindictation", "name": "vite-1",
"author": {"email": "rurik19@yandex.ru", "name": "Юрий Евдокимов"},
"private": true, "private": true,
"version": "1.1.0", "version": "0.0.0",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@ -42,23 +42,6 @@
} }
.btn { .btn {
margin-top: 5px;
margin-bottom: 5px; margin-bottom: 5px;
margin-right: 5px; margin-right: 5px;
} }
.syllable {
font-weight: bold;
font-size: xx-large;
}
.syllable:hover {
cursor:pointer
}
.tone1 { color: rgb(255, 0, 0) }
.tone2 { color: rgb(255, 170, 0) }
.tone3 { color: rgb(85, 170, 0) }
.tone4 { color: rgb(0, 0, 255) }
.tone5 { color: rgb(50, 50, 50) }
.cpr { color: gray}

View File

@ -1,86 +1,66 @@
import { useEffect, useState } from 'react' import { useState } from 'react'
import './App.css' import './App.css'
import { Button, ListGroup } from 'react-bootstrap'; import { Badge, Button, ListGroup } from 'react-bootstrap';
import { initials, finales } from './pinyin'; import { initials, finales } from './data';
import { BtnColor, Status, Tone } from './types'; import { BtnColor, Tone } from './types';
import { strings } from './strings'; import { strings } from './strings';
import { useStateContext } from './store'; import { useStateContext } from './store';
import { Params } from './params'; import { Params } from './params';
import { ActionType, ToggleType } from './reducer'; import { ActionType, ToggleType } from './reducer';
import { ButtonSet } from './buttons'; import { ButtonSet } from './buttons';
import { RenderTones } from './rendertones';
import { getAudio } from './audio'; enum Status {params, plaing, plaied, showlist}
import { Copyright } from './copyright';
import { SelectTones } from './selecttones';
import { FormatString } from './utils';
function App() { function App() {
const { state, dispatch } = useStateContext(); const { state, dispatch } = useStateContext();
const [ plaingNo, setPlaingNo ] = useState(0) //---------------- to found state
const [ status, setStatus ] = useState(Status.params) const isFound = ():boolean => state.foundSyllables!.length > 0
const [ playlist, setPlaylist ] = useState( [] as HTMLAudioElement[] ) const [plaingNo, setPlaingNo ] = useState(0)
useEffect( () => { const [ status, setStatus ] = useState(Status.params)
if (status===Status.params) {
console.log('effect for params')
dispatch({ type: ActionType.refresh })
}
if (status===Status.prepare) {
console.log('effect for preparing')
preparePlayList(state.randomTones!)
}
if (status===Status.playing) {
console.log('effect for playing')
playPlayList()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [status])
useEffect( () => { const beginDictation = (): void => {
console.log(`effect for playList ${playlist.length}`) dispatch({ type: ActionType.refreshPlayList })
if (playlist.length > 0) setStatus(Status.playing) playDictation2(state.randomTones!)
} , [playlist[0]] ) }
const preparePlayList = (tones: Tone[]) => { const playDictation2 = (randomTones: Tone[]) => {
console.log('tones for playing') if ( randomTones.length == 0 ) return
tones!.forEach(element => console.log(element)) let audios:HTMLAudioElement[] = []
if ( tones!.length == 0 ) return randomTones.forEach(element => {
const audios = tones!.map(element => getAudio(element.tone) ) audios = [...audios, new Audio(`/src/assets/audio/${element.tone}.mp3`) ]
});
console.debug(audios) console.debug(audios)
if ( audios.length == 0 ) return if ( audios.length == 0 ) return
for(let x=0; x<audios.length-1;x++) for(let x=0; x<audios.length-1;x++)
{ {
audios[x].onended = () => setTimeout( () => { audios[x].onended = () => setTimeout( () => {
const pno = x+2 let pno = x+2
setPlaingNo(pno) setPlaingNo(pno)
audios[x+1].play() audios[x+1].play()
}, 1000*state.sylPause! ) ; }, 1000*state.sylPause! ) ;
} }
audios[audios.length-1].onended = () => setStatus(Status.plaied) audios[audios.length-1].onended = () => setStatus(Status.plaied)
setPlaylist(audios) setStatus(Status.plaing)
}
const playPlayList = () => {
if (playlist.length===0) return;
setStatus(Status.playing)
setPlaingNo(1) setPlaingNo(1)
playlist[0].play() audios[0].play()
} }
const info = () => FormatString(strings.infoFormat, const renderRandomTones2 = () => {
state.initiales!.length.toString(), return state.randomTones!.map( (ton, i) => { return <span key={i}><Badge bg="success" pill>{ton.caption}</Badge>{' '}</span> })
state.finales!.length.toString(), }
state.toneChecks!.length.toString(),
state.foundSyllables!.length.toString(),
state.foundTones!.length.toString())
return ( const refresh = () => {
dispatch({ type: ActionType.refreshPlayList })
setStatus(Status.params)
}
return (
<> <>
<h1>{strings.mainHeader}</h1> <h1>Диктант pīnyīn</h1>
<ListGroup> <ListGroup>
<ListGroup.Item disabled={status != Status.params}> <ListGroup.Item disabled={status != Status.params}>
<h2>{strings.selectInitiales}</h2> <h2>{strings.selectInitiales}</h2>
<ButtonSet source={initials} color={BtnColor.blue} toggle={ToggleType.init}/> <ButtonSet source={initials} color={BtnColor.blue} toggle={ToggleType.init}/>
@ -91,54 +71,41 @@ return (
<ButtonSet source={finales} color={BtnColor.green} toggle={ToggleType.fin}/> <ButtonSet source={finales} color={BtnColor.green} toggle={ToggleType.fin}/>
</ListGroup.Item> </ListGroup.Item>
<ListGroup.Item disabled={status != Status.params}>
<h2>{strings.selectTones}</h2>
<SelectTones/>
</ListGroup.Item>
<ListGroup.Item disabled={status != Status.params}> <ListGroup.Item disabled={status != Status.params}>
<h2>{strings.params}</h2> <h2>{strings.params}</h2>
<Params/> <Params/>
</ListGroup.Item> </ListGroup.Item>
<ListGroup.Item> <ListGroup.Item>
{ status == Status.params && { status == Status.params &&
<> <>
{info()} Выбрано {state.initiales!.length} инициалей, {state.finales!.length} финалей, найдено {state.foundSyllables!.length} слогов, { state.foundTones!.length } тонов ,
<br/> <br/>
<Button <Button variant={isFound() ? "success" : "secondary"} size="lg" onClick={()=>beginDictation()} disabled={!isFound()}>
variant={state.isFound!() ? "success" : "secondary"} {isFound() ? "Начать диктант!" : "Выберите инициали и финали" }
size="lg"
onClick={()=>setStatus(Status.prepare)}
disabled={!state.isFound!()}>
{state.isFound!() ? strings.beginDictation : strings.selectInitFinAndTone }
</Button> </Button>
</> </>
} }
{ {
status == Status.playing && status == Status.plaing &&
<h1>Воспроизводится...{plaingNo}</h1> <h1>Воспроизводится...{plaingNo}</h1>
} }
{ {
status == Status.plaied && status == Status.plaied &&
<> <Button variant="success" onClick={()=>setStatus(Status.showlist)}>Показать слоги</Button>
<Button variant="success" onClick={()=>setStatus(Status.showlist)}>{strings.showSyllables}</Button>
<Button variant="success" onClick={()=>{setStatus(Status.playing)}}>{strings.playAgain}</Button>
</>
} }
{ {
status == Status.showlist && status == Status.showlist &&
<> <>
<RenderTones tones={state.randomTones!}/> <div>{renderRandomTones2()}</div>
<br/> <br/>
<Button variant="success" onClick={()=> setStatus(Status.params)}>{strings.toBegin}</Button> <Button variant="success" onClick={()=>refresh()}>В начало</Button>
</> </>
} }
</ListGroup.Item> </ListGroup.Item>
<ListGroup.Item><Copyright/></ListGroup.Item>
</ListGroup> </ListGroup>
</> </>
) )
} }
export default App export default App

1742
src/Data.ts Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,25 +1,7 @@
export const strings = { export const strings = {
mainHeader: "Диктант pīnyīn",
selectInitiales: 'Выберите инициали', selectInitiales: 'Выберите инициали',
selectFinales: 'Выберите финали', selectFinales: 'Выберите финали',
selectAll: 'Выбрать все', selectAll: 'Выбрать все',
unselectAll: 'Снять все', unselectAll: 'Снять все',
params: 'Параметры', params: 'Параметры'
sylCount: 'Количество слогов: ',
beginDictation: "Начать диктант!",
selectInitFinAndTone: "Выберите инициали, финали и тоны",
showSyllables: "Показать слоги",
playAgain: "Повторить",
toBegin: "В начало",
version: "Версия",
cpr: "©",
selectTones: "Выберите тоны",
first: "Первый",
second: "Второй",
third: "Третий",
fourth: "Четвёртый",
zeroth: "Нулевой",
infoFormat: "Выбрано {0} инициалей, {1} финалей, {2} тонов, найдено {3} слогов, {4} тонов",
pauseFormat: "Пауза между слогами, в секундах: {0}",
mail: "емайл:"
} }

View File

@ -15,8 +15,7 @@ export type SylPart = {
export type Tone = { export type Tone = {
tone: string, tone: string,
caption: string, caption: string
num: number
} }
export type Found = { export type Found = {
@ -34,4 +33,4 @@ export enum BtnColor {
green = "success" green = "success"
} }
export enum Status {params, prepare, playing, plaied, showlist} export enum Status {params, plaing, plaied, showlist}

View File

@ -1,42 +1,19 @@
import { syllables } from "./pinyin" import { syllables } from "./data"
import { BtnColor, SylPart, Syllable } from "./types" import { BtnColor, SylPart, Syllable } from "./types"
export const isEnabled = (arr: SylPart[], find: string): boolean => arr.some((str) => str.index==find) export const isEnabled = (arr: SylPart[], find: string): boolean => arr.some((str) => str.index==find)
export const toggleSylPart = ( arr: SylPart[], part: SylPart ):SylPart[] => { export const toggle = ( arr: SylPart[], part: SylPart ):SylPart[] => {
if ( arr.some((btn) => btn.index==part.index) ) if ( arr.some((btn) => btn.index==part.index) )
return arr.filter((el)=>el.index!==part.index) return arr.filter((el)=>el.index!==part.index)
else else
return [ ...arr, part] return [ ...arr, part]
} }
type TComparer = (index: any) => (el: any) => boolean
const SylPartComparer:TComparer = (index: string) => (el: SylPart) => el.index === index
const SylPartFilter:TComparer = (index: SylPart) => (el: SylPart) => el.index !== index.index
const defToggleComparer:TComparer = (index: any) => (el: any) => el === index
const defToggleFilter:TComparer = (index: any) => (el: any) => el !== index
export const toggleSylPartG = ( arr: SylPart[], part: SylPart ):SylPart[] =>
toggle<SylPart>(arr, part, SylPartComparer, SylPartFilter)
export const included = <T>( arr: T[], part: T, comparer:TComparer = defToggleComparer) =>
arr.some( comparer(part) )
const exclude = <T>( arr: T[], part: T,
filter:TComparer = defToggleFilter) => arr.filter( filter(part) )
const include = <T>( arr: T[], part: T):T[] => [ ...arr, part]
export const toggle = <T>( arr: T[], part: T,
comparer:TComparer = defToggleComparer,
filter:TComparer = defToggleFilter):T[] =>
included<T>(arr,part, comparer) ? exclude<T>(arr,part, filter ) : include<T>(arr, part)
export const genrateRandomNumber = (min: number, max: number):number => { export const genrateRandomNumber = (min: number, max: number):number => {
min = Math.ceil(min) min = Math.ceil(min)
max = Math.floor(max) max = Math.floor(max)
const rnd = Math.floor(Math.random() * (max - min + 1)) + min let rnd = Math.floor(Math.random() * (max - min + 1)) + min
return rnd return rnd
} }
@ -46,23 +23,16 @@ export const getRandomArray = <T>(fromArray:T[], count:number):T[] => {
return _tone return _tone
} }
for( let x=0; x<count; x++) { for( let x=0; x<count; x++) {
const randomN = genrateRandomNumber(0,fromArray.length-1) let randomN = genrateRandomNumber(0,fromArray.length-1)
_tone = [..._tone, fromArray[randomN]] _tone = [..._tone, fromArray[randomN]]
} }
return _tone return _tone
} }
export const GetSyllablesByInitAndFin = (initiales:SylPart[], finales: SylPart[]):Syllable[] => { export const GetSyllablesByInitAndFin = (initiales:SylPart[], finales: SylPart[]):Syllable[] => {
const inits = initiales.map( (i) => i.index) let inits = initiales.map( (i) => i.index)
const fins = finales.map( (i) =>i.index ) let fins = finales.map( (i) =>i.index )
return syllables.filter( syl => inits.includes(syl.initiale) && fins.includes(syl.finale) ) return syllables.filter( syl => inits.includes(syl.initiale) && fins.includes(syl.finale) )
} }
export const Outlined = (color: BtnColor):string => `outline-${color}` export const Outlined = (color: BtnColor):string => `outline-${color}`
export function FormatString(str: string, ...val: string[]) {
for (let index = 0; index < val.length; index++) {
str = str.replace(`{${index}}`, val[index]);
}
return str;
}

Some files were not shown because too many files have changed in this diff Show More