fix(front): Correct fallback cover images for activity list in user profile

This commit is contained in:
ArneBo 2025-01-20 14:02:41 +01:00
parent 9af9dc4a3f
commit fc10fca736
2 changed files with 65 additions and 35 deletions

View File

@ -1,10 +1,11 @@
<script setup lang="ts">
import type { Track, Listening } from '~/types'
import { ref, reactive, watch } from 'vue'
import { ref, reactive, watch, computed } from 'vue'
import { useStore } from '~/store'
import { clone } from 'lodash-es'
import { useI18n } from 'vue-i18n'
import { getArtistCoverUrl } from '~/utils/utils'
import axios from 'axios'
@ -14,10 +15,10 @@ import PlayButton from '~/components/audio/PlayButton.vue'
import TagsList from '~/components/tags/List.vue'
import Alert from '~/components/ui/Alert.vue'
import Spacer from '~/components/ui/Spacer.vue'
import Loader from '~/components/ui/Loader.vue'
import useErrorHandler from '~/composables/useErrorHandler'
import { getArtistCoverUrl } from '~/utils/utils'
interface Events {
(e: 'count', count: number): void
@ -84,14 +85,48 @@ watch(
watch(count, (to) => emit('count', to))
watch(() => props.websocketHandlers.includes('Listen'), (to) => {
useWebSocketHandler('Listen', (event) => {
// TODO (wvffle): Add reactivity to recently listened / favorited / added (#1316, #1534)
// count.value += 1
if (to) {
useWebSocketHandler('Listen', (event) => {
// Handle WebSocket events for "Listen"
// objects.unshift(event as Listening)
// objects.pop()
})
})
// Add the event to `objects` reactively
objects.unshift(event as Listening);
// Keep the array size within limits (e.g., remove the last item if needed)
if (objects.length > props.limit) {
objects.pop();
}
// Recompute coverUrl for the updated `objects`
console.log('WebSocket event received:', event);
console.log('Updated cover URL:', coverUrl.value);
});
}
});
watch(
objects,
(newObjects) => {
// Process each object to attach a `coverUrl`
newObjects.forEach((object) => {
const track = object.track;
if (!("coverUrl" in object)) {
object.coverUrl = undefined; // Initial value for clarity
}
if (track?.album?.cover) {
object.coverUrl = store.getters["instance/absoluteUrl"](track.album.cover.urls.medium_square_crop);
} else if (track?.cover) {
object.coverUrl = store.getters["instance/absoluteUrl"](track.cover.urls.medium_square_crop);
} else if (track?.artist_credit?.length > 0) {
object.coverUrl = getArtistCoverUrl(track.artist_credit) || false;
} else {
object.coverUrl = false; // Fallback if no cover is found
}
});
},
{ deep: true }
);
</script>
<template>
@ -120,26 +155,15 @@ watch(() => props.websocketHandlers.includes('Listen'), (to) => {
@click="navigate('track')"
>
<div class="activity-image">
<img
v-if="object.track.album && object.track.album.cover"
v-lazy="store.getters['instance/absoluteUrl'](object.track.album.cover.urls.medium_square_crop)"
alt=""
>
<img
v-else-if="object.track.cover"
v-lazy="store.getters['instance/absoluteUrl'](object.track.cover.urls.medium_square_crop)"
alt=""
>
<img
v-else-if="object.track.artist_credit && object.track.artist_credit.length > 0"
v-lazy="getArtistCoverUrl(object.track.artist_credit)"
alt=""
>
<img
<img
v-if="object.coverUrl"
v-lazy="object.coverUrl"
alt="Cover"
/>
<i
v-else
alt=""
src="../../../../public/embed-default-cover.jpeg"
>
class="bi bi-vinyl-fill"
/>
<PlayButton
class="play-overlay"
:icon-only="true"
@ -250,6 +274,10 @@ watch(() => props.websocketHandlers.includes('Listen'), (to) => {
object-fit: cover;
}
> i {
font-size: 36px;
}
> .play-button {
position: absolute;
top: 0;

View File

@ -85,6 +85,7 @@ const fetchData = async () => {
}
watch(props, fetchData, { immediate: true })
const recentActivity = ref(0)
</script>
<template>
@ -140,12 +141,12 @@ watch(props, fetchData, { immediate: true })
<h2 class="ui with-actions header">
{{ t('views.auth.ProfileOverview.header.libraries') }}
<div
v-if="store.state.auth.authenticated && object.full_username === store.state.auth.fullUsername"
v-if="store.state.auth.authenticated && object?.full_username === store.state.auth.fullUsername"
class="actions"
>
</div>
</h2>
<library-widget :url="`federation/actors/${object.full_username}/libraries/`">
<library-widget :url="`federation/actors/${object?.full_username}/libraries/`">
<template #title>
{{ t('views.auth.ProfileOverview.header.sharedLibraries') }}
</template>
@ -156,7 +157,7 @@ watch(props, fetchData, { immediate: true })
<h2 class="ui with-actions header">
{{ t('views.auth.ProfileOverview.header.channels') }}
<div
v-if="store.state.auth.authenticated && object.full_username === store.state.auth.fullUsername"
v-if="store.state.auth.authenticated && object?.full_username === store.state.auth.fullUsername"
class="actions"
>
<a
@ -168,15 +169,16 @@ watch(props, fetchData, { immediate: true })
</a>
</div>
</h2>
<channels-widget :filters="{scope: `actor:${object.full_username}`}" />
<channels-widget :filters="{scope: `actor:${object?.full_username}`}" />
</Tab>
<Tab :title="t('views.auth.ProfileBase.link.activity')" :to="{name: 'profile.activity', params: routerParams}">
<track-widget
:url="'history/listenings/'"
:filters="{ scope, ordering: '-creation_date', ...qualityFilters}"
:filters="{ scope: `actor:${object.full_username}`, ordering: '-creation_date', ...qualityFilters}"
:websocket-handlers="['Listen']"
@count="recentActivity = $event"
>
<template #title>
{{ t('components.library.Home.header.recentlyListened') }}
@ -185,14 +187,14 @@ watch(props, fetchData, { immediate: true })
<track-widget
:url="'favorites/tracks/'"
:filters="{scope: scope, ordering: '-creation_date'}"
:filters="{scope: 'actor:${object.full_username}', ordering: '-creation_date'}"
>
<template #title>
{{ t('components.library.Home.header.recentlyFavorited') }}
</template>
</track-widget>
<album-widget :filters="{scope: scope, playable: true, ordering: '-creation_date', ...qualityFilters}">
<album-widget :filters="{scope: `actor:${object.full_username}`, playable: true, ordering: '-creation_date', ...qualityFilters}">
<template #title>
{{ t('components.library.Home.header.recentlyAdded') }}
</template>