mirror of
https://github.com/Dvorinka/swingmusic-extended.git
synced 2026-06-04 04:23:01 +00:00
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:
committed by
Mungai Njoroge
parent
075765088f
commit
e54fea2d4d
@@ -24,7 +24,6 @@ defineProps<{
|
||||
<style lang="scss">
|
||||
.albums-from-artist {
|
||||
overflow: hidden;
|
||||
padding-top: 1rem;
|
||||
|
||||
h3 {
|
||||
display: grid;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -40,5 +40,6 @@ defineProps<{
|
||||
transition: all 0.5s ease-in-out;
|
||||
color: $white;
|
||||
background: $darkestblue !important;
|
||||
border-radius: 2rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
@@ -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,
|
||||
},
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user