Skip to content
This repository has been archived by the owner on May 24, 2021. It is now read-only.

Commit

Permalink
Use local storage to persist the current playtime
Browse files Browse the repository at this point in the history
  • Loading branch information
alexander-heimbuch committed Feb 26, 2017
1 parent fa0e12c commit 17f78db
Show file tree
Hide file tree
Showing 16 changed files with 150 additions and 105 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
"howler": "^2.0.2",
"iframe-resizer": "^3.5.7",
"lodash": "^4.17.3",
"object-hash": "^1.1.5",
"query-string": "^4.3.1",
"react": "15.3.0",
"react-dom": "15.3.0",
Expand Down
20 changes: 0 additions & 20 deletions src/app.js
Original file line number Diff line number Diff line change
@@ -1,40 +1,20 @@
import Vue from 'vue'
import head from 'lodash/head'

import { timeToSeconds } from 'utils/time'
import registerDirectives from './directives'

registerDirectives(Vue)

// Store
import store from 'store'
import * as effects from './effects'

// Media Driver
import mediaPlayer from './media-player'

// UI Components
import App from './components/App.vue'


export default config => {
// Initialize meta for store
store.dispatch(store.actions.init(config))

const media = mediaPlayer(config.audio, {
playtime: timeToSeconds(config.playtime),
setPlaytime: playtime => store.dispatch(store.actions.setPlaytime(playtime)),
setBufferState: buffer => store.dispatch(store.actions.setBuffer(buffer)),
setDuration: duration => store.dispatch(store.actions.setDuration(duration)),
onPlay: () => store.dispatch(store.actions.playEvent()),
onPause: () => store.dispatch(store.actions.pauseEvent()),
onStop: () => store.dispatch(store.actions.stopEvent()),
onLoad: () => store.dispatch(store.actions.loading())
})

effects.registerMediaEffects(media)
effects.registerIdleEffects()

window.PODLOVE_STORE = store

return new Vue({
Expand Down
17 changes: 0 additions & 17 deletions src/effects/idle.js

This file was deleted.

29 changes: 0 additions & 29 deletions src/effects/index.js

This file was deleted.

22 changes: 0 additions & 22 deletions src/effects/media.js

This file was deleted.

Empty file removed src/effects/storage.js
Empty file.
8 changes: 3 additions & 5 deletions src/media-player/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import buffer from './buffer'
*/
let ticker

export default (audio = [], {playtime, setPlaytime, setBufferState, setDuration, onPlay, onPause, onStop, onLoad}) => {
export default (audio = [], {setPlaytime, setBufferState, setDuration, onPlay, onPause, onStop, onLoad}) => {
const player = new Howl({
src: audio,
html5: true,
Expand All @@ -24,8 +24,6 @@ export default (audio = [], {playtime, setPlaytime, setBufferState, setDuration,
// No api sugar for the audio node :/
audioNode = get(player, ['_sounds', 0, '_node'])
setDuration(player.duration())
player.seek(playtime)
// onLoad()
})

player.on('play', onPlay)
Expand All @@ -50,8 +48,8 @@ export default (audio = [], {playtime, setPlaytime, setBufferState, setDuration,

// Extend seek functionality to be capable of jumping in without loaded player
player.setPlaytime = playtime => {
if (player._state !== 'loaded') {
player.play()
if (player.state() === 'unloaded') {
player.load()
}

player.seek(playtime)
Expand Down
22 changes: 22 additions & 0 deletions src/store/effects/idle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import actions from '../actions'

let idle = null
const IDLE_TIMEOUT = 10 * 60 * 1000

export default (store, action) => {
switch (action.type) {
case 'INIT':
if (action.payload.playtime > 0) {
store.dispatch(actions.idle())
}
break
case 'PLAY':
clearTimeout(idle)
break
case 'PAUSE':
idle = setTimeout(() => {
store.dispatch(actions.idle())
}, IDLE_TIMEOUT)
break
}
}
11 changes: 11 additions & 0 deletions src/store/effects/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import mediaEffects from './media'
import idleEffects from './idle'
import storageEffects from './storage'

export default store => next => action => {
mediaEffects(store, action)
storageEffects(store, action)
idleEffects(store, action)

return next(action)
}
39 changes: 39 additions & 0 deletions src/store/effects/media.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import mediaPlayer from '../../media-player'
import actions from '../actions'

const initMediaPlayer = (dispatch, config) =>
mediaPlayer(config.audio, {
setPlaytime: playtime => dispatch(actions.setPlaytime(playtime)),
setBufferState: buffer => dispatch(actions.setBuffer(buffer)),
setDuration: duration => dispatch(actions.setDuration(duration)),
onPlay: () => dispatch(actions.playEvent()),
onPause: () => dispatch(actions.pauseEvent()),
onStop: () => dispatch(actions.stopEvent()),
onLoad: () => dispatch(actions.loading())
})

let mediaElement

export default (store, action) => {
const state = store.getState()

switch (action.type) {
case 'INIT':
mediaElement = initMediaPlayer(store.dispatch, action.payload)
break
case 'UI_PLAY':
mediaElement.setPlaytime(state.playtime)
mediaElement.play()
break
case 'UI_PAUSE':
mediaElement.pause()
break
case 'UI_RESTART':
mediaElement.setPlaytime(0)
mediaElement.play()
break
case 'UPDATE_PLAYTIME':
mediaElement.setPlaytime(action.payload)
break
}
}
24 changes: 24 additions & 0 deletions src/store/effects/storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import actions from '../actions'

import storage from 'utils/storage'
import hash from 'object-hash'

let podloveStorage

export default (store, action) => {
switch (action.type) {
case 'INIT':
podloveStorage = storage(hash(action.payload))

let storedPlaytime = podloveStorage.get('playtime')

if (storedPlaytime !== undefined) {
store.dispatch(actions.setPlaytime(storedPlaytime))
store.dispatch(actions.idle())
}
break
case 'SET_PLAYTIME':
podloveStorage.set('playtime', action.payload)
break
}
}
9 changes: 7 additions & 2 deletions src/store/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import Vue from 'vue'
import Revue from 'revue'
import { createStore } from 'redux'
import { createStore, applyMiddleware, compose } from 'redux'

import reducers from './reducers'
import actions from './actions'
import effects from './effects'

const reduxStore = createStore(reducers, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__())
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose

const reduxStore = createStore(reducers, composeEnhancers(
applyMiddleware(effects)
))

const store = new Revue(Vue, reduxStore, actions)

Expand Down
7 changes: 0 additions & 7 deletions src/store/reducers/effects.js

This file was deleted.

3 changes: 1 addition & 2 deletions src/store/reducers/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@ import { combineReducers } from 'redux'

import * as init from './init'
import * as player from './player'
import * as effects from './effects'
import * as chapters from './chapters'
import * as tabs from './tabs'
import * as theme from './theme'
import * as share from './share'

export default combineReducers(
Object.assign({}, init, player, effects, chapters, tabs, theme, share)
Object.assign({}, init, player, chapters, tabs, theme, share)
)
2 changes: 1 addition & 1 deletion src/store/reducers/player.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { timeToSeconds } from 'utils/time'
const playtime = (state = 0, action) => {
switch (action.type) {
case 'INIT':
const playtime = get(action.payload, 'playtime', state)
const playtime = state > 0 ? state : get(action.payload, 'playtime', state)
return timeToSeconds(playtime)
case 'UPDATE_PLAYTIME':
return parseFloat(action.payload)
Expand Down
41 changes: 41 additions & 0 deletions src/utils/storage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import curry from 'lodash/fp/curry'
import get from 'lodash/get'

const getItem = curry((hash, key) => {
try {
const fromStore = window.localStorage.getItem(hash) || ''
let obj = JSON.parse(fromStore)

if (!key) {
return obj || {}
}

return get(obj, key)
} catch (err) {
return undefined
}
})

const setItem = curry((hash, first, second) => {
let data

if (!second) {
data = first
} else {
data = {[first]: second}
}

try {
const currentStore = getItem(hash, null)
const toStore = JSON.stringify(Object.assign({}, currentStore, data))

return window.localStorage.setItem(hash, toStore)
} catch (err) {
return undefined
}
})

export default hash => ({
set: setItem(hash),
get: getItem(hash)
})

0 comments on commit 17f78db

Please sign in to comment.