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

Commit

Permalink
Merge pull request #411 from alexander-heimbuch/feature/chapters
Browse files Browse the repository at this point in the history
feat(chapters): Rework chapter interaction, introduce play icon in ch…
  • Loading branch information
alexander-heimbuch committed Jul 2, 2017
2 parents cd83aa6 + 847d69b commit 023fa01
Show file tree
Hide file tree
Showing 14 changed files with 126 additions and 61 deletions.
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
],
"scripts": {
"dist:clean": "mkdir -p dist && rm -rf dist/*",
"docs": "cd docs && bundle install && jekyll build && cd .. && cp -R docs/fixtures/* dist/fixtures",
"postinstall": "cd docs && bundle install",
"docs": "jekyll build --source docs --destination dist && cp -R docs/fixtures/* dist/fixtures",
"webpack:dev": "webpack-dashboard -- webpack-dev-server --content-base dist/ --progress --hot --inline --config src/webpack.config.js",
"webpack:build": "NODE_ENV='production' webpack --config src/webpack.config.js",
"build": "npm run webpack:build",
Expand Down
14 changes: 7 additions & 7 deletions src/components/icons/PlayIcon.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
<template>
<svg width="21px" height="24px" viewBox="0 0 21 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(-17.000000, -13.000000)" :fill="color">
<polygon points="17.2886 13.0896 37.4846 24.7496 17.2886 36.4096"></polygon>
</g>
<svg :width="size" :height="size * 1.142" viewBox="0 0 21 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g transform="translate(-17.000000, -13.000000)" :fill="color">
<polygon points="17.2886 13.0896 37.4846 24.7496 17.2886 36.4096"></polygon>
</g>
</svg>
</g>
</svg>
</template>

<script>
export default {
props: ['color']
props: ['color', 'size']
}
</script>
14 changes: 7 additions & 7 deletions src/components/player/control-bar/PlayButton.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,30 @@
components.controls.button.variant.retry
}">
<span class="inner" v-if="components.controls.button.variant.loading">
<LoadingIndicator />
<LoadingIndicator></LoadingIndicator>
</span>

<PauseIcon :color="theme.player.actions.icon" v-if="components.controls.button.variant.playing" />
<PauseIcon :color="theme.player.actions.icon" v-if="components.controls.button.variant.playing"></PauseIcon>

<PlayIcon :color="theme.player.actions.icon" class="reset" v-if="components.controls.button.variant.pause" />
<PlayIcon size="21" :color="theme.player.actions.icon" class="reset" v-if="components.controls.button.variant.pause"></PlayIcon>

<span class="inner" v-if="components.controls.button.variant.remaining">
<PlayIcon :color="theme.player.actions.icon" />
<PlayIcon size="21" :color="theme.player.actions.icon"></PlayIcon>
<span class="label" :style="textStyle(theme)">{{ secondsToTime(playtime) }}</span>
</span>

<span class="inner" v-if="components.controls.button.variant.duration">
<PlayIcon :color="theme.player.actions.icon" />
<PlayIcon size="21" :color="theme.player.actions.icon"></PlayIcon>
<span class="label" :style="textStyle(theme)">{{ secondsToTime(duration) }}</span>
</span>

<span class="inner" v-if="components.controls.button.variant.replay">
<PlayIcon :color="theme.player.actions.icon" />
<PlayIcon size="21" :color="theme.player.actions.icon"></PlayIcon>
<span class="label" :style="textStyle(theme)">{{ $t('PLAYER.REPLAY') }}</span>
</span>

