fix: make progress bars work again
Part-of: <https://dev.funkwhale.audio/funkwhale/funkwhale/-/merge_requests/2346>
This commit is contained in:
parent
665e001087
commit
502773b7b4
|
@ -38,7 +38,6 @@ module.exports = {
|
||||||
'@intlify/vue-i18n/valid-message-syntax': 'error',
|
'@intlify/vue-i18n/valid-message-syntax': 'error',
|
||||||
'@intlify/vue-i18n/no-i18n-t-path-prop': 'error',
|
'@intlify/vue-i18n/no-i18n-t-path-prop': 'error',
|
||||||
'@intlify/vue-i18n/no-missing-keys': 'error',
|
'@intlify/vue-i18n/no-missing-keys': 'error',
|
||||||
'@intlify/vue-i18n/no-dynamic-keys': 'error',
|
|
||||||
'@intlify/vue-i18n/no-unused-keys': ['error', {
|
'@intlify/vue-i18n/no-unused-keys': ['error', {
|
||||||
extensions: ['.ts', '.vue'],
|
extensions: ['.ts', '.vue'],
|
||||||
enableFix: true
|
enableFix: true
|
||||||
|
|
|
@ -58,7 +58,7 @@
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@intlify/eslint-plugin-vue-i18n": "2.0.0",
|
"@intlify/eslint-plugin-vue-i18n": "2.0.0",
|
||||||
"@intlify/unplugin-vue-i18n": "^0.8.1",
|
"@intlify/unplugin-vue-i18n": "^0.8.2",
|
||||||
"@types/diff": "5.0.2",
|
"@types/diff": "5.0.2",
|
||||||
"@types/dompurify": "2.4.0",
|
"@types/dompurify": "2.4.0",
|
||||||
"@types/howler": "2.2.7",
|
"@types/howler": "2.2.7",
|
||||||
|
@ -72,7 +72,7 @@
|
||||||
"@typescript-eslint/eslint-plugin": "5.48.2",
|
"@typescript-eslint/eslint-plugin": "5.48.2",
|
||||||
"@vitejs/plugin-vue": "4.0.0",
|
"@vitejs/plugin-vue": "4.0.0",
|
||||||
"@vitest/coverage-c8": "0.25.8",
|
"@vitest/coverage-c8": "0.25.8",
|
||||||
"@vue/compiler-sfc": "3.2.45",
|
"@vue/compiler-sfc": "3.2.47",
|
||||||
"@vue/eslint-config-standard": "8.0.1",
|
"@vue/eslint-config-standard": "8.0.1",
|
||||||
"@vue/eslint-config-typescript": "11.0.2",
|
"@vue/eslint-config-typescript": "11.0.2",
|
||||||
"@vue/test-utils": "2.2.7",
|
"@vue/test-utils": "2.2.7",
|
||||||
|
|
|
@ -79,15 +79,15 @@ export class HTMLSound implements Sound {
|
||||||
})
|
})
|
||||||
|
|
||||||
useEventListener(this.#audio, 'waiting', () => {
|
useEventListener(this.#audio, 'waiting', () => {
|
||||||
console.log('>> AUDIO WAITING', this.__track?.title)
|
console.log('>> AUDIO WAITING', this)
|
||||||
})
|
})
|
||||||
|
|
||||||
useEventListener(this.#audio, 'playing', () => {
|
useEventListener(this.#audio, 'playing', () => {
|
||||||
console.log('>> AUDIO PLAYING', this.__track?.title)
|
console.log('>> AUDIO PLAYING', this)
|
||||||
})
|
})
|
||||||
|
|
||||||
useEventListener(this.#audio, 'stalled', () => {
|
useEventListener(this.#audio, 'stalled', () => {
|
||||||
console.log('>> AUDIO STALLED', this.__track?.title)
|
console.log('>> AUDIO STALLED', this)
|
||||||
})
|
})
|
||||||
|
|
||||||
useEventListener(this.#audio, 'loadeddata', () => {
|
useEventListener(this.#audio, 'loadeddata', () => {
|
||||||
|
@ -96,7 +96,7 @@ export class HTMLSound implements Sound {
|
||||||
})
|
})
|
||||||
|
|
||||||
useEventListener(this.#audio, 'error', (err) => {
|
useEventListener(this.#audio, 'error', (err) => {
|
||||||
console.error('>> AUDIO ERRORED', err, this.__track?.title)
|
console.error('>> AUDIO ERRORED', err, this)
|
||||||
this.isErrored.value = true
|
this.isErrored.value = true
|
||||||
this.isLoaded.value = true
|
this.isLoaded.value = true
|
||||||
})
|
})
|
||||||
|
@ -104,7 +104,7 @@ export class HTMLSound implements Sound {
|
||||||
|
|
||||||
async preload () {
|
async preload () {
|
||||||
this.isErrored.value = false
|
this.isErrored.value = false
|
||||||
console.log('CALLING PRELOAD ON', this.__track?.title)
|
console.log('CALLING PRELOAD ON', this)
|
||||||
this.#audio.load()
|
this.#audio.load()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
import type { QueueItemSource } from '~/types'
|
import type { QueueItemSource } from '~/types'
|
||||||
|
|
||||||
import { whenever, watchDebounced, useCurrentElement, useScrollLock, useFullscreen, useIdle, refAutoReset, useStorage } from '@vueuse/core'
|
import { whenever, watchDebounced, useCurrentElement, useScrollLock, useFullscreen, useIdle, refAutoReset, useStorage } from '@vueuse/core'
|
||||||
import { nextTick, ref, computed, watchEffect, watch, defineAsyncComponent } from 'vue'
|
import { nextTick, ref, computed, watchEffect, defineAsyncComponent } from 'vue'
|
||||||
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
|
import { useFocusTrap } from '@vueuse/integrations/useFocusTrap'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useI18n } from 'vue-i18n'
|
import { useI18n } from 'vue-i18n'
|
||||||
|
@ -27,7 +27,6 @@ const {
|
||||||
isPlaying,
|
isPlaying,
|
||||||
currentTime,
|
currentTime,
|
||||||
duration,
|
duration,
|
||||||
progress,
|
|
||||||
bufferProgress,
|
bufferProgress,
|
||||||
seekTo,
|
seekTo,
|
||||||
loading: isLoadingAudio,
|
loading: isLoadingAudio,
|
||||||
|
@ -108,13 +107,6 @@ const touchProgress = (event: MouseEvent) => {
|
||||||
seekTo(time)
|
seekTo(time)
|
||||||
}
|
}
|
||||||
|
|
||||||
const animated = ref(false)
|
|
||||||
watch(currentTrack, async track => {
|
|
||||||
animated.value = false
|
|
||||||
await nextTick()
|
|
||||||
animated.value = true
|
|
||||||
})
|
|
||||||
|
|
||||||
const play = async (index: number) => {
|
const play = async (index: number) => {
|
||||||
isPlaying.value = true
|
isPlaying.value = true
|
||||||
return playTrack(index)
|
return playTrack(index)
|
||||||
|
@ -368,7 +360,7 @@ if (!isWebGLSupported) {
|
||||||
:style="{ 'transform': `translateX(${bufferProgress - 100}%)` }"
|
:style="{ 'transform': `translateX(${bufferProgress - 100}%)` }"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
:class="['position bar', { animated }]"
|
class="position bar"
|
||||||
:style="{
|
:style="{
|
||||||
animationDuration: duration + 's',
|
animationDuration: duration + 's',
|
||||||
animationPlayState: isPlaying
|
animationPlayState: isPlaying
|
||||||
|
@ -416,23 +408,21 @@ if (!isWebGLSupported) {
|
||||||
<h2 class="ui header">
|
<h2 class="ui header">
|
||||||
<div class="content">
|
<div class="content">
|
||||||
<button
|
<button
|
||||||
|
v-t="'components.Queue.button.close'"
|
||||||
class="ui right floated basic button"
|
class="ui right floated basic button"
|
||||||
@click="$store.commit('ui/queueFocused', null)"
|
@click="$store.commit('ui/queueFocused', null)"
|
||||||
>
|
/>
|
||||||
{{ $t('components.Queue.button.close') }}
|
|
||||||
</button>
|
|
||||||
<button
|
<button
|
||||||
|
v-t="'components.Queue.button.clear'"
|
||||||
class="ui right floated basic button danger"
|
class="ui right floated basic button danger"
|
||||||
@click="clear"
|
@click="clear"
|
||||||
>
|
/>
|
||||||
{{ $t('components.Queue.button.clear') }}
|
|
||||||
</button>
|
|
||||||
{{ labels.queue }}
|
{{ labels.queue }}
|
||||||
<div class="sub header">
|
<div class="sub header">
|
||||||
<div>
|
<div>
|
||||||
{{ $t('components.Queue.meta.queuePosition', {index: currentIndex +1, length: queue.length}) }}
|
<span v-t="{ path: 'components.Queue.meta.queuePosition', args: { index: currentIndex + 1, length: queue.length } }" />
|
||||||
<span class="middle pipe symbol" />
|
<span class="middle pipe symbol" />
|
||||||
{{ $t('components.Queue.meta.end') }}
|
<span v-t="'components.Queue.meta.end'" />
|
||||||
<span :title="labels.duration">
|
<span :title="labels.duration">
|
||||||
{{ endsIn }}
|
{{ endsIn }}
|
||||||
</span>
|
</span>
|
||||||
|
|
|
@ -27,7 +27,6 @@ const {
|
||||||
seekTo,
|
seekTo,
|
||||||
currentTime,
|
currentTime,
|
||||||
duration,
|
duration,
|
||||||
progress,
|
|
||||||
bufferProgress,
|
bufferProgress,
|
||||||
loading: isLoadingAudio
|
loading: isLoadingAudio
|
||||||
} = usePlayer()
|
} = usePlayer()
|
||||||
|
@ -131,10 +130,9 @@ const hideArtist = () => {
|
||||||
>
|
>
|
||||||
<h1
|
<h1
|
||||||
id="player-label"
|
id="player-label"
|
||||||
|
v-t="'components.audio.Player.header.player'"
|
||||||
class="visually-hidden"
|
class="visually-hidden"
|
||||||
>
|
/>
|
||||||
{{ $t('components.audio.Player.header.player') }}
|
|
||||||
</h1>
|
|
||||||
<div
|
<div
|
||||||
class="ui inverted segment fixed-controls"
|
class="ui inverted segment fixed-controls"
|
||||||
@click.prevent.stop="toggleMobilePlayer"
|
@click.prevent.stop="toggleMobilePlayer"
|
||||||
|
@ -305,18 +303,14 @@ const hideArtist = () => {
|
||||||
@click.stop="toggleMobilePlayer"
|
@click.stop="toggleMobilePlayer"
|
||||||
>
|
>
|
||||||
<i class="stream icon" />
|
<i class="stream icon" />
|
||||||
<span>
|
<span v-t="{ path: 'components.audio.Player.meta.position', args: { index: currentIndex + 1, length: queue.length } }" />
|
||||||
{{ $t('components.audio.Player.meta.position', { index: currentIndex + 1, length: queue.length }) }}
|
|
||||||
</span>
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="position circular control button desktop-and-below"
|
class="position circular control button desktop-and-below"
|
||||||
@click.stop="switchTab"
|
@click.stop="switchTab"
|
||||||
>
|
>
|
||||||
<i class="stream icon" />
|
<i class="stream icon" />
|
||||||
<span>
|
<span v-t="{ path: 'components.audio.Player.meta.position', args: { index: currentIndex + 1, length: queue.length } }" />
|
||||||
{{ $t('components.audio.Player.meta.position', { index: currentIndex + 1, length: queue.length }) }}
|
|
||||||
</span>
|
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
|
|
|
@ -167,7 +167,13 @@ export const usePlayer = createGlobalState(() => {
|
||||||
}, 1000)
|
}, 1000)
|
||||||
|
|
||||||
// Progress
|
// Progress
|
||||||
const progress = ref(0)
|
useRafFn(() => {
|
||||||
|
const sound = currentSound.value
|
||||||
|
document.documentElement.style.setProperty('--fw-track-progress', sound
|
||||||
|
? `${(sound.currentTime / sound.duration * 100).toFixed(Math.log(window.innerWidth))}%`
|
||||||
|
: '0'
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
// Loading
|
// Loading
|
||||||
const loading = computed(() => {
|
const loading = computed(() => {
|
||||||
|
@ -207,7 +213,6 @@ export const usePlayer = createGlobalState(() => {
|
||||||
seekBy,
|
seekBy,
|
||||||
seekTo,
|
seekTo,
|
||||||
bufferProgress,
|
bufferProgress,
|
||||||
progress,
|
|
||||||
loading,
|
loading,
|
||||||
errored
|
errored
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import type { Track, Upload } from '~/types'
|
import type { Track, Upload } from '~/types'
|
||||||
|
|
||||||
import { createGlobalState, useNow, useStorage, useTimeAgo, whenever } from '@vueuse/core'
|
import { createGlobalState, useStorage, useTimeAgo, whenever } from '@vueuse/core'
|
||||||
import { computed, ref, shallowReactive, watchEffect } from 'vue'
|
import { computed, ref, shallowReactive, watchEffect } from 'vue'
|
||||||
import { shuffle as shuffleArray, sum } from 'lodash-es'
|
import { shuffle as shuffleArray, sum } from 'lodash-es'
|
||||||
import { useClamp } from '@vueuse/math'
|
import { useClamp } from '@vueuse/math'
|
||||||
|
|
|
@ -127,7 +127,6 @@ export const useTracks = createGlobalState(() => {
|
||||||
// Preload next track
|
// Preload next track
|
||||||
const { start: preload, stop: abortPreload } = useTimeoutFn(async (track: QueueTrack) => {
|
const { start: preload, stop: abortPreload } = useTimeoutFn(async (track: QueueTrack) => {
|
||||||
const sound = await createSound(track)
|
const sound = await createSound(track)
|
||||||
sound.__track = track
|
|
||||||
await sound.preload()
|
await sound.preload()
|
||||||
}, 100, { immediate: false })
|
}, 100, { immediate: false })
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@
|
||||||
.ui.progress .bar {
|
.ui.progress .bar {
|
||||||
transition: none;
|
transition: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
transform: translateX(-100%);
|
transform: translateX(calc(var(--fw-track-progress) - 100%));
|
||||||
transform-origin: top left;
|
transform-origin: top left;
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
@ -160,7 +160,7 @@
|
||||||
.ui.progress .bar {
|
.ui.progress .bar {
|
||||||
transition: none;
|
transition: none;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
transform: translateX(-100%);
|
transform: translateX(calc(var(--fw-track-progress) - 100%));
|
||||||
transform-origin: top left;
|
transform-origin: top left;
|
||||||
will-change: transform;
|
will-change: transform;
|
||||||
}
|
}
|
||||||
|
@ -171,18 +171,6 @@
|
||||||
.ui.progress:not(.indeterminate)
|
.ui.progress:not(.indeterminate)
|
||||||
.bar.position:not(.buffer) {
|
.bar.position:not(.buffer) {
|
||||||
background: var(--vibrant-color);
|
background: var(--vibrant-color);
|
||||||
will-change: transform;
|
|
||||||
|
|
||||||
|
|
||||||
&.animated {
|
|
||||||
@keyframes progress {
|
|
||||||
0% { transform: translate3d(-100%, 0, 0) }
|
|
||||||
100% { transform: translate3d(0, 0, 0) }
|
|
||||||
}
|
|
||||||
|
|
||||||
animation-name: progress;
|
|
||||||
animation-timing-function: linear;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.indicating.progress .bar {
|
.indicating.progress .bar {
|
||||||
|
|
|
@ -2,6 +2,11 @@
|
||||||
html {
|
html {
|
||||||
scroll-behavior: smooth;
|
scroll-behavior: smooth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--fw-track-progress: 0;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (prefers-reduced-motion: reduce) {
|
@media screen and (prefers-reduced-motion: reduce) {
|
||||||
html {
|
html {
|
||||||
scroll-behavior: auto;
|
scroll-behavior: auto;
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { resolve } from 'path'
|
||||||
import { visualizer } from 'rollup-plugin-visualizer'
|
import { visualizer } from 'rollup-plugin-visualizer'
|
||||||
|
|
||||||
import manifest from './pwa-manifest.json'
|
import manifest from './pwa-manifest.json'
|
||||||
|
import Vue from '@vitejs/plugin-vue'
|
||||||
|
|
||||||
const port = +(process.env.VUE_PORT ?? 8080)
|
const port = +(process.env.VUE_PORT ?? 8080)
|
||||||
|
|
||||||
|
|
|
@ -1331,7 +1331,7 @@
|
||||||
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.3.0-beta.17.tgz#1180dcb0b30741555fad0b62e4621802e8272ee5"
|
resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.3.0-beta.17.tgz#1180dcb0b30741555fad0b62e4621802e8272ee5"
|
||||||
integrity sha512-mscf7RQsUTOil35jTij4KGW1RC9SWQjYScwLxP53Ns6g24iEd5HN7ksbt9O6FvTmlQuX77u+MXpBdfJsGqizLQ==
|
integrity sha512-mscf7RQsUTOil35jTij4KGW1RC9SWQjYScwLxP53Ns6g24iEd5HN7ksbt9O6FvTmlQuX77u+MXpBdfJsGqizLQ==
|
||||||
|
|
||||||
"@intlify/unplugin-vue-i18n@^0.8.1":
|
"@intlify/unplugin-vue-i18n@^0.8.2":
|
||||||
version "0.8.2"
|
version "0.8.2"
|
||||||
resolved "https://registry.yarnpkg.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-0.8.2.tgz#4196cb5bee4243bb3a33af76ce9663f3e106809a"
|
resolved "https://registry.yarnpkg.com/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-0.8.2.tgz#4196cb5bee4243bb3a33af76ce9663f3e106809a"
|
||||||
integrity sha512-cRnzPqSEZQOmTD+p4pwc3RTS9HxreLqfID0keoqZDZweCy/CGRMLLTNd15S4TUf1vSBhPF03DItEFDr1F+8MDA==
|
integrity sha512-cRnzPqSEZQOmTD+p4pwc3RTS9HxreLqfID0keoqZDZweCy/CGRMLLTNd15S4TUf1vSBhPF03DItEFDr1F+8MDA==
|
||||||
|
@ -2226,7 +2226,7 @@
|
||||||
postcss "^8.1.10"
|
postcss "^8.1.10"
|
||||||
source-map "^0.6.1"
|
source-map "^0.6.1"
|
||||||
|
|
||||||
"@vue/compiler-sfc@^3.2.45":
|
"@vue/compiler-sfc@3.2.47", "@vue/compiler-sfc@^3.2.45":
|
||||||
version "3.2.47"
|
version "3.2.47"
|
||||||
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz#1bdc36f6cdc1643f72e2c397eb1a398f5004ad3d"
|
resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz#1bdc36f6cdc1643f72e2c397eb1a398f5004ad3d"
|
||||||
integrity sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==
|
integrity sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==
|
||||||
|
|
Loading…
Reference in New Issue