Migrate home component

Adds some code that is a starter to #1316 and #1534 but depends on #1827
This commit is contained in:
wvffle 2022-07-19 08:09:19 +00:00 committed by Georg Krause
parent 7408fe17ec
commit e03e2ec901
4 changed files with 148 additions and 151 deletions

View File

@ -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>

View File

@ -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>

View File

@ -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 }) {

View File

@ -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