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:
geoffrey45 2022-12-05 18:27:55 +03:00 committed by Mungai Njoroge
parent e54fea2d4d
commit 580dce1da9
14 changed files with 162 additions and 53 deletions

View File

@ -25,10 +25,10 @@ import { isLight } from "@/composables/colors/album";
const album = useAlbumStore(); const album = useAlbumStore();
onMounted(() => { onMounted(async () => {
// onMounted, fetch data to be used in the component below this one. // onMounted, fetch data to be used in the component below this one.
const album = useAlbumStore(); const album = useAlbumStore();
album.fetchArtistAlbums(); await album.fetchArtistAlbums();
}); });
</script> </script>

View File

@ -40,11 +40,10 @@
<div class="stats ellip"> <div class="stats ellip">
<div class="border rounded-sm pad-sm"> <div class="border rounded-sm pad-sm">
<ArtistName <ArtistName
:artists="album.albumartists.map((a) => a.name)" :artists="album.albumartists"
:albumartists="''" :albumartists="''"
:small="true" :small="true"
/> />&nbsp; {{ album.date }} {{ album.count }}
&nbsp; {{ album.date }} {{ album.count }}
{{ album.count === 1 ? "Track" : "Tracks" }} {{ album.count === 1 ? "Track" : "Tracks" }}
{{ formatSeconds(album.duration, true) }} {{ formatSeconds(album.duration, true) }}
</div> </div>
@ -57,7 +56,7 @@
v-for="a in album.albumartists" v-for="a in album.albumartists"
:to="{ :to="{
name: Routes.artist, name: Routes.artist,
params: { hash: a.hash }, params: { hash: a.artisthash },
}" }"
> >
<img <img
@ -130,6 +129,21 @@ useVisibility(albumheaderthing, handleVisibilityState);
background-color: $black; background-color: $black;
align-items: flex-end; 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 { .big-img {
height: calc(100%); height: calc(100%);
width: 16rem; width: 16rem;
@ -204,6 +218,7 @@ useVisibility(albumheaderthing, handleVisibilityState);
.artist { .artist {
font-size: 1.15rem; font-size: 1.15rem;
background-color: red;
} }
} }
@ -215,11 +230,20 @@ useVisibility(albumheaderthing, handleVisibilityState);
margin-bottom: 0.75rem; margin-bottom: 0.75rem;
font-size: 0.8rem; font-size: 0.8rem;
.artistname {
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
div { div {
font-size: 0.8rem; font-size: 0.8rem;
display: flex; display: flex;
width: fit-content; flex-wrap: wrap;
cursor: text; // width: fit-content;
// cursor: text;
} }
} }
} }

View File

@ -1,30 +1,55 @@
<template> <template>
<div <div
v-tooltip
style="width: fit-content" style="width: fit-content"
class="ellip" v-tooltip
class="artistname ellip"
:style="{ :style="{
width: 'fit-content',
fontSize: small ? '0.85rem' : smaller ? 'small' : '', fontSize: small ? '0.85rem' : smaller ? 'small' : '',
}" }"
@click.stop="() => {}"
> >
<div v-if="artists === null || artists.length === 0"> <div v-if="artists === null || artists.length === 0">
<span>{{ albumartists }}</span> <span>{{ albumartists }}</span>
</div> </div>
<div v-else> <div v-else>
<span v-for="artist in putCommas(artists)" :key="artist">{{ <template v-for="(artist, index) in artists" :key="artist.artisthash">
artist <RouterLink
}}</span> class="artist"
:to="{
name: Routes.artist,
params: { hash: artist.artisthash },
}"
>{{ `${artist.name}` }}</RouterLink
>
{{ index === artists.length - 1 ? "" : ",&nbsp;" }}
</template>
</div> </div>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Artist } from "@/interfaces";
import { putCommas } from "@/utils"; import { putCommas } from "@/utils";
import { Routes } from "@/composables/enums";
const props = defineProps<{ const props = defineProps<{
artists: string[] | null; artists: Artist[] | null;
albumartists: string | null; albumartists: string | null;
small?: boolean; small?: boolean;
smaller?: boolean; smaller?: boolean;
}>(); }>();
</script> </script>
<style lang="scss">
.artistname {
a {
color: inherit;
cursor: pointer !important;
&:hover {
text-decoration: underline;
}
}
}
</style>

View File

@ -131,6 +131,7 @@ function showMenu(e: MouseEvent) {
.song-album { .song-album {
max-width: max-content; max-width: max-content;
cursor: pointer !important; cursor: pointer !important;
&:hover { &:hover {

View File

@ -5,22 +5,32 @@ import { Artist, Track, Album } from "@/interfaces";
const getArtistData = async (hash: string) => { const getArtistData = async (hash: string) => {
interface ArtistData { interface ArtistData {
artist: Artist; artist: Artist;
albums: Album[];
tracks: Track[]; tracks: Track[];
} }
const { data, error } = await useAxios({ const { data, error } = await useAxios({
get: true, get: true,
url: paths.api.artist + `/${hash}?limit=6`, url: paths.api.artist + `/${hash}`,
}); });
if (error) { if (error) {
console.error(error); console.error(error);
} }
console.log(data);
return data as ArtistData; 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 };

View File

@ -6,11 +6,16 @@ export default () => {
if ("mediaSession" in navigator) { if ("mediaSession" in navigator) {
const queue = useQueueStore(); const queue = useQueueStore();
const { currenttrack: track } = queue; 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({ navigator.mediaSession.metadata = new window.MediaMetadata({
title: track.title, title: track.title,
artist: track.artist.join(", "), artist: track.artist.map((a) => a.name).join(", "),
artwork: [ artwork: [
{ {
src: url + track.image, src: url + track.image,

View File

@ -20,7 +20,6 @@ const imageRoutes = {
large: "/t/", large: "/t/",
small: "/t/s/", small: "/t/s/",
}, },
// artist: "/a/",
artist: { artist: {
large: "/a/", large: "/a/",
small: "/a/s/", small: "/a/s/",

View File

@ -9,7 +9,7 @@ export interface Track extends AlbumDisc {
id: string; id: string;
title: string; title: string;
album?: string; album?: string;
artist: string[]; artist: Artist[];
albumartist: string; albumartist: string;
albumhash?: string; albumhash?: string;
folder?: string; folder?: string;
@ -37,11 +37,7 @@ export interface Folder {
export interface Album { export interface Album {
albumid: string; albumid: string;
title: string; title: string;
albumartists: { albumartists: Artist[];
name: string;
hash: string;
image: string;
}[];
count: number; count: number;
duration: number; duration: number;
date: string; date: string;

View File

@ -21,5 +21,17 @@ const isMedium = computed(() => {
const albumHeaderSmall = computed(() => { const albumHeaderSmall = computed(() => {
return content_width.value <= brk.album_header_small; 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,
};

View File

@ -4,11 +4,11 @@ import { ComputedRef } from "vue";
import { AlbumDisc } from "./../../interfaces"; import { AlbumDisc } from "./../../interfaces";
import { FuseTrackOptions } from "@/composables/enums"; import { FuseTrackOptions } from "@/composables/enums";
import { content_width } from "@/stores/content-width"; import { maxAbumCards } from "@/stores/content-width";
import { import {
getAlbumsFromArtist, getAlbumsFromArtist,
getAlbumTracks getAlbumTracks,
} from "../../composables/fetch/album"; } from "../../composables/fetch/album";
import { Album, Artist, FuseResult, Track } from "../../interfaces"; import { Album, Artist, FuseResult, Track } from "../../interfaces";
import { useNotifStore } from "../notification"; import { useNotifStore } from "../notification";
@ -27,11 +27,11 @@ function sortByTrackNumber(tracks: Track[]) {
}); });
} }
function albumHasNoDiscs(album: Album) { // function albumHasNoDiscs(album: Album) {
if (album.is_single) return true; // if (album.is_single) return true;
return false; // return false;
} // }
/** /**
* *
@ -68,13 +68,12 @@ export default defineStore("album", {
}, },
async fetchArtistAlbums() { async fetchArtistAlbums() {
const albumartists = this.info.albumartists; const albumartists = this.info.albumartists;
const cardWidth = 10 * 16;
const visible_cards = Math.floor(content_width.value / cardWidth); const albumartisthashes = albumartists.map((artist) => artist.artisthash);
const albumartisthashes = albumartists.map((artist) => artist.hash);
this.albumArtists = await getAlbumsFromArtist( this.albumArtists = await getAlbumsFromArtist(
albumartisthashes.join(), albumartisthashes.join(),
visible_cards, maxAbumCards.value,
this.info.albumhash this.info.albumhash
); );
}, },

View File

@ -1,7 +1,8 @@
import { defineStore } from "pinia"; import { defineStore } from "pinia";
import { Artist, Album, Track } from "@/interfaces"; 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", { export default defineStore("artistPage", {
state: () => ({ state: () => ({
@ -11,11 +12,23 @@ export default defineStore("artistPage", {
}), }),
actions: { actions: {
async getData(hash: string) { async getData(hash: string) {
const { artist, albums, tracks } = await getArtistData(hash); const { artist, tracks } = await getArtistData(hash);
this.info = artist; this.info = artist;
this.tracks = tracks; 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 = [];
}, },
}, },
}); });

View File

@ -86,7 +86,7 @@ function getSongItems() {
function getArtistAlbumComponents(): ScrollerItem[] { function getArtistAlbumComponents(): ScrollerItem[] {
return album.albumArtists.map((artist) => { return album.albumArtists.map((artist) => {
const artist_name = album.info.albumartists.find( const artist_name = album.info.albumartists.find(
(a) => a.hash === artist.artisthash (a) => a.artisthash === artist.artisthash
)?.name; )?.name;
return { return {
id: Math.random().toString(), id: Math.random().toString(),
@ -121,7 +121,7 @@ function playFromAlbum(index: number) {
queue.play(index); queue.play(index);
} }
onBeforeRouteUpdate(async (to: RouteLocationNormalized) => { onBeforeRouteUpdate(async (to) => {
await album.fetchTracksAndArtists(to.params.hash.toString()).then(() => { await album.fetchTracksAndArtists(to.params.hash.toString()).then(() => {
album.resetQuery(); album.resetQuery();
album.resetAlbumArtists(); album.resetAlbumArtists();

View 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>

View File

@ -34,14 +34,13 @@ 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 Albums from "@/components/ArtistView/Albums.vue";
import useArtistPageStore from "@/stores/pages/artist"; import useArtistPageStore from "@/stores/pages/artist";
import ArtistAlbums from "@/components/AlbumView/ArtistAlbums.vue"; import ArtistAlbums from "@/components/AlbumView/ArtistAlbums.vue";
import ArtistAlbumsFetcher from "./ArtistAlbumsFetcher.vue";
import { computed } from "vue"; import { computed } from "vue";
import { onBeforeRouteUpdate } from "vue-router"; import { onBeforeRouteUpdate } from "vue-router";
const artistStore = useArtistPageStore(); const store = useArtistPageStore();
interface ScrollerItem { interface ScrollerItem {
id: string | number; id: string | number;
@ -59,18 +58,25 @@ const top_tracks: ScrollerItem = {
component: TopTracks, component: TopTracks,
}; };
const artist_albums_fetcher: ScrollerItem = {
id: "artist-albums-fetcher",
component: ArtistAlbumsFetcher,
};
const scrollerItems = computed(() => { const scrollerItems = computed(() => {
let components = [header]; let components = [header];
if (artistStore.tracks.length > 0) { if (store.tracks.length > 0) {
components.push(top_tracks); components.push(top_tracks);
} }
if (artistStore.albums.length > 0) { components = [...components, artist_albums_fetcher];
if (store.albums.length > 0) {
const artistAlbums: ScrollerItem = { const artistAlbums: ScrollerItem = {
id: "artist-albums", id: "artist-albums",
component: ArtistAlbums, component: ArtistAlbums,
props: { title: "Albums", albums: artistStore.albums }, props: { title: "Albums", albums: store.albums },
}; };
components.push(artistAlbums); components.push(artistAlbums);
@ -79,8 +85,9 @@ const scrollerItems = computed(() => {
return components; return components;
}); });
onBeforeRouteUpdate((to, from, next) => { onBeforeRouteUpdate(async (to) => {
artistStore.getData(to.params.hash as string);
await store.getData(to.params.hash as string);
}); });
</script> </script>