mirror of
https://github.com/tcsenpai/swingmusic.git
synced 2025-06-07 03:35:35 +00:00
attach artist page link to ArtistName component
+ separate fetching artist albums with fetching artist info + include limit when fetching artist albums + refactor interfaces
This commit is contained in:
parent
e54fea2d4d
commit
580dce1da9
@ -25,10 +25,10 @@ import { isLight } from "@/composables/colors/album";
|
||||
|
||||
const album = useAlbumStore();
|
||||
|
||||
onMounted(() => {
|
||||
onMounted(async () => {
|
||||
// onMounted, fetch data to be used in the component below this one.
|
||||
const album = useAlbumStore();
|
||||
album.fetchArtistAlbums();
|
||||
await album.fetchArtistAlbums();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -40,11 +40,10 @@
|
||||
<div class="stats ellip">
|
||||
<div class="border rounded-sm pad-sm">
|
||||
<ArtistName
|
||||
:artists="album.albumartists.map((a) => a.name)"
|
||||
:artists="album.albumartists"
|
||||
:albumartists="''"
|
||||
:small="true"
|
||||
/>
|
||||
• {{ album.date }} • {{ album.count }}
|
||||
/> • {{ album.date }} • {{ album.count }}
|
||||
{{ album.count === 1 ? "Track" : "Tracks" }} •
|
||||
{{ formatSeconds(album.duration, true) }}
|
||||
</div>
|
||||
@ -57,7 +56,7 @@
|
||||
v-for="a in album.albumartists"
|
||||
:to="{
|
||||
name: Routes.artist,
|
||||
params: { hash: a.hash },
|
||||
params: { hash: a.artisthash },
|
||||
}"
|
||||
>
|
||||
<img
|
||||
@ -130,6 +129,21 @@ useVisibility(albumheaderthing, handleVisibilityState);
|
||||
background-color: $black;
|
||||
align-items: flex-end;
|
||||
|
||||
// .albumartists {
|
||||
// outline: solid 1px;
|
||||
// // display: flex;
|
||||
// // width: 100%;
|
||||
// // flex-wrap: nowrap;
|
||||
// // height: 1rem;
|
||||
// // overflow: hidden;
|
||||
|
||||
// span {
|
||||
// // white-space: nowrap;
|
||||
// // overflow: hidden;
|
||||
// // text-overflow: ellipsis;
|
||||
// }
|
||||
// }
|
||||
|
||||
.big-img {
|
||||
height: calc(100%);
|
||||
width: 16rem;
|
||||
@ -204,6 +218,7 @@ useVisibility(albumheaderthing, handleVisibilityState);
|
||||
|
||||
.artist {
|
||||
font-size: 1.15rem;
|
||||
background-color: red;
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,11 +230,20 @@ useVisibility(albumheaderthing, handleVisibilityState);
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 0.8rem;
|
||||
|
||||
.artistname {
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
div {
|
||||
font-size: 0.8rem;
|
||||
display: flex;
|
||||
width: fit-content;
|
||||
cursor: text;
|
||||
flex-wrap: wrap;
|
||||
// width: fit-content;
|
||||
// cursor: text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +1,55 @@
|
||||
<template>
|
||||
<div
|
||||
v-tooltip
|
||||
style="width: fit-content"
|
||||
class="ellip"
|
||||
v-tooltip
|
||||
class="artistname ellip"
|
||||
:style="{
|
||||
width: 'fit-content',
|
||||
fontSize: small ? '0.85rem' : smaller ? 'small' : '',
|
||||
}"
|
||||
@click.stop="() => {}"
|
||||
>
|
||||
<div v-if="artists === null || artists.length === 0">
|
||||
<span>{{ albumartists }}</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span v-for="artist in putCommas(artists)" :key="artist">{{
|
||||
artist
|
||||
}}</span>
|
||||
<template v-for="(artist, index) in artists" :key="artist.artisthash">
|
||||
<RouterLink
|
||||
class="artist"
|
||||
:to="{
|
||||
name: Routes.artist,
|
||||
params: { hash: artist.artisthash },
|
||||
}"
|
||||
>{{ `${artist.name}` }}</RouterLink
|
||||
>
|
||||
{{ index === artists.length - 1 ? "" : ", " }}
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Artist } from "@/interfaces";
|
||||
import { putCommas } from "@/utils";
|
||||
import { Routes } from "@/composables/enums";
|
||||
|
||||
const props = defineProps<{
|
||||
artists: string[] | null;
|
||||
artists: Artist[] | null;
|
||||
albumartists: string | null;
|
||||
small?: boolean;
|
||||
smaller?: boolean;
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.artistname {
|
||||
a {
|
||||
color: inherit;
|
||||
cursor: pointer !important;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -131,6 +131,7 @@ function showMenu(e: MouseEvent) {
|
||||
|
||||
.song-album {
|
||||
max-width: max-content;
|
||||
|
||||
cursor: pointer !important;
|
||||
|
||||
&:hover {
|
||||
|
@ -5,22 +5,32 @@ 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}?limit=6`,
|
||||
url: paths.api.artist + `/${hash}`,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
console.log(data);
|
||||
|
||||
return data as ArtistData;
|
||||
};
|
||||
|
||||
export { getArtistData };
|
||||
const getArtistAlbums = async (hash: string, limit = 6) => {
|
||||
const { data, error } = await useAxios({
|
||||
get: true,
|
||||
url: paths.api.artist + `/${hash}/albums?limit=${limit}`,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
return data.albums as Album[];
|
||||
};
|
||||
|
||||
export { getArtistData, getArtistAlbums };
|
||||
|
@ -6,11 +6,16 @@ export default () => {
|
||||
if ("mediaSession" in navigator) {
|
||||
const queue = useQueueStore();
|
||||
const { currenttrack: track } = queue;
|
||||
const url = paths.images.thumb.large
|
||||
|
||||
if (track === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const url = paths.images.thumb.large;
|
||||
|
||||
navigator.mediaSession.metadata = new window.MediaMetadata({
|
||||
title: track.title,
|
||||
artist: track.artist.join(", "),
|
||||
artist: track.artist.map((a) => a.name).join(", "),
|
||||
artwork: [
|
||||
{
|
||||
src: url + track.image,
|
||||
|
@ -20,7 +20,6 @@ const imageRoutes = {
|
||||
large: "/t/",
|
||||
small: "/t/s/",
|
||||
},
|
||||
// artist: "/a/",
|
||||
artist: {
|
||||
large: "/a/",
|
||||
small: "/a/s/",
|
||||
|
@ -9,7 +9,7 @@ export interface Track extends AlbumDisc {
|
||||
id: string;
|
||||
title: string;
|
||||
album?: string;
|
||||
artist: string[];
|
||||
artist: Artist[];
|
||||
albumartist: string;
|
||||
albumhash?: string;
|
||||
folder?: string;
|
||||
@ -37,11 +37,7 @@ export interface Folder {
|
||||
export interface Album {
|
||||
albumid: string;
|
||||
title: string;
|
||||
albumartists: {
|
||||
name: string;
|
||||
hash: string;
|
||||
image: string;
|
||||
}[];
|
||||
albumartists: Artist[];
|
||||
count: number;
|
||||
duration: number;
|
||||
date: string;
|
||||
|
@ -21,5 +21,17 @@ const isMedium = computed(() => {
|
||||
const albumHeaderSmall = computed(() => {
|
||||
return content_width.value <= brk.album_header_small;
|
||||
});
|
||||
const album_card_with = 10 * 16;
|
||||
|
||||
export { content_width, window_width, isSmall, isMedium, albumHeaderSmall };
|
||||
const maxAbumCards = computed(() => {
|
||||
return Math.floor(content_width.value / album_card_with);
|
||||
});
|
||||
|
||||
export {
|
||||
content_width,
|
||||
window_width,
|
||||
isSmall,
|
||||
isMedium,
|
||||
albumHeaderSmall,
|
||||
maxAbumCards,
|
||||
};
|
||||
|
@ -4,11 +4,11 @@ import { ComputedRef } from "vue";
|
||||
import { AlbumDisc } from "./../../interfaces";
|
||||
|
||||
import { FuseTrackOptions } from "@/composables/enums";
|
||||
import { content_width } from "@/stores/content-width";
|
||||
import { maxAbumCards } from "@/stores/content-width";
|
||||
|
||||
import {
|
||||
getAlbumsFromArtist,
|
||||
getAlbumTracks
|
||||
getAlbumsFromArtist,
|
||||
getAlbumTracks,
|
||||
} from "../../composables/fetch/album";
|
||||
import { Album, Artist, FuseResult, Track } from "../../interfaces";
|
||||
import { useNotifStore } from "../notification";
|
||||
@ -27,11 +27,11 @@ function sortByTrackNumber(tracks: Track[]) {
|
||||
});
|
||||
}
|
||||
|
||||
function albumHasNoDiscs(album: Album) {
|
||||
if (album.is_single) return true;
|
||||
// function albumHasNoDiscs(album: Album) {
|
||||
// if (album.is_single) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
// return false;
|
||||
// }
|
||||
|
||||
/**
|
||||
*
|
||||
@ -68,13 +68,12 @@ export default defineStore("album", {
|
||||
},
|
||||
async fetchArtistAlbums() {
|
||||
const albumartists = this.info.albumartists;
|
||||
const cardWidth = 10 * 16;
|
||||
const visible_cards = Math.floor(content_width.value / cardWidth);
|
||||
const albumartisthashes = albumartists.map((artist) => artist.hash);
|
||||
|
||||
const albumartisthashes = albumartists.map((artist) => artist.artisthash);
|
||||
|
||||
this.albumArtists = await getAlbumsFromArtist(
|
||||
albumartisthashes.join(),
|
||||
visible_cards,
|
||||
maxAbumCards.value,
|
||||
this.info.albumhash
|
||||
);
|
||||
},
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { defineStore } from "pinia";
|
||||
|
||||
import { Artist, Album, Track } from "@/interfaces";
|
||||
import { getArtistData } from "@/composables/fetch/artists";
|
||||
import { getArtistData, getArtistAlbums } from "@/composables/fetch/artists";
|
||||
import { maxAbumCards } from "@/stores/content-width";
|
||||
|
||||
export default defineStore("artistPage", {
|
||||
state: () => ({
|
||||
@ -11,11 +12,23 @@ export default defineStore("artistPage", {
|
||||
}),
|
||||
actions: {
|
||||
async getData(hash: string) {
|
||||
const { artist, albums, tracks } = await getArtistData(hash);
|
||||
const { artist, tracks } = await getArtistData(hash);
|
||||
|
||||
this.info = artist;
|
||||
this.tracks = tracks;
|
||||
this.albums = albums;
|
||||
},
|
||||
async getArtistAlbums() {
|
||||
const albums = await getArtistAlbums(
|
||||
this.info.artisthash,
|
||||
maxAbumCards.value
|
||||
);
|
||||
|
||||
if (albums.length > 0) {
|
||||
this.albums = albums;
|
||||
}
|
||||
},
|
||||
resetAlbums() {
|
||||
this.albums = [];
|
||||
},
|
||||
},
|
||||
});
|
||||
|
@ -86,7 +86,7 @@ function getSongItems() {
|
||||
function getArtistAlbumComponents(): ScrollerItem[] {
|
||||
return album.albumArtists.map((artist) => {
|
||||
const artist_name = album.info.albumartists.find(
|
||||
(a) => a.hash === artist.artisthash
|
||||
(a) => a.artisthash === artist.artisthash
|
||||
)?.name;
|
||||
return {
|
||||
id: Math.random().toString(),
|
||||
@ -121,7 +121,7 @@ function playFromAlbum(index: number) {
|
||||
queue.play(index);
|
||||
}
|
||||
|
||||
onBeforeRouteUpdate(async (to: RouteLocationNormalized) => {
|
||||
onBeforeRouteUpdate(async (to) => {
|
||||
await album.fetchTracksAndArtists(to.params.hash.toString()).then(() => {
|
||||
album.resetQuery();
|
||||
album.resetAlbumArtists();
|
||||
|
18
src/views/ArtistView/ArtistAlbumsFetcher.vue
Normal file
18
src/views/ArtistView/ArtistAlbumsFetcher.vue
Normal file
@ -0,0 +1,18 @@
|
||||
<template><div style="height: 1px;"></div></template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { onMounted } from "vue";
|
||||
import useArtistPageStore from "@/stores/pages/artist";
|
||||
import { onBeforeRouteUpdate } from "vue-router";
|
||||
|
||||
const store = useArtistPageStore();
|
||||
|
||||
onMounted(async () => {
|
||||
await store.getArtistAlbums();
|
||||
});
|
||||
|
||||
onBeforeRouteUpdate(async (to) => {
|
||||
store.resetAlbums();
|
||||
await store.getArtistAlbums();
|
||||
});
|
||||
</script>
|
@ -34,14 +34,13 @@ import { isMedium, isSmall } from "@/stores/content-width";
|
||||
|
||||
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 ArtistAlbumsFetcher from "./ArtistAlbumsFetcher.vue";
|
||||
import { computed } from "vue";
|
||||
import { onBeforeRouteUpdate } from "vue-router";
|
||||
|
||||
const artistStore = useArtistPageStore();
|
||||
const store = useArtistPageStore();
|
||||
|
||||
interface ScrollerItem {
|
||||
id: string | number;
|
||||
@ -59,18 +58,25 @@ const top_tracks: ScrollerItem = {
|
||||
component: TopTracks,
|
||||
};
|
||||
|
||||
const artist_albums_fetcher: ScrollerItem = {
|
||||
id: "artist-albums-fetcher",
|
||||
component: ArtistAlbumsFetcher,
|
||||
};
|
||||
|
||||
const scrollerItems = computed(() => {
|
||||
let components = [header];
|
||||
|
||||
if (artistStore.tracks.length > 0) {
|
||||
if (store.tracks.length > 0) {
|
||||
components.push(top_tracks);
|
||||
}
|
||||
|
||||
if (artistStore.albums.length > 0) {
|
||||
components = [...components, artist_albums_fetcher];
|
||||
|
||||
if (store.albums.length > 0) {
|
||||
const artistAlbums: ScrollerItem = {
|
||||
id: "artist-albums",
|
||||
component: ArtistAlbums,
|
||||
props: { title: "Albums", albums: artistStore.albums },
|
||||
props: { title: "Albums", albums: store.albums },
|
||||
};
|
||||
|
||||
components.push(artistAlbums);
|
||||
@ -79,8 +85,9 @@ const scrollerItems = computed(() => {
|
||||
return components;
|
||||
});
|
||||
|
||||
onBeforeRouteUpdate((to, from, next) => {
|
||||
artistStore.getData(to.params.hash as string);
|
||||
onBeforeRouteUpdate(async (to) => {
|
||||
|
||||
await store.getData(to.params.hash as string);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user