mirror of
https://github.com/swingmx/webclient.git
synced 2025-12-23 19:00:21 +00:00
fix: double click on tracks is now working
+ fix see all link not being shown in favorites and artist page
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="statitem" :class="props.icon">
|
||||
<div class="statitem" :class="props.icon" :style="dynamicBackgroundStyle">
|
||||
<svg
|
||||
class="noise"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
@@ -35,7 +35,7 @@
|
||||
surfaceScale="21"
|
||||
specularConstant="1.7"
|
||||
specularExponent="20"
|
||||
lighting-color="#7957A8"
|
||||
lighting-color="transparent"
|
||||
x="0%"
|
||||
y="0%"
|
||||
width="100%"
|
||||
@@ -50,20 +50,20 @@
|
||||
<rect width="700" height="700" fill="transparent"></rect>
|
||||
<rect width="700" height="700" fill="#7957a8" filter="url(#nnnoise-filter)"></rect>
|
||||
</svg>
|
||||
<div class="itemcontent">
|
||||
<div class="itemcontent" :style="{ color: textColor }">
|
||||
<div class="count ellip2" :title="formattedValue">{{ formattedValue }}</div>
|
||||
<div class="title">{{ text }}</div>
|
||||
</div>
|
||||
|
||||
<component :is="icon" class="staticon" v-if="!props.icon.startsWith('top')" />
|
||||
<component :is="icon" v-if="!props.icon.startsWith('top')" class="staticon" :style="{ color: textColor }" />
|
||||
<router-link
|
||||
v-if="props.icon.startsWith('top') && props.image"
|
||||
:to="{
|
||||
name: Routes.album,
|
||||
params: {
|
||||
albumhash: props.image?.replace('.webp', ''),
|
||||
},
|
||||
}"
|
||||
v-if="props.icon.startsWith('top') && props.image"
|
||||
>
|
||||
<img class="staticon statimage shadow-sm" :src="paths.images.thumb.small + props.image" alt="" />
|
||||
</router-link>
|
||||
@@ -81,6 +81,11 @@ import SparklesSvg from '@/assets/icons/sparkles.svg'
|
||||
|
||||
import { paths } from '@/config'
|
||||
import { Routes } from '@/router'
|
||||
import useArtistStore from '@/stores/pages/artist'
|
||||
import useAlbumStore from '@/stores/pages/album'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { getTextColor } from '@/utils/colortools/shift'
|
||||
|
||||
const props = defineProps<{
|
||||
value: string
|
||||
@@ -89,6 +94,13 @@ const props = defineProps<{
|
||||
image?: string
|
||||
}>()
|
||||
|
||||
// Get current route and colors from stores
|
||||
const route = useRoute()
|
||||
const artistStore = useArtistStore()
|
||||
const albumStore = useAlbumStore()
|
||||
const { colors: artistColors } = storeToRefs(artistStore)
|
||||
const { colors: albumColors } = storeToRefs(albumStore)
|
||||
|
||||
const icon = computed(() => {
|
||||
switch (props.icon) {
|
||||
case 'streams':
|
||||
@@ -110,6 +122,61 @@ const icon = computed(() => {
|
||||
const formattedValue = computed(() => {
|
||||
return props.value.toLocaleString()
|
||||
})
|
||||
|
||||
// Determine which dynamic color to use based on current route
|
||||
const dynamicColor = computed(() => {
|
||||
switch (route.name) {
|
||||
// Album-related pages should use album colors
|
||||
case Routes.album:
|
||||
return albumColors.value?.bg || null
|
||||
|
||||
// Artist-related pages should use artist colors
|
||||
case Routes.artist:
|
||||
return artistColors.value?.bg || null
|
||||
|
||||
// All other pages should use default colors
|
||||
default:
|
||||
return null
|
||||
}
|
||||
})
|
||||
|
||||
// Default hardcoded background styles
|
||||
const defaultBackgroundStyles = computed(() => {
|
||||
switch (props.icon) {
|
||||
case 'streams':
|
||||
return 'linear-gradient(to top, #c79081 0%, #dfa579 100%)'
|
||||
case 'playtime':
|
||||
return 'linear-gradient(-225deg, #3d4e81 0%, #5753c9 48%, #6e7ff3 100%)'
|
||||
case 'trackcount':
|
||||
return 'linear-gradient(to top, #6a66b9 0%, #7777db 52%, #7b7bd4 100%)'
|
||||
case 'toptrack':
|
||||
return 'linear-gradient(-225deg, #65379b 0%, #6750b3 53%, #6457c6 100%)'
|
||||
default:
|
||||
return 'linear-gradient(to top right, rgb(120, 76, 129), #9643da91, rgb(132, 80, 228))'
|
||||
}
|
||||
})
|
||||
|
||||
// Computed style that uses dynamic color or falls back to hardcoded
|
||||
const dynamicBackgroundStyle = computed(() => {
|
||||
if (dynamicColor.value) {
|
||||
return {
|
||||
backgroundColor: dynamicColor.value,
|
||||
backgroundImage: 'none',
|
||||
}
|
||||
}
|
||||
return {
|
||||
backgroundImage: defaultBackgroundStyles.value,
|
||||
}
|
||||
})
|
||||
|
||||
// Computed text color based on background using the same logic as headers
|
||||
const textColor = computed(() => {
|
||||
if (dynamicColor.value) {
|
||||
return getTextColor(dynamicColor.value)
|
||||
}
|
||||
// Return default white color when using gradients
|
||||
return '#ffffff'
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@@ -121,25 +188,10 @@ const formattedValue = computed(() => {
|
||||
aspect-ratio: 1;
|
||||
overflow: hidden;
|
||||
|
||||
// Default background - will be overridden by dynamic styles
|
||||
background-image: linear-gradient(to top right, rgb(120, 76, 129), #9643da91, rgb(132, 80, 228));
|
||||
position: relative;
|
||||
|
||||
&.streams {
|
||||
background-image: linear-gradient(to top, #c79081 0%, #dfa579 100%);
|
||||
}
|
||||
|
||||
&.playtime {
|
||||
background-image: linear-gradient(-225deg, #3d4e81 0%, #5753c9 48%, #6e7ff3 100%);
|
||||
}
|
||||
|
||||
&.trackcount {
|
||||
background-image: linear-gradient(to top, #6a66b9 0%, #7777db 52%, #7b7bd4 100%);
|
||||
}
|
||||
|
||||
&.toptrack {
|
||||
background-image: linear-gradient(-225deg, #65379b 0%, #6750b3 53%, #6457c6 100%);
|
||||
}
|
||||
|
||||
.itemcontent {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
@@ -7,11 +7,18 @@
|
||||
{{ title }}
|
||||
</RouterLink>
|
||||
</b>
|
||||
<!-- INFO: This SEE ALL is shown when there's no description. Eg. in favorites page -->
|
||||
<SeeAll
|
||||
v-if="!description && route && itemlist.length >= maxAbumCards"
|
||||
:route="route"
|
||||
:text="seeAllText"
|
||||
/>
|
||||
</div>
|
||||
<div v-if="description" class="rdesc">
|
||||
<RouterLink :to="route || ''">
|
||||
{{ description }}
|
||||
</RouterLink>
|
||||
<!-- INFO: This SEE ALL is shown when there's a description. Eg. in the home page -->
|
||||
<SeeAll v-if="route && itemlist.length >= maxAbumCards" :route="route" :text="seeAllText" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -163,6 +170,9 @@ function getProps(item: { type: string; item?: any; with_helptext?: boolean }) {
|
||||
|
||||
.rtitle {
|
||||
font-size: 1.15rem;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.rdesc {
|
||||
|
||||
@@ -2,14 +2,15 @@
|
||||
<div
|
||||
class="songlist-item rounded-sm"
|
||||
:class="[{ current: isCurrent() }, { contexton: context_menu_showing }]"
|
||||
@dblclick="emitUpdate"
|
||||
@contextmenu.prevent="showMenu"
|
||||
>
|
||||
<TrackIndex
|
||||
v-if="!isSmall"
|
||||
:index="index"
|
||||
:is_fav="is_fav"
|
||||
@add-to-fav="addToFav(track.trackhash)"
|
||||
:show-inline-fav-icon="settings.showInlineFavIcon"
|
||||
@add-to-fav="addToFav(track.trackhash)"
|
||||
/>
|
||||
|
||||
<TrackTitle
|
||||
@@ -29,13 +30,13 @@
|
||||
/>
|
||||
<TrackDuration
|
||||
:duration="track.duration || 0"
|
||||
@showMenu="showMenu"
|
||||
@toggleFav="addToFav(track.trackhash)"
|
||||
:help_text="track.help_text"
|
||||
:is_fav="is_fav"
|
||||
:showFavIcon="!isFavoritesPage"
|
||||
:showInlineFavIcon="settings.showInlineFavIcon"
|
||||
:highlightFavoriteTracks="settings.highlightFavoriteTracks"
|
||||
@showMenu="showMenu"
|
||||
@toggleFav="addToFav(track.trackhash)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -10,7 +10,7 @@ import useTracklist from './queue/tracklist'
|
||||
import useSettings from './settings'
|
||||
import useTracker from './tracker'
|
||||
|
||||
import { paths } from '@/config'
|
||||
import { getBaseUrl, paths } from '@/config'
|
||||
import updateMediaNotif from '@/helpers/mediaNotification'
|
||||
import { crossFade } from '@/utils/audio/crossFade'
|
||||
|
||||
@@ -127,9 +127,11 @@ export function getUrl(filepath: string, trackhash: string, use_legacy: boolean)
|
||||
use_legacy = true
|
||||
const { streaming_container, streaming_quality } = useSettings()
|
||||
|
||||
return `${paths.api.files}/${trackhash + (use_legacy ? '/legacy' : '')}?filepath=${encodeURIComponent(
|
||||
const url = `${paths.api.files}/${trackhash + (use_legacy ? '/legacy' : '')}?filepath=${encodeURIComponent(
|
||||
filepath
|
||||
)}&container=${streaming_container}&quality=${streaming_quality}`
|
||||
|
||||
return getBaseUrl() + url
|
||||
}
|
||||
|
||||
const audioSource = new AudioSource()
|
||||
|
||||
@@ -1,128 +1,128 @@
|
||||
<template>
|
||||
<div class="content-page favorites-page">
|
||||
<GenericHeader>
|
||||
<template #name>Favorites</template>
|
||||
<template #description
|
||||
>{{ count.tracks }} Tracks • {{ count.albums }} Albums • {{ count.artists }} Artists</template
|
||||
>
|
||||
</GenericHeader>
|
||||
<CardScroller
|
||||
v-if="recentFavs.length"
|
||||
class="recent-favs"
|
||||
:items="recentFavs"
|
||||
:title="'Recent'"
|
||||
:play-source="playSources.favorite"
|
||||
/>
|
||||
<div v-if="favTracks.length" class="fav-tracks">
|
||||
<TopTracks
|
||||
:tracks="favTracks"
|
||||
:route="'/favorites/tracks'"
|
||||
:title="'Tracks'"
|
||||
:play-handler="handlePlay"
|
||||
:source="dropSources.favorite"
|
||||
:total="count.tracks"
|
||||
/>
|
||||
<div class="content-page favorites-page">
|
||||
<GenericHeader>
|
||||
<template #name>Favorites</template>
|
||||
<template #description
|
||||
>{{ count.tracks }} Tracks • {{ count.albums }} Albums • {{ count.artists }} Artists</template
|
||||
>
|
||||
</GenericHeader>
|
||||
<CardScroller
|
||||
v-if="recentFavs.length"
|
||||
class="recent-favs"
|
||||
:items="recentFavs"
|
||||
:title="'Recent'"
|
||||
:play-source="playSources.favorite"
|
||||
/>
|
||||
<div v-if="favTracks.length" class="fav-tracks">
|
||||
<TopTracks
|
||||
:tracks="favTracks"
|
||||
:route="'/favorites/tracks'"
|
||||
:title="'Tracks'"
|
||||
:play-handler="handlePlay"
|
||||
:source="dropSources.favorite"
|
||||
:total="count.tracks"
|
||||
/>
|
||||
</div>
|
||||
<br />
|
||||
<CardScroller
|
||||
v-if="favAlbums.length"
|
||||
:items="favAlbums.map(i => ({ type: 'album', item: i }))"
|
||||
:title="'Albums'"
|
||||
:route="'/favorites/albums'"
|
||||
/>
|
||||
|
||||
<CardScroller
|
||||
v-if="favArtists.length"
|
||||
:items="favArtists.map(i => ({ type: 'artist', item: i }))"
|
||||
:title="'Artists'"
|
||||
:route="'/favorites/artists'"
|
||||
/>
|
||||
|
||||
<NoItems :flag="noFavs" :icon="HeartSvg" :title="'No favorites found'" :description="description" />
|
||||
</div>
|
||||
<br />
|
||||
<CardScroller
|
||||
v-if="favAlbums.length"
|
||||
:items="favAlbums.map((i) => ({ type: 'album', item: i }))"
|
||||
:title="'Albums'"
|
||||
:route="'/favorites/albums'"
|
||||
/>
|
||||
|
||||
<CardScroller
|
||||
v-if="favArtists.length"
|
||||
:items="favArtists.map((i) => ({ type: 'artist', item: i }))"
|
||||
:title="'Artists'"
|
||||
:route="'/favorites/artists'"
|
||||
/>
|
||||
|
||||
<NoItems :flag="noFavs" :icon="HeartSvg" :title="'No favorites found'" :description="description" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { nextTick, onMounted, Ref, ref } from "vue";
|
||||
import { nextTick, onMounted, Ref, ref } from 'vue'
|
||||
|
||||
import { maxAbumCards, updateCardWidth } from "@/stores/content-width";
|
||||
import { maxAbumCards, updateCardWidth } from '@/stores/content-width'
|
||||
|
||||
import { dropSources, playSources } from "@/enums";
|
||||
import { playFromFavorites } from "@/helpers/usePlayFrom";
|
||||
import { Album, Artist, RecentFavResult, Track } from "@/interfaces";
|
||||
import { getAllFavs } from "@/requests/favorite";
|
||||
import updatePageTitle from "@/utils/updatePageTitle";
|
||||
import { dropSources, playSources } from '@/enums'
|
||||
import { playFromFavorites } from '@/helpers/usePlayFrom'
|
||||
import { Album, Artist, RecentFavResult, Track } from '@/interfaces'
|
||||
import { getAllFavs } from '@/requests/favorite'
|
||||
import updatePageTitle from '@/utils/updatePageTitle'
|
||||
|
||||
import HeartSvg from "@/assets/icons/heart-no-color.svg";
|
||||
import TopTracks from "@/components/ArtistView/TopTracks.vue";
|
||||
import CardScroller from "@/components/shared/CardScroller.vue";
|
||||
import GenericHeader from "@/components/shared/GenericHeader.vue";
|
||||
import NoItems from "@/components/shared/NoItems.vue";
|
||||
import HeartSvg from '@/assets/icons/heart-no-color.svg'
|
||||
import TopTracks from '@/components/ArtistView/TopTracks.vue'
|
||||
import CardScroller from '@/components/shared/CardScroller.vue'
|
||||
import GenericHeader from '@/components/shared/GenericHeader.vue'
|
||||
import NoItems from '@/components/shared/NoItems.vue'
|
||||
|
||||
const description = `You can add tracks, albums and artists to your favorites by clicking the heart icon`;
|
||||
const description = `You can add tracks, albums and artists to your favorites by clicking the heart icon`
|
||||
|
||||
const recentFavs: Ref<RecentFavResult[]> = ref([]);
|
||||
const favAlbums: Ref<Album[]> = ref([]);
|
||||
const favTracks: Ref<Track[]> = ref([]);
|
||||
const favArtists: Ref<Artist[]> = ref([]);
|
||||
const recentFavs: Ref<RecentFavResult[]> = ref([])
|
||||
const favAlbums: Ref<Album[]> = ref([])
|
||||
const favTracks: Ref<Track[]> = ref([])
|
||||
const favArtists: Ref<Artist[]> = ref([])
|
||||
const count = ref({
|
||||
albums: 0,
|
||||
tracks: 0,
|
||||
artists: 0,
|
||||
});
|
||||
const noFavs = ref(false);
|
||||
albums: 0,
|
||||
tracks: 0,
|
||||
artists: 0,
|
||||
})
|
||||
const noFavs = ref(false)
|
||||
|
||||
onMounted(() => {
|
||||
updatePageTitle("Favorites");
|
||||
const max = maxAbumCards.value;
|
||||
updatePageTitle('Favorites')
|
||||
const max = maxAbumCards.value
|
||||
|
||||
getAllFavs(6, max, max)
|
||||
.then((favs) => {
|
||||
recentFavs.value = favs.recents;
|
||||
favAlbums.value = favs.albums;
|
||||
favTracks.value = favs.tracks;
|
||||
favArtists.value = favs.artists;
|
||||
count.value = favs.count;
|
||||
})
|
||||
.then(() => {
|
||||
noFavs.value = !favAlbums.value.length && !favTracks.value.length && !favArtists.value.length;
|
||||
})
|
||||
.then(async () => {
|
||||
await nextTick();
|
||||
updateCardWidth();
|
||||
});
|
||||
});
|
||||
getAllFavs(6, max, max)
|
||||
.then(favs => {
|
||||
recentFavs.value = favs.recents
|
||||
favAlbums.value = favs.albums
|
||||
favTracks.value = favs.tracks
|
||||
favArtists.value = favs.artists
|
||||
count.value = favs.count
|
||||
})
|
||||
.then(() => {
|
||||
noFavs.value = !favAlbums.value.length && !favTracks.value.length && !favArtists.value.length
|
||||
})
|
||||
.then(async () => {
|
||||
await nextTick()
|
||||
updateCardWidth()
|
||||
})
|
||||
})
|
||||
|
||||
function handlePlay(index: number) {
|
||||
const track = favTracks.value[index];
|
||||
if (!track) return;
|
||||
playFromFavorites(track);
|
||||
const track = favTracks.value[index]
|
||||
if (!track) return
|
||||
playFromFavorites(track)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.favorites-page {
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
|
||||
.recent-favs {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.nothing h3 {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.fav-tracks {
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
.recent-favs {
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.artist-top-tracks {
|
||||
h3 {
|
||||
padding-right: $small;
|
||||
}
|
||||
.nothing h3 {
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
.fav-tracks {
|
||||
h3 {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.artist-top-tracks {
|
||||
h3 {
|
||||
padding-right: $small;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user