Skip to content

Commit

Permalink
feat: custom seeking fast-forward and rewind (#112)
Browse files Browse the repository at this point in the history
  • Loading branch information
cdrani authored Sep 12, 2023
1 parent 09f4ea7 commit d7df1a6
Show file tree
Hide file tree
Showing 18 changed files with 567 additions and 14 deletions.
14 changes: 12 additions & 2 deletions src/components/header.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,22 @@ export const createHeader = () => `
role="speed"
id="chorus-speed-button"
ariaLabel="Speed Controls"
style="height:21px;padding:0 .25rem;padding-bottom:.125rem;margin:0 .75rem;font-size:14px"
style="height:21px;padding:0 .25rem;padding-bottom:.125rem;margin-left:.5rem;font-size:14px"
class="chorus-text-button"
>
speed
<span>speed</span>
</button>
<button
role="seek"
id="chorus-seek-button"
ariaLabel="Seek Controls"
style="height:21px;padding:0 .25rem;padding-bottom:.125rem;margin:0 .5rem;font-size:14px"
class="chorus-text-button"
>
seek
</button>
</div>
<button id="chorus-modal-close-button" class="chorus-close-button">
<svg
role="img"
Expand Down
19 changes: 19 additions & 0 deletions src/components/seek/seek-buttons.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const createSeekButtons = () => `
<div style="display:flex;margin-top:12px;justify-content:space-between;height:1.5rem;">
<button
class="chorus-text-button"
id="chorus-seek-reset-button"
style="padding:0 10px;height:100%;font-size:1rem;"
>
<span>reset</span>
</button>
<button
id="chorus-seek-save-button"
class="chorus-text-button"
style="font-size:1rem;padding:0 10px;height:100%;"
>
<span>save</span>
</button>
</div>
`
15 changes: 15 additions & 0 deletions src/components/seek/seek-controls.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { createSeekInputs } from './seek-inputs.js'
import { createSeekButtons } from './seek-buttons.js'
import { createSeekToggler } from './seek-toggler.js'

export const createSeekControls = () => `
<div id="chorus-seek-controls" style="display: none">
<div style="margin:.75rem 0">
${createSeekInputs()}
</div>
<div style="margin-bottom:.75rem">
${createSeekToggler()}
</div>
${createSeekButtons()}
</div>
`
29 changes: 29 additions & 0 deletions src/components/seek/seek-icon.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// type: rw | ff;
export const createSeekIcon = type => `
<button
role="${type}"
class="chorus-hover-white"
id="seek-player-${type}-button"
role="seek-player-${type}-button"
style="display:flex;justify-content:center;align-items:center;position:relative;border:none;background:none;width:2rem;height:2rem;"
>
<span
id="seek-icon-${type}-label"
style="position:absolute;z-index:-10;background:transparent;font-weight:bold;font-size:smaller;top:50%;${type =='ff'? 'left' :'right'}:50%;text-align:center;">
</span>
<svg
fill="none"
stroke="currentColor"
stroke-width="5"
viewBox="0 0 64 64"
width="1.5rem"
height="1.5rem"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid meet"
style="transform: scaleX(${type == 'ff' ? -1 : 1});z-index:-10"
>
<path d="M34.46,53.91A21.91,21.91,0,1,0,12.55,31.78"></path>
<polyline points="4.65 22.33 12.52 32.62 22.81 24.75"></polyline>
</svg>
</button>
`
84 changes: 84 additions & 0 deletions src/components/seek/seek-inputs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
export const createSeekInputs = () => `
<div style="display:flex;justify-content:space-between">
<div style="display:flex;flex-wrap:wrap;">
<div class="chorus-common" style="height:unset;width: 100%">
<button role="rw-down" id="seek-rw-down-button" class="chorus-pill" style="z-index:10;width:2rem;height:2rem;">
<span class="chorus-text" style="position:relative;z-index:-1;display:inline-block;font-size:x-large;">&#8722;</span>
</button>
<div style="position:relative;height:3rem;margin:0 .5rem;">
<input
min="1"
max="60"
step="1"
type="number"
id="seek-rw-input"
name="chorus-seek-back"
style="position:absolute;width:2rem;background:transparent;font-size:larger;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;"
>
<svg
fill="none"
style="height:100%"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<g
stroke="#1ed760"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
>
<path d="m14.55 21.67c4.29-1.13 7.45-5.03 7.45-9.67 0-5.52-4.44-10-10-10-6.67 0-10 5.56-10 5.56m0 0v-4.56m0 4.56h2.01 2.43" />
<path d="m2 12c0 5.52 4.48 10 10 10" opacity=".4" stroke-dasharray="3 3"/>
</g>
</svg>
</div>
<button role="rw-up" id="seek-rw-up-button" class="chorus-pill" style="z-index:10;width:2rem;height:2rem;">
<span class="chorus-text" style="position:relative;z-index:-1;display:inline-block;font-size:x-large;">&#43;</span>
</button>
</div>
</div>
<div style="display:flex;flex-wrap:wrap;">
<div class="chorus-common" style="height:unset;width:100%">
<button role="ff-down" id="seek-ff-down-button" class="chorus-pill" style="z-index:10;width:2rem;height:2rem;">
<span class="chorus-text" style="position:relative;z-index:-1;display:inline-block;font-size:x-large;">&#8722;</span>
</button>
<div style="position:relative;height:3rem;margin:0 .5rem;">
<input
min="1"
max="60"
step="1"
type="number"
id="seek-ff-input"
name="chorus-seek-forward"
style="z-index:1;position:absolute;width:2rem;background:transparent;font-size:larger;top:50%;left:50%;transform:translate(-50%,-50%);text-align:center;"
>
<svg
fill="none"
viewBox="0 0 24 24"
style="transform: scaleX(-1);height:100%"
xmlns="http://www.w3.org/2000/svg"
>
<g
stroke="#1ed760"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="1.5"
>
<path d="m14.55 21.67c4.29-1.13 7.45-5.03 7.45-9.67 0-5.52-4.44-10-10-10-6.67 0-10 5.56-10 5.56m0 0v-4.56m0 4.56h2.01 2.43" />
<path d="m2 12c0 5.52 4.48 10 10 10" opacity=".4" stroke-dasharray="3 3"/>
</g>
</svg>
</div>
<button role="ff-up" id="seek-ff-up-button" class="chorus-pill" style="z-index:10;width:2rem;height:2rem;">
<span class="chorus-text" style="position:relative;z-index:-1;display:inline-block;font-size:x-large;">&#43;</span>
</button>
</div>
</div>
</div>
`
31 changes: 31 additions & 0 deletions src/components/seek/seek-toggler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { createToggleButton } from '../toggle-button.js'

export const createSeekToggler = () => `
<div style="display:flex;justify-content:space-between;align-items:center;width:100%">
<div style="display:flex;justify-content:space-between;">
<div style="display:flex;align-items:center;">
<span id="seek-global-label" class="chorus-text chorus-pill" style="background:green">G</span>
</div>
<div style="display:flex;align-items:center;">
<span id="seek-shows-label" class="chorus-text chorus-pill">PA</span>
</div>
</div>
<div>
<div style="display:flex;flex-direction:column;justify-content:space-between;align-items:flex-end">
${
createToggleButton({
labelId: 'seek-label',
labelText: 'Global',
onPathId: 'seek-toggle-on',
offPathId: 'seek-toggle-off',
checkboxId: 'seek-checkbox',
buttonId: 'seek-toggle-button',
})
}
</div>
</div>
</div>
`

12 changes: 12 additions & 0 deletions src/data/current.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ class CurrentData {
}
}

async getSeekValues() {
const seekValues = await this.#store.getTrack({
id: 'chorus-seek',
value: {
shows: { ff: 15, rw: 15 }, // audiobooks, podcasts, (longform audio)
global: { ff: 10, rw: 10 }, // albums, playlists, tracks (shortform audio)
}
})

return seekValues
}

async readTrack() {
const track = await this.#store.getTrack({
id: this.#songId,
Expand Down
40 changes: 39 additions & 1 deletion src/events/listeners.js
Original file line number Diff line number Diff line change
@@ -1,24 +1,32 @@
import Chorus from '../models/chorus.js'
import Seek from '../models/seek/seek.js'
import Speed from '../models/speed/speed.js'

export default class ButtonListeners {
#snip
#seek
#speed
#chorus
#currentView = 'snip'

constructor(snip) {
this.#snip = snip
this.#speed = new Speed(snip._store)
this.#seek = new Seek()
this.#speed = new Speed()
this.#chorus = new Chorus()
}

init() {
this.#snipViewToggle()
this.#seekViewToggle()
this.#speedViewToggle()

this.#closeModalListener()

this.#saveSeekListener()
this.#saveTrackListener()
this.#saveSpeedListener()

this.#shareTrackListener()
this.#deleteTrackListener()
this.#resetSpeedListener()
Expand All @@ -33,6 +41,7 @@ export default class ButtonListeners {
this.#chorus.hide()
this.#snip.isEditing = false
this.#snipContainer.style.display = 'block'
this.#seekContainer.style.display = 'none'
this.#speedContainer.style.display = 'none'
}

Expand All @@ -44,6 +53,25 @@ export default class ButtonListeners {
return document.getElementById('chorus-speed-controls')
}

get #seekContainer() {
return document.getElementById('chorus-seek-controls')
}

#seekViewToggle() {
const seekButton = document.getElementById('chorus-seek-button')

seekButton?.addEventListener('click', async () => {
const showingSeekControls = this.#seekContainer?.style?.display == 'block'
if (showingSeekControls) return

this.#currentView = 'seek'
this.#speedContainer.style.display = 'none'
this.#snipContainer.style.display = 'none'
this.#seekContainer.style.display = 'block'
await this.#seek.init()
})
}

#snipViewToggle() {
const snipButton = document.getElementById('chorus-snip-button')

Expand All @@ -53,6 +81,7 @@ export default class ButtonListeners {

this.#currentView = 'snip'
this.#speedContainer.style.display = 'none'
this.#seekContainer.style.display = 'none'
this.#snipContainer.style.display = 'block'
})
}
Expand All @@ -65,6 +94,7 @@ export default class ButtonListeners {

this.#currentView = 'speed'
this.#snipContainer.style.display = 'none'
this.#seekContainer.style.display = 'none'
this.#speedContainer.style.display = 'block'
await this.#speed.init()
})
Expand Down Expand Up @@ -109,6 +139,14 @@ export default class ButtonListeners {
})
}

#saveSeekListener() {
const seekSaveButton = document.getElementById('chorus-seek-save-button')
seekSaveButton?.addEventListener('click', async () => {
await this.#seek.save()
this.#hide()
})
}

#handleShare = e => {
e.stopPropagation()
this.#snip.share()
Expand Down
3 changes: 3 additions & 0 deletions src/models/chorus.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { createSnipControls } from '../components/snip/snip-controls.js'
import { createSpeedControls } from '../components/speed/speed-controls.js'
import { createSeekControls } from '../components/seek/seek-controls.js'

import { parseNodeString } from '../utils/parser.js'

Expand Down Expand Up @@ -31,9 +32,11 @@ export default class Chorus {

const snipControlsEl = parseNodeString(createSnipControls())
const speedControlsEl = parseNodeString(createSpeedControls())
const seekControlsEl = parseNodeString(createSeekControls())

this.chorusControls.appendChild(snipControlsEl)
this.chorusControls.appendChild(speedControlsEl)
this.chorusControls.appendChild(seekControlsEl)
}

hide() {
Expand Down
1 change: 1 addition & 0 deletions src/models/icon.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createControls } from '../components/controls.js'
export default class Icon {
constructor() {}

// TODO: Icon should not be concerned with creating the main UI
createRootContainer() {
return `
<div id="chorus">
Expand Down
Loading

0 comments on commit d7df1a6

Please sign in to comment.