diff --git a/package.json b/package.json
index 100fa0c..73fad4b 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,8 @@
{
"name": "pinyindictation",
- "author": {"email": "rurik19@yandex.ru", "name": "Yuriy Evdokimov"},
+ "author": {"email": "rurik19@yandex.ru", "name": "Юрий Евдокимов"},
"private": true,
- "version": "1.0.2",
+ "version": "1.1.0",
"type": "module",
"scripts": {
"dev": "vite",
diff --git a/src/App.tsx b/src/App.tsx
index 789f056..fa065c2 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -12,6 +12,7 @@ import { RenderTones } from './rendertones';
import { getAudio } from './audio';
import { Copyright } from './copyright';
import { SelectTones } from './selecttones';
+import { FormatString } from './utils';
function App() {
@@ -68,6 +69,13 @@ const playPlayList = () => {
playlist[0].play()
}
+const info = () => FormatString(strings.infoFormat,
+ state.initiales!.length.toString(),
+ state.finales!.length.toString(),
+ state.toneChecks!.length.toString(),
+ state.foundSyllables!.length.toString(),
+ state.foundTones!.length.toString())
+
return (
<>
{strings.mainHeader}
@@ -96,14 +104,14 @@ return (
{ status == Status.params &&
<>
- Выбрано {state.initiales!.length} инициалей, {state.finales!.length} финалей, найдено {state.foundSyllables!.length} слогов, { state.foundTones!.length } тонов ,
+ {info()}
>
}
diff --git a/src/Strings.ts b/src/Strings.ts
index c7972f1..fc94967 100644
--- a/src/Strings.ts
+++ b/src/Strings.ts
@@ -5,13 +5,21 @@ export const strings = {
selectAll: 'Выбрать все',
unselectAll: 'Снять все',
params: 'Параметры',
- sylCount: 'Количество слогов',
+ sylCount: 'Количество слогов: ',
beginDictation: "Начать диктант!",
- selectInitAndFin: "Выберите инициали и финали",
+ selectInitFinAndTone: "Выберите инициали, финали и тоны",
showSyllables: "Показать слоги",
playAgain: "Повторить",
toBegin: "В начало",
version: "Версия",
cpr: "©",
- selectTones: "Выберите тоны"
+ selectTones: "Выберите тоны",
+ first: "Первый",
+ second: "Второй",
+ third: "Третий",
+ fourth: "Четвёртый",
+ zeroth: "Нулевой",
+ infoFormat: "Выбрано {0} инициалей, {1} финалей, {2} тонов, найдено {3} слогов, {4} тонов",
+ pauseFormat: "Пауза между слогами, в секундах: {0}",
+ mail: "емайл:"
}
\ No newline at end of file
diff --git a/src/Utils.ts b/src/Utils.ts
index 17cda21..94818c0 100644
--- a/src/Utils.ts
+++ b/src/Utils.ts
@@ -3,13 +3,36 @@ import { BtnColor, SylPart, Syllable } from "./types"
export const isEnabled = (arr: SylPart[], find: string): boolean => arr.some((str) => str.index==find)
-export const toggle = ( arr: SylPart[], part: SylPart ):SylPart[] => {
+export const toggleSylPart = ( arr: SylPart[], part: SylPart ):SylPart[] => {
if ( arr.some((btn) => btn.index==part.index) )
return arr.filter((el)=>el.index!==part.index)
else
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(arr, part, SylPartComparer, SylPartFilter)
+
+export const included = ( arr: T[], part: T, comparer:TComparer = defToggleComparer) =>
+ arr.some( comparer(part) )
+
+const exclude = ( arr: T[], part: T,
+ filter:TComparer = defToggleFilter) => arr.filter( filter(part) )
+
+const include = ( arr: T[], part: T):T[] => [ ...arr, part]
+
+export const toggle = ( arr: T[], part: T,
+ comparer:TComparer = defToggleComparer,
+ filter:TComparer = defToggleFilter):T[] =>
+ included(arr,part, comparer) ? exclude(arr,part, filter ) : include(arr, part)
+
export const genrateRandomNumber = (min: number, max: number):number => {
min = Math.ceil(min)
max = Math.floor(max)
@@ -36,3 +59,10 @@ export const GetSyllablesByInitAndFin = (initiales:SylPart[], finales: SylPart[]
}
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;
+}
\ No newline at end of file
diff --git a/src/copyright.tsx b/src/copyright.tsx
index 42a8ec7..c5cb4e9 100644
--- a/src/copyright.tsx
+++ b/src/copyright.tsx
@@ -2,4 +2,5 @@ import { ReactElement } from "react";
import { strings } from "./strings";
import { author } from '../package.json'
-export const Copyright = ():ReactElement => {strings.version} {import.meta.env.VITE_REACT_APP_VERSION} {strings.cpr}{author.name}
\ No newline at end of file
+export const Copyright = ():ReactElement =>
+{strings.version} {import.meta.env.VITE_REACT_APP_VERSION} {strings.cpr}{author.name} {strings.mail}{author.email}
\ No newline at end of file
diff --git a/src/params.tsx b/src/params.tsx
index 2ab7c22..71edab4 100644
--- a/src/params.tsx
+++ b/src/params.tsx
@@ -3,6 +3,7 @@ import { useStateContext } from "./store";
import { Form } from "react-bootstrap";
import { ActionType } from "./reducer";
import { strings } from "./strings";
+import { FormatString } from "./utils";
export const Params = (): ReactElement => {
@@ -16,7 +17,7 @@ export const Params = (): ReactElement => {
return <>
{strings.sylCount} {state.sylCount}
- Пауза между слогами {state.sylPause} секунд
+ { FormatString( strings.pauseFormat , state.sylPause!.toString() )}
>
diff --git a/src/reducer.ts b/src/reducer.ts
index 53e691a..8f5a0ec 100644
--- a/src/reducer.ts
+++ b/src/reducer.ts
@@ -1,10 +1,10 @@
import { finales, initials, tones } from "./pinyin";
import { IState } from "./store";
import { SylPart, Syllable, Tone } from "./types";
-import { GetSyllablesByInitAndFin, getRandomArray, toggle } from "./utils";
+import { GetSyllablesByInitAndFin, getRandomArray, toggleSylPart, toggle, included } from "./utils";
export enum ActionType {
- toggleOne, toggleAll, refresh, setPause, setCount, setStatus, playing
+ toggleOne, toggleAll, refresh, setPause, setCount, setStatus, playing, toneSelect
}
export enum ToggleType { init, fin }
@@ -12,28 +12,30 @@ export type TogglePayload = { type: ToggleType, part: SylPart }
export type Action = { type: ActionType, payload?: number | ToggleType | TogglePayload };
-interface IFounds { foundSyllables:Syllable[], foundTones: Tone[], randomTones: Tone[] }
+interface IFounds {
+ foundSyllables:Syllable[],
+ foundTones: Tone[],
+ randomTones: Tone[]
+}
-interface IInitResult {
+interface IInitResult extends IFounds {
allInitiales?:boolean,
- initiales: SylPart[],
- foundSyllables:Syllable[],
- foundTones: Tone[],
- randomTones: Tone[]
+ initiales: SylPart[]
}
-interface IFinResult {
+interface IFinResult extends IFounds {
allfinales?:boolean,
- finales: SylPart[],
- foundSyllables:Syllable[],
- foundTones: Tone[],
- randomTones: Tone[]
+ finales: SylPart[]
}
-const proceedFounds = ( initiales: SylPart[], finales:SylPart[], count: number):IFounds=>
+interface IToneSelectResult extends IFounds {
+ toneChecks: number[]
+}
+
+const proceedFounds = ( initiales: SylPart[], finales:SylPart[], tonesCheck:number[], count: number):IFounds=>
{
const foundSyllables:Syllable[] = GetSyllablesByInitAndFin( initiales, finales )
- const foundTones = tones.filter( t => foundSyllables.some( syl => syl.tones.some( st => st===t.tone) ) )
+ const foundTones = tones.filter( t => foundSyllables.some( syl => syl.tones.some( st => st===t.tone && included(tonesCheck, t.num) ) ) )
const randomTones = getRandomArray(foundTones, count)
return { foundSyllables, foundTones, randomTones }
}
@@ -44,7 +46,7 @@ const ProceedAllInitials = (state: IState):IInitResult =>
return {
allInitiales: !state.allInitiales,
initiales: toggled,
- ...proceedFounds(toggled, state.finales, state.sylCount)
+ ...proceedFounds(toggled, state.finales, state.toneChecks, state.sylCount)
}
}
@@ -54,25 +56,32 @@ const ProceedAllFinales = (state: IState):IFinResult =>
return {
allfinales: !state.allfinales,
finales: toggled,
- ...proceedFounds(state.initiales, toggled, state.sylCount)
+ ...proceedFounds(state.initiales, toggled, state.toneChecks, state.sylCount)
}
}
const ProceedInitiale = (state: IState, index: SylPart):IInitResult =>
{
- const toggled = toggle(state.initiales,index)
+ const toggled = toggleSylPart(state.initiales,index)
return {
initiales: toggled,
- ...proceedFounds(toggled, state.finales, state.sylCount)
+ ...proceedFounds(toggled, state.finales, state.toneChecks, state.sylCount)
}
}
const ProceedFinale = (state: IState, index: SylPart):IFinResult =>
{
- const toggled = toggle(state.finales,index)
+ const toggled = toggleSylPart(state.finales,index)
return {
finales: toggled,
- ...proceedFounds(state.initiales, toggled, state.sylCount)
+ ...proceedFounds(state.initiales, toggled, state.toneChecks, state.sylCount)
+ }
+}
+
+const ProceedToneSelect = (state: IState, index: number):IToneSelectResult => {
+ const toggled = toggle(state.toneChecks, index)
+ return { toneChecks: toggled,
+ ...proceedFounds(state.initiales, state.finales, toggled, state.sylCount)
}
}
@@ -95,6 +104,8 @@ export const reducer = (state:IState, action:Action):IState => {
return { ...state, ...ProceedFinale(state, (action.payload as TogglePayload).part ) }
return state
}
+ case ActionType.toneSelect: return { ...state, ...ProceedToneSelect(state, (action.payload as number) ) }
+
default: return state
}
}
\ No newline at end of file
diff --git a/src/selecttones.tsx b/src/selecttones.tsx
index 016d2eb..a5b5781 100644
--- a/src/selecttones.tsx
+++ b/src/selecttones.tsx
@@ -1,34 +1,53 @@
import { useStateContext } from "./store"
import { Form } from "react-bootstrap";
+import { strings } from "./strings";
+import { ActionType } from "./reducer";
export const SelectTones = () => {
- // const { state, dispatch } = useStateContext()
+ const { state, dispatch } = useStateContext()
+ const onToneChange = ( toneNo: number ) =>
+ dispatch({ type:ActionType.toneSelect, payload: toneNo })
return <>
onToneChange(1) }
/>
onToneChange(2) }
/>
- onToneChange(3) }
/>
onToneChange(4) }
+ />
+ onToneChange(5) }
/>
>
diff --git a/src/store.ts b/src/store.ts
index c45a408..6e4a8af 100644
--- a/src/store.ts
+++ b/src/store.ts
@@ -13,9 +13,11 @@ export interface IState {
foundSyllables: Syllable[],
foundTones: Tone[],
randomTones: Tone[],
+ toneChecks: number[],
allEnabled: (type: ToggleType) => false,
isEnabled: (type: ToggleType, index: string) => false,
- isFound: () => false
+ isFound: () => false,
+ isToneChecked: (toneNo: number) => false
}
export interface IStore {
@@ -33,6 +35,7 @@ export const defaultState:object = {
foundSyllables: [],
foundTones: [],
randomTones: [],
+ toneChecks: [ 1, 2, 3, 4, 5 ],
allEnabled: function(type: ToggleType) {
if ( type === ToggleType.init ) return (this as IState).allInitiales
if ( type === ToggleType.fin ) return (this as IState).allfinales
@@ -43,7 +46,9 @@ export const defaultState:object = {
if ( type === ToggleType.fin ) return isEnabled((this as IState).finales!, index)
return false
},
- isFound: function():boolean { return (this as IState).foundSyllables!.length > 0 }
+ isFound: function():boolean { return (this as IState).foundSyllables!.length > 0 && (this as IState).toneChecks!.length > 0}
+ ,
+ isToneChecked: function(toneNo: number):boolean{ return (this as IState).toneChecks.includes(toneNo) }
}
export const AppContext:Context = createContext({ state: defaultState as IState, dispatch: () => null })