<span class="inner" v-if="components.controls.button.variant.retry">
<ReloadIcon :color="theme.player.actions.icon" />
<ReloadIcon :color="theme.player.actions.icon"></ReloadIcon>
<span class="label" :style="textStyle(theme)">{{ $t('PLAYER.RETRY') }}</span>
</span>
</span>
Expand Down
12 changes: 8 additions & 4 deletions src/components/player/progress-bar/Progress.vue
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
@input="onInput"
@mousemove="onMouseMove"
@mouseout="onMouseOut"
/>
>
<span class="progress-range" :style="rangeStyle(theme)"></span>
<span class="progress-buffer" :style="bufferStyle(theme, buffer, duration)"></span>
<span v-for="(quantile, index) in quantiles" class="progress-track" :style="trackStyle(theme, duration, quantile)" :key="index"></span>
Expand Down Expand Up @@ -55,25 +55,29 @@
let playtime = this.$select('playtime')
let duration = this.$select('duration')
let theme = this.$select('theme')
let ghost = this.$select('ghost')
return {
playtime,
duration,
theme,
ghost,
buffer: this.$select('buffer'),
playstate: this.$select('playstate'),
thumbPosition: relativePosition(playtime, duration),
quantiles: this.$select('quantiles'),
ghost: this.$select('ghost'),
ghostPosition: 0,
ghostPosition: relativePosition(ghost.time, duration),
thumbActive: false
}
},
watch: {
playtime: function (time) {
this.thumbPosition = relativePosition(time, this.duration)
},
ghost: function (ghost) {
this.ghostPosition = relativePosition(ghost.time, this.duration)
}
},
methods: {
Expand All @@ -97,7 +101,7 @@
this.thumbAnimated = true
this.thumbActive = true
this.ghostPosition = relativePosition(event.offsetX, event.target.clientWidth)
store.dispatch(store.actions.simulatePlaytime(this.duration * event.offsetX / event.target.clientWidth))
store.dispatch(store.actions.enableGhostMode())
},
Expand Down
68 changes: 49 additions & 19 deletions src/components/tabs/chapters/ChapterEntry.vue
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
<template>
<div class="chapters--entry"
:style="chapterStyle(theme, chapter, hover)"
@click="onChapterClick(index)"
@mouseover="hover = true"
@mouseleave="hover = false">

<span class="index">{{index + 1}}</span>
@click="onChapterClick(ghost)"
@mouseout="onMouseOut"
@mousemove="onMouseMove">

<span class="index" v-if="hover">
<PlayIcon size="12" :color="theme.tabs.body.icon"></PlayIcon>
</span>
<span class="index" v-else>{{index + 1}}</span>
<span class="title truncate">{{chapter.title}}</span>
<span class="timer">{{remainingTime(chapter, ghost.active ? ghost.time : playtime)}}</span>

<span class="progress" :style="progressStyle(theme, chapter, ghost.active ? ghost.time : playtime)"></span>
<span class="progress" :style="progressStyle(theme, chapter, ghost, playtime)"></span>
</div>
</template>

