mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-05 04:53:01 +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
@@ -110,7 +110,7 @@ $g-border: solid 1px $gray5;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-scroll-page.isSmall {
|
.isSmall {
|
||||||
.songlist-item {
|
.songlist-item {
|
||||||
grid-template-columns: 1.75rem 2fr 2.5rem 2.5rem;
|
grid-template-columns: 1.75rem 2fr 2.5rem 2.5rem;
|
||||||
}
|
}
|
||||||
@@ -128,7 +128,7 @@ $g-border: solid 1px $gray5;
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.v-scroll-page.isMedium {
|
.isMedium {
|
||||||
// hide album column
|
// hide album column
|
||||||
.songlist-item {
|
.songlist-item {
|
||||||
grid-template-columns: 1.75rem 1.5fr 1fr 2.5rem 2.5rem;
|
grid-template-columns: 1.75rem 1.5fr 1fr 2.5rem 2.5rem;
|
||||||
|
|||||||
@@ -3,17 +3,13 @@
|
|||||||
<h3>
|
<h3>
|
||||||
<span>{{ title }} </span>
|
<span>{{ title }} </span>
|
||||||
<span
|
<span
|
||||||
class="see-more"
|
class="see-all"
|
||||||
v-if="maxAbumCards <= albums.length"
|
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>
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
<div class="cards">
|
<div class="cards">
|
||||||
@@ -23,22 +19,19 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import AlbumCard from "../shared/AlbumCard.vue";
|
|
||||||
import { Album } from "@/interfaces";
|
import { Album } from "@/interfaces";
|
||||||
|
|
||||||
import { maxAbumCards } from "@/stores/content-width";
|
import { maxAbumCards } from "@/stores/content-width";
|
||||||
import { Routes } from "@/router/routes";
|
|
||||||
|
|
||||||
import { discographyAlbumTypes } from "@/composables/enums";
|
import { discographyAlbumTypes } from "@/composables/enums";
|
||||||
import useArtistDiscographyStore from "@/stores/pages/artistDiscog";
|
import useArtistDiscographyStore from "@/stores/pages/artistDiscog";
|
||||||
|
|
||||||
const store = useArtistDiscographyStore();
|
import AlbumCard from "../shared/AlbumCard.vue";
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
title: string;
|
title: string;
|
||||||
artisthash: string;
|
|
||||||
albums: Album[];
|
albums: Album[];
|
||||||
albumType: discographyAlbumTypes;
|
albumType?: discographyAlbumTypes;
|
||||||
|
favorites?: boolean;
|
||||||
|
route: string;
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -53,7 +46,7 @@ defineProps<{
|
|||||||
padding: 0 $medium;
|
padding: 0 $medium;
|
||||||
margin-bottom: $small;
|
margin-bottom: $small;
|
||||||
|
|
||||||
.see-more {
|
.see-all {
|
||||||
font-size: $medium;
|
font-size: $medium;
|
||||||
|
|
||||||
a:hover {
|
a:hover {
|
||||||
|
|||||||
@@ -1,59 +1,35 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="artist-top-tracks">
|
<div class="artist-top-tracks">
|
||||||
<h3 class="section-title">
|
<h3 class="section-title">
|
||||||
Tracks
|
{{ title }}
|
||||||
<span class="see-more">
|
<span class="see-all">
|
||||||
<RouterLink
|
|
||||||
:to="{
|
<RouterLink :to="route">SEE ALL</RouterLink>
|
||||||
name: Routes.artistTracks,
|
|
||||||
params: {
|
|
||||||
hash: artist.info.artisthash,
|
|
||||||
},
|
|
||||||
query: {
|
|
||||||
artist: artist.info.name,
|
|
||||||
},
|
|
||||||
}"
|
|
||||||
>SEE ALL</RouterLink
|
|
||||||
>
|
|
||||||
</span>
|
</span>
|
||||||
</h3>
|
</h3>
|
||||||
<div class="tracks">
|
<div class="tracks" :class="{ isSmall, isMedium }">
|
||||||
<SongItem
|
<SongItem
|
||||||
v-for="(song, index) in artist.tracks"
|
v-for="(song, index) in tracks"
|
||||||
:track="song"
|
:track="song"
|
||||||
:index="index + 1"
|
:index="index + 1"
|
||||||
@playThis="playFromPage(index)"
|
@playThis="playHandler(index)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="error" v-if="!artist.tracks.length">No tracks</div>
|
<div class="error" v-if="!tracks.length">No tracks</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import SongItem from "../shared/SongItem.vue";
|
import SongItem from "../shared/SongItem.vue";
|
||||||
import useArtistPageStore from "@/stores/pages/artist";
|
import { Track } from "@/interfaces";
|
||||||
import useQueueStore from "@/stores/queue";
|
import { isMedium, isSmall } from "@/stores/content-width";
|
||||||
import { FromOptions, playSources } from "@/composables/enums";
|
|
||||||
|
|
||||||
import { getArtistTracks } from "@/composables/fetch/artists";
|
defineProps<{
|
||||||
import { Routes } from "@/router/routes";
|
tracks: Track[];
|
||||||
|
route: string;
|
||||||
const artist = useArtistPageStore();
|
title: string;
|
||||||
const queue = useQueueStore();
|
playHandler: (index: number) => void;
|
||||||
|
}>();
|
||||||
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);
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -74,7 +50,7 @@ async function playFromPage(index: number) {
|
|||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
|
||||||
.see-more {
|
.see-all {
|
||||||
font-size: $medium;
|
font-size: $medium;
|
||||||
|
|
||||||
a:hover {
|
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 ArtistSvg from "@/assets/icons/artist.svg";
|
||||||
|
|
||||||
import { RouteLocationRaw } from "vue-router";
|
import { RouteLocationRaw } from "vue-router";
|
||||||
|
import HeartSvg from "@/assets/icons/heart.fill.svg";
|
||||||
|
|
||||||
const queue = useQueueStore();
|
const queue = useQueueStore();
|
||||||
|
|
||||||
@@ -95,6 +96,15 @@ function getSource() {
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
case FromOptions.favorite:
|
||||||
|
return {
|
||||||
|
name: "Favorite tracks",
|
||||||
|
icon: HeartSvg,
|
||||||
|
location: {
|
||||||
|
name: Routes.favorites,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return { name: "👻 No source", location: {} };
|
return { name: "👻 No source", location: {} };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,16 +7,8 @@
|
|||||||
},
|
},
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<div
|
<div class="artist-card">
|
||||||
class="artist-card"
|
<img class="artist-image circular" :src="imguri + artist.image" />
|
||||||
:class="{ _is_on_sidebar: alt }"
|
|
||||||
:style="{ backgroundColor: `${artist.colors[0]}` }"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
class="artist-image circular"
|
|
||||||
:src="imguri + artist.image"
|
|
||||||
loading="lazy"
|
|
||||||
/>
|
|
||||||
<div class="artist-name t-center">
|
<div class="artist-name t-center">
|
||||||
{{ artist.name }}
|
{{ artist.name }}
|
||||||
</div>
|
</div>
|
||||||
@@ -33,7 +25,6 @@ const imguri = paths.images.artist.large;
|
|||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
artist: Artist;
|
artist: Artist;
|
||||||
alt?: boolean;
|
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -58,7 +49,6 @@ defineProps<{
|
|||||||
|
|
||||||
.artist-image {
|
.artist-image {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
// margin-bottom: $small;
|
|
||||||
transition: all 0.5s ease-in-out;
|
transition: all 0.5s ease-in-out;
|
||||||
object-fit: cover;
|
object-fit: cover;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ export enum playSources {
|
|||||||
search,
|
search,
|
||||||
folder,
|
folder,
|
||||||
artist,
|
artist,
|
||||||
albumCard,
|
favorite,
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum NotifType {
|
export enum NotifType {
|
||||||
@@ -22,6 +22,7 @@ export enum FromOptions {
|
|||||||
search = "search",
|
search = "search",
|
||||||
artist = "artist",
|
artist = "artist",
|
||||||
albumCard = "albumCard",
|
albumCard = "albumCard",
|
||||||
|
favorite = "favorite",
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ContextSrc {
|
export enum ContextSrc {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const {
|
|||||||
albumartists: albumArtistsUrl,
|
albumartists: albumArtistsUrl,
|
||||||
albumbio: albumBioUrl,
|
albumbio: albumBioUrl,
|
||||||
albumsByArtistUrl,
|
albumsByArtistUrl,
|
||||||
|
favAlbums,
|
||||||
} = paths.api;
|
} = paths.api;
|
||||||
|
|
||||||
const getAlbumData = async (hash: string, ToastStore: typeof useNotifStore) => {
|
const getAlbumData = async (hash: string, ToastStore: typeof useNotifStore) => {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { paths } from "@/config";
|
|||||||
import { favType, NotifType } from "@/composables/enums";
|
import { favType, NotifType } from "@/composables/enums";
|
||||||
|
|
||||||
import { useNotifStore as notif } from "@/stores/notification";
|
import { useNotifStore as notif } from "@/stores/notification";
|
||||||
|
import { Album, Artist, Track } from "@/interfaces";
|
||||||
|
|
||||||
export async function addFavorite(favtype: favType, itemhash: string) {
|
export async function addFavorite(favtype: favType, itemhash: string) {
|
||||||
const { data, error } = await useAxios({
|
const { data, error } = await useAxios({
|
||||||
@@ -45,3 +46,30 @@ export async function removeFavorite(favtype: favType, itemhash: string) {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getFavAlbums(limit = 6) {
|
||||||
|
const { data } = await useAxios({
|
||||||
|
url: paths.api.favAlbums + `?limit=${limit}`,
|
||||||
|
get: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return data.albums as Album[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getFavTracks(limit = 5) {
|
||||||
|
const { data } = await useAxios({
|
||||||
|
url: paths.api.favTracks + `?limit=${limit}`,
|
||||||
|
get: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return data.tracks as Track[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getFavArtists(limit = 6) {
|
||||||
|
const { data } = await useAxios({
|
||||||
|
url: paths.api.favArtists + `?limit=${limit}`,
|
||||||
|
get: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
return data.artists as Artist[];
|
||||||
|
}
|
||||||
|
|||||||
@@ -35,6 +35,9 @@ const baseImgUrl = baseApiUrl + "/img";
|
|||||||
const paths = {
|
const paths = {
|
||||||
api: {
|
api: {
|
||||||
album: baseApiUrl + "/album",
|
album: baseApiUrl + "/album",
|
||||||
|
favAlbums: baseApiUrl + "/albums/favorite",
|
||||||
|
favTracks: baseApiUrl + "/tracks/favorite",
|
||||||
|
favArtists: baseApiUrl + "/artists/favorite",
|
||||||
artist: baseApiUrl + "/artist",
|
artist: baseApiUrl + "/artist",
|
||||||
favorite: baseApiUrl + "/favorite",
|
favorite: baseApiUrl + "/favorite",
|
||||||
get removeFavorite() {
|
get removeFavorite() {
|
||||||
|
|||||||
@@ -121,6 +121,10 @@ export interface fromArtist {
|
|||||||
artistname: string;
|
artistname: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface fromFav {
|
||||||
|
type: FromOptions.favorite;
|
||||||
|
}
|
||||||
|
|
||||||
export interface subPath {
|
export interface subPath {
|
||||||
name: string;
|
name: string;
|
||||||
path: string;
|
path: string;
|
||||||
|
|||||||
@@ -39,7 +39,8 @@ export default defineStore("artistDiscography", {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setPage(page: discographyAlbumTypes) {
|
setPage(page: discographyAlbumTypes | undefined) {
|
||||||
|
// @ts-ignore
|
||||||
this.page = page;
|
this.page = page;
|
||||||
},
|
},
|
||||||
fetchAlbums(artisthash: string) {
|
fetchAlbums(artisthash: string) {
|
||||||
|
|||||||
+15
-1
@@ -9,6 +9,7 @@ import updateMediaNotif from "../composables/mediaNotification";
|
|||||||
import {
|
import {
|
||||||
fromAlbum,
|
fromAlbum,
|
||||||
fromArtist,
|
fromArtist,
|
||||||
|
fromFav,
|
||||||
fromFolder,
|
fromFolder,
|
||||||
fromPlaylist,
|
fromPlaylist,
|
||||||
fromSearch,
|
fromSearch,
|
||||||
@@ -24,7 +25,13 @@ function shuffle(tracks: Track[]) {
|
|||||||
return shuffled;
|
return shuffled;
|
||||||
}
|
}
|
||||||
|
|
||||||
type From = fromFolder | fromAlbum | fromPlaylist | fromSearch | fromArtist;
|
type From =
|
||||||
|
| fromFolder
|
||||||
|
| fromAlbum
|
||||||
|
| fromPlaylist
|
||||||
|
| fromSearch
|
||||||
|
| fromArtist
|
||||||
|
| fromFav;
|
||||||
|
|
||||||
let audio = new Audio();
|
let audio = new Audio();
|
||||||
audio.autoplay = false;
|
audio.autoplay = false;
|
||||||
@@ -189,6 +196,13 @@ export default defineStore("Queue", {
|
|||||||
|
|
||||||
this.setNewQueue(tracks);
|
this.setNewQueue(tracks);
|
||||||
},
|
},
|
||||||
|
playFromFav(tracks: Track[]) {
|
||||||
|
this.from = <fromFav>{
|
||||||
|
type: FromOptions.favorite,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.setNewQueue(tracks);
|
||||||
|
},
|
||||||
addTrackToQueue(track: Track) {
|
addTrackToQueue(track: Track) {
|
||||||
this.tracklist.push(track);
|
this.tracklist.push(track);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div class="artist-page v-scroll-page" style="height: 100%">
|
||||||
class="artist-page v-scroll-page"
|
|
||||||
style="height: 100%"
|
|
||||||
:class="{ isSmall, isMedium }"
|
|
||||||
>
|
|
||||||
<DynamicScroller
|
<DynamicScroller
|
||||||
:items="scrollerItems"
|
:items="scrollerItems"
|
||||||
:min-item-size="64"
|
:min-item-size="64"
|
||||||
@@ -29,8 +25,6 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { isMedium, isSmall } from "@/stores/content-width";
|
|
||||||
|
|
||||||
import Header from "@/components/ArtistView/Header.vue";
|
import Header from "@/components/ArtistView/Header.vue";
|
||||||
import TopTracks from "@/components/ArtistView/TopTracks.vue";
|
import TopTracks from "@/components/ArtistView/TopTracks.vue";
|
||||||
import useArtistPageStore from "@/stores/pages/artist";
|
import useArtistPageStore from "@/stores/pages/artist";
|
||||||
@@ -39,9 +33,12 @@ import ArtistAlbumsFetcher from "@/components/ArtistView/ArtistAlbumsFetcher.vue
|
|||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
import { onBeforeRouteLeave, onBeforeRouteUpdate, useRoute } from "vue-router";
|
import { onBeforeRouteLeave, onBeforeRouteUpdate, useRoute } from "vue-router";
|
||||||
import { Album } from "@/interfaces";
|
import { Album } from "@/interfaces";
|
||||||
import { discographyAlbumTypes } from "@/composables/enums";
|
import { discographyAlbumTypes, FromOptions } from "@/composables/enums";
|
||||||
|
import useQueueStore from "@/stores/queue";
|
||||||
|
import { getArtistTracks } from "@/composables/fetch/artists";
|
||||||
|
|
||||||
const store = useArtistPageStore();
|
const store = useArtistPageStore();
|
||||||
|
const queue = useQueueStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
interface ScrollerItem {
|
interface ScrollerItem {
|
||||||
@@ -55,11 +52,6 @@ const header: ScrollerItem = {
|
|||||||
component: Header,
|
component: Header,
|
||||||
};
|
};
|
||||||
|
|
||||||
const top_tracks: ScrollerItem = {
|
|
||||||
id: "artist-top-tracks",
|
|
||||||
component: TopTracks,
|
|
||||||
};
|
|
||||||
|
|
||||||
const artist_albums_fetcher: ScrollerItem = {
|
const artist_albums_fetcher: ScrollerItem = {
|
||||||
id: "artist-albums-fetcher",
|
id: "artist-albums-fetcher",
|
||||||
component: ArtistAlbumsFetcher,
|
component: ArtistAlbumsFetcher,
|
||||||
@@ -100,6 +92,20 @@ function createAbumComponent(title: AlbumType, albums: Album[]) {
|
|||||||
albums,
|
albums,
|
||||||
title,
|
title,
|
||||||
artisthash: route.params.hash,
|
artisthash: route.params.hash,
|
||||||
|
route: `/artists/${store.info.artisthash}/discography?artist=${store.info.name}`,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTopTracksComponent(): ScrollerItem {
|
||||||
|
return {
|
||||||
|
id: "artist-top-tracks",
|
||||||
|
component: TopTracks,
|
||||||
|
props: {
|
||||||
|
tracks: store.tracks,
|
||||||
|
title: "Tracks",
|
||||||
|
route: `/artists/${store.info.artisthash}/tracks?artist=${store.info.name}`,
|
||||||
|
playHandler: handlePlay,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -108,7 +114,7 @@ const scrollerItems = computed(() => {
|
|||||||
let components = [header];
|
let components = [header];
|
||||||
|
|
||||||
if (store.tracks.length > 0) {
|
if (store.tracks.length > 0) {
|
||||||
components.push(top_tracks);
|
components.push(getTopTracksComponent());
|
||||||
}
|
}
|
||||||
|
|
||||||
components = [...components, artist_albums_fetcher];
|
components = [...components, artist_albums_fetcher];
|
||||||
@@ -139,6 +145,20 @@ const scrollerItems = computed(() => {
|
|||||||
return components;
|
return components;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function handlePlay(index: number) {
|
||||||
|
if (
|
||||||
|
queue.from.type == FromOptions.artist &&
|
||||||
|
queue.from.artisthash == store.info.artisthash
|
||||||
|
) {
|
||||||
|
queue.play(index);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tracks = await getArtistTracks(store.info.artisthash);
|
||||||
|
queue.playFromArtist(store.info.artisthash, store.info.name, tracks);
|
||||||
|
queue.play(index);
|
||||||
|
}
|
||||||
|
|
||||||
onBeforeRouteUpdate(async (to) => {
|
onBeforeRouteUpdate(async (to) => {
|
||||||
await store.getData(to.params.hash as string);
|
await store.getData(to.params.hash as string);
|
||||||
});
|
});
|
||||||
|
|||||||
+82
-3
@@ -6,13 +6,66 @@
|
|||||||
<div class="artists">Artists</div>
|
<div class="artists">Artists</div>
|
||||||
<div class="folders">Folders</div>
|
<div class="folders">Folders</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="fav-tracks">
|
||||||
|
<TopTracks
|
||||||
|
:tracks="favTracks"
|
||||||
|
:route="'/home'"
|
||||||
|
:title="'Favorite tracks'"
|
||||||
|
:playHandler="handlePlay"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fav-albums">
|
||||||
|
<ArtistAlbums
|
||||||
|
:albums="favAlbums"
|
||||||
|
:albumType="discographyAlbumTypes.albums"
|
||||||
|
:title="'Favorite albums'"
|
||||||
|
:route="'some'"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="fav-artists">
|
||||||
|
<FeaturedArtists :artists="favArtists" :title="'Favorite artists'" />
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import AlbumSvg from "@/assets/icons/album.svg";
|
import { onMounted, Ref, ref } from "vue";
|
||||||
import ArtistSvg from "@/assets/icons/artist.svg";
|
|
||||||
import TrackSvg from "@/assets/icons/artist.svg";
|
import ArtistAlbums from "@/components/AlbumView/ArtistAlbums.vue";
|
||||||
|
import TopTracks from "@/components/ArtistView/TopTracks.vue";
|
||||||
|
import FeaturedArtists from "@/components/PlaylistView/ArtistsList.vue";
|
||||||
|
import { discographyAlbumTypes } from "@/composables/enums";
|
||||||
|
import {
|
||||||
|
getFavAlbums,
|
||||||
|
getFavArtists,
|
||||||
|
getFavTracks,
|
||||||
|
} from "@/composables/fetch/favorite";
|
||||||
|
import { Album, Artist, Track } from "@/interfaces";
|
||||||
|
import useQueueStore from "@/stores/queue";
|
||||||
|
import { maxAbumCards } from "@/stores/content-width";
|
||||||
|
|
||||||
|
const queue = useQueueStore();
|
||||||
|
|
||||||
|
const favAlbums: Ref<Album[]> = ref([]);
|
||||||
|
const favTracks: Ref<Track[]> = ref([]);
|
||||||
|
const favArtists: Ref<Artist[]> = ref([]);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getFavTracks().then((tracks) => (favTracks.value = tracks));
|
||||||
|
getFavAlbums(maxAbumCards.value).then((albums) => (favAlbums.value = albums));
|
||||||
|
getFavArtists(maxAbumCards.value).then(
|
||||||
|
(artists) => (favArtists.value = artists)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function handlePlay(index: number) {
|
||||||
|
console.log(index);
|
||||||
|
|
||||||
|
const tracks = await getFavTracks(0);
|
||||||
|
queue.playFromFav(tracks);
|
||||||
|
queue.play(index);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
@@ -23,6 +76,7 @@ $artistsbg: rgb(0, 255, 21);
|
|||||||
.favorites-page {
|
.favorites-page {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: scroll;
|
overflow: scroll;
|
||||||
|
padding-bottom: 4rem;
|
||||||
|
|
||||||
.header > * {
|
.header > * {
|
||||||
padding: 1rem;
|
padding: 1rem;
|
||||||
@@ -54,5 +108,30 @@ $artistsbg: rgb(0, 255, 21);
|
|||||||
background-color: $gray2;
|
background-color: $gray2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.fav-tracks {
|
||||||
|
h3 {
|
||||||
|
padding-left: 2rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
|
||||||
|
.see-all {
|
||||||
|
font-size: $medium;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
margin: 1rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.fav-albums {
|
||||||
|
// margin-top: 3rem;
|
||||||
|
.album-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fav-artists {
|
||||||
|
margin-top: 3rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user