style: added new release style; refactor: Artist page with composition API (to finish); refactor: extracted new release check logic

This commit is contained in:
Roberto Tonino 2020-11-14 22:27:47 +01:00
parent d04439857a
commit ced8650ee6
9 changed files with 139 additions and 98 deletions

File diff suppressed because one or more lines are too long

View File

@ -17,7 +17,7 @@
<li <li
v-for="(item, name) in artistReleases" v-for="(item, name) in artistReleases"
:key="name" :key="name"
class="section-tabs__tab" class="section-tabs__tab uppercase-first-letter"
@click="changeTab(name)" @click="changeTab(name)"
:class="{ active: currentTab === name }" :class="{ active: currentTab === name }"
> >
@ -33,6 +33,7 @@
:key="data.title" :key="data.title"
@click="data.sortKey ? sortBy(data.sortKey) : null" @click="data.sortKey ? sortBy(data.sortKey) : null"
:style="{ width: data.width ? data.width : 'auto' }" :style="{ width: data.width ? data.width : 'auto' }"
class="uppercase-first-letter"
:class="{ :class="{
'sort-asc': data.sortKey == sortKey && sortOrder == 'asc', 'sort-asc': data.sortKey == sortKey && sortOrder == 'asc',
'sort-desc': data.sortKey == sortKey && sortOrder == 'desc', 'sort-desc': data.sortKey == sortKey && sortOrder == 'desc',
@ -46,8 +47,8 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr v-for="release in showTable"> <tr v-for="release in sortedData" :key="release.releaseID">
<router-link <RouterLink
tag="td" tag="td"
class="flex items-center clickable" class="flex items-center clickable"
:to="{ name: 'Album', params: { id: release.releaseID } }" :to="{ name: 'Album', params: { id: release.releaseID } }"
@ -57,12 +58,17 @@
:src="release.releaseCover" :src="release.releaseCover"
style="margin-right: 16px; width: 56px; height: 56px" style="margin-right: 16px; width: 56px; height: 56px"
/> />
<i v-if="release.isReleaseExplicit" class="material-icons explicit-icon">explicit</i> <i v-if="release.isReleaseExplicit" class="material-icons title-icon title-icon--explicit">explicit</i>
{{ release.releaseTitle }} {{ release.releaseTitle }}
<i v-if="checkNewRelease(release.releaseDate)" class="material-icons" style="color: #ff7300">fiber_new</i> <i
</router-link> v-if="checkNewRelease(release.releaseDate)"
<td>{{ release.releaseDate }}</td> class="material-icons title-icon title-icon--new title-icon--right"
<td>{{ release.releaseTracksNumber }}</td> >
fiber_new
</i>
</RouterLink>
<td class="text-center">{{ release.releaseDate }}</td>
<td class="text-center">{{ release.releaseTracksNumber }}</td>
<td @click.stop="sendAddToQueue(release.releaseLink)" class="clickable"> <td @click.stop="sendAddToQueue(release.releaseLink)" class="clickable">
<i class="material-icons" :title="$t('globals.download_hint')"> file_download </i> <i class="material-icons" :title="$t('globals.download_hint')"> file_download </i>
</td> </td>
@ -73,80 +79,75 @@
</template> </template>
<script> <script>
import { defineComponent, ref, unref, reactive, computed, onMounted, toRefs } from '@vue/composition-api'
import { orderBy } from 'lodash-es' import { orderBy } from 'lodash-es'
import { socket } from '@/utils/socket' import { socket } from '@/utils/socket'
import { sendAddToQueue } from '@/utils/downloads' import { sendAddToQueue } from '@/utils/downloads'
import EventBus from '@/utils/EventBus' import { checkNewRelease } from '@/utils/dates'
import { formatArtistData } from '@/data/artist' import { formatArtistData, getArtistData } from '@/data/artist'
import { standardizeData } from '@/data/standardize' import { standardizeData } from '@/data/standardize'
import { ref, reactive, computed, onMounted, toRefs, onDeactivated } from '@vue/composition-api'
export default { export default defineComponent({
setup() { setup(props, ctx) {
const state = reactive({ const state = reactive({
currentTab: '', currentTab: '',
sortKey: 'releaseDate', sortKey: 'releaseDate',
sortOrder: 'desc', sortOrder: 'desc',
artistReleases: {}, artistReleases: {},
artistID: '',
artistName: '', artistName: '',
artistPicture: '' artistPicture: '',
currentRelease: computed(() => state.artistReleases[state.currentTab])
}) })
const currentRelease = computed(() => state.artistReleases[state.currentTab]) const artistID = computed(() => ctx.root.$router.currentRoute.params.id)
const hasDataLoaded = ref(false)
getArtistData(unref(artistID))
.then(artistData => {
hasDataLoaded.value = true
const setupData = data => {
const { const {
data: [{ artistID, artistName, artistPictureXL, artistReleases }] data: [{ artistName, artistPictureXL, artistReleases }]
} = standardizeData({ data: [data], hasLoaded: true }, formatArtistData) } = standardizeData({ data: [artistData], hasLoaded: unref(hasDataLoaded) }, formatArtistData)
Object.assign(state, { Object.assign(state, {
artistID,
artistName, artistName,
artistPicture: artistPictureXL, artistPicture: artistPictureXL,
artistReleases artistReleases,
currentTab: Object.keys(artistReleases)[0]
}) })
})
.catch(err => console.error(err))
// ? Is it not granted that it's always 'all' ? const sortedData = computed(() => {
state.currentTab = Object.keys(artistReleases)[0] if (!unref(hasDataLoaded)) {
return []
} }
const reset = () => {
state.currentTab = ''
state.sortKey = 'releaseDate'
state.sortOrder = 'desc'
}
onDeactivated(reset)
onMounted(() => {
socket.on('show_artist', setupData)
})
const showTable = computed(() => {
if (Object.keys(state.artistReleases).length !== 0) {
let sortKey = state.sortKey let sortKey = state.sortKey
if (sortKey == 'releaseTracksNumber') { if (sortKey === 'releaseTracksNumber') {
sortKey = o => new Number(o.releaseTracksNumber) sortKey = o => new Number(o.releaseTracksNumber)
} }
return orderBy(currentRelease.value, sortKey, state.sortOrder) return orderBy(state.currentRelease, sortKey, state.sortOrder)
}
return []
}) })
const changeTab = newTab => {
state.currentTab = newTab
}
return { return {
...toRefs(state), ...toRefs(state),
downloadLink: computed(() => `https://www.deezer.com/artist/${state.artistID}`), downloadLink: computed(() => `https://www.deezer.com/artist/${unref(artistID)}`),
headerStyle: computed(() => ({ headerStyle: computed(() => ({
backgroundImage: `linear-gradient(to bottom, transparent 0%, var(--main-background) 100%), url(${state.artistPicture})` backgroundImage: `linear-gradient(to bottom, transparent 0%, var(--main-background) 100%), url(${state.artistPicture})`
})), })),
showTable, sortedData,
sendAddToQueue, sendAddToQueue,
currentRelease checkNewRelease,
changeTab
} }
}, },
data() { data() {
@ -170,19 +171,8 @@ export default {
this.sortKey = key this.sortKey = key
this.sortOrder = 'asc' this.sortOrder = 'asc'
} }
},
changeTab(tab) {
this.currentTab = tab
},
checkNewRelease(date) {
let g1 = new Date()
let g2 = new Date(date)
g2.setDate(g2.getDate() + 3)
g1.setHours(0, 0, 0, 0)
return g1.getTime() <= g2.getTime()
} }
} }
} })
</script> </script>

