mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-04 20:43:04 +00:00
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:
committed by
Mungai Njoroge
parent
62fb70d26c
commit
905fff04b4
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
@@ -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: {} };
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user