Migrate home component
Adds some code that is a starter to #1316 and #1534 but depends on #1827
This commit is contained in:
parent
7408fe17ec
commit
e03e2ec901
|
@ -1,3 +1,78 @@
|
|||
<script setup lang="ts">
|
||||
import type { Track, Listening } from '~/types'
|
||||
import useWebSocketHandler from '~/composables/useWebSocketHandler'
|
||||
|
||||
// TODO (wvffle): Fix websocket update (#1534)
|
||||
import { clone } from 'lodash-es'
|
||||
import axios from 'axios'
|
||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||
import TagsList from '~/components/tags/List.vue'
|
||||
import { ref, reactive, watch } from 'vue'
|
||||
|
||||
interface Props {
|
||||
filters: Record<string, string>
|
||||
url: string
|
||||
isActivity?: boolean
|
||||
showCount?: boolean
|
||||
limit?: number
|
||||
itemClasses?: string
|
||||
websocketHandlers?: string[]
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
isActivity: true,
|
||||
showCount: false,
|
||||
limit: 5,
|
||||
itemClasses: '',
|
||||
websocketHandlers: () => []
|
||||
})
|
||||
|
||||
const objects = reactive([] as Listening[])
|
||||
const count = ref(0)
|
||||
const nextPage = ref<string | null>(null)
|
||||
|
||||
const isLoading = ref(false)
|
||||
const fetchData = async (url = props.url) => {
|
||||
isLoading.value = true
|
||||
|
||||
const params = {
|
||||
...clone(props.filters),
|
||||
page_size: props.limit
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.get(url, { params })
|
||||
nextPage.value = response.data.next
|
||||
count.value = response.data.count
|
||||
|
||||
const newObjects = !props.isActivity
|
||||
? response.data.results.map((track: Track) => { track })
|
||||
: response.data.results
|
||||
|
||||
objects.push(...newObjects)
|
||||
} catch (error) {
|
||||
// TODO (wvffle): Handle error
|
||||
}
|
||||
|
||||
isLoading.value = false
|
||||
}
|
||||
|
||||
fetchData()
|
||||
|
||||
const emit = defineEmits(['count'])
|
||||
watch(count, (to) => emit('count', to))
|
||||
|
||||
if (props.websocketHandlers.includes('Listen')) {
|
||||
useWebSocketHandler('Listen', (event) => {
|
||||
// TODO (wvffle): Add reactivity to recently listened / favorited / added (#1316, #1534)
|
||||
// count.value += 1
|
||||
|
||||
// objects.unshift(event as Listening)
|
||||
// objects.pop()
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="component-track-widget">
|
||||
<h3 v-if="!!$slots.title">
|
||||
|
@ -28,7 +103,7 @@
|
|||
alt=""
|
||||
>
|
||||
<img
|
||||
v-else-if="object.track.artist.cover"
|
||||
v-else-if="object.track.artist?.cover"
|
||||
v-lazy="$store.getters['instance/absoluteUrl'](object.track.artist.cover.urls.medium_square_crop)"
|
||||
alt=""
|
||||
>
|
||||
|
@ -52,7 +127,7 @@
|
|||
{{ object.track.title }}
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="meta ellipsis">
|
||||
<div v-if="object.track.artist" class="meta ellipsis">
|
||||
<span>
|
||||
<router-link
|
||||
class="discrete link"
|
||||
|
@ -122,9 +197,8 @@
|
|||
<template v-if="nextPage">
|
||||
<div class="ui hidden divider" />
|
||||
<button
|
||||
v-if="nextPage"
|
||||
:class="['ui', 'basic', 'button']"
|
||||
@click="fetchData(nextPage)"
|
||||
@click="fetchData(nextPage as string)"
|
||||
>
|
||||
<translate translate-context="*/*/Button,Label">
|
||||
Show more
|
||||
|
@ -133,87 +207,3 @@
|
|||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { clone } from 'lodash-es'
|
||||
import axios from 'axios'
|
||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||
import TagsList from '~/components/tags/List.vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
PlayButton,
|
||||
TagsList
|
||||
},
|
||||
props: {
|
||||
filters: { type: Object, required: true },
|
||||
url: { type: String, required: true },
|
||||
isActivity: { type: Boolean, default: true },
|
||||
showCount: { type: Boolean, default: false },
|
||||
limit: { type: Number, default: 5 },
|
||||
itemClasses: { type: String, default: '' }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
objects: [],
|
||||
count: 0,
|
||||
isLoading: false,
|
||||
errors: null,
|
||||
previousPage: null,
|
||||
nextPage: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
offset () {
|
||||
this.fetchData()
|
||||
},
|
||||
'$store.state.moderation.lastUpdate': function () {
|
||||
this.fetchData(this.url)
|
||||
},
|
||||
count (v) {
|
||||
this.$emit('count', v)
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.fetchData(this.url)
|
||||
},
|
||||
methods: {
|
||||
fetchData (url) {
|
||||
if (!url) {
|
||||
return
|
||||
}
|
||||
this.isLoading = true
|
||||
const self = this
|
||||
const params = clone(this.filters)
|
||||
params.page_size = this.limit
|
||||
params.offset = this.offset
|
||||
axios.get(url, { params: params }).then((response) => {
|
||||
self.previousPage = response.data.previous
|
||||
self.nextPage = response.data.next
|
||||
self.isLoading = false
|
||||
self.count = response.data.count
|
||||
let newObjects
|
||||
if (self.isActivity) {
|
||||
// we have listening/favorites objects, not directly tracks
|
||||
newObjects = response.data.results
|
||||
} else {
|
||||
newObjects = response.data.results.map((r) => {
|
||||
return { track: r }
|
||||
})
|
||||
}
|
||||
self.objects = [...self.objects, ...newObjects]
|
||||
}, error => {
|
||||
self.isLoading = false
|
||||
self.errors = error.backendErrors
|
||||
})
|
||||
},
|
||||
updateOffset (increment) {
|
||||
if (increment) {
|
||||
this.offset += this.limit
|
||||
} else {
|
||||
this.offset = Math.max(this.offset - this.limit, 0)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,6 +1,57 @@
|
|||
<script setup lang="ts">
|
||||
import axios from 'axios'
|
||||
import ChannelsWidget from '~/components/audio/ChannelsWidget.vue'
|
||||
import TrackWidget from '~/components/audio/track/Widget.vue'
|
||||
import AlbumWidget from '~/components/audio/album/Widget.vue'
|
||||
import PlaylistWidget from '~/components/playlists/Widget.vue'
|
||||
import useLogger from '~/composables/useLogger'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
interface Props {
|
||||
scope?: string
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
scope: 'all'
|
||||
})
|
||||
|
||||
const artists = ref([])
|
||||
|
||||
const logger = useLogger()
|
||||
|
||||
const { $pgettext } = useGettext()
|
||||
const labels = computed(() => ({
|
||||
title: $pgettext('Head/Home/Title', 'Library')
|
||||
}))
|
||||
|
||||
const isLoading = ref(false)
|
||||
const fetchData = async () => {
|
||||
isLoading.value = true
|
||||
logger.time('Loading latest artists')
|
||||
|
||||
const params = {
|
||||
ordering: '-creation_date',
|
||||
playable: true
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await axios.get('artists/', { params: params })
|
||||
artists.value = response.data.results
|
||||
} catch (error) {
|
||||
// TODO (wvffle): Handle error
|
||||
}
|
||||
|
||||
isLoading.value = false
|
||||
logger.timeEnd('Loading latest artists')
|
||||
}
|
||||
|
||||
fetchData()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<main
|
||||
:key="$route.name"
|
||||
:key="$route?.name ?? undefined"
|
||||
v-title="labels.title"
|
||||
>
|
||||
<section class="ui vertical stripe segment">
|
||||
|
@ -8,7 +59,8 @@
|
|||
<div class="column">
|
||||
<track-widget
|
||||
:url="'history/listenings/'"
|
||||
:filters="{scope: scope, ordering: '-creation_date'}"
|
||||
:filters="{ scope, ordering: '-creation_date' }"
|
||||
:websocket-handlers="['Listen']"
|
||||
>
|
||||
<template #title>
|
||||
<translate translate-context="Content/Home/Title">
|
||||
|
@ -69,62 +121,3 @@
|
|||
</section>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import ChannelsWidget from '~/components/audio/ChannelsWidget.vue'
|
||||
import TrackWidget from '~/components/audio/track/Widget.vue'
|
||||
import AlbumWidget from '~/components/audio/album/Widget.vue'
|
||||
import PlaylistWidget from '~/components/playlists/Widget.vue'
|
||||
import useLogger from '~/composables/useLogger'
|
||||
|
||||
const logger = useLogger()
|
||||
|
||||
const ARTISTS_URL = 'artists/'
|
||||
|
||||
export default {
|
||||
name: 'Library',
|
||||
components: {
|
||||
TrackWidget,
|
||||
AlbumWidget,
|
||||
PlaylistWidget,
|
||||
ChannelsWidget
|
||||
},
|
||||
props: {
|
||||
scope: { type: String, default: 'all' }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
artists: [],
|
||||
isLoadingArtists: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
labels () {
|
||||
return {
|
||||
title: this.$pgettext('Head/Home/Title', 'Library')
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.fetchArtists()
|
||||
},
|
||||
methods: {
|
||||
fetchArtists () {
|
||||
const self = this
|
||||
this.isLoadingArtists = true
|
||||
const params = {
|
||||
ordering: '-creation_date',
|
||||
playable: true
|
||||
}
|
||||
const url = ARTISTS_URL
|
||||
logger.time('Loading latest artists')
|
||||
axios.get(url, { params: params }).then(response => {
|
||||
self.artists = response.data.results
|
||||
logger.timeEnd('Loading latest artists')
|
||||
self.isLoadingArtists = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -148,8 +148,9 @@ const store: Module<State, RootState> = {
|
|||
if (!rootState.auth.authenticated) {
|
||||
return
|
||||
}
|
||||
return axios.post('history/listenings/', { track: track.id }).then(() => {}, () => {
|
||||
logger.error('Could not record track in history')
|
||||
|
||||
return axios.post('history/listenings/', { track: track.id }).catch((error) => {
|
||||
logger.error('Could not record track in history', error)
|
||||
})
|
||||
},
|
||||
trackEnded ({ commit, dispatch, rootState }) {
|
||||
|
|
|
@ -186,6 +186,14 @@ export interface Radio {
|
|||
user: User
|
||||
}
|
||||
|
||||
export interface Listening {
|
||||
id: string
|
||||
track: Track
|
||||
user: User
|
||||
actor: Actor
|
||||
creation_date: string
|
||||
}
|
||||
|
||||
// API stuff
|
||||
export interface APIErrorResponse extends Record<string, APIErrorResponse | string[] | { code: string }[]> {}
|
||||
|
||||
|
@ -226,6 +234,11 @@ export interface ListenWSEvent {
|
|||
object: ListenWsEventObject
|
||||
}
|
||||
|
||||
// TODO (wvffle): Add reactivity to recently listened / favorited / added (#1316, #1534)
|
||||
// export interface ListenWSEvent extends Listening {
|
||||
// type: 'Listen'
|
||||
// }
|
||||
|
||||
export type WebSocketEvent = PendingReviewEditsWSEvent | PendingReviewReportsWSEvent | PendingReviewRequestsWSEvent | ListenWSEvent
|
||||
|
||||
// FS Browser
|
||||
|
|
Loading…
Reference in New Issue