connect favorites data to favorites page

+ detach isSmall and isMedium classes from the v-scroll-page class
+ customize the TopTracks component to be usable with the favorite tracks page
+ add queue methods to play tracks from favorites page
+ handle playing from artist top tracks in parent component
This commit is contained in:
geoffrey45
2022-12-28 14:49:02 +03:00
committed by Mungai Njoroge
parent 62fb70d26c
commit 905fff04b4
16 changed files with 254 additions and 204 deletions
+10 -17
View File
@@ -3,17 +3,13 @@
<h3>
<span>{{ title }} </span>
<span
class="see-more"
class="see-all"
v-if="maxAbumCards <= albums.length"
@click="store.setPage(albumType)"
@click="
!favorites ? useArtistDiscographyStore().setPage(albumType) : null
"
>
<RouterLink
:to="{
name: Routes.artistDiscography,
params: { hash: artisthash },
}"
>SEE ALL</RouterLink
>
<RouterLink :to="route">SEE ALL</RouterLink>
</span>
</h3>
<div class="cards">
@@ -23,22 +19,19 @@
</template>
<script setup lang="ts">
import AlbumCard from "../shared/AlbumCard.vue";
import { Album } from "@/interfaces";
import { maxAbumCards } from "@/stores/content-width";
import { Routes } from "@/router/routes";
import { discographyAlbumTypes } from "@/composables/enums";
import useArtistDiscographyStore from "@/stores/pages/artistDiscog";
const store = useArtistDiscographyStore();
import AlbumCard from "../shared/AlbumCard.vue";
defineProps<{
title: string;
artisthash: string;
albums: Album[];
albumType: discographyAlbumTypes;
albumType?: discographyAlbumTypes;
favorites?: boolean;
route: string;
}>();
</script>
@@ -53,7 +46,7 @@ defineProps<{
padding: 0 $medium;
margin-bottom: $small;
.see-more {
.see-all {
font-size: $medium;
a:hover {
+17 -41
View File
@@ -1,59 +1,35 @@
<template>
<div class="artist-top-tracks">
<h3 class="section-title">
Tracks
<span class="see-more">
<RouterLink
:to="{
name: Routes.artistTracks,
params: {
hash: artist.info.artisthash,
},
query: {
artist: artist.info.name,
},
}"
>SEE ALL</RouterLink
>
{{ title }}
<span class="see-all">
<RouterLink :to="route">SEE ALL</RouterLink>
</span>
</h3>
<div class="tracks">
<div class="tracks" :class="{ isSmall, isMedium }">
<SongItem
v-for="(song, index) in artist.tracks"
v-for="(song, index) in tracks"
:track="song"
:index="index + 1"
@playThis="playFromPage(index)"
@playThis="playHandler(index)"
/>
</div>
<div class="error" v-if="!artist.tracks.length">No tracks</div>
<div class="error" v-if="!tracks.length">No tracks</div>
</div>
</template>
<script setup lang="ts">
import SongItem from "../shared/SongItem.vue";
import useArtistPageStore from "@/stores/pages/artist";
import useQueueStore from "@/stores/queue";
import { FromOptions, playSources } from "@/composables/enums";
import { Track } from "@/interfaces";
import { isMedium, isSmall } from "@/stores/content-width";
import { getArtistTracks } from "@/composables/fetch/artists";
import { Routes } from "@/router/routes";
const artist = useArtistPageStore();
const queue = useQueueStore();
async function playFromPage(index: number) {
if (
queue.from.type == FromOptions.artist &&
queue.from.artisthash == artist.info.artisthash
) {
queue.play(index);
return;
}
const tracks = await getArtistTracks(artist.info.artisthash);
queue.playFromArtist(artist.info.artisthash, artist.info.name, tracks);
queue.play(index);
}
defineProps<{
tracks: Track[];
route: string;
title: string;
playHandler: (index: number) => void;
}>();
</script>
<style lang="scss">
@@ -74,7 +50,7 @@ async function playFromPage(index: number) {
justify-content: space-between;
}
.see-more {
.see-all {
font-size: $medium;
a:hover {
@@ -0,0 +1,42 @@
<template>
<div class="f-artists">
<h3>{{ title }} <span class="see-all">SEE ALL</span></h3>
<div class="artist-list">
<ArtistCard
v-for="artist in artists.slice(0, maxAbumCards)"
:key="artist.image"
:artist="artist"
/>
</div>
</div>
</template>
<script setup lang="ts">
import ArtistCard from "@/components/shared/ArtistCard.vue";
import { Artist } from "@/interfaces";
import { maxAbumCards } from "@/stores/content-width";
defineProps<{
artists: Artist[];
title: string;
}>();
</script>
<style lang="scss">
.f-artists {
h3 {
display: flex;
justify-content: space-between;
padding-left: $medium;
.see-all {
font-size: $medium;
}
}
.artist-list {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
}
}
</style>
@@ -1,112 +0,0 @@
<template>
<div class="f-artists">
<div class="header">
<div class="headin">Featured Artists</div>
<div class="xcontrols">
<div class="prev icon" @click="scrollLeft()"><ArrowSvg /></div>
<div class="next icon" @click="scrollRight()"><ArrowSvg /></div>
</div>
</div>
<div class="separator no-border"></div>
<div class="artists" ref="artists_dom">
<ArtistCard
v-for="artist in artists"
:key="artist.image"
:artist="artist"
:color="'ffffff00'"
/>
</div>
</div>
</template>
<script setup lang="ts">
import ArtistCard from "@/components/shared/ArtistCard.vue";
import { Artist } from "@/interfaces";
import { ref } from "@vue/reactivity";
import ArrowSvg from "../../assets/icons/right-arrow.svg";
defineProps<{
artists: Artist[];
}>();
const artists_dom = ref(null);
const scrollLeft = () => {
const dom = artists_dom.value;
dom.scrollBy({
left: -700,
behavior: "smooth",
});
};
const scrollRight = () => {
const dom = artists_dom.value;
dom.scrollBy({
left: 700,
behavior: "smooth",
});
};
</script>
<style lang="scss">
.f-artists {
width: 100%;
padding: 0 $small;
border-radius: $small;
user-select: none;
position: relative;
.header {
display: flex;
align-items: center;
position: relative;
.headin {
font-size: 1.5rem;
font-weight: 900;
margin-left: $small;
}
}
}
.f-artists .xcontrols {
z-index: 1;
position: absolute;
top: 0;
right: 0;
display: flex;
gap: 1rem;
.prev {
transform: rotate(180deg);
}
.icon {
border-radius: $small;
transition: all 0.5s ease;
background-color: rgb(51, 51, 51);
padding: $smaller;
svg {
display: flex;
}
&:hover {
background-color: $accent;
transition: all 0.5s ease;
}
}
}
.f-artists > .artists {
display: flex;
align-items: flex-end;
flex-wrap: nowrap;
overflow-x: scroll;
gap: $small;
scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
}
</style>
+10
View File
@@ -27,6 +27,7 @@ import SearchSvg from "@/assets/icons/search.svg";
import ArtistSvg from "@/assets/icons/artist.svg";
import { RouteLocationRaw } from "vue-router";
import HeartSvg from "@/assets/icons/heart.fill.svg";
const queue = useQueueStore();
@@ -95,6 +96,15 @@ function getSource() {
},
};
case FromOptions.favorite:
return {
name: "Favorite tracks",
icon: HeartSvg,
location: {
name: Routes.favorites,
},
};
default:
return { name: "👻 No source", location: {} };
}
+2 -12
View File
@@ -7,16 +7,8 @@
},
}"
>
<div
class="artist-card"
:class="{ _is_on_sidebar: alt }"
:style="{ backgroundColor: `${artist.colors[0]}` }"
>
<img
class="artist-image circular"
:src="imguri + artist.image"
loading="lazy"
/>
<div class="artist-card">
<img class="artist-image circular" :src="imguri + artist.image" />
<div class="artist-name t-center">
{{ artist.name }}
</div>
@@ -33,7 +25,6 @@ const imguri = paths.images.artist.large;
defineProps<{
artist: Artist;
alt?: boolean;
}>();
</script>
@@ -58,7 +49,6 @@ defineProps<{
.artist-image {
width: 100%;
// margin-bottom: $small;
transition: all 0.5s ease-in-out;
object-fit: cover;
}