build: v1.5.2; fix: spotify favorites playlist not showing in Favorites page until the refresh button is clicked (closes #21); refactor: favorites logic
This commit is contained in:
parent
835b0e9f8d
commit
d752efa30f
File diff suppressed because one or more lines are too long
@ -3,13 +3,13 @@
|
|||||||
<h1 class="mb-8 text-5xl">
|
<h1 class="mb-8 text-5xl">
|
||||||
{{ $t('favorites.title') }}
|
{{ $t('favorites.title') }}
|
||||||
<div
|
<div
|
||||||
@click="reloadTabs"
|
@click="refreshFavorites"
|
||||||
class="inline-block clickable reload-button"
|
class="inline-block clickable"
|
||||||
ref="reloadButton"
|
ref="reloadButton"
|
||||||
role="button"
|
role="button"
|
||||||
aria-label="reload"
|
aria-label="reload"
|
||||||
>
|
>
|
||||||
<i class="material-icons">sync</i>
|
<i class="material-icons" :class="{ spin: isRefreshingFavorites }">sync</i>
|
||||||
</div>
|
</div>
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
@ -20,10 +20,10 @@
|
|||||||
</BaseTabs>
|
</BaseTabs>
|
||||||
|
|
||||||
<button class="btn btn-primary" v-if="!activeTabEmpty" style="margin-bottom: 2rem" @click="downloadAllOfType">
|
<button class="btn btn-primary" v-if="!activeTabEmpty" style="margin-bottom: 2rem" @click="downloadAllOfType">
|
||||||
{{ $t('globals.download', { thing: $tc(`globals.listTabs.${activeTab}N`, getTabLenght()) }) }}
|
{{ $t('globals.download', { thing: $tc(`globals.listTabs.${activeTab}N`, getTabLength()) }) }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<div class="favorites_tabcontent" :class="{ 'favorites_tabcontent--active': activeTab === 'playlist' }">
|
<div v-show="activeTab === 'playlist'">
|
||||||
<div v-if="playlists.length == 0">
|
<div v-if="playlists.length == 0">
|
||||||
<h1>{{ $t('favorites.noPlaylists') }}</h1>
|
<h1>{{ $t('favorites.noPlaylists') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
@ -62,7 +62,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="favorites_tabcontent" :class="{ 'favorites_tabcontent--active': activeTab === 'album' }">
|
<div v-show="activeTab === 'album'">
|
||||||
<div v-if="albums.length == 0">
|
<div v-if="albums.length == 0">
|
||||||
<h1>{{ $t('favorites.noAlbums') }}</h1>
|
<h1>{{ $t('favorites.noAlbums') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
@ -81,7 +81,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="favorites_tabcontent" :class="{ 'favorites_tabcontent--active': activeTab === 'artist' }">
|
<div v-show="activeTab === 'artist'">
|
||||||
<div v-if="artists.length == 0">
|
<div v-if="artists.length == 0">
|
||||||
<h1>{{ $t('favorites.noArtists') }}</h1>
|
<h1>{{ $t('favorites.noArtists') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
@ -99,7 +99,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="favorites_tabcontent" :class="{ 'favorites_tabcontent--active': activeTab === 'track' }">
|
<div v-show="activeTab === 'track'">
|
||||||
<div v-if="tracks.length == 0">
|
<div v-if="tracks.length == 0">
|
||||||
<h1>{{ $t('favorites.noTracks') }}</h1>
|
<h1>{{ $t('favorites.noTracks') }}</h1>
|
||||||
</div>
|
</div>
|
||||||
@ -163,86 +163,70 @@
|
|||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.favorites_tabcontent {
|
|
||||||
display: none;
|
|
||||||
|
|
||||||
&--active {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.reload-button {
|
|
||||||
&.spin {
|
|
||||||
i {
|
|
||||||
animation: spin 500ms infinite ease-out reverse;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { socket } from '@/utils/socket'
|
import { defineComponent, onMounted, reactive, toRefs, watchEffect, ref, computed, watch } from '@vue/composition-api'
|
||||||
import { sendAddToQueue, aggregateDownloadLinks } from '@/utils/downloads'
|
|
||||||
import { convertDuration } from '@/utils/utils'
|
|
||||||
import { toast } from '@/utils/toasts'
|
|
||||||
import { getFavoritesData } from '@/data/favorites'
|
|
||||||
|
|
||||||
import PreviewControls from '@components/globals/PreviewControls.vue'
|
import PreviewControls from '@components/globals/PreviewControls.vue'
|
||||||
import CoverContainer from '@components/globals/CoverContainer.vue'
|
import CoverContainer from '@components/globals/CoverContainer.vue'
|
||||||
import { playPausePreview } from '@components/globals/TheTrackPreview.vue'
|
import { playPausePreview } from '@components/globals/TheTrackPreview.vue'
|
||||||
import { BaseTabs, BaseTab } from '@components/globals/BaseTabs'
|
import { BaseTabs, BaseTab } from '@components/globals/BaseTabs'
|
||||||
|
|
||||||
export default {
|
import { sendAddToQueue, aggregateDownloadLinks } from '@/utils/downloads'
|
||||||
|
import { convertDuration } from '@/utils/utils'
|
||||||
|
import { toast } from '@/utils/toasts'
|
||||||
|
import { useFavorites } from '@/use/favorites'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
components: {
|
components: {
|
||||||
PreviewControls,
|
PreviewControls,
|
||||||
CoverContainer,
|
CoverContainer,
|
||||||
BaseTabs,
|
BaseTabs,
|
||||||
BaseTab
|
BaseTab
|
||||||
},
|
},
|
||||||
data() {
|
setup(props, ctx) {
|
||||||
return {
|
const state = reactive({
|
||||||
tracks: [],
|
|
||||||
albums: [],
|
|
||||||
artists: [],
|
|
||||||
playlists: [],
|
|
||||||
spotifyPlaylists: [],
|
|
||||||
activeTab: 'playlist',
|
activeTab: 'playlist',
|
||||||
tabs: ['playlist', 'album', 'artist', 'track']
|
tabs: ['playlist', 'album', 'artist', 'track']
|
||||||
|
})
|
||||||
|
const {
|
||||||
|
favoriteArtists,
|
||||||
|
favoriteAlbums,
|
||||||
|
favoriteSpotifyPlaylists,
|
||||||
|
favoritePlaylists,
|
||||||
|
favoriteTracks,
|
||||||
|
isRefreshingFavorites,
|
||||||
|
refreshFavorites
|
||||||
|
} = useFavorites()
|
||||||
|
const reloadButton = computed(() => ctx.refs.reloadButton)
|
||||||
|
|
||||||
|
watch(isRefreshingFavorites, (newVal, oldVal) => {
|
||||||
|
// If oldVal is true and newOne is false, it means that a refreshing has just terminated
|
||||||
|
// because isRefreshingFavorites represents the status of the refresh functionality
|
||||||
|
const isRefreshingTerminated = oldVal && !newVal
|
||||||
|
|
||||||
|
if (!isRefreshingTerminated) return
|
||||||
|
|
||||||
|
toast(ctx.root.$t('toasts.refreshFavs'), 'done', true)
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
...toRefs(state),
|
||||||
|
tracks: favoriteTracks,
|
||||||
|
albums: favoriteAlbums,
|
||||||
|
artists: favoriteArtists,
|
||||||
|
playlists: favoritePlaylists,
|
||||||
|
spotifyPlaylists: favoriteSpotifyPlaylists,
|
||||||
|
refreshFavorites,
|
||||||
|
isRefreshingFavorites
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
activeTabEmpty() {
|
activeTabEmpty() {
|
||||||
let toCheck = this.getActiveRelease()
|
let toCheck = this.getActiveRelease()
|
||||||
|
|
||||||
return toCheck.length === 0
|
return toCheck?.length === 0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created() {
|
|
||||||
const favoritesData = await getFavoritesData()
|
|
||||||
|
|
||||||
// TODO Change with isLoggedIn vuex getter
|
|
||||||
if (Object.entries(favoritesData).length === 0) return
|
|
||||||
|
|
||||||
this.setFavorites(favoritesData)
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
socket.on('updated_userFavorites', this.updated_userFavorites)
|
|
||||||
socket.on('updated_userSpotifyPlaylists', this.updated_userSpotifyPlaylists)
|
|
||||||
socket.on('updated_userPlaylists', this.updated_userPlaylists)
|
|
||||||
socket.on('updated_userAlbums', this.updated_userAlbums)
|
|
||||||
socket.on('updated_userArtist', this.updated_userArtist)
|
|
||||||
socket.on('updated_userTracks', this.updated_userTracks)
|
|
||||||
|
|
||||||
this.$on('hook:destroyed', () => {
|
|
||||||
socket.off('updated_userFavorites')
|
|
||||||
socket.off('updated_userSpotifyPlaylists')
|
|
||||||
socket.off('updated_userPlaylists')
|
|
||||||
socket.off('updated_userAlbums')
|
|
||||||
socket.off('updated_userArtist')
|
|
||||||
socket.off('updated_userTracks')
|
|
||||||
})
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
playPausePreview,
|
playPausePreview,
|
||||||
convertDuration,
|
convertDuration,
|
||||||
@ -264,54 +248,9 @@ export default {
|
|||||||
addToQueue(e) {
|
addToQueue(e) {
|
||||||
sendAddToQueue(e.currentTarget.dataset.link)
|
sendAddToQueue(e.currentTarget.dataset.link)
|
||||||
},
|
},
|
||||||
updated_userSpotifyPlaylists(data) {
|
|
||||||
this.spotifyPlaylists = data
|
|
||||||
},
|
|
||||||
updated_userPlaylists(data) {
|
|
||||||
this.playlists = data
|
|
||||||
},
|
|
||||||
updated_userAlbums(data) {
|
|
||||||
this.albums = data
|
|
||||||
},
|
|
||||||
updated_userArtist(data) {
|
|
||||||
this.artists = data
|
|
||||||
},
|
|
||||||
updated_userTracks(data) {
|
|
||||||
this.tracks = data
|
|
||||||
},
|
|
||||||
reloadTabs() {
|
|
||||||
this.$refs.reloadButton.classList.add('spin')
|
|
||||||
|
|
||||||
socket.emit('update_userFavorites')
|
|
||||||
|
|
||||||
if (localStorage.getItem('spotifyUser')) {
|
|
||||||
socket.emit('update_userSpotifyPlaylists', localStorage.getItem('spotifyUser'))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
updated_userFavorites(data) {
|
|
||||||
this.setFavorites(data)
|
|
||||||
|
|
||||||
// Removing animation class only when the animation has completed an iteration
|
|
||||||
// Prevents animation ugly stutter
|
|
||||||
this.$refs.reloadButton.addEventListener(
|
|
||||||
'animationiteration',
|
|
||||||
() => {
|
|
||||||
this.$refs.reloadButton.classList.remove('spin')
|
|
||||||
toast(this.$t('toasts.refreshFavs'), 'done', true)
|
|
||||||
},
|
|
||||||
{ once: true }
|
|
||||||
)
|
|
||||||
},
|
|
||||||
setFavorites(data) {
|
|
||||||
const { tracks, albums, artists, playlists } = data
|
|
||||||
|
|
||||||
this.tracks = tracks
|
|
||||||
this.albums = albums
|
|
||||||
this.artists = artists
|
|
||||||
this.playlists = playlists
|
|
||||||
},
|
|
||||||
getActiveRelease(tab = this.activeTab) {
|
getActiveRelease(tab = this.activeTab) {
|
||||||
let toDownload
|
let toDownload
|
||||||
|
// console.log({ tab, play: this.playlists })
|
||||||
|
|
||||||
switch (tab) {
|
switch (tab) {
|
||||||
case 'playlist':
|
case 'playlist':
|
||||||
@ -333,11 +272,11 @@ export default {
|
|||||||
|
|
||||||
return toDownload
|
return toDownload
|
||||||
},
|
},
|
||||||
getTabLenght(tab = this.activeTab) {
|
getTabLength(tab = this.activeTab) {
|
||||||
let total = this[`${tab}s`].length
|
let total = this[`${tab}s`]?.length
|
||||||
// TODO: Add Spotify playlists to downlaod queue as well
|
// TODO: Add Spotify playlists to downlaod queue as well
|
||||||
//if (tab === "playlist") total += this.spotifyPlaylists.length
|
//if (tab === "playlist") total += this.spotifyPlaylists.length
|
||||||
return total
|
return total || 0
|
||||||
},
|
},
|
||||||
getLovedTracksPlaylist() {
|
getLovedTracksPlaylist() {
|
||||||
let lovedTracks = this.playlists.filter(playlist => {
|
let lovedTracks = this.playlists.filter(playlist => {
|
||||||
@ -351,5 +290,5 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
</script>
|
</script>
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
import { socket } from '@/utils/socket'
|
|
||||||
|
|
||||||
let favoritesData = {}
|
|
||||||
let cached = false
|
|
||||||
|
|
||||||
export function getFavoritesData() {
|
|
||||||
if (cached) {
|
|
||||||
return favoritesData
|
|
||||||
} else {
|
|
||||||
socket.emit('get_favorites_data')
|
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
|
||||||
socket.on('init_favorites', data => {
|
|
||||||
favoritesData = data
|
|
||||||
cached = true
|
|
||||||
|
|
||||||
socket.off('init_favorites')
|
|
||||||
resolve(data)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,10 +6,17 @@ const getDefaultState = () => ({
|
|||||||
name: '',
|
name: '',
|
||||||
picture: ''
|
picture: ''
|
||||||
},
|
},
|
||||||
|
spotifyUser: {
|
||||||
|
id: localStorage.getItem('spotifyUser'),
|
||||||
|
name: null,
|
||||||
|
picture: null
|
||||||
|
},
|
||||||
clientMode: false
|
clientMode: false
|
||||||
})
|
})
|
||||||
|
|
||||||
const state = getDefaultState()
|
const state = () => {
|
||||||
|
return getDefaultState()
|
||||||
|
}
|
||||||
|
|
||||||
const actions = {
|
const actions = {
|
||||||
login({ commit, dispatch }, payload) {
|
login({ commit, dispatch }, payload) {
|
||||||
@ -51,8 +58,11 @@ const actions = {
|
|||||||
const getters = {
|
const getters = {
|
||||||
getARL: state => state.arl,
|
getARL: state => state.arl,
|
||||||
getUser: state => state.user,
|
getUser: state => state.user,
|
||||||
|
getSpotifyUser: state => state.spotifyUser,
|
||||||
getClientMode: state => state.clientMode,
|
getClientMode: state => state.clientMode,
|
||||||
isLoggedIn: state => !!state.arl
|
|
||||||
|
isLoggedIn: state => !!state.arl,
|
||||||
|
isLoggedWithSpotify: state => !!state.spotifyUser.id
|
||||||
}
|
}
|
||||||
|
|
||||||
const mutations = {
|
const mutations = {
|
||||||
|
@ -22,6 +22,10 @@
|
|||||||
width: 156px;
|
width: 156px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.spin {
|
||||||
|
animation: spin 500ms infinite ease-out reverse;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes spin {
|
@keyframes spin {
|
||||||
0% {
|
0% {
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
|
79
src/use/favorites.js
Normal file
79
src/use/favorites.js
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { ref } from '@vue/composition-api'
|
||||||
|
|
||||||
|
import store from '@/store'
|
||||||
|
import { socket } from '@/utils/socket'
|
||||||
|
|
||||||
|
const favoriteArtists = ref([])
|
||||||
|
const favoriteAlbums = ref([])
|
||||||
|
const favoriteSpotifyPlaylists = ref([])
|
||||||
|
const favoritePlaylists = ref([])
|
||||||
|
const favoriteTracks = ref([])
|
||||||
|
|
||||||
|
const isRefreshingFavorites = ref(false)
|
||||||
|
|
||||||
|
if (store.getters.isLoggedIn) {
|
||||||
|
refreshFavorites({ isInitial: true })
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshFavorites({ isInitial = false }) {
|
||||||
|
if (!isInitial) {
|
||||||
|
isRefreshingFavorites.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.emit('get_favorites_data')
|
||||||
|
|
||||||
|
if (store.getters.isLoggedWithSpotify) {
|
||||||
|
socket.emit('update_userSpotifyPlaylists', store.getters.getSpotifyUser.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useFavorites() {
|
||||||
|
return {
|
||||||
|
favoriteArtists,
|
||||||
|
favoriteAlbums,
|
||||||
|
favoriteSpotifyPlaylists,
|
||||||
|
favoritePlaylists,
|
||||||
|
favoriteTracks,
|
||||||
|
isRefreshingFavorites,
|
||||||
|
refreshFavorites
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAllFavorites(data) {
|
||||||
|
const { tracks, albums, artists, playlists } = data
|
||||||
|
|
||||||
|
favoriteArtists.value = artists
|
||||||
|
favoriteAlbums.value = albums
|
||||||
|
favoritePlaylists.value = playlists
|
||||||
|
favoriteTracks.value = tracks
|
||||||
|
}
|
||||||
|
|
||||||
|
socket.on('updated_userFavorites', data => {
|
||||||
|
setAllFavorites(data)
|
||||||
|
// Commented out because the corresponding emit function is never called at the moment
|
||||||
|
// therefore isRefreshingFavorites is never set to true
|
||||||
|
// isRefreshingFavorites.value = false
|
||||||
|
})
|
||||||
|
socket.on('init_favorites', data => {
|
||||||
|
setAllFavorites(data)
|
||||||
|
isRefreshingFavorites.value = false
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('updated_userSpotifyPlaylists', data => {
|
||||||
|
favoriteSpotifyPlaylists.value = data
|
||||||
|
})
|
||||||
|
socket.on('updated_userSpotifyPlaylists', data => {
|
||||||
|
favoriteSpotifyPlaylists.value = data
|
||||||
|
})
|
||||||
|
socket.on('updated_userPlaylists', data => {
|
||||||
|
favoritePlaylists.value = data
|
||||||
|
})
|
||||||
|
socket.on('updated_userAlbums', data => {
|
||||||
|
favoriteAlbums.value = data
|
||||||
|
})
|
||||||
|
socket.on('updated_userArtist', data => {
|
||||||
|
favoriteArtists.value = data
|
||||||
|
})
|
||||||
|
socket.on('updated_userTracks', data => {
|
||||||
|
favoriteTracks.value = data
|
||||||
|
})
|
@ -19,7 +19,3 @@ export function aggregateDownloadLinks(releases) {
|
|||||||
|
|
||||||
return links.join(';')
|
return links.join(';')
|
||||||
}
|
}
|
||||||
|
|
||||||
// export default {
|
|
||||||
// sendAddToQueue
|
|
||||||
// }
|
|
||||||
|
Loading…
Reference in New Issue
Block a user