Expand All @@ -18,21 +21,29 @@
import store from 'store'
import { secondsToTime } from 'utils/time'
import PlayIcon from 'icons/PlayIcon.vue'
const activeChapter = theme => ({
'background-color': color(theme.tabs.body.backgroundActive).fade(0.9),
color: theme.tabs.body.textActives
color: theme.tabs.body.textActive
})
const chapterStyle = (theme, chapter, hover) => {
return chapter.active || hover ? activeChapter(theme) : {}
const chapterStyle = (theme, chapter, hover, even) => {
if (chapter.active || hover) {
return activeChapter(theme)
}
return {}
}
const progressStyle = (theme, chapter, playtime) => {
if (!chapter.active || playtime > chapter.end) {
const progressStyle = (theme, chapter, ghost, playtime) => {
let time = ghost.active ? ghost.time : playtime
if (time < chapter.start || time > chapter.end) {
return {}
}
let progress = ((playtime - chapter.start) * 100) / (chapter.end - chapter.start)
let progress = ((time - chapter.start) * 100) / (chapter.end - chapter.start)
return {
'width': progress + '%',
Expand All @@ -48,15 +59,15 @@
return secondsToTime(chapter.end - chapter.start)
}
const onChapterClick = index => {
store.dispatch(store.actions.setChapter(index))
const onChapterClick = ghost => {
store.dispatch(store.actions.updatePlaytime(ghost.time))
store.dispatch(store.actions.play())
}
export default {
data () {
return {
theme: this.$select('theme'),
chapters: this.$select('chapters'),
playtime: this.$select('playtime'),
ghost: this.$select('ghost'),
hover: false
Expand All @@ -66,7 +77,21 @@
chapterStyle,
progressStyle,
remainingTime,
onChapterClick
onChapterClick,
onMouseOut () {
this.hover = false
store.dispatch(store.actions.disableGhostMode())
},
onMouseMove (event) {
this.hover = true
store.dispatch(store.actions.enableGhostMode())
store.dispatch(store.actions.simulatePlaytime(this.chapter.start + (this.chapter.end - this.chapter.start) * event.offsetX / event.target.clientWidth))
}
},
components: {
PlayIcon
},
props: ['chapter', 'index']
}
Expand All @@ -87,28 +112,33 @@
transition: background $animation-duration, color $animation-duration;
.index {
display: block;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
width: 30px;
pointer-events: none;
}
.title {
display: block;
width: calc(100% - 30px - 30px)
width: calc(100% - 30px);
pointer-events: none;
}
.timer {
display: block;
text-align: right;
@include font-monospace();
pointer-events: none;
}
.progress {
position: absolute;
left: 0;
bottom: 0;
height: 3px;
transition: width $animation-duration;
pointer-events: none;
}
}
</style>
7 changes: 6 additions & 1 deletion src/components/tabs/chapters/Chapters.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<template>
<div class="chapters">
<ChapterEntryComponent v-for="(chapter, index) in chapters" :chapter="chapter" :index="index" :key="index"></ChapterEntryComponent>
<ChapterEntryComponent
v-for="(chapter, index) in chapters"
:chapter="chapter"
:index="index"
:key="index">
</ChapterEntryComponent>
</div>
</template>

Expand Down
19 changes: 11 additions & 8 deletions src/media-player/extensions.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,22 @@ export default (player, { onLoad }) => {
// Maybe this could be a useful plugin
const howlerPlay = player.play.bind(player)
const howlerSeek = player.seek.bind(player)
let initialPlay = false

player.once('play', () => {
initialPlay = true
})
let loading = false

const loadPlayer = (sprite, internal) => {
onLoad()
howlerPlay(sprite, internal)
loading = true
}

// Safe Play
player.play = (sprite, internal) => {
if (!initialPlay) {
onLoad()
if (!loading) {
loadPlayer()
} else {
howlerPlay(sprite, internal)
}

howlerPlay(sprite, internal)
}

// Load Hooks
Expand Down
8 changes: 7 additions & 1 deletion src/store/actions/chapters.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,14 @@ const setChapter = (chapterIndex) => ({
payload: chapterIndex
})

const updateChapter = (playtime) => ({
type: 'UPDATE_CHAPTER',
payload: playtime
})

export {
setChapter,
nextChapter,
previousChapter
previousChapter,
updateChapter
}
9 changes: 8 additions & 1 deletion src/store/actions/chapters.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import test from 'ava'
import { setChapter, nextChapter, previousChapter } from './chapters'
import { setChapter, nextChapter, previousChapter, updateChapter } from './chapters'

test(`nextChapter: creates the NEXT_CHAPTER action`, t => {
t.deepEqual(nextChapter(), {
Expand All @@ -19,3 +19,10 @@ test(`setChapter: creates the SET_CHAPTER action`, t => {
payload: 1
})
})

test(`setChapter: creates the UPDATE_CHAPTER action`, t => {
t.deepEqual(updateChapter(1), {
type: 'UPDATE_CHAPTER',
payload: 1
})
})
10 changes: 10 additions & 0 deletions src/store/effects/chapters.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import actions from '../actions'
export default (store, action) => {
const state = store.getState()
const chapters = get(state, 'chapters', [])
const ghost = get(state, 'ghost', {})
const current = currentChapter(chapters)
const currentIndex = currentChapterIndex(chapters)

Expand All @@ -26,5 +27,14 @@ export default (store, action) => {
case 'SET_CHAPTER':
store.dispatch(actions.updatePlaytime(current.start))
break
case 'SIMULATE_PLAYTIME':
store.dispatch(actions.updateChapter(action.payload))
break
case 'SET_PLAYTIME':
case 'UPDATE_PLAYTIME':
if (!ghost.active) {
store.dispatch(actions.updateChapter(action.payload))
}
break
}
}
2 changes: 1 addition & 1 deletion src/store/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import reducers from './reducers'
import actions from './actions'
import effects from './effects'

Raven.config(sentry, { release: version }).install()
Raven.config(sentry, { release: version, ignoreUrls: ['localhost'] }).install()

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose

Expand Down
4 changes: 1 addition & 3 deletions src/store/reducers/chapters.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,7 @@ const chapters = (state = [], action) => {
return chapters
.reduce(parseChapters(action.payload.duration), [])
.map(setActiveByPlaytime(action.payload.playtime || 0))
case 'SIMULATE_PLAYTIME':
case 'SET_PLAYTIME':
case 'UPDATE_PLAYTIME':
case 'UPDATE_CHAPTER':
const nextChapters = state.map(setActiveByPlaytime(action.payload))

if (currentChapterIndex(nextChapters) === -1) {
Expand Down
Loading

0 comments on commit 023fa01

Please sign in to comment.