View File

@ -7,7 +7,7 @@
}" }"
> >
<h1 class="flex items-center m-0 text-5xl"> <h1 class="flex items-center m-0 text-5xl">
{{ title }} <i v-if="explicit" class="material-icons explicit-icon explicit-icon--right">explicit</i> {{ title }} <i v-if="explicit" class="material-icons title-icon title-icon--right">explicit</i>
</h1> </h1>
<h2 class="m-0 mb-3 text-lg"> <h2 class="m-0 mb-3 text-lg">
@ -60,7 +60,7 @@
</td> </td>
<td class="table__cell--large table__cell--with-icon"> <td class="table__cell--large table__cell--with-icon">
<div class="table__cell-content table__cell-content--vertical-center"> <div class="table__cell-content table__cell-content--vertical-center">
<i v-if="track.explicit_lyrics" class="material-icons explicit-icon"> explicit </i> <i v-if="track.explicit_lyrics" class="material-icons title-icon"> explicit </i>
{{ {{
track.title + track.title +
(track.title_version && track.title.indexOf(track.title_version) == -1 (track.title_version && track.title.indexOf(track.title_version) == -1
@ -127,7 +127,7 @@
</td> </td>
<td>{{ i + 1 }}</td> <td>{{ i + 1 }}</td>
<td class="flex items-center"> <td class="flex items-center">
<i v-if="track.explicit" class="material-icons explicit-icon">explicit</i> <i v-if="track.explicit" class="material-icons title-icon">explicit</i>
{{ track.name }} {{ track.name }}
</td> </td>
<td>{{ track.artists[0].name }}</td> <td>{{ track.artists[0].name }}</td>

View File

@ -20,7 +20,7 @@
<span class="primary-text"> <span class="primary-text">
<i <i
v-if="release.isAlbumExplicit" v-if="release.isAlbumExplicit"
class="material-icons explicit-icon" class="material-icons title-icon"
style="font-size: 1.0625rem !important" style="font-size: 1.0625rem !important"
> >
explicit explicit

View File

@ -36,7 +36,7 @@
<td class="table__cell table__cell--large"> <td class="table__cell table__cell--large">
<div class="break-words table__cell-content table__cell-content--vertical-center"> <div class="break-words table__cell-content table__cell-content--vertical-center">
<i v-if="track.isTrackExplicit" class="material-icons explicit-icon">explicit</i> <i v-if="track.isTrackExplicit" class="material-icons title-icon">explicit</i>
{{ formatTitle(track) }} {{ formatTitle(track) }}
</div> </div>
</td> </td>

View File

@ -1,8 +1,8 @@
import { socket } from '@/utils/socket'
import { getPropertyWithFallback } from '@/utils/utils' import { getPropertyWithFallback } from '@/utils/utils'
export function formatArtistData(artistData) { export function formatArtistData(artistData) {
return { return {
artistID: getPropertyWithFallback(artistData, 'id'),
artistName: getPropertyWithFallback(artistData, 'name'), artistName: getPropertyWithFallback(artistData, 'name'),
artistPictureXL: getPropertyWithFallback(artistData, 'picture_xl'), artistPictureXL: getPropertyWithFallback(artistData, 'picture_xl'),
artistReleases: formatArtistReleases(getPropertyWithFallback(artistData, 'releases')) artistReleases: formatArtistReleases(getPropertyWithFallback(artistData, 'releases'))
@ -33,3 +33,27 @@ function formatArtistReleases(artistReleases) {
return formattedReleases return formattedReleases
} }
let artistData = {}
let cached = false
export function getArtistData(artistID) {
if (cached) {
return artistData
} else {
socket.emit('getTracklist', {
type: 'artist',
id: artistID
})
return new Promise((resolve, reject) => {
socket.on('show_artist', data => {
artistData = data
// cached = true
socket.off('show_artist')
resolve(data)
})
})
}
}

View File

@ -35,7 +35,10 @@ const routes = [
{ {
path: '/artist/:id', path: '/artist/:id',
name: 'Artist', name: 'Artist',
component: Artist component: Artist,
meta: {
notKeepAlive: true
}
}, },
{ {
path: '/album/:id', path: '/album/:id',
@ -123,12 +126,6 @@ router.beforeEach((to, from, next) => {
let getTracklistParams = null let getTracklistParams = null
switch (to.name) { switch (to.name) {
case 'Artist':
getTracklistParams = {
type: 'artist',
id: to.params.id
}
break
case 'Tracklist': case 'Tracklist':
getTracklistParams = { getTracklistParams = {
type: to.params.type, type: to.params.type,

View File

@ -26,15 +26,22 @@
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
} }
.material-icons.explicit-icon { .material-icons.title-icon {
margin-right: 0.3125em; margin-right: 0.3125em;
margin-left: -3px; margin-left: -3px;
}
.material-icons.title-icon.title-icon--right {
margin-right: 0px;
margin-left: 0.3125em;
}
.material-icons.title-icon.title-icon--explicit {
color: hsl(240, 5%, 59%); color: hsl(240, 5%, 59%);
} }
.material-icons.explicit-icon.explicit-icon--right { .material-icons.title-icon.title-icon--new {
margin-right: 0px; color: hsl(27, 100%, 50%);
margin-left: 0.3125em;
} }
.material-icons.disabled { .material-icons.disabled {

14
src/utils/dates.js Normal file
View File

@ -0,0 +1,14 @@
/**
* @param {Date} dateToCheck
* @returns {boolean} The passed date is less than 3 days distant from today,
* therefore it's considered a new release, if referring to a track or album
*/
export function checkNewRelease(dateToCheck) {
let now = new Date()
now.setHours(0, 0, 0, 0)
dateToCheck = new Date(dateToCheck)
dateToCheck.setDate(dateToCheck.getDate() + 3)
return now.getTime() <= dateToCheck.getTime()
}