mirror of
https://github.com/advplyr/audiobookshelf-app.git
synced 2026-08-09 05:18:40 +02:00
Update:Show loading indicator on play buttons when starting playback
This commit is contained in:
@@ -204,6 +204,7 @@ export default {
|
||||
message: `Cannot cast downloaded media items. Confirm to close cast and play on your device.`
|
||||
})
|
||||
if (!value) {
|
||||
this.$store.commit('setPlayerDoneStartingPlayback')
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -217,6 +218,7 @@ export default {
|
||||
} else if (this.$refs.audioPlayer) {
|
||||
this.$refs.audioPlayer.play()
|
||||
}
|
||||
this.$store.commit('setPlayerDoneStartingPlayback')
|
||||
return
|
||||
}
|
||||
|
||||
@@ -254,6 +256,9 @@ export default {
|
||||
console.error('Failed', error)
|
||||
this.$toast.error('Failed to play')
|
||||
})
|
||||
.finally(() => {
|
||||
this.$store.commit('setPlayerDoneStartingPlayback')
|
||||
})
|
||||
},
|
||||
pauseItem() {
|
||||
if (this.$refs.audioPlayer && this.$refs.audioPlayer.isPlaying) {
|
||||
|
||||
@@ -46,8 +46,13 @@
|
||||
</div>
|
||||
|
||||
<!-- Play/pause button for podcast episode -->
|
||||
<div v-if="recentEpisode" class="absolute z-10 top-0 left-0 bottom-0 right-0 m-auto flex items-center justify-center w-12 h-12 rounded-full bg-white bg-opacity-70" @click.stop="playEpisode">
|
||||
<span class="material-icons text-6xl text-black text-opacity-80">{{ streamIsPlaying ? 'pause_circle' : 'play_circle_filled' }}</span>
|
||||
<div v-if="recentEpisode" class="absolute z-10 top-0 left-0 bottom-0 right-0 m-auto flex items-center justify-center w-12 h-12 rounded-full" :class="{ 'bg-white/70': !playerIsStartingForThisMedia }" @click.stop="playEpisode">
|
||||
<span v-if="!playerIsStartingForThisMedia" class="material-icons text-6xl text-black/80">{{ streamIsPlaying ? 'pause_circle' : 'play_circle_filled' }}</span>
|
||||
<div v-else class="text-fg absolute top-0 left-0 w-full h-full flex items-center justify-center bg-black/80 rounded-full overflow-hidden">
|
||||
<svg class="animate-spin" style="width: 24px; height: 24px" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" />
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- No progress shown for collapsed series in library -->
|
||||
@@ -322,6 +327,14 @@ export default {
|
||||
streamIsPlaying() {
|
||||
return this.store.state.playerIsPlaying && this.isStreaming
|
||||
},
|
||||
playerIsStartingPlayback() {
|
||||
// Play has been pressed and waiting for native play response
|
||||
return this.store.state.playerIsStartingPlayback
|
||||
},
|
||||
playerIsStartingForThisMedia() {
|
||||
const mediaId = this.store.state.playerStartingPlaybackMediaId
|
||||
return mediaId === this.recentEpisode?.id
|
||||
},
|
||||
isMissing() {
|
||||
return this._libraryItem.isMissing
|
||||
},
|
||||
@@ -447,6 +460,8 @@ export default {
|
||||
},
|
||||
async play() {},
|
||||
async playEpisode() {
|
||||
if (this.playerIsStartingPlayback) return
|
||||
|
||||
await this.$hapticsImpact()
|
||||
const eventBus = this.$eventBus || this.$nuxt.$eventBus
|
||||
if (this.streamIsPlaying) {
|
||||
@@ -454,6 +469,7 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
this.store.commit('setPlayerIsStartingPlayback', this.recentEpisode.id)
|
||||
if (this.localEpisode) {
|
||||
// Play episode locally
|
||||
eventBus.$emit('play-item', {
|
||||
|
||||
@@ -176,7 +176,7 @@ export default {
|
||||
return this.mediaMetadata.series
|
||||
},
|
||||
seriesSequence() {
|
||||
return this.series ? this.series.sequence : null
|
||||
return this.series?.sequence || null
|
||||
},
|
||||
collapsedSeries() {
|
||||
// Only added to item object when collapseSeries is enabled
|
||||
@@ -209,10 +209,10 @@ export default {
|
||||
return this.store.getters['user/getUserMediaProgress'](this.libraryItemId)
|
||||
},
|
||||
userProgressPercent() {
|
||||
return this.userProgress ? this.userProgress.progress || 0 : 0
|
||||
return this.userProgress?.progress || 0
|
||||
},
|
||||
itemIsFinished() {
|
||||
return this.userProgress ? !!this.userProgress.isFinished : false
|
||||
return !!this.userProgress?.isFinished
|
||||
},
|
||||
showError() {
|
||||
return this.numMissingParts || this.isMissing || this.isInvalid
|
||||
|
||||
@@ -13,8 +13,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="w-8 min-w-8 flex justify-center">
|
||||
<button v-if="showPlayBtn" class="w-8 h-8 rounded-full border border-white border-opacity-20 flex items-center justify-center" @click.stop.prevent="playClick">
|
||||
<span class="material-icons" :class="streamIsPlaying ? '' : 'text-success'">{{ streamIsPlaying ? 'pause' : 'play_arrow' }}</span>
|
||||
<button v-if="showPlayBtn" class="w-8 h-8 rounded-full border border-white/20 flex items-center justify-center" @click.stop.prevent="playClick">
|
||||
<span v-if="!playerIsStartingForThisMedia" class="material-icons" :class="streamIsPlaying ? '' : 'text-success'">{{ streamIsPlaying ? 'pause' : 'play_arrow' }}</span>
|
||||
<svg v-else class="animate-spin" style="width: 18px; height: 18px" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="w-8 min-w-8 flex justify-center">
|
||||
@@ -117,6 +120,15 @@ export default {
|
||||
streamIsPlaying() {
|
||||
return this.$store.state.playerIsPlaying && this.isStreaming
|
||||
},
|
||||
playerIsStartingPlayback() {
|
||||
// Play has been pressed and waiting for native play response
|
||||
return this.$store.state.playerIsStartingPlayback
|
||||
},
|
||||
playerIsStartingForThisMedia() {
|
||||
const mediaId = this.$store.state.playerStartingPlaybackMediaId
|
||||
let thisMediaId = this.episodeId || this.libraryItem.id
|
||||
return mediaId === thisMediaId
|
||||
},
|
||||
userItemProgress() {
|
||||
return this.$store.getters['user/getUserMediaProgress'](this.libraryItem.id, this.episodeId)
|
||||
},
|
||||
@@ -142,10 +154,14 @@ export default {
|
||||
this.$emit('showMore', playlistItem)
|
||||
},
|
||||
async playClick() {
|
||||
if (this.playerIsStartingPlayback) return
|
||||
|
||||
await this.$hapticsImpact()
|
||||
let mediaId = this.episodeId || this.libraryItem.id
|
||||
if (this.streamIsPlaying) {
|
||||
this.$eventBus.$emit('pause-item')
|
||||
} else if (this.localLibraryItem) {
|
||||
this.$store.commit('setPlayerIsStartingPlayback', mediaId)
|
||||
this.$eventBus.$emit('play-item', {
|
||||
libraryItemId: this.localLibraryItem.id,
|
||||
episodeId: this.localEpisode?.id,
|
||||
@@ -153,6 +169,7 @@ export default {
|
||||
serverEpisodeId: this.episodeId
|
||||
})
|
||||
} else {
|
||||
this.$store.commit('setPlayerIsStartingPlayback', mediaId)
|
||||
this.$eventBus.$emit('play-item', {
|
||||
libraryItemId: this.libraryItem.id,
|
||||
episodeId: this.episodeId
|
||||
|
||||
@@ -29,7 +29,10 @@
|
||||
|
||||
<div class="flex items-center pt-2">
|
||||
<div class="h-8 px-4 border border-border rounded-full flex items-center justify-center cursor-pointer" :class="userIsFinished ? 'text-white text-opacity-40' : ''" @click.stop="playClick">
|
||||
<span class="material-icons" :class="streamIsPlaying ? '' : 'text-success'">{{ streamIsPlaying ? 'pause' : 'play_arrow' }}</span>
|
||||
<span v-if="!playerIsStartingForThisMedia" class="material-icons" :class="streamIsPlaying ? '' : 'text-success'">{{ streamIsPlaying ? 'pause' : 'play_arrow' }}</span>
|
||||
<svg v-else class="animate-spin" style="width: 24px; height: 24px" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" />
|
||||
</svg>
|
||||
<p class="pl-2 pr-1 text-sm font-semibold">{{ timeRemaining }}</p>
|
||||
</div>
|
||||
|
||||
@@ -118,6 +121,14 @@ export default {
|
||||
streamIsPlaying() {
|
||||
return this.$store.state.playerIsPlaying && this.isStreaming
|
||||
},
|
||||
playerIsStartingPlayback() {
|
||||
// Play has been pressed and waiting for native play response
|
||||
return this.$store.state.playerIsStartingPlayback
|
||||
},
|
||||
playerIsStartingForThisMedia() {
|
||||
const mediaId = this.$store.state.playerStartingPlaybackMediaId
|
||||
return mediaId === this.episode?.id
|
||||
},
|
||||
itemProgress() {
|
||||
if (this.isLocal) return this.$store.getters['globals/getLocalMediaProgressById'](this.libraryItemId, this.episode.id)
|
||||
return this.$store.getters['user/getUserMediaProgress'](this.libraryItemId, this.episode.id)
|
||||
@@ -227,10 +238,14 @@ export default {
|
||||
}
|
||||
},
|
||||
async playClick() {
|
||||
if (this.playerIsStartingPlayback) return
|
||||
|
||||
await this.$hapticsImpact()
|
||||
if (this.streamIsPlaying) {
|
||||
this.$eventBus.$emit('pause-item')
|
||||
} else {
|
||||
this.$store.commit('setPlayerIsStartingPlayback', this.episode.id)
|
||||
|
||||
if (this.localEpisode && this.localLibraryItemId) {
|
||||
console.log('Play local episode', this.localEpisode.id, this.localLibraryItemId)
|
||||
|
||||
|
||||
@@ -29,7 +29,10 @@
|
||||
|
||||
<div class="flex items-center pt-2">
|
||||
<div class="h-8 px-4 border border-border hover:bg-white hover:bg-opacity-10 rounded-full flex items-center justify-center cursor-pointer" :class="userIsFinished ? 'text-fg text-opacity-40' : ''" @click.stop="playClick">
|
||||
<span class="material-icons" :class="streamIsPlaying ? '' : 'text-success'">{{ streamIsPlaying ? 'pause' : 'play_arrow' }}</span>
|
||||
<span v-if="!playerIsStartingForThisMedia" class="material-icons" :class="streamIsPlaying ? '' : 'text-success'">{{ streamIsPlaying ? 'pause' : 'play_arrow' }}</span>
|
||||
<svg v-else class="animate-spin" style="width: 24px; height: 24px" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" />
|
||||
</svg>
|
||||
<p class="pl-2 pr-1 text-sm font-semibold">{{ timeRemaining }}</p>
|
||||
</div>
|
||||
|
||||
@@ -122,6 +125,15 @@ export default {
|
||||
streamIsPlaying() {
|
||||
return this.$store.state.playerIsPlaying && this.isStreaming
|
||||
},
|
||||
playerIsStartingPlayback() {
|
||||
// Play has been pressed and waiting for native play response
|
||||
return this.$store.state.playerIsStartingPlayback
|
||||
},
|
||||
playerIsStartingForThisMedia() {
|
||||
if (!this.episode?.id) return false
|
||||
const mediaId = this.$store.state.playerStartingPlaybackMediaId
|
||||
return mediaId === this.episode.id
|
||||
},
|
||||
itemProgress() {
|
||||
if (this.isLocal) return this.$store.getters['globals/getLocalMediaProgressById'](this.libraryItemId, this.episode.id)
|
||||
return this.$store.getters['user/getUserMediaProgress'](this.libraryItemId, this.episode.id)
|
||||
@@ -135,10 +147,10 @@ export default {
|
||||
}
|
||||
},
|
||||
itemProgressPercent() {
|
||||
return this.itemProgress ? this.itemProgress.progress : 0
|
||||
return this.itemProgress?.progress || 0
|
||||
},
|
||||
userIsFinished() {
|
||||
return this.itemProgress ? !!this.itemProgress.isFinished : false
|
||||
return !!this.itemProgress?.isFinished
|
||||
},
|
||||
timeRemaining() {
|
||||
if (this.streamIsPlaying) return 'Playing'
|
||||
@@ -154,7 +166,7 @@ export default {
|
||||
return this.$store.getters['globals/getDownloadItem'](this.libraryItemId, this.episode.id)
|
||||
},
|
||||
localEpisodeId() {
|
||||
return this.localEpisode ? this.localEpisode.id : null
|
||||
return this.localEpisode?.id || null
|
||||
},
|
||||
podcast() {
|
||||
return this.episode.podcast || {}
|
||||
@@ -238,10 +250,14 @@ export default {
|
||||
}
|
||||
},
|
||||
async playClick() {
|
||||
if (this.playerIsStartingPlayback) return
|
||||
|
||||
await this.$hapticsImpact()
|
||||
if (this.streamIsPlaying) {
|
||||
this.$eventBus.$emit('pause-item')
|
||||
} else {
|
||||
this.$store.commit('setPlayerIsStartingPlayback', this.episode.id)
|
||||
|
||||
if (this.localEpisode && this.localLibraryItemId) {
|
||||
console.log('Play local episode', this.localEpisode.id, this.localLibraryItemId)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user