multiple changes in states, useEffect for chaining
parent
4546eb9140
commit
f118066b9b
15
src/App.css
15
src/App.css
|
|
@ -42,6 +42,21 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.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) }
|
||||||
91
src/App.tsx
91
src/App.tsx
|
|
@ -1,33 +1,44 @@
|
||||||
import { useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import './App.css'
|
import './App.css'
|
||||||
import { Badge, Button, ListGroup } from 'react-bootstrap';
|
import { Button, ListGroup } from 'react-bootstrap';
|
||||||
import { initials, finales } from './data';
|
import { initials, finales } from './data';
|
||||||
import { BtnColor, Status } from './types';
|
import { BtnColor, Status, 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 { ToggleType } from './reducer';
|
||||||
import { ButtonSet } from './buttons';
|
import { ButtonSet } from './buttons';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
|
|
||||||
const { state, dispatch } = useStateContext();
|
const { state, dispatch } = useStateContext();
|
||||||
|
|
||||||
const [plaingNo, setPlaingNo ] = useState(0)
|
const [ plaingNo, setPlaingNo ] = useState(0)
|
||||||
|
const [ status, setStatus ] = useState(Status.params)
|
||||||
|
const [ playlist, setPlaylist ] = useState( [] as HTMLAudioElement[] )
|
||||||
|
|
||||||
const setStatus = (status: Status) => { dispatch({ type: ActionType.setStatus, payload: status}) }
|
useEffect( () => {
|
||||||
|
if (status===Status.prepare) {
|
||||||
|
console.log('effect for preparing')
|
||||||
|
preparePlayList(state.randomTones!)
|
||||||
|
}
|
||||||
|
if (status===Status.playing) {
|
||||||
|
console.log('effect for playing')
|
||||||
|
playPlayList()
|
||||||
|
}
|
||||||
|
}, [status])
|
||||||
|
|
||||||
const beginDictation = (): void => {
|
useEffect( () => {
|
||||||
dispatch({ type: ActionType.prepare })
|
console.log('effect for playList')
|
||||||
playDictation2()
|
setStatus(Status.playing)
|
||||||
}
|
} , [playlist] )
|
||||||
|
|
||||||
const playDictation2 = () => {
|
const preparePlayList = (tones: Tone[]) => {
|
||||||
console.log('tones for playing')
|
console.log('tones for playing')
|
||||||
state.randomTones!.forEach(element => console.log(element))
|
tones!.forEach(element => console.log(element))
|
||||||
if ( state.randomTones!.length == 0 ) return
|
if ( tones!.length == 0 ) return
|
||||||
let audios:HTMLAudioElement[] = []
|
let audios:HTMLAudioElement[] = []
|
||||||
state.randomTones!.forEach(element => {
|
tones!.forEach(element => {
|
||||||
audios = [...audios, new Audio(`/assets/audio/${element.tone}.mp3`) ]
|
audios = [...audios, new Audio(`/assets/audio/${element.tone}.mp3`) ]
|
||||||
});
|
});
|
||||||
console.debug(audios)
|
console.debug(audios)
|
||||||
|
|
@ -41,14 +52,30 @@ const playDictation2 = () => {
|
||||||
}, 1000*state.sylPause! ) ;
|
}, 1000*state.sylPause! ) ;
|
||||||
}
|
}
|
||||||
audios[audios.length-1].onended = () => setStatus(Status.plaied)
|
audios[audios.length-1].onended = () => setStatus(Status.plaied)
|
||||||
setStatus(Status.plaing)
|
setPlaylist(audios)
|
||||||
setPlaingNo(1)
|
|
||||||
audios[0].play()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderRandomTones2 = () => {
|
const playPlayList = () => {
|
||||||
|
if (playlist.length===0) return;
|
||||||
|
setStatus(Status.playing)
|
||||||
|
setPlaingNo(1)
|
||||||
|
playlist[0].play()
|
||||||
|
}
|
||||||
|
|
||||||
|
const playTone = (index: string) => {
|
||||||
|
let audio:HTMLAudioElement = new Audio(`/assets/audio/${index}.mp3`)
|
||||||
|
audio.play()
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderPlayList = () => {
|
||||||
|
|
||||||
return state.randomTones!.map(
|
return state.randomTones!.map(
|
||||||
(ton, i) => { return <span key={i}><Badge bg="success" pill>{ton.caption}</Badge>{' '}</span> }
|
(ton, i) => { return <span key={i}
|
||||||
|
className={`syllable tone${ton.num}`}
|
||||||
|
onClick = {()=>playTone(ton.tone)}
|
||||||
|
>
|
||||||
|
{i+1}{'. '}{ton.caption}{' '}
|
||||||
|
</span> }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,49 +84,53 @@ const renderRandomTones2 = () => {
|
||||||
<h1>{strings.mainHeader}</h1>
|
<h1>{strings.mainHeader}</h1>
|
||||||
<ListGroup>
|
<ListGroup>
|
||||||
|
|
||||||
<ListGroup.Item disabled={state.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}/>
|
||||||
</ListGroup.Item>
|
</ListGroup.Item>
|
||||||
|
|
||||||
<ListGroup.Item disabled={state.status != Status.params}>
|
<ListGroup.Item disabled={status != Status.params}>
|
||||||
<h2>{strings.selectFinales}</h2>
|
<h2>{strings.selectFinales}</h2>
|
||||||
<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={state.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>
|
||||||
{ state.status == Status.params &&
|
{ status == Status.params &&
|
||||||
<>
|
<>
|
||||||
Выбрано {state.initiales!.length} инициалей, {state.finales!.length} финалей, найдено {state.foundSyllables!.length} слогов, { state.foundTones!.length } тонов ,
|
Выбрано {state.initiales!.length} инициалей, {state.finales!.length} финалей, найдено {state.foundSyllables!.length} слогов, { state.foundTones!.length } тонов ,
|
||||||
<br/>
|
<br/>
|
||||||
<Button
|
<Button
|
||||||
variant={state.isFound!() ? "success" : "secondary"}
|
variant={state.isFound!() ? "success" : "secondary"}
|
||||||
size="lg"
|
size="lg"
|
||||||
onClick={()=>beginDictation()}
|
onClick={()=>setStatus(Status.prepare)}
|
||||||
disabled={!state.isFound!()}>
|
disabled={!state.isFound!()}>
|
||||||
{state.isFound!() ? strings.beginDictation : strings.selectInitAndFin }
|
{state.isFound!() ? strings.beginDictation : strings.selectInitAndFin }
|
||||||
</Button>
|
</Button>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
state.status == Status.plaing &&
|
status == Status.playing &&
|
||||||
<h1>Воспроизводится...{plaingNo}</h1>
|
<h1>Воспроизводится...{plaingNo}</h1>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
state.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>
|
||||||
|
</>
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
state.status == Status.showlist &&
|
status == Status.showlist &&
|
||||||
<>
|
<>
|
||||||
<div>{renderRandomTones2()}</div>
|
<div>{renderPlayList()}</div>
|
||||||
<br/>
|
<br/>
|
||||||
<Button variant="success" onClick={()=> setStatus(Status.params)}>В начало</Button>
|
// here add effect to new randomTones
|
||||||
|
<Button variant="success" onClick={()=> setStatus(Status.params)}>{strings.toBegin}</Button>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
</ListGroup.Item>
|
</ListGroup.Item>
|
||||||
|
|
|
||||||
2530
src/Data.ts
2530
src/Data.ts
File diff suppressed because it is too large
Load Diff
|
|
@ -7,5 +7,8 @@ export const strings = {
|
||||||
params: 'Параметры',
|
params: 'Параметры',
|
||||||
sylCount: 'Количество слогов',
|
sylCount: 'Количество слогов',
|
||||||
beginDictation: "Начать диктант!",
|
beginDictation: "Начать диктант!",
|
||||||
selectInitAndFin: "Выберите инициали и финали"
|
selectInitAndFin: "Выберите инициали и финали",
|
||||||
|
showSyllables: "Показать слоги",
|
||||||
|
playAgain: "Повторить",
|
||||||
|
toBegin: "В начало"
|
||||||
}
|
}
|
||||||
|
|
@ -15,7 +15,8 @@ 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 = {
|
||||||
|
|
@ -33,4 +34,4 @@ export enum BtnColor {
|
||||||
green = "success"
|
green = "success"
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum Status {params, plaing, plaied, showlist}
|
export enum Status {params, prepare, playing, plaied, showlist}
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
import { finales, initials, tones } from "./data";
|
import { finales, initials, tones } from "./data";
|
||||||
import { IState } from "./store";
|
import { IState } from "./store";
|
||||||
import { SylPart, Syllable, Tone, Status } from "./types";
|
import { SylPart, Syllable, Tone } from "./types";
|
||||||
import { GetSyllablesByInitAndFin, getRandomArray, toggle } from "./utils";
|
import { GetSyllablesByInitAndFin, getRandomArray, toggle } from "./utils";
|
||||||
|
|
||||||
export enum ActionType {
|
export enum ActionType {
|
||||||
toggleOne, toggleAll, refreshPlayList, setPause, setCount, setStatus, prepare
|
toggleOne, toggleAll, refreshPlayList, setPause, setCount, setStatus, playing
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ToggleType { init, fin }
|
export enum ToggleType { init, fin }
|
||||||
|
|
@ -12,89 +12,77 @@ export type TogglePayload = { type: ToggleType, part: SylPart }
|
||||||
|
|
||||||
export type Action = { type: ActionType, payload?: any };
|
export type Action = { type: ActionType, payload?: any };
|
||||||
|
|
||||||
const ProceedAllInitials = (state: IState):{ allInitiales:boolean, initiales: SylPart[], foundSyllables:Syllable[], foundTones: Tone[]} =>
|
interface IFounds { foundSyllables:Syllable[], foundTones: Tone[], randomTones: Tone[] }
|
||||||
|
|
||||||
|
interface IInitResult {
|
||||||
|
allInitiales?:boolean,
|
||||||
|
initiales: SylPart[],
|
||||||
|
foundSyllables:Syllable[],
|
||||||
|
foundTones: Tone[],
|
||||||
|
randomTones: Tone[]
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IFinResult {
|
||||||
|
allfinales?:boolean,
|
||||||
|
finales: SylPart[],
|
||||||
|
foundSyllables:Syllable[],
|
||||||
|
foundTones: Tone[],
|
||||||
|
randomTones: Tone[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const proceedFounds = ( initiales: SylPart[], finales:SylPart[], count: number):IFounds=>
|
||||||
|
{
|
||||||
|
let foundSyllables:Syllable[] = GetSyllablesByInitAndFin( initiales, finales )
|
||||||
|
let foundTones = tones.filter( t => foundSyllables.some( syl => syl.tones.some( st => st===t.tone) ) )
|
||||||
|
let randomTones = getRandomArray(foundTones, count)
|
||||||
|
return { foundSyllables, foundTones, randomTones }
|
||||||
|
}
|
||||||
|
|
||||||
|
const ProceedAllInitials = (state: IState):IInitResult =>
|
||||||
{
|
{
|
||||||
let toggled = state.allInitiales ? [] as SylPart[] : initials
|
let toggled = state.allInitiales ? [] as SylPart[] : initials
|
||||||
let foundSyllables:Syllable[] = GetSyllablesByInitAndFin( toggled, state.finales )
|
|
||||||
let foundTones = tones.filter( t => foundSyllables.some( syl => syl.tones.some( st => st===t.tone) ) )
|
|
||||||
return {
|
return {
|
||||||
allInitiales: !state.allInitiales,
|
allInitiales: !state.allInitiales,
|
||||||
initiales: toggled,
|
initiales: toggled,
|
||||||
foundSyllables: foundSyllables,
|
...proceedFounds(toggled, state.finales, state.sylCount)
|
||||||
foundTones: foundTones
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProceedAllFinales = (state: IState):{ allfinales:boolean, finales: SylPart[], foundSyllables:Syllable[], foundTones: Tone[]} =>
|
const ProceedAllFinales = (state: IState):IFinResult =>
|
||||||
{
|
{
|
||||||
let toggled = state.allfinales ? [] as SylPart[] : finales
|
let toggled = state.allfinales ? [] as SylPart[] : finales
|
||||||
let foundSyllables:Syllable[] = GetSyllablesByInitAndFin( state.initiales, toggled )
|
|
||||||
let foundTones = tones.filter( t => foundSyllables.some( syl => syl.tones.some( st => st===t.tone) ) )
|
|
||||||
return {
|
return {
|
||||||
allfinales: !state.allfinales,
|
allfinales: !state.allfinales,
|
||||||
finales: toggled,
|
finales: toggled,
|
||||||
foundSyllables: foundSyllables,
|
...proceedFounds(state.initiales, toggled, state.sylCount)
|
||||||
foundTones: foundTones
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProceedInitiale = (state: IState, index: SylPart):{ initiales: SylPart[], foundSyllables:Syllable[], foundTones: Tone[]} =>
|
const ProceedInitiale = (state: IState, index: SylPart):IInitResult =>
|
||||||
{
|
{
|
||||||
let toggled = toggle(state.initiales,index)
|
let toggled = toggle(state.initiales,index)
|
||||||
let foundSyllables:Syllable[] = GetSyllablesByInitAndFin( toggled, state.finales )
|
|
||||||
let foundTones = tones.filter( t => foundSyllables.some( syl => syl.tones.some( st => st===t.tone) ) )
|
|
||||||
return {
|
return {
|
||||||
initiales: toggled,
|
initiales: toggled,
|
||||||
foundSyllables: foundSyllables,
|
...proceedFounds(toggled, state.finales, state.sylCount)
|
||||||
foundTones: foundTones
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const ProceedFinale = (state: IState, index: SylPart):{ finales: SylPart[], foundSyllables:Syllable[], foundTones: Tone[]} =>
|
const ProceedFinale = (state: IState, index: SylPart):IFinResult =>
|
||||||
{
|
{
|
||||||
let toggled = toggle(state.finales,index)
|
let toggled = toggle(state.finales,index)
|
||||||
let foundSyllables:Syllable[] = GetSyllablesByInitAndFin( state.initiales , toggled )
|
|
||||||
let foundTones = tones.filter( t => foundSyllables.some( syl => syl.tones.some( st => st===t.tone) ) )
|
|
||||||
return {
|
return {
|
||||||
finales: toggled,
|
finales: toggled,
|
||||||
foundSyllables: foundSyllables,
|
...proceedFounds(state.initiales, toggled, state.sylCount)
|
||||||
foundTones: foundTones
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const prepareDictation = (state: IState): { randomTones: Tone[] } => {
|
|
||||||
var newRandom = getRandomArray( state.foundTones, state.sylCount! )
|
|
||||||
if (newRandom.length > 0) {
|
|
||||||
let audios:HTMLAudioElement[] = []
|
|
||||||
newRandom!.forEach(element => {
|
|
||||||
audios = [...audios, new Audio(`/assets/audio/${element.tone}.mp3`) ]
|
|
||||||
});
|
|
||||||
if ( audios.length > 0 ) {
|
|
||||||
for(let x=0; x<audios.length-1;x++)
|
|
||||||
{
|
|
||||||
audios[x].onended = () => setTimeout( () => {
|
|
||||||
// let pno = x+2
|
|
||||||
// setPlaingNo(pno)
|
|
||||||
audios[x+1].play()
|
|
||||||
}, 1000*state.sylPause! ) ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
console.log('tones for refresh')
|
|
||||||
newRandom!.forEach(element => console.log(element))
|
|
||||||
|
|
||||||
return { randomTones: newRandom }
|
|
||||||
}
|
|
||||||
// see https://stackoverflow.com/questions/36730793/can-i-dispatch-an-action-in-reducer
|
|
||||||
export const reducer = (state:IState, action:Action):IState => {
|
export const reducer = (state:IState, action:Action):IState => {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case ActionType.setPause: return { ...state, sylPause: action.payload as number }
|
case ActionType.setPause: return { ...state, sylPause: action.payload as number }
|
||||||
case ActionType.setCount: return { ...state, sylCount: action.payload as number }
|
case ActionType.setCount: return { ...state,
|
||||||
// case ActionType.refreshPlayList: return { ...state, randomTones: getRandomArray( state.foundTones, state.sylCount! ) }
|
sylCount: action.payload as number,
|
||||||
case ActionType.setStatus: return { ...state, status: action.payload as Status}
|
randomTones: getRandomArray(state.foundTones, action.payload as number) }
|
||||||
|
// case ActionType.setStatus: return { ...state, status: action.payload as Status}
|
||||||
case ActionType.prepare: return { ...state, ...prepareDictation(state) }
|
|
||||||
|
|
||||||
case ActionType.toggleAll: {
|
case ActionType.toggleAll: {
|
||||||
if (action.payload as ToggleType === ToggleType.init) return { ...state, ...ProceedAllInitials(state) }
|
if (action.payload as ToggleType === ToggleType.init) return { ...state, ...ProceedAllInitials(state) }
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { Context, createContext, Dispatch, ReactElement, useContext, useReducer } from "react";
|
import { Context, createContext, Dispatch, ReactElement, useContext, useReducer } from "react";
|
||||||
import { reducer, Action, ToggleType } from "./reducer"
|
import { reducer, Action, ToggleType } from "./reducer"
|
||||||
import { Status, Syllable, SylPart, Tone } from "./types";
|
import { Syllable, SylPart, Tone } from "./types";
|
||||||
import { isEnabled } from "./utils";
|
import { isEnabled } from "./utils";
|
||||||
|
|
||||||
export interface IState {
|
export interface IState {
|
||||||
|
|
@ -15,8 +15,7 @@ export interface IState {
|
||||||
randomTones: Tone[],
|
randomTones: Tone[],
|
||||||
allEnabled: (type: ToggleType) => false,
|
allEnabled: (type: ToggleType) => false,
|
||||||
isEnabled: (type: ToggleType, index: string) => false,
|
isEnabled: (type: ToggleType, index: string) => false,
|
||||||
isFound: () => false,
|
isFound: () => false
|
||||||
status: Status
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IStore {
|
export interface IStore {
|
||||||
|
|
@ -44,8 +43,7 @@ export const defaultState:Object = {
|
||||||
if ( type === ToggleType.fin ) return isEnabled((this as IState).finales!, index)
|
if ( type === ToggleType.fin ) return isEnabled((this as IState).finales!, index)
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
isFound: function():boolean { return (this as IState).foundSyllables!.length > 0 },
|
isFound: function():boolean { return (this as IState).foundSyllables!.length > 0 }
|
||||||
status: Status.params
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const AppContext:Context<IStore> = createContext<IStore>({ state: defaultState as IState, dispatch: () => null })
|
export const AppContext:Context<IStore> = createContext<IStore>({ state: defaultState as IState, dispatch: () => null })
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue