Use new store

This commit is contained in:
Alfred Melch 2020-02-01 18:13:16 +01:00
parent 653627033a
commit 16cdc1acf7
16 changed files with 140 additions and 61 deletions

View File

@ -1,17 +1,15 @@
import React from 'react'
import { useDispatch } from 'react-redux'
import { setText, setLang } from '../store/actions'
import { getBook } from '../lib/gutenberg'
import styles from './Book.css'
import { useStore } from '../store/RSVPStore'
export const Book = ({ author, language, rights, subject, title, id }) => {
const dispatch = useDispatch()
export const Book = ({ author, language, title, id }) => {
const [_, { setText, setLang }] = useStore()
const handleClick = async () => {
dispatch(setText(await getBook(id)))
dispatch(setLang(language[0]))
setText(await getBook(id))
setLang(language[0])
}
return (
<div className={styles.book} onClick={handleClick}>

View File

@ -1,5 +1,10 @@
import React from 'react'
import { useCounter, CounterProvider } from './CounterStore'
import {
useCounter,
CounterProvider,
selectors,
useCounterSelector
} from './CounterStore'
export const Counter = () => {
return (
@ -16,12 +21,12 @@ export const Counter = () => {
const Logger = () => {
const store = useCounter()
console.log(store[0])
return <></>
}
const MyCounter = () => {
const [{ counter }, { increment, decrement }] = useCounter()
const [_, { increment, decrement }] = useCounter()
const counter = useCounterSelector(selectors.$count)
return (
<div>
{counter}

View File

@ -0,0 +1,9 @@
export const $count = state => {
return state.counter
}
export const lala = 'lalal'
export default {
$count,
lala
}

View File

@ -1,4 +1,5 @@
import { createStore } from 'context-store'
import { createStore } from 'potent-reducer'
import selectors from './CounterSelector'
const initialState = {
counter: 0,
@ -17,6 +18,8 @@ const reducer = {
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 useCounter = store.useStore
export const useCounterSelector = store.useSelector
export { selectors }

View File

@ -1,22 +1,20 @@
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { debounce } from 'debounce'
import { setMaxLength, setWpm, setOffset, setLang } from '../store/actions'
import { Slider } from './Slider'
import { selectOffset, selectLang } from '../store/selectors'
import { useSelector, useDispatch } from '../store/RSVPStore'
import { useTranslation } from 'react-i18next'
const availableLanguages = ['en', 'de']
export const Options = () => {
const { t } = useTranslation()
const dispatch = useDispatch()
const maxLength = useSelector(state => state.maxLength)
const wpm = useSelector(state => state.wpm)
const offset = useSelector(selectOffset)
const lang = useSelector(selectLang)
const { setMaxLength, setWpm, setOffset, setLang } = useDispatch()
return (
<div>
<h2>{t('options.title')}</h2>
@ -25,25 +23,25 @@ export const Options = () => {
min={3}
max={15}
value={maxLength}
onChange={debounce(val => dispatch(setMaxLength(val)), 100)}
onChange={debounce(val => setMaxLength(val), 100)}
/>
<Slider
title={t('options.wpm')}
min={100}
max={1000}
value={wpm}
onChange={debounce(val => dispatch(setWpm(val)), 50)}
onChange={debounce(val => setWpm(val), 50)}
/>
<Slider
title={t('options.offset')}
min={-50}
max={50}
value={offset}
onChange={val => dispatch(setOffset(val))}
onChange={val => setOffset(val)}
/>
<label>
Language:
<select value={lang} onChange={e => dispatch(setLang(e.target.value))}>
<select value={lang} onChange={e => setLang(e.target.value)}>
{availableLanguages.map(l => (
<option key={l}>{l}</option>
))}

View File

@ -1,5 +1,5 @@
import React from 'react'
import { useSelector } from 'react-redux'
import { useSelector } from '../store/RSVPStore'
import styles from './PivotMarker.css'
import { selectOffset } from '../store/selectors'

View File

@ -1,36 +1,37 @@
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { useDispatch, useSelector } from '../store/RSVPStore'
import {
selectHasNextSegment,
selectRunning,
selectInterval
} from '../store/selectors'
import { incrementSegment, stop, pause, start } from '../store/actions'
import { FiPlay, FiPause, FiSquare } from 'react-icons/fi'
import { useInterval } from './generics/useInterval'
import { IconButton } from '../styles/IconButton'
export const PlayerControl = () => {
const dispatch = useDispatch()
const running = useSelector(selectRunning)
const hasNext = useSelector(selectHasNextSegment)
const interval = useSelector(selectInterval)
const { pause, start, stop, incSegment } = useDispatch()
useInterval(
() => {
if (!hasNext) dispatch(pause())
else dispatch(incrementSegment())
if (!hasNext) pause()
else incSegment()
},
running ? interval : null
)
return (
<>
<IconButton Icon={FiSquare} onClick={() => dispatch(stop())} />
<IconButton Icon={FiSquare} onClick={() => stop()} />
<IconButton
Icon={running ? FiPause : FiPlay}
onClick={() => dispatch(running ? pause() : start())}
onClick={() => (running ? pause() : start())}
disabled={!hasNext}
/>
</>

View File

@ -1,5 +1,5 @@
import React from 'react'
import { useSelector } from 'react-redux'
import { useSelector } from '../store/RSVPStore'
import { selectCurrentSegmentIndex, selectSegments } from '../store/selectors'
export const Progress = () => {

View File

@ -2,7 +2,8 @@ import React, { useState, useEffect, useCallback } from 'react'
import { debounce } from 'debounce'
import classNames from 'classnames'
import { FiSearch } from 'react-icons/fi'
import { useDispatch } from 'react-redux'
import { useDispatch } from '../store/RSVPStore'
import { useTranslation } from 'react-i18next'
import axios from 'axios'
@ -14,16 +15,15 @@ import { Book } from './Book'
import styles from './SearchBar.css'
import { CursorList } from './generics/CursorList'
import { setText, setLang } from '../store/actions.js'
export const SearchBar = () => {
const { t } = useTranslation()
const dispatch = useDispatch()
const [searchTerm, setSearchTerm] = useState('')
const [results, setResults] = useState([])
const [loading, setLoading] = useState(false)
const [isFocused, setFocus] = useState(false)
const { setText, setLang } = useDispatch()
const debouncedSearch = useCallback(
debounce(async term => {
await search(term, 100).then(setResults)
@ -35,9 +35,9 @@ export const SearchBar = () => {
const handleClick = async idx => {
const { id, language } = results[idx]
const url = `https://gutenberg.muperfredi.de/texts/${id}/stripped-body`
const text = await axios.get(url).then(res => res.data.body)
dispatch(setText(text))
dispatch(setLang(language[0]))
const text = await axios.get(url).then(res => res.data.body)(setText(text))(
setLang(language[0])
)
}
useEffect(() => {

View File

@ -1,5 +1,5 @@
import React from 'react'
import { useSelector } from 'react-redux'
import { useSelector } from '../store/RSVPStore'
import { selectPivotizedSegment, selectOffset } from '../store/selectors'
import styles from './Segment.css'

View File

@ -1,12 +1,11 @@
import React from 'react'
import { useDispatch, useSelector } from 'react-redux'
import {
FiSkipBack,
FiSkipForward,
FiRewind,
FiFastForward
} from 'react-icons/fi'
import { useDispatch, useSelector } from '../store/RSVPStore'
import { IconButton } from '../styles/IconButton'
import {
@ -16,45 +15,39 @@ import {
selectHasNextWord
} from '../store/selectors'
import {
incrementSentence,
incrementWord,
decrementSentence,
decrementWord
} from '../store/actions'
export const SegmentControl = ({ children }) => {
const dispatch = useDispatch()
const hasPrevSentence = useSelector(selectHasPrevSentence)
const hasNextSentence = useSelector(selectHasNextSentence)
const hasPrevWord = useSelector(selectHasPrevWord)
const hasNextWord = useSelector(selectHasNextWord)
const { decSentence, decWord } = useDispatch()
const { incSentence, incWord } = useDispatch()
return (
<>
<IconButton
title="Previous Sentence"
onClick={() => dispatch(decrementSentence())}
onClick={() => decSentence()}
Icon={FiRewind}
disabled={!hasPrevSentence}
/>
<IconButton
Icon={FiSkipBack}
title="Previous Word"
onClick={() => dispatch(decrementWord())}
onClick={() => decWord()}
disabled={!hasPrevWord}
/>
{children}
<IconButton
Icon={FiSkipForward}
title="Next Word"
onClick={() => dispatch(incrementWord())}
onClick={() => incWord()}
disabled={!hasNextWord}
/>
<IconButton
Icon={FiFastForward}
title="Next Sentence"
onClick={() => dispatch(incrementSentence())}
onClick={() => incSentence()}
disabled={!hasNextSentence}
/>
</>

View File

@ -1,7 +1,5 @@
import React, { useState } from 'react'
import { useDispatch } from 'react-redux'
import { setText } from '../store/actions.js'
import { useDispatch } from '../store/RSVPStore'
import styles from './TextInput.css'
@ -10,7 +8,7 @@ const lorem =
export const TextInput = () => {
const [text, setTextState] = useState(lorem)
const dispatch = useDispatch()
const { setText } = useDispatch()
return (
<div>
<textarea
@ -18,7 +16,7 @@ export const TextInput = () => {
defaultValue={text}
onInput={e => setTextState(e.target.value)}
></textarea>
<button className={styles.load} onClick={() => dispatch(setText(text))}>
<button className={styles.load} onClick={() => setText(text)}>
Load
</button>
</div>

View File

@ -1,5 +1,5 @@
import React from 'react'
import { useSelector } from 'react-redux'
import { useSelector } from '../store/RSVPStore'
import classNames from 'classnames'
import { getNextSmallerNumber } from '../lib/array-util.js'

View File

@ -1,5 +1,5 @@
import React from 'react'
import { useSelector } from 'react-redux'
import { useSelector } from '../store/RSVPStore'
import { selectTotalTime } from '../store/selectors'
function formatTime(totalSeconds) {

View File

@ -2,10 +2,9 @@ import 'regenerator-runtime/runtime'
import React from 'react'
import ReactDOM from 'react-dom'
import { Provider } from 'react-redux'
import { Provider } from './store/RSVPStore'
import { App } from './App'
import { store } from './store/index.js'
import './i18n'
@ -17,7 +16,7 @@ function createRootElement() {
return root
}
ReactDOM.render(
<Provider store={store}>
<Provider>
<App />
</Provider>,
createRootElement()

75
src/store/RSVPStore.js Normal file
View 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