feat: store state in localStorage

This commit is contained in:
Deluan 2020-03-31 14:04:10 -04:00
parent 944f3695c4
commit 083a11a563
6 changed files with 140 additions and 39 deletions

5
ui/package-lock.json generated
View File

@ -10436,6 +10436,11 @@
"lodash._reinterpolate": "^3.0.0"
}
},
"lodash.throttle": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz",
"integrity": "sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ="
},
"lodash.uniq": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",

View File

@ -8,6 +8,7 @@
"@testing-library/user-event": "^10.0.0",
"deepmerge": "^4.2.2",
"jwt-decode": "^2.2.0",
"lodash.throttle": "^4.1.1",
"md5-hex": "^3.0.1",
"prop-types": "^15.7.2",
"ra-data-json-server": "^3.3.1",

View File

@ -1,4 +1,6 @@
import React from 'react'
import { Provider } from 'react-redux'
import { createHashHistory } from 'history'
import { Admin, resolveBrowserLocale, Resource } from 'react-admin'
import dataProvider from './dataProvider'
import authProvider from './authProvider'
@ -15,12 +17,15 @@ import { Player, playQueueReducer } from './audioplayer'
import { albumViewReducer } from './album/albumState'
import customRoutes from './routes'
import themeReducer from './configuration/themeReducer'
import createAdminStore from './store/createAdminStore'
const i18nProvider = polyglotI18nProvider(
(locale) => (messages[locale] ? messages[locale] : messages.en),
resolveBrowserLocale()
)
const history = createHashHistory()
const App = () => {
try {
const appConfig = JSON.parse(window.__APP_CONFIG__)
@ -32,44 +37,57 @@ const App = () => {
} catch (e) {}
return (
<Admin
customReducers={{
queue: playQueueReducer,
albumView: albumViewReducer,
theme: themeReducer
}}
dataProvider={dataProvider}
authProvider={authProvider}
i18nProvider={i18nProvider}
customRoutes={customRoutes}
layout={Layout}
loginPage={Login}
<Provider
store={createAdminStore({
authProvider,
dataProvider,
history,
customReducers: {
queue: playQueueReducer,
albumView: albumViewReducer,
theme: themeReducer
}
})}
>
{(permissions) => [
<Resource name="artist" {...artist} options={{ subMenu: 'library' }} />,
<Resource name="album" {...album} options={{ subMenu: 'library' }} />,
<Resource name="song" {...song} options={{ subMenu: 'library' }} />,
<Resource name="albumSong" />,
permissions === 'admin' ? (
<Resource name="user" {...user} options={{ subMenu: 'settings' }} />
) : null,
<Resource
name="player"
{...player}
options={{ subMenu: 'settings' }}
/>,
permissions === 'admin' ? (
<Admin
dataProvider={dataProvider}
authProvider={authProvider}
i18nProvider={i18nProvider}
customRoutes={customRoutes}
history={history}
layout={Layout}
loginPage={Login}
>
{(permissions) => [
<Resource
name="transcoding"
{...transcoding}
name="artist"
{...artist}
options={{ subMenu: 'library' }}
/>,
<Resource name="album" {...album} options={{ subMenu: 'library' }} />,
<Resource name="song" {...song} options={{ subMenu: 'library' }} />,
<Resource name="albumSong" />,
permissions === 'admin' ? (
<Resource name="user" {...user} options={{ subMenu: 'settings' }} />
) : null,
<Resource
name="player"
{...player}
options={{ subMenu: 'settings' }}
/>
) : (
<Resource name="transcoding" />
),
<Player />
]}
</Admin>
/>,
permissions === 'admin' ? (
<Resource
name="transcoding"
{...transcoding}
options={{ subMenu: 'settings' }}
/>
) : (
<Resource name="transcoding" />
),
<Player />
]}
</Admin>
</Provider>
)
}

View File

@ -22,8 +22,8 @@ const selectAlbumList = (mode) => ({ type: mode })
const albumViewReducer = (
previousState = {
mode: localStorage.getItem('albumViewMode') || ALBUM_MODE_LIST,
list: localStorage.getItem('albumListType') || ALBUM_LIST_ALL,
mode: ALBUM_MODE_LIST,
list: ALBUM_LIST_ALL,
params: { sort: {}, filter: {} }
},
payload
@ -32,14 +32,12 @@ const albumViewReducer = (
switch (type) {
case ALBUM_MODE_GRID:
case ALBUM_MODE_LIST:
localStorage.setItem('albumViewMode', type)
return { ...previousState, mode: type }
case ALBUM_LIST_ALL:
case ALBUM_LIST_RANDOM:
case ALBUM_LIST_NEWEST:
case ALBUM_LIST_RECENT:
case ALBUM_LIST_STARRED:
localStorage.setItem('albumListType', type)
return { ...previousState, list: type, params: albumListParams[type] }
default:
return previousState

View File

@ -0,0 +1,59 @@
import { applyMiddleware, combineReducers, compose, createStore } from 'redux'
import { routerMiddleware, connectRouter } from 'connected-react-router'
import createSagaMiddleware from 'redux-saga'
import { all, fork } from 'redux-saga/effects'
import { adminReducer, adminSaga, USER_LOGOUT } from 'react-admin'
import throttle from 'lodash.throttle'
import { loadState, saveState } from './persistState'
export default ({
authProvider,
dataProvider,
history,
customReducers = {}
}) => {
const reducer = combineReducers({
admin: adminReducer,
router: connectRouter(history),
...customReducers
})
const resettableAppReducer = (state, action) =>
reducer(action.type !== USER_LOGOUT ? state : undefined, action)
const saga = function* rootSaga() {
yield all([adminSaga(dataProvider, authProvider)].map(fork))
}
const sagaMiddleware = createSagaMiddleware()
const composeEnhancers =
(process.env.NODE_ENV === 'development' &&
typeof window !== 'undefined' &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ &&
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
trace: true,
traceLimit: 25
})) ||
compose
const persistedState = loadState()
const store = createStore(
resettableAppReducer,
persistedState,
composeEnhancers(applyMiddleware(sagaMiddleware, routerMiddleware(history)))
)
store.subscribe(
throttle(() => {
const state = store.getState()
saveState({
theme: state.theme,
// queue: state.queue, TODO: Need to make queue serializable (remove functions from it)
albumView: state.albumView
})
}),
1000
)
sagaMiddleware.run(saga)
return store
}

View File

@ -0,0 +1,20 @@
export const loadState = () => {
try {
const serializedState = localStorage.getItem('state')
if (serializedState === null) {
return undefined
}
return JSON.parse(serializedState)
} catch (err) {
return undefined
}
}
export const saveState = (state) => {
try {
const serializedState = JSON.stringify(state)
localStorage.setItem('state', serializedState)
} catch (err) {
// Ignore write errors
}
}