mirror of
https://github.com/tcsenpai/swingmusic.git
synced 2025-07-29 06:02:06 +00:00
build artist page
+ connect artist page to backend ~ bugs introduced as there are hashing changes in the backend [will fix later]
This commit is contained in:
parent
fff2c53801
commit
075765088f
@ -15,7 +15,7 @@ $larger: 2rem;
|
|||||||
|
|
||||||
$banner-height: 18rem;
|
$banner-height: 18rem;
|
||||||
$song-item-height: 4rem;
|
$song-item-height: 4rem;
|
||||||
$content-padding-bottom: 4rem;
|
$content-padding-bottom: 2rem;
|
||||||
|
|
||||||
// apple human design guideline colors
|
// apple human design guideline colors
|
||||||
$black: #181a1c;
|
$black: #181a1c;
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="albums-from-artist">
|
<div class="albums-from-artist">
|
||||||
<h3>
|
<h3>
|
||||||
<span>More from {{ artist.artist }} </span>
|
<span>{{ title }} </span>
|
||||||
<span class="see-more">SEE ALL</span>
|
<span class="see-more">SEE ALL</span>
|
||||||
</h3>
|
</h3>
|
||||||
<div class="cards">
|
<div class="cards">
|
||||||
<AlbumCard v-for="a in artist.albums" :album="a" />
|
<AlbumCard v-for="a in albums" :album="a" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@ -13,26 +13,25 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import AlbumCard from "../shared/AlbumCard.vue";
|
import AlbumCard from "../shared/AlbumCard.vue";
|
||||||
|
|
||||||
import { AlbumInfo } from "@/interfaces";
|
import { Album } from "@/interfaces";
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
artist: {
|
title: string;
|
||||||
artist: string;
|
albums: Album[];
|
||||||
albums: AlbumInfo[];
|
|
||||||
};
|
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.albums-from-artist {
|
.albums-from-artist {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding-top: 2rem;
|
padding-top: 1rem;
|
||||||
|
|
||||||
h3 {
|
h3 {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 1fr max-content;
|
grid-template-columns: 1fr max-content;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 $medium;
|
padding: 0 $medium;
|
||||||
|
margin-bottom: $small;
|
||||||
|
|
||||||
.see-more {
|
.see-more {
|
||||||
font-size: $medium;
|
font-size: $medium;
|
||||||
|
@ -53,14 +53,21 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="art" v-if="!albumHeaderSmall">
|
<div class="art" v-if="!albumHeaderSmall">
|
||||||
<img
|
<RouterLink
|
||||||
v-for="a in album.albumartists"
|
v-for="a in album.albumartists"
|
||||||
:src="imguri.artist + a.image"
|
:to="{
|
||||||
class="shadow-lg circular"
|
name: Routes.artist,
|
||||||
loading="lazy"
|
params: { hash: a.hash },
|
||||||
:title="a.name"
|
}"
|
||||||
:style="{ border: `solid 2px ${album.colors[0]}` }"
|
>
|
||||||
/>
|
<img
|
||||||
|
:src="imguri.artist + a.image"
|
||||||
|
class="shadow-lg circular"
|
||||||
|
loading="lazy"
|
||||||
|
:title="a.name"
|
||||||
|
:style="{ border: `solid 2px ${album.colors[0]}` }"
|
||||||
|
/>
|
||||||
|
</RouterLink>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -69,20 +76,20 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
import ArtistName from "@/components/shared/ArtistName.vue";
|
||||||
import { paths } from "@/config";
|
import { paths } from "@/config";
|
||||||
import { albumHeaderSmall } from "@/stores/content-width";
|
import { albumHeaderSmall } from "@/stores/content-width";
|
||||||
import useNavStore from "@/stores/nav";
|
import useNavStore from "@/stores/nav";
|
||||||
import useAlbumStore from "@/stores/pages/album";
|
import useAlbumStore from "@/stores/pages/album";
|
||||||
import { formatSeconds, useVisibility } from "@/utils";
|
import { formatSeconds, useVisibility } from "@/utils";
|
||||||
import { isLight } from "../../composables/colors/album";
|
import { isLight } from "../../composables/colors/album";
|
||||||
import { playSources } from "../../composables/enums";
|
import { playSources, Routes } from "../../composables/enums";
|
||||||
import { AlbumInfo } from "../../interfaces";
|
import { Album } from "../../interfaces";
|
||||||
import ArtistName from "@/components/shared/ArtistName.vue";
|
|
||||||
|
|
||||||
import PlayBtnRect from "../shared/PlayBtnRect.vue";
|
import PlayBtnRect from "../shared/PlayBtnRect.vue";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
album: AlbumInfo;
|
album: Album;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const albumheaderthing = ref<any>(null);
|
const albumheaderthing = ref<any>(null);
|
||||||
|
53
src/components/ArtistView/Albums.vue
Normal file
53
src/components/ArtistView/Albums.vue
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<template>
|
||||||
|
<div class="albums-list">
|
||||||
|
<div class="section-title">
|
||||||
|
<b>
|
||||||
|
{{ title }}
|
||||||
|
</b>
|
||||||
|
<div class="see-all"><b>SEE ALL</b></div>
|
||||||
|
</div>
|
||||||
|
<div class="cars">
|
||||||
|
<AlbumCard
|
||||||
|
v-for="album in search.albums.value.slice(0, 6)"
|
||||||
|
:album="album"
|
||||||
|
:key="Math.random()"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import AlbumCard from "../shared/AlbumCard.vue";
|
||||||
|
import useSearchStore from "@/stores/search";
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
// albums: Album[];
|
||||||
|
title: string;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const search = useSearchStore();
|
||||||
|
|
||||||
|
// TODO: use AlbumView's ArtistAlbums component instead of this
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.albums-list {
|
||||||
|
.section-title {
|
||||||
|
margin: 0;
|
||||||
|
margin-bottom: -$small;
|
||||||
|
|
||||||
|
.see-all {
|
||||||
|
float: right;
|
||||||
|
font-size: small;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cars {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(10rem, 1fr));
|
||||||
|
// padding: 1rem 0;
|
||||||
|
// gap: 2rem 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,9 +1,119 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="artist-page-header">
|
<div class="artist-page-header rounded no-scroll">
|
||||||
This is the header
|
<div
|
||||||
|
class="artist-info"
|
||||||
|
:class="{
|
||||||
|
nocontrast: artist.info.colors ? isLight(artist.info.colors[0]) : false,
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<section class="text">
|
||||||
|
<div class="card-title">ARTIST</div>
|
||||||
|
<div class="artist-name">{{ artist.info.name }}</div>
|
||||||
|
<div class="stats">
|
||||||
|
{{ artist.info.trackcount }} Tracks •
|
||||||
|
{{ artist.info.albumcount }} Albums •
|
||||||
|
{{ formatSeconds(artist.info.duration, true) }}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<PlayBtnRect />
|
||||||
|
</div>
|
||||||
|
<div class="artist-img">
|
||||||
|
<img :src="paths.images.artist + artist.info.image" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="gradient"
|
||||||
|
:style="{
|
||||||
|
backgroundImage: `linear-gradient(to left, transparent 10%,
|
||||||
|
${artist.info.colors[0]} 50%,
|
||||||
|
${artist.info.colors[0]} 100%)`,
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts"></script>
|
<script setup lang="ts">
|
||||||
|
import useArtistPageStore from "@/stores/pages/artist";
|
||||||
|
import PlayBtnRect from "../shared/PlayBtnRect.vue";
|
||||||
|
import formatSeconds from "@/utils/useFormatSeconds";
|
||||||
|
import { isLight } from "@/composables/colors/album";
|
||||||
|
import { paths } from "@/config";
|
||||||
|
|
||||||
<style lang="scss"></style>
|
const artist = useArtistPageStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.artist-page-header {
|
||||||
|
height: 18rem;
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.artist-img {
|
||||||
|
height: 18rem;
|
||||||
|
img {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
object-position: 0% 20%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.gradient {
|
||||||
|
position: absolute;
|
||||||
|
background-image: linear-gradient(
|
||||||
|
to left,
|
||||||
|
transparent 10%,
|
||||||
|
#434142 50%,
|
||||||
|
#434142 100%
|
||||||
|
);
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.artist-info {
|
||||||
|
z-index: 1;
|
||||||
|
padding: 1rem;
|
||||||
|
padding-right: 0;
|
||||||
|
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-end;
|
||||||
|
|
||||||
|
gap: 1rem;
|
||||||
|
|
||||||
|
.text {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: $small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
opacity: 0.5;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.artist-name {
|
||||||
|
font-size: 3rem;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stats {
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
|
||||||
|
.playbtnrect {
|
||||||
|
border-radius: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.artist-info.nocontrast {
|
||||||
|
color: $black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
33
src/components/ArtistView/TopTracks.vue
Normal file
33
src/components/ArtistView/TopTracks.vue
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<template>
|
||||||
|
<div class="artist-top-tracks">
|
||||||
|
<h3 class="section-title">Tracks</h3>
|
||||||
|
<div class="tracks">
|
||||||
|
<SongItem
|
||||||
|
v-for="(song, index) in artist.tracks"
|
||||||
|
:track="song"
|
||||||
|
:index="index + 1"
|
||||||
|
:isCurrent="false"
|
||||||
|
:isCurrentPlaying="false"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import useQueueStore from "@/stores/queue";
|
||||||
|
import SongItem from "../shared/SongItem.vue";
|
||||||
|
import useArtistPageStore from "@/stores/pages/artist";
|
||||||
|
|
||||||
|
const queue = useQueueStore();
|
||||||
|
const artist = useArtistPageStore();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
.artist-top-tracks {
|
||||||
|
// padding-bottom: 2rem;
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
@ -16,11 +16,11 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { paths } from "../../config";
|
import { paths } from "../../config";
|
||||||
import { AlbumInfo } from "../../interfaces";
|
import { Album } from "../../interfaces";
|
||||||
|
|
||||||
const imguri = paths.images.thumb.large;
|
const imguri = paths.images.thumb.large;
|
||||||
defineProps<{
|
defineProps<{
|
||||||
album: AlbumInfo;
|
album: Album;
|
||||||
}>();
|
}>();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -31,6 +31,7 @@ export enum Routes {
|
|||||||
playlists = "PlaylistList",
|
playlists = "PlaylistList",
|
||||||
playlist = "PlaylistView",
|
playlist = "PlaylistView",
|
||||||
albums = "AlbumsView",
|
albums = "AlbumsView",
|
||||||
|
artist = "ArtistView",
|
||||||
album = "AlbumView",
|
album = "AlbumView",
|
||||||
artists = "ArtistsView",
|
artists = "ArtistsView",
|
||||||
settings = "SettingsView",
|
settings = "SettingsView",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { paths } from "@/config";
|
import { paths } from "@/config";
|
||||||
import { NotifType, useNotifStore } from "@/stores/notification";
|
import { NotifType, useNotifStore } from "@/stores/notification";
|
||||||
import { AlbumInfo, Track } from "../../interfaces";
|
import { Album, Track } from "../../interfaces";
|
||||||
import useAxios from "./useAxios";
|
import useAxios from "./useAxios";
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -12,7 +12,7 @@ const {
|
|||||||
|
|
||||||
const getAlbumData = async (hash: string, ToastStore: typeof useNotifStore) => {
|
const getAlbumData = async (hash: string, ToastStore: typeof useNotifStore) => {
|
||||||
interface AlbumData {
|
interface AlbumData {
|
||||||
info: AlbumInfo;
|
info: Album;
|
||||||
tracks: Track[];
|
tracks: Track[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,7 +26,7 @@ const getAlbumData = async (hash: string, ToastStore: typeof useNotifStore) => {
|
|||||||
if (status == 204) {
|
if (status == 204) {
|
||||||
ToastStore().showNotification("Album not created yet!", NotifType.Error);
|
ToastStore().showNotification("Album not created yet!", NotifType.Error);
|
||||||
return {
|
return {
|
||||||
info: {} as AlbumInfo,
|
info: {} as Album,
|
||||||
tracks: [],
|
tracks: [],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -88,9 +88,9 @@ const getAlbumsFromArtist = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export {
|
export {
|
||||||
getAlbumData as getAlbumTracks,
|
getAlbumData as getAlbumTracks,
|
||||||
getAlbumArtists,
|
getAlbumArtists,
|
||||||
getAlbumBio,
|
getAlbumBio,
|
||||||
getAlbumsFromArtist,
|
getAlbumsFromArtist,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
26
src/composables/fetch/artists.ts
Normal file
26
src/composables/fetch/artists.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { paths } from "@/config";
|
||||||
|
import useAxios from "./useAxios";
|
||||||
|
import { Artist, Track, Album } from "@/interfaces";
|
||||||
|
|
||||||
|
const getArtistData = async (hash: string) => {
|
||||||
|
interface ArtistData {
|
||||||
|
artist: Artist;
|
||||||
|
albums: Album[];
|
||||||
|
tracks: Track[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data, error } = await useAxios({
|
||||||
|
get: true,
|
||||||
|
url: paths.api.artist + `/${hash}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(data);
|
||||||
|
|
||||||
|
return data as ArtistData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { getArtistData };
|
@ -38,14 +38,15 @@ const baseApiUrl = domain() + ports.api;
|
|||||||
const paths = {
|
const paths = {
|
||||||
api: {
|
api: {
|
||||||
album: baseApiUrl + "/album",
|
album: baseApiUrl + "/album",
|
||||||
|
artist: baseApiUrl + "/artist",
|
||||||
get albumartists() {
|
get albumartists() {
|
||||||
return this.album + "/artists";
|
return this.album + "/artists";
|
||||||
},
|
},
|
||||||
get albumbio() {
|
get albumbio() {
|
||||||
return this.album + "/bio";
|
return this.album + "/bio";
|
||||||
},
|
},
|
||||||
get albumsByArtistUrl(){
|
get albumsByArtistUrl() {
|
||||||
return this.album + "/from-artist"
|
return this.album + "/from-artist";
|
||||||
},
|
},
|
||||||
folder: baseApiUrl + "/folder",
|
folder: baseApiUrl + "/folder",
|
||||||
playlist: {
|
playlist: {
|
||||||
|
@ -34,7 +34,7 @@ export interface Folder {
|
|||||||
is_sym: boolean;
|
is_sym: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface AlbumInfo {
|
export interface Album {
|
||||||
albumid: string;
|
albumid: string;
|
||||||
title: string;
|
title: string;
|
||||||
albumartists: {
|
albumartists: {
|
||||||
@ -61,6 +61,11 @@ export interface AlbumInfo {
|
|||||||
export interface Artist {
|
export interface Artist {
|
||||||
name: string;
|
name: string;
|
||||||
image: string;
|
image: string;
|
||||||
|
artisthash: string;
|
||||||
|
trackcount: number;
|
||||||
|
albumcount: number;
|
||||||
|
duration: number;
|
||||||
|
colors: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Option {
|
export interface Option {
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
import state from "@/composables/state";
|
import state from "@/composables/state";
|
||||||
import useAStore from "@/stores/pages/album";
|
import useAlbumPageStore from "@/stores/pages/album";
|
||||||
import useFStore from "@/stores/pages/folder";
|
import useFolderPageStore from "@/stores/pages/folder";
|
||||||
import usePTrackStore from "@/stores/pages/playlist";
|
import usePlaylistPageStore from "@/stores/pages/playlist";
|
||||||
import usePStore from "@/stores/pages/playlists";
|
import usePlaylistListPageStore from "@/stores/pages/playlists";
|
||||||
|
import useArtistPageStore from "@/stores/pages/artist";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
@ -16,7 +17,7 @@ const routes = [
|
|||||||
component: () => import("@/views/FolderView.vue"),
|
component: () => import("@/views/FolderView.vue"),
|
||||||
beforeEnter: async (to: any) => {
|
beforeEnter: async (to: any) => {
|
||||||
state.loading.value = true;
|
state.loading.value = true;
|
||||||
await useFStore()
|
await useFolderPageStore()
|
||||||
.fetchAll(to.params.path)
|
.fetchAll(to.params.path)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
state.loading.value = false;
|
state.loading.value = false;
|
||||||
@ -29,7 +30,7 @@ const routes = [
|
|||||||
component: () => import("@/views/PlaylistList.vue"),
|
component: () => import("@/views/PlaylistList.vue"),
|
||||||
beforeEnter: async () => {
|
beforeEnter: async () => {
|
||||||
state.loading.value = true;
|
state.loading.value = true;
|
||||||
await usePStore()
|
await usePlaylistListPageStore()
|
||||||
.fetchAll()
|
.fetchAll()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
state.loading.value = false;
|
state.loading.value = false;
|
||||||
@ -42,7 +43,7 @@ const routes = [
|
|||||||
component: () => import("@/views/PlaylistView/index.vue"),
|
component: () => import("@/views/PlaylistView/index.vue"),
|
||||||
beforeEnter: async (to: any) => {
|
beforeEnter: async (to: any) => {
|
||||||
state.loading.value = true;
|
state.loading.value = true;
|
||||||
await usePTrackStore()
|
await usePlaylistPageStore()
|
||||||
.fetchAll(to.params.pid)
|
.fetchAll(to.params.pid)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
state.loading.value = false;
|
state.loading.value = false;
|
||||||
@ -60,7 +61,7 @@ const routes = [
|
|||||||
component: () => import("@/views/AlbumView/index.vue"),
|
component: () => import("@/views/AlbumView/index.vue"),
|
||||||
beforeEnter: async (to: any) => {
|
beforeEnter: async (to: any) => {
|
||||||
state.loading.value = true;
|
state.loading.value = true;
|
||||||
const store = useAStore();
|
const store = useAlbumPageStore();
|
||||||
|
|
||||||
await store.fetchTracksAndArtists(to.params.hash).then(() => {
|
await store.fetchTracksAndArtists(to.params.hash).then(() => {
|
||||||
state.loading.value = false;
|
state.loading.value = false;
|
||||||
@ -76,6 +77,15 @@ const routes = [
|
|||||||
path: "/artists/:hash",
|
path: "/artists/:hash",
|
||||||
name: "ArtistView",
|
name: "ArtistView",
|
||||||
component: () => import("@/views/ArtistView"),
|
component: () => import("@/views/ArtistView"),
|
||||||
|
beforeEnter: async (to: any) => {
|
||||||
|
state.loading.value = true;
|
||||||
|
|
||||||
|
await useArtistPageStore()
|
||||||
|
.getData(to.params.hash)
|
||||||
|
.then(() => {
|
||||||
|
state.loading.value = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/settings",
|
path: "/settings",
|
||||||
|
@ -7,10 +7,10 @@ import { FuseTrackOptions } from "@/composables/enums";
|
|||||||
import { content_width } from "@/stores/content-width";
|
import { content_width } from "@/stores/content-width";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getAlbumsFromArtist,
|
getAlbumsFromArtist,
|
||||||
getAlbumTracks,
|
getAlbumTracks
|
||||||
} from "../../composables/fetch/album";
|
} from "../../composables/fetch/album";
|
||||||
import { AlbumInfo, Artist, FuseResult, Track } from "../../interfaces";
|
import { Album, Artist, FuseResult, Track } from "../../interfaces";
|
||||||
import { useNotifStore } from "../notification";
|
import { useNotifStore } from "../notification";
|
||||||
|
|
||||||
interface Disc {
|
interface Disc {
|
||||||
@ -27,7 +27,7 @@ function sortByTrackNumber(tracks: Track[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function albumHasNoDiscs(album: AlbumInfo) {
|
function albumHasNoDiscs(album: Album) {
|
||||||
if (album.is_single) return true;
|
if (album.is_single) return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -50,10 +50,10 @@ function createDiscs(tracks: Track[]) {
|
|||||||
export default defineStore("album", {
|
export default defineStore("album", {
|
||||||
state: () => ({
|
state: () => ({
|
||||||
query: "",
|
query: "",
|
||||||
info: <AlbumInfo>{},
|
info: <Album>{},
|
||||||
rawTracks: <Track[]>[],
|
rawTracks: <Track[]>[],
|
||||||
artists: <Artist[]>[],
|
artists: <Artist[]>[],
|
||||||
albumArtists: <{ artist: string; albums: AlbumInfo[] }[]>[],
|
albumArtists: <{ artist: string; albums: Album[] }[]>[],
|
||||||
bio: null,
|
bio: null,
|
||||||
}),
|
}),
|
||||||
actions: {
|
actions: {
|
||||||
|
21
src/stores/pages/artist.ts
Normal file
21
src/stores/pages/artist.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { defineStore } from "pinia";
|
||||||
|
|
||||||
|
import { Artist, Album, Track } from "@/interfaces";
|
||||||
|
import { getArtistData } from "@/composables/fetch/artists";
|
||||||
|
|
||||||
|
export default defineStore("artistPage", {
|
||||||
|
state: () => ({
|
||||||
|
info: <Artist>{},
|
||||||
|
albums: <Album[]>[],
|
||||||
|
tracks: <Track[]>[],
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
async getData(hash: string) {
|
||||||
|
const { artist, albums, tracks } = await getArtistData(hash);
|
||||||
|
|
||||||
|
this.info = artist;
|
||||||
|
this.albums = albums;
|
||||||
|
this.tracks = tracks;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
@ -1,20 +1,17 @@
|
|||||||
import { Routes } from "./../composables/enums";
|
import { reactive, ref } from "@vue/reactivity";
|
||||||
import { ref, reactive } from "@vue/reactivity";
|
|
||||||
import { defineStore } from "pinia";
|
|
||||||
import { AlbumInfo, Artist, Playlist, Track } from "../interfaces";
|
|
||||||
import {
|
|
||||||
searchTracks,
|
|
||||||
searchAlbums,
|
|
||||||
searchArtists,
|
|
||||||
loadMoreTracks,
|
|
||||||
loadMoreAlbums,
|
|
||||||
loadMoreArtists,
|
|
||||||
} from "../composables/fetch/searchMusic";
|
|
||||||
import { watch } from "vue";
|
|
||||||
import useTabStore from "./tabs";
|
|
||||||
import useLoaderStore from "./loader";
|
|
||||||
import { useRoute } from "vue-router";
|
|
||||||
import { useDebounce } from "@vueuse/core";
|
import { useDebounce } from "@vueuse/core";
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { watch } from "vue";
|
||||||
|
import { useRoute } from "vue-router";
|
||||||
|
import {
|
||||||
|
loadMoreAlbums,
|
||||||
|
loadMoreArtists, loadMoreTracks, searchAlbums,
|
||||||
|
searchArtists, searchTracks
|
||||||
|
} from "../composables/fetch/searchMusic";
|
||||||
|
import { Album, Artist, Playlist, Track } from "../interfaces";
|
||||||
|
import { Routes } from "./../composables/enums";
|
||||||
|
import useLoaderStore from "./loader";
|
||||||
|
import useTabStore from "./tabs";
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* Scrolls on clicking the loadmore button
|
* Scrolls on clicking the loadmore button
|
||||||
@ -56,7 +53,7 @@ export default defineStore("search", () => {
|
|||||||
|
|
||||||
const albums = reactive({
|
const albums = reactive({
|
||||||
query: "",
|
query: "",
|
||||||
value: <AlbumInfo[]>[],
|
value: <Album[]>[],
|
||||||
more: false,
|
more: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -4,16 +4,28 @@
|
|||||||
style="height: 100%"
|
style="height: 100%"
|
||||||
:class="{ isSmall, isMedium }"
|
:class="{ isSmall, isMedium }"
|
||||||
>
|
>
|
||||||
<RecycleScroller
|
<DynamicScroller
|
||||||
class="scroller"
|
|
||||||
:items="scrollerItems"
|
:items="scrollerItems"
|
||||||
:item-size="null"
|
:min-item-size="64"
|
||||||
key-field="id"
|
class="scroller"
|
||||||
v-slot="{ item }"
|
|
||||||
style="height: 100%"
|
style="height: 100%"
|
||||||
>
|
>
|
||||||
<component :is="item.component" v-bind="item.props" />
|
<template v-slot="{ item, index, active }">
|
||||||
</RecycleScroller>
|
<DynamicScrollerItem
|
||||||
|
:item="item"
|
||||||
|
:active="active"
|
||||||
|
:size-dependencies="[item.props]"
|
||||||
|
:data-index="index"
|
||||||
|
>
|
||||||
|
<component
|
||||||
|
:key="index"
|
||||||
|
:is="item.component"
|
||||||
|
v-bind="item.props"
|
||||||
|
></component>
|
||||||
|
<!-- @playThis="playFromPage(item.props.index - 1)" -->
|
||||||
|
</DynamicScrollerItem>
|
||||||
|
</template>
|
||||||
|
</DynamicScroller>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -21,29 +33,54 @@
|
|||||||
import { isMedium, isSmall } from "@/stores/content-width";
|
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 Albums from "@/components/ArtistView/Albums.vue";
|
||||||
|
import useArtistPageStore from "@/stores/pages/artist";
|
||||||
|
import ArtistAlbums from "@/components/AlbumView/ArtistAlbums.vue";
|
||||||
|
|
||||||
import { computed } from "vue";
|
import { computed } from "vue";
|
||||||
|
import { onBeforeRouteUpdate } from "vue-router";
|
||||||
|
|
||||||
|
const artistStore = useArtistPageStore();
|
||||||
|
|
||||||
interface ScrollerItem {
|
interface ScrollerItem {
|
||||||
id: string | number;
|
id: string | number;
|
||||||
component: typeof Header;
|
component: any;
|
||||||
// props: Record<string, unknown>;
|
props?: Record<string, unknown>;
|
||||||
size: number;
|
// size: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const header: ScrollerItem = {
|
const header: ScrollerItem = {
|
||||||
id: "artist-header",
|
id: "artist-header",
|
||||||
component: Header,
|
component: Header,
|
||||||
size: 19 * 16,
|
// size: 16 * 19,
|
||||||
|
};
|
||||||
|
|
||||||
|
const top_tracks: ScrollerItem = {
|
||||||
|
id: "artist-top-tracks",
|
||||||
|
component: TopTracks,
|
||||||
|
// size: 16 * 25,
|
||||||
|
};
|
||||||
|
|
||||||
|
const artistAlbums: ScrollerItem = {
|
||||||
|
id: "artist-albums",
|
||||||
|
component: ArtistAlbums,
|
||||||
|
// size: 16 * 16,
|
||||||
|
props: { title: "Albums", albums: artistStore.albums },
|
||||||
};
|
};
|
||||||
|
|
||||||
const scrollerItems = computed(() => {
|
const scrollerItems = computed(() => {
|
||||||
return [header];
|
return [header, top_tracks, artistAlbums];
|
||||||
|
});
|
||||||
|
|
||||||
|
onBeforeRouteUpdate((to, from, next) => {
|
||||||
|
artistStore.getData(to.params.hash as string);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
.artist-page {
|
.section-title {
|
||||||
border: solid 1px;
|
margin: 1rem;
|
||||||
|
padding-left: 1rem;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user