make albums on artist page reactive

+ show artist name on artist album component on album page
+ attach artist page link to artist card
+ use small artist page on album header
+ use album color on genre banner on album page
This commit is contained in:
geoffrey45
2022-12-04 19:35:16 +03:00
committed by Mungai Njoroge
parent 075765088f
commit e54fea2d4d
14 changed files with 104 additions and 60 deletions
@@ -24,7 +24,6 @@ defineProps<{
<style lang="scss">
.albums-from-artist {
overflow: hidden;
padding-top: 1rem;
h3 {
display: grid;
+18 -4
View File
@@ -1,9 +1,18 @@
<template>
<div class="genres-banner">
<div
class="genres-banner"
:class="{
nocontrast: album.info.colors ? isLight(album.info.colors[0]) : false,
}"
>
<div class="rounded pad-sm">
{{ album.info.genres.length ? "Genres" : "No genres" }}
</div>
<div v-for="genre in album.info.genres" class="rounded pad-sm">
<div
v-for="genre in album.info.genres"
class="rounded pad-sm"
:style="{ backgroundColor: album.info.colors[0] }"
>
{{ genre }}
</div>
</div>
@@ -12,6 +21,7 @@
<script setup lang="ts">
import { onMounted } from "vue";
import useAlbumStore from "@/stores/pages/album";
import { isLight } from "@/composables/colors/album";
const album = useAlbumStore();
@@ -23,6 +33,9 @@ onMounted(() => {
</script>
<style lang="scss">
.genres-banner.nocontrast {
color: $black;
}
.genres-banner {
display: flex;
gap: 1rem;
@@ -36,7 +49,7 @@ onMounted(() => {
background-color: $gray5;
min-width: 4rem;
text-align: center;
outline: solid 1px $gray3;
outline: solid 1px $gray;
padding: $small 1rem;
&:first-child {
@@ -47,8 +60,9 @@ onMounted(() => {
}
&:hover {
background-color: $darkestblue;
background-color: $darkestblue !important;
outline-color: $darkestblue;
color: $white;
}
}
}
+8 -14
View File
@@ -61,7 +61,7 @@
}"
>
<img
:src="imguri.artist + a.image"
:src="imguri.artist.small + a.image"
class="shadow-lg circular"
loading="lazy"
:title="a.name"
@@ -164,27 +164,21 @@ useVisibility(albumheaderthing, handleVisibilityState);
.art {
display: inline-flex;
align-items: center;
flex-direction: row-reverse;
// background-color: red;
// align-items: center;
gap: $small;
img {
height: 4rem;
height: 3rem;
background-color: $gray;
border: solid 2px $white;
}
img:last-child {
height: 4rem;
margin-top: 5px;
a {
transition: all 0.25s ease-in-out;
}
img:not(:last-child) {
margin-left: -2rem;
}
img:hover {
z-index: 10;
a:hover {
transform: scale(1.4);
}
}
+11 -10
View File
@@ -10,15 +10,20 @@
<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
{{ artist.info.trackcount }} Track{{
`${artist.info.trackcount == 1 ? "" : "s"}`
}}
{{ artist.info.albumcount }} Album{{
`${artist.info.albumcount == 1 ? "" : "s"}`
}}
{{ formatSeconds(artist.info.duration, true) }}
</div>
</section>
<PlayBtnRect />
</div>
<div class="artist-img">
<img :src="paths.images.artist + artist.info.image" />
<div class="artist-img no-select">
<img :src="paths.images.artist.large + artist.info.image" />
</div>
<div
class="gradient"
@@ -63,8 +68,8 @@ const artist = useArtistPageStore();
background-image: linear-gradient(
to left,
transparent 10%,
#434142 50%,
#434142 100%
$gray2 50%,
$gray2 100%
);
height: 100%;
width: 100%;
@@ -106,10 +111,6 @@ const artist = useArtistPageStore();
.stats {
font-size: small;
}
.playbtnrect {
border-radius: 2rem;
}
}
.artist-info.nocontrast {
+8 -4
View File
@@ -10,24 +10,28 @@
:isCurrentPlaying="false"
/>
</div>
<div class="error" v-if="!artist.tracks.length">No tracks</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;
margin-bottom: 1rem;
margin-top: 2rem;
.section-title {
margin-left: 0;
}
.error {
padding-left: 1rem;
color: $red;
}
}
</style>
+24 -6
View File
@@ -1,17 +1,35 @@
<template>
<div class="artist-card" :class="{ _is_on_sidebar: alt }">
<img class="artist-image circular" :src="imguri + artist.image" loading="lazy" />
<div class="artist-name t-center">
{{ artist.name }}
<RouterLink
:to="{
name: Routes.artist,
params: {
hash: artist.artisthash,
},
}"
>
<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-name t-center">
{{ artist.name }}
</div>
</div>
</div>
</RouterLink>
</template>
<script setup lang="ts">
import { Routes } from "@/composables/enums";
import { Artist } from "@/interfaces";
import { paths } from "../../config";
const imguri = paths.images.artist;
const imguri = paths.images.artist.large;
defineProps<{
artist: Artist;
+1
View File
@@ -40,5 +40,6 @@ defineProps<{
transition: all 0.5s ease-in-out;
color: $white;
background: $darkestblue !important;
border-radius: 2rem;
}
</style>
+1 -2
View File
@@ -10,7 +10,7 @@ export function isLight(rgb: string): boolean {
const [r, g, b] = rgb.match(/\d+/g)!.map(Number);
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
return brightness > 170;
return brightness > 165;
}
interface BtnColor {
@@ -18,7 +18,6 @@ interface BtnColor {
isDark: boolean;
}
/**
* Returns the luminance of a color.
* @param r The red value of the color.
+1 -1
View File
@@ -11,7 +11,7 @@ const getArtistData = async (hash: string) => {
const { data, error } = await useAxios({
get: true,
url: paths.api.artist + `/${hash}`,
url: paths.api.artist + `/${hash}?limit=6`,
});
if (error) {
+9 -2
View File
@@ -20,7 +20,11 @@ const imageRoutes = {
large: "/t/",
small: "/t/s/",
},
artist: "/a/",
// artist: "/a/",
artist: {
large: "/a/",
small: "/a/s/",
},
playlist: "/p/",
raw: "/raw/",
};
@@ -83,7 +87,10 @@ const paths = {
small: baseImgUrl + imageRoutes.thumb.small,
large: baseImgUrl + imageRoutes.thumb.large,
},
artist: baseImgUrl + imageRoutes.artist,
artist: {
small: baseImgUrl + imageRoutes.artist.small,
large: baseImgUrl + imageRoutes.artist.large,
},
playlist: baseImgUrl + imageRoutes.playlist,
raw: baseImgUrl + imageRoutes.raw,
},
+1 -2
View File
@@ -52,8 +52,7 @@ export default defineStore("album", {
query: "",
info: <Album>{},
rawTracks: <Track[]>[],
artists: <Artist[]>[],
albumArtists: <{ artist: string; albums: Album[] }[]>[],
albumArtists: <{ artisthash: string; albums: Album[] }[]>[],
bio: null,
}),
actions: {
+1 -1
View File
@@ -14,8 +14,8 @@ export default defineStore("artistPage", {
const { artist, albums, tracks } = await getArtistData(hash);
this.info = artist;
this.albums = albums;
this.tracks = tracks;
this.albums = albums;
},
},
});
+4 -2
View File
@@ -85,10 +85,13 @@ function getSongItems() {
function getArtistAlbumComponents(): ScrollerItem[] {
return album.albumArtists.map((artist) => {
const artist_name = album.info.albumartists.find(
(a) => a.hash === artist.artisthash
)?.name;
return {
id: Math.random().toString(),
component: ArtistAlbums,
props: { artist },
props: { title: `More from ${artist_name}`, albums: artist.albums },
size: 20 * 16,
};
});
@@ -113,7 +116,6 @@ const scrollerItems = computed(() => {
});
function playFromAlbum(index: number) {
const { title, albumartists, albumhash } = album.info;
queue.playFromAlbum(title, albumhash, album.allTracks);
queue.play(index);
+17 -11
View File
@@ -47,30 +47,36 @@ interface ScrollerItem {
id: string | number;
component: any;
props?: Record<string, unknown>;
// size: number;
}
const header: ScrollerItem = {
id: "artist-header",
component: Header,
// 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(() => {
return [header, top_tracks, artistAlbums];
let components = [header];
if (artistStore.tracks.length > 0) {
components.push(top_tracks);
}
if (artistStore.albums.length > 0) {
const artistAlbums: ScrollerItem = {
id: "artist-albums",
component: ArtistAlbums,
props: { title: "Albums", albums: artistStore.albums },
};
components.push(artistAlbums);
}
return components;
});
onBeforeRouteUpdate((to, from, next) => {