Use new store
This commit is contained in:
parent
653627033a
commit
16cdc1acf7
@ -1,17 +1,15 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useDispatch } from 'react-redux'
|
|
||||||
|
|
||||||
import { setText, setLang } from '../store/actions'
|
|
||||||
import { getBook } from '../lib/gutenberg'
|
import { getBook } from '../lib/gutenberg'
|
||||||
|
|
||||||
import styles from './Book.css'
|
import styles from './Book.css'
|
||||||
|
import { useStore } from '../store/RSVPStore'
|
||||||
|
|
||||||
export const Book = ({ author, language, rights, subject, title, id }) => {
|
export const Book = ({ author, language, title, id }) => {
|
||||||
const dispatch = useDispatch()
|
const [_, { setText, setLang }] = useStore()
|
||||||
|
|
||||||
const handleClick = async () => {
|
const handleClick = async () => {
|
||||||
dispatch(setText(await getBook(id)))
|
setText(await getBook(id))
|
||||||
dispatch(setLang(language[0]))
|
setLang(language[0])
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className={styles.book} onClick={handleClick}>
|
<div className={styles.book} onClick={handleClick}>
|
||||||
|
@ -1,5 +1,10 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useCounter, CounterProvider } from './CounterStore'
|
import {
|
||||||
|
useCounter,
|
||||||
|
CounterProvider,
|
||||||
|
selectors,
|
||||||
|
useCounterSelector
|
||||||
|
} from './CounterStore'
|
||||||
|
|
||||||
export const Counter = () => {
|
export const Counter = () => {
|
||||||
return (
|
return (
|
||||||
@ -16,12 +21,12 @@ export const Counter = () => {
|
|||||||
|
|
||||||
const Logger = () => {
|
const Logger = () => {
|
||||||
const store = useCounter()
|
const store = useCounter()
|
||||||
console.log(store[0])
|
|
||||||
return <></>
|
return <></>
|
||||||
}
|
}
|
||||||
|
|
||||||
const MyCounter = () => {
|
const MyCounter = () => {
|
||||||
const [{ counter }, { increment, decrement }] = useCounter()
|
const [_, { increment, decrement }] = useCounter()
|
||||||
|
const counter = useCounterSelector(selectors.$count)
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
{counter}
|
{counter}
|
||||||
|
9
src/components/CounterSelector.js
Normal file
9
src/components/CounterSelector.js
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
export const $count = state => {
|
||||||
|
return state.counter
|
||||||
|
}
|
||||||
|
export const lala = 'lalal'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
$count,
|
||||||
|
lala
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import { createStore } from 'context-store'
|
import { createStore } from 'potent-reducer'
|
||||||
|
import selectors from './CounterSelector'
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
counter: 0,
|
counter: 0,
|
||||||
@ -17,6 +18,8 @@ const reducer = {
|
|||||||
SET_TEXT: (state, { text }) => ({ ...state, text })
|
SET_TEXT: (state, { text }) => ({ ...state, text })
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = createStore({ reducer, thunks, initialState })
|
const store = createStore({ reducer, thunks, initialState, logging: true })
|
||||||
export const CounterProvider = store.Provider
|
export const CounterProvider = store.Provider
|
||||||
export const useCounter = store.useStore
|
export const useCounter = store.useStore
|
||||||
|
export const useCounterSelector = store.useSelector
|
||||||
|
export { selectors }
|
||||||
|
@ -1,22 +1,20 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useSelector, useDispatch } from 'react-redux'
|
|
||||||
import { debounce } from 'debounce'
|
import { debounce } from 'debounce'
|
||||||
|
|
||||||
import { setMaxLength, setWpm, setOffset, setLang } from '../store/actions'
|
|
||||||
import { Slider } from './Slider'
|
import { Slider } from './Slider'
|
||||||
import { selectOffset, selectLang } from '../store/selectors'
|
import { selectOffset, selectLang } from '../store/selectors'
|
||||||
|
import { useSelector, useDispatch } from '../store/RSVPStore'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
const availableLanguages = ['en', 'de']
|
const availableLanguages = ['en', 'de']
|
||||||
|
|
||||||
export const Options = () => {
|
export const Options = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const dispatch = useDispatch()
|
|
||||||
const maxLength = useSelector(state => state.maxLength)
|
const maxLength = useSelector(state => state.maxLength)
|
||||||
const wpm = useSelector(state => state.wpm)
|
const wpm = useSelector(state => state.wpm)
|
||||||
const offset = useSelector(selectOffset)
|
const offset = useSelector(selectOffset)
|
||||||
const lang = useSelector(selectLang)
|
const lang = useSelector(selectLang)
|
||||||
|
const { setMaxLength, setWpm, setOffset, setLang } = useDispatch()
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h2>{t('options.title')}</h2>
|
<h2>{t('options.title')}</h2>
|
||||||
@ -25,25 +23,25 @@ export const Options = () => {
|
|||||||
min={3}
|
min={3}
|
||||||
max={15}
|
max={15}
|
||||||
value={maxLength}
|
value={maxLength}
|
||||||
onChange={debounce(val => dispatch(setMaxLength(val)), 100)}
|
onChange={debounce(val => setMaxLength(val), 100)}
|
||||||
/>
|
/>
|
||||||
<Slider
|
<Slider
|
||||||
title={t('options.wpm')}
|
title={t('options.wpm')}
|
||||||
min={100}
|
min={100}
|
||||||
max={1000}
|
max={1000}
|
||||||
value={wpm}
|
value={wpm}
|
||||||
onChange={debounce(val => dispatch(setWpm(val)), 50)}
|
onChange={debounce(val => setWpm(val), 50)}
|
||||||
/>
|
/>
|
||||||
<Slider
|
<Slider
|
||||||
title={t('options.offset')}
|
title={t('options.offset')}
|
||||||
min={-50}
|
min={-50}
|
||||||
max={50}
|
max={50}
|
||||||
value={offset}
|
value={offset}
|
||||||
onChange={val => dispatch(setOffset(val))}
|
onChange={val => setOffset(val)}
|
||||||
/>
|
/>
|
||||||
<label>
|
<label>
|
||||||
Language:
|
Language:
|
||||||
<select value={lang} onChange={e => dispatch(setLang(e.target.value))}>
|
<select value={lang} onChange={e => setLang(e.target.value)}>
|
||||||
{availableLanguages.map(l => (
|
{availableLanguages.map(l => (
|
||||||
<option key={l}>{l}</option>
|
<option key={l}>{l}</option>
|
||||||
))}
|
))}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from '../store/RSVPStore'
|
||||||
|
|
||||||
import styles from './PivotMarker.css'
|
import styles from './PivotMarker.css'
|
||||||
import { selectOffset } from '../store/selectors'
|
import { selectOffset } from '../store/selectors'
|
||||||
|
@ -1,36 +1,37 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
|
|
||||||
import { useSelector, useDispatch } from 'react-redux'
|
import { useDispatch, useSelector } from '../store/RSVPStore'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
selectHasNextSegment,
|
selectHasNextSegment,
|
||||||
selectRunning,
|
selectRunning,
|
||||||
selectInterval
|
selectInterval
|
||||||
} from '../store/selectors'
|
} from '../store/selectors'
|
||||||
import { incrementSegment, stop, pause, start } from '../store/actions'
|
|
||||||
import { FiPlay, FiPause, FiSquare } from 'react-icons/fi'
|
import { FiPlay, FiPause, FiSquare } from 'react-icons/fi'
|
||||||
import { useInterval } from './generics/useInterval'
|
import { useInterval } from './generics/useInterval'
|
||||||
import { IconButton } from '../styles/IconButton'
|
import { IconButton } from '../styles/IconButton'
|
||||||
|
|
||||||
export const PlayerControl = () => {
|
export const PlayerControl = () => {
|
||||||
const dispatch = useDispatch()
|
|
||||||
const running = useSelector(selectRunning)
|
const running = useSelector(selectRunning)
|
||||||
const hasNext = useSelector(selectHasNextSegment)
|
const hasNext = useSelector(selectHasNextSegment)
|
||||||
const interval = useSelector(selectInterval)
|
const interval = useSelector(selectInterval)
|
||||||
|
|
||||||
|
const { pause, start, stop, incSegment } = useDispatch()
|
||||||
|
|
||||||
useInterval(
|
useInterval(
|
||||||
() => {
|
() => {
|
||||||
if (!hasNext) dispatch(pause())
|
if (!hasNext) pause()
|
||||||
else dispatch(incrementSegment())
|
else incSegment()
|
||||||
},
|
},
|
||||||
running ? interval : null
|
running ? interval : null
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<IconButton Icon={FiSquare} onClick={() => dispatch(stop())} />
|
<IconButton Icon={FiSquare} onClick={() => stop()} />
|
||||||
<IconButton
|
<IconButton
|
||||||
Icon={running ? FiPause : FiPlay}
|
Icon={running ? FiPause : FiPlay}
|
||||||
onClick={() => dispatch(running ? pause() : start())}
|
onClick={() => (running ? pause() : start())}
|
||||||
disabled={!hasNext}
|
disabled={!hasNext}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from '../store/RSVPStore'
|
||||||
import { selectCurrentSegmentIndex, selectSegments } from '../store/selectors'
|
import { selectCurrentSegmentIndex, selectSegments } from '../store/selectors'
|
||||||
|
|
||||||
export const Progress = () => {
|
export const Progress = () => {
|
||||||
|
@ -2,7 +2,8 @@ import React, { useState, useEffect, useCallback } from 'react'
|
|||||||
import { debounce } from 'debounce'
|
import { debounce } from 'debounce'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
import { FiSearch } from 'react-icons/fi'
|
import { FiSearch } from 'react-icons/fi'
|
||||||
import { useDispatch } from 'react-redux'
|
import { useDispatch } from '../store/RSVPStore'
|
||||||
|
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
@ -14,16 +15,15 @@ import { Book } from './Book'
|
|||||||
import styles from './SearchBar.css'
|
import styles from './SearchBar.css'
|
||||||
import { CursorList } from './generics/CursorList'
|
import { CursorList } from './generics/CursorList'
|
||||||
|
|
||||||
import { setText, setLang } from '../store/actions.js'
|
|
||||||
|
|
||||||
export const SearchBar = () => {
|
export const SearchBar = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const dispatch = useDispatch()
|
|
||||||
const [searchTerm, setSearchTerm] = useState('')
|
const [searchTerm, setSearchTerm] = useState('')
|
||||||
const [results, setResults] = useState([])
|
const [results, setResults] = useState([])
|
||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [isFocused, setFocus] = useState(false)
|
const [isFocused, setFocus] = useState(false)
|
||||||
|
|
||||||
|
const { setText, setLang } = useDispatch()
|
||||||
|
|
||||||
const debouncedSearch = useCallback(
|
const debouncedSearch = useCallback(
|
||||||
debounce(async term => {
|
debounce(async term => {
|
||||||
await search(term, 100).then(setResults)
|
await search(term, 100).then(setResults)
|
||||||
@ -35,9 +35,9 @@ export const SearchBar = () => {
|
|||||||
const handleClick = async idx => {
|
const handleClick = async idx => {
|
||||||
const { id, language } = results[idx]
|
const { id, language } = results[idx]
|
||||||
const url = `https://gutenberg.muperfredi.de/texts/${id}/stripped-body`
|
const url = `https://gutenberg.muperfredi.de/texts/${id}/stripped-body`
|
||||||
const text = await axios.get(url).then(res => res.data.body)
|
const text = await axios.get(url).then(res => res.data.body)(setText(text))(
|
||||||
dispatch(setText(text))
|
setLang(language[0])
|
||||||
dispatch(setLang(language[0]))
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from '../store/RSVPStore'
|
||||||
import { selectPivotizedSegment, selectOffset } from '../store/selectors'
|
import { selectPivotizedSegment, selectOffset } from '../store/selectors'
|
||||||
|
|
||||||
import styles from './Segment.css'
|
import styles from './Segment.css'
|
||||||
|
@ -1,12 +1,11 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useDispatch, useSelector } from 'react-redux'
|
|
||||||
import {
|
import {
|
||||||
FiSkipBack,
|
FiSkipBack,
|
||||||
FiSkipForward,
|
FiSkipForward,
|
||||||
FiRewind,
|
FiRewind,
|
||||||
FiFastForward
|
FiFastForward
|
||||||
} from 'react-icons/fi'
|
} from 'react-icons/fi'
|
||||||
|
import { useDispatch, useSelector } from '../store/RSVPStore'
|
||||||
import { IconButton } from '../styles/IconButton'
|
import { IconButton } from '../styles/IconButton'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -16,45 +15,39 @@ import {
|
|||||||
selectHasNextWord
|
selectHasNextWord
|
||||||
} from '../store/selectors'
|
} from '../store/selectors'
|
||||||
|
|
||||||
import {
|
|
||||||
incrementSentence,
|
|
||||||
incrementWord,
|
|
||||||
decrementSentence,
|
|
||||||
decrementWord
|
|
||||||
} from '../store/actions'
|
|
||||||
|
|
||||||
export const SegmentControl = ({ children }) => {
|
export const SegmentControl = ({ children }) => {
|
||||||
const dispatch = useDispatch()
|
|
||||||
const hasPrevSentence = useSelector(selectHasPrevSentence)
|
const hasPrevSentence = useSelector(selectHasPrevSentence)
|
||||||
const hasNextSentence = useSelector(selectHasNextSentence)
|
const hasNextSentence = useSelector(selectHasNextSentence)
|
||||||
const hasPrevWord = useSelector(selectHasPrevWord)
|
const hasPrevWord = useSelector(selectHasPrevWord)
|
||||||
const hasNextWord = useSelector(selectHasNextWord)
|
const hasNextWord = useSelector(selectHasNextWord)
|
||||||
|
|
||||||
|
const { decSentence, decWord } = useDispatch()
|
||||||
|
const { incSentence, incWord } = useDispatch()
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<IconButton
|
<IconButton
|
||||||
title="Previous Sentence"
|
title="Previous Sentence"
|
||||||
onClick={() => dispatch(decrementSentence())}
|
onClick={() => decSentence()}
|
||||||
Icon={FiRewind}
|
Icon={FiRewind}
|
||||||
disabled={!hasPrevSentence}
|
disabled={!hasPrevSentence}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
Icon={FiSkipBack}
|
Icon={FiSkipBack}
|
||||||
title="Previous Word"
|
title="Previous Word"
|
||||||
onClick={() => dispatch(decrementWord())}
|
onClick={() => decWord()}
|
||||||
disabled={!hasPrevWord}
|
disabled={!hasPrevWord}
|
||||||
/>
|
/>
|
||||||
{children}
|
{children}
|
||||||
<IconButton
|
<IconButton
|
||||||
Icon={FiSkipForward}
|
Icon={FiSkipForward}
|
||||||
title="Next Word"
|
title="Next Word"
|
||||||
onClick={() => dispatch(incrementWord())}
|
onClick={() => incWord()}
|
||||||
disabled={!hasNextWord}
|
disabled={!hasNextWord}
|
||||||
/>
|
/>
|
||||||
<IconButton
|
<IconButton
|
||||||
Icon={FiFastForward}
|
Icon={FiFastForward}
|
||||||
title="Next Sentence"
|
title="Next Sentence"
|
||||||
onClick={() => dispatch(incrementSentence())}
|
onClick={() => incSentence()}
|
||||||
disabled={!hasNextSentence}
|
disabled={!hasNextSentence}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
import { useDispatch } from 'react-redux'
|
import { useDispatch } from '../store/RSVPStore'
|
||||||
|
|
||||||
import { setText } from '../store/actions.js'
|
|
||||||
|
|
||||||
import styles from './TextInput.css'
|
import styles from './TextInput.css'
|
||||||
|
|
||||||
@ -10,7 +8,7 @@ const lorem =
|
|||||||
|
|
||||||
export const TextInput = () => {
|
export const TextInput = () => {
|
||||||
const [text, setTextState] = useState(lorem)
|
const [text, setTextState] = useState(lorem)
|
||||||
const dispatch = useDispatch()
|
const { setText } = useDispatch()
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<textarea
|
<textarea
|
||||||
@ -18,7 +16,7 @@ export const TextInput = () => {
|
|||||||
defaultValue={text}
|
defaultValue={text}
|
||||||
onInput={e => setTextState(e.target.value)}
|
onInput={e => setTextState(e.target.value)}
|
||||||
></textarea>
|
></textarea>
|
||||||
<button className={styles.load} onClick={() => dispatch(setText(text))}>
|
<button className={styles.load} onClick={() => setText(text)}>
|
||||||
Load
|
Load
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from '../store/RSVPStore'
|
||||||
import classNames from 'classnames'
|
import classNames from 'classnames'
|
||||||
|
|
||||||
import { getNextSmallerNumber } from '../lib/array-util.js'
|
import { getNextSmallerNumber } from '../lib/array-util.js'
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import { useSelector } from 'react-redux'
|
import { useSelector } from '../store/RSVPStore'
|
||||||
import { selectTotalTime } from '../store/selectors'
|
import { selectTotalTime } from '../store/selectors'
|
||||||
|
|
||||||
function formatTime(totalSeconds) {
|
function formatTime(totalSeconds) {
|
||||||
|
@ -2,10 +2,9 @@ import 'regenerator-runtime/runtime'
|
|||||||
|
|
||||||
import React from 'react'
|
import React from 'react'
|
||||||
import ReactDOM from 'react-dom'
|
import ReactDOM from 'react-dom'
|
||||||
import { Provider } from 'react-redux'
|
import { Provider } from './store/RSVPStore'
|
||||||
|
|
||||||
import { App } from './App'
|
import { App } from './App'
|
||||||
import { store } from './store/index.js'
|
|
||||||
|
|
||||||
import './i18n'
|
import './i18n'
|
||||||
|
|
||||||
@ -17,7 +16,7 @@ function createRootElement() {
|
|||||||
return root
|
return root
|
||||||
}
|
}
|
||||||
ReactDOM.render(
|
ReactDOM.render(
|
||||||
<Provider store={store}>
|
<Provider>
|
||||||
<App />
|
<App />
|
||||||
</Provider>,
|
</Provider>,
|
||||||
createRootElement()
|
createRootElement()
|
||||||
|
75
src/store/RSVPStore.js
Normal file
75
src/store/RSVPStore.js
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import { createStore } from 'potent-reducer'
|
||||||
|
import {
|
||||||
|
safeSelectNextWord,
|
||||||
|
safeSelectNextSentence,
|
||||||
|
safeSelectPrevWord,
|
||||||
|
safeSelectPrevSentence,
|
||||||
|
safeSelectNextSegment,
|
||||||
|
safeSelectPrevSegment
|
||||||
|
} from './selectors'
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
originalText: 'Sample Text',
|
||||||
|
curIdx: 0,
|
||||||
|
maxLength: 10,
|
||||||
|
isPlaying: false,
|
||||||
|
wpm: 300,
|
||||||
|
offset: -15,
|
||||||
|
running: false,
|
||||||
|
lang: 'en',
|
||||||
|
textDisplay: {
|
||||||
|
mode: 'pagination',
|
||||||
|
options: {
|
||||||
|
pagination: { pageLength: 50 },
|
||||||
|
segments: { prev: 9, next: 40 },
|
||||||
|
sentences: { prev: 1, next: 8 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const thunks = {
|
||||||
|
setText: text => dispatch => dispatch({ text }),
|
||||||
|
resetSegment: () => dispatch => dispatch(),
|
||||||
|
incSegment: () => dispatch => dispatch(),
|
||||||
|
decSegment: () => dispatch => dispatch(),
|
||||||
|
incWord: () => dispatch => dispatch(),
|
||||||
|
decWord: () => dispatch => dispatch(),
|
||||||
|
incSentence: () => dispatch => dispatch(),
|
||||||
|
decSentence: () => dispatch => dispatch(),
|
||||||
|
setMaxLength: length => dispatch => dispatch({ length }),
|
||||||
|
setWpm: wpm => dispatch => dispatch({ wpm }),
|
||||||
|
setOffset: offset => dispatch => dispatch({ offset }),
|
||||||
|
start: () => dispatch => dispatch(),
|
||||||
|
stop: () => dispatch => dispatch(),
|
||||||
|
pause: () => dispatch => dispatch(),
|
||||||
|
setLang: lang => dispatch => dispatch({ lang })
|
||||||
|
}
|
||||||
|
|
||||||
|
const reducer = {
|
||||||
|
SET_TEXT: (state, { text }) => ({ ...state, originalText: text, curIdx: 0 }),
|
||||||
|
RESET_SEGMENT: state => ({ ...state, curIdx: 0 }),
|
||||||
|
INC_SEGMENT: state => ({ ...state, curIdx: safeSelectNextSegment(state) }),
|
||||||
|
DEC_SEGMENT: state => ({ ...state, curIdx: safeSelectPrevSegment(state) }),
|
||||||
|
INC_WORD: state => ({ ...state, curIdx: safeSelectNextWord(state) }),
|
||||||
|
DEC_WORD: state => ({ ...state, curIdx: safeSelectPrevWord(state) }),
|
||||||
|
INC_SENTENCE: state => ({ ...state, curIdx: safeSelectNextSentence(state) }),
|
||||||
|
DEC_SENTENCE: state => ({ ...state, curIdx: safeSelectPrevSentence(state) }),
|
||||||
|
SET_MAX_LENGTH: (state, { maxLength }) => ({
|
||||||
|
...state,
|
||||||
|
maxLength,
|
||||||
|
running: false,
|
||||||
|
curIdx: 0
|
||||||
|
}),
|
||||||
|
SET_WPM: (state, { wpm }) => ({ ...state, wpm }),
|
||||||
|
SET_OFFSET: (state, { offset }) => ({ ...state, offset }),
|
||||||
|
START: state => ({ ...state, running: true }),
|
||||||
|
STOP: state => ({ ...state, running: false, curIdx: 0 }),
|
||||||
|
PAUSE: state => ({ ...state, running: false }),
|
||||||
|
SET_LANG: (state, { lang }) => ({ ...state, lang })
|
||||||
|
}
|
||||||
|
|
||||||
|
const store = createStore({ reducer, thunks, initialState, logging: true })
|
||||||
|
export const Provider = store.Provider
|
||||||
|
export const useStore = store.useStore
|
||||||
|
export const useSelector = store.useSelector
|
||||||
|
export const useDispatch = store.useDispatch
|
Loading…
Reference in New Issue
Block a user