Update:Paginated listening sessions

This commit is contained in:
advplyr 2022-06-04 10:52:37 -05:00
parent 0e1692d26b
commit 3171ce5aba
7 changed files with 122 additions and 49 deletions

View File

@ -1,5 +1,5 @@
<template> <template>
<button class="icon-btn rounded-md flex items-center justify-center h-9 w-9 relative" @mousedown.prevent :disabled="disabled || loading" :class="className" @click="clickBtn"> <button class="icon-btn rounded-md flex items-center justify-center relative" @mousedown.prevent :disabled="disabled || loading" :class="className" @click="clickBtn">
<div v-if="loading" class="text-white absolute top-0 left-0 w-full h-full flex items-center justify-center text-opacity-100"> <div v-if="loading" class="text-white absolute top-0 left-0 w-full h-full flex items-center justify-center text-opacity-100">
<svg class="animate-spin" style="width: 24px; height: 24px" viewBox="0 0 24 24"> <svg class="animate-spin" style="width: 24px; height: 24px" viewBox="0 0 24 24">
<path fill="currentColor" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" /> <path fill="currentColor" d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z" />
@ -20,20 +20,29 @@ export default {
}, },
outlined: Boolean, outlined: Boolean,
borderless: Boolean, borderless: Boolean,
loading: Boolean loading: Boolean,
iconFontSize: {
type: String,
default: ''
},
size: {
type: Number,
default: 9
}
}, },
data() { data() {
return {} return {}
}, },
computed: { computed: {
className() { className() {
var classes = [] var classes = [`h-${this.size} w-${this.size}`]
if (!this.borderless) { if (!this.borderless) {
classes.push(`bg-${this.bgColor} border border-gray-600`) classes.push(`bg-${this.bgColor} border border-gray-600`)
} }
return classes.join(' ') return classes.join(' ')
}, },
fontSize() { fontSize() {
if (this.iconFontSize) return this.iconFontSize
if (this.icon === 'edit') return '1.25rem' if (this.icon === 'edit') return '1.25rem'
return '1.4rem' return '1.4rem'
} }

View File

@ -138,7 +138,9 @@ export default {
this.$copyToClipboard(str, this) this.$copyToClipboard(str, this)
}, },
async init() { async init() {
this.listeningSessions = await this.$axios.$get(`/api/users/${this.user.id}/listening-sessions`).catch((err) => { this.listeningSessions = await this.$axios.$get(`/api/users/${this.user.id}/listening-sessions?page=0&itemsPerPage=10`).then((data) => {
return data.sessions || []
}).catch((err) => {
console.error('Failed to load listening sesions', err) console.error('Failed to load listening sesions', err)
return [] return []
}) })

View File

@ -18,7 +18,8 @@
<div class="py-2"> <div class="py-2">
<h1 class="text-lg mb-2 text-white text-opacity-90 px-2 sm:px-0">Listening Sessions ({{ listeningSessions.length }})</h1> <h1 class="text-lg mb-2 text-white text-opacity-90 px-2 sm:px-0">Listening Sessions ({{ listeningSessions.length }})</h1>
<table v-if="listeningSessions.length" class="userSessionsTable"> <div v-if="listeningSessions.length">
<table class="userSessionsTable">
<tr class="bg-primary bg-opacity-40"> <tr class="bg-primary bg-opacity-40">
<th class="flex-grow text-left">Item</th> <th class="flex-grow text-left">Item</th>
<th class="w-32 text-left hidden md:table-cell">Play Method</th> <th class="w-32 text-left hidden md:table-cell">Play Method</th>
@ -51,6 +52,12 @@
</td> </td>
</tr> </tr>
</table> </table>
<div class="flex items-center justify-end py-1">
<ui-icon-btn icon="arrow_back_ios_new" :size="7" icon-font-size="1rem" class="mx-1" :disabled="currentPage === 0" @click="prevPage" />
<p class="text-sm mx-1">Page {{ currentPage + 1 }} of {{ numPages }}</p>
<ui-icon-btn icon="arrow_forward_ios" :size="7" icon-font-size="1rem" class="mx-1" :disabled="currentPage >= numPages - 1" @click="nextPage" />
</div>
</div>
<p v-else class="text-white text-opacity-50">No sessions yet...</p> <p v-else class="text-white text-opacity-50">No sessions yet...</p>
</div> </div>
</div> </div>
@ -75,7 +82,10 @@ export default {
return { return {
showSessionModal: false, showSessionModal: false,
selectedSession: null, selectedSession: null,
listeningSessions: [] listeningSessions: [],
numPages: 0,
total: 0,
currentPage: 0
} }
}, },
computed: { computed: {
@ -87,6 +97,12 @@ export default {
} }
}, },
methods: { methods: {
prevPage() {
this.loadSessions(this.currentPage - 1)
},
nextPage() {
this.loadSessions(this.currentPage + 1)
},
showSession(session) { showSession(session) {
this.selectedSession = session this.selectedSession = session
this.showSessionModal = true this.showSessionModal = true
@ -108,13 +124,23 @@ export default {
else if (playMethod === this.$constants.PlayMethod.LOCAL) return 'Local' else if (playMethod === this.$constants.PlayMethod.LOCAL) return 'Local'
return 'Unknown' return 'Unknown'
}, },
async init() { async loadSessions(page) {
console.log(navigator) const data = await this.$axios.$get(`/api/users/${this.user.id}/listening-sessions?page=${page}&itemsPerPage=10`).catch((err) => {
this.listeningSessions = await this.$axios.$get(`/api/users/${this.user.id}/listening-sessions`).catch((err) => {
console.error('Failed to load listening sesions', err) console.error('Failed to load listening sesions', err)
return [] return null
}) })
if (!data) {
this.$toast.error('Failed to load listening sessions')
return
}
this.numPages = data.numPages
this.total = data.total
this.currentPage = data.page
this.listeningSessions = data.sessions
},
init() {
this.loadSessions(0)
} }
}, },
mounted() { mounted() {

View File

@ -1,5 +1,5 @@
const Logger = require('../Logger') const Logger = require('../Logger')
const { isObject } = require('../utils/index') const { isObject, toNumber } = require('../utils/index')
class MeController { class MeController {
constructor() { } constructor() { }
@ -7,7 +7,22 @@ class MeController {
// GET: api/me/listening-sessions // GET: api/me/listening-sessions
async getListeningSessions(req, res) { async getListeningSessions(req, res) {
var listeningSessions = await this.getUserListeningSessionsHelper(req.user.id) var listeningSessions = await this.getUserListeningSessionsHelper(req.user.id)
res.json(listeningSessions.slice(0, 10))
const itemsPerPage = toNumber(req.query.itemsPerPage, 10) || 10
const page = toNumber(req.query.page, 0)
const start = page * itemsPerPage
const sessions = listeningSessions.slice(start, start + itemsPerPage)
const payload = {
total: listeningSessions.length,
numPages: Math.ceil(listeningSessions.length / itemsPerPage),
page,
itemsPerPage,
sessions
}
res.json(payload)
} }
// GET: api/me/listening-stats // GET: api/me/listening-stats

View File

@ -1,7 +1,7 @@
const Logger = require('../Logger') const Logger = require('../Logger')
const User = require('../objects/user/User') const User = require('../objects/user/User')
const { getId } = require('../utils/index') const { getId, toNumber } = require('../utils/index')
class UserController { class UserController {
constructor() { } constructor() { }
@ -142,8 +142,24 @@ class UserController {
if (!req.user.isAdminOrUp && req.user.id !== req.params.id) { if (!req.user.isAdminOrUp && req.user.id !== req.params.id) {
return res.sendStatus(403) return res.sendStatus(403)
} }
var listeningSessions = await this.getUserListeningSessionsHelper(req.params.id) var listeningSessions = await this.getUserListeningSessionsHelper(req.params.id)
res.json(listeningSessions.slice(0, 10))
const itemsPerPage = toNumber(req.query.itemsPerPage, 10) || 10
const page = toNumber(req.query.page, 0)
const start = page * itemsPerPage
const sessions = listeningSessions.slice(start, start + itemsPerPage)
const payload = {
total: listeningSessions.length,
numPages: Math.ceil(listeningSessions.length / itemsPerPage),
page,
itemsPerPage,
sessions
}
res.json(payload)
} }
// GET: api/users/:id/listening-stats // GET: api/users/:id/listening-stats

View File

@ -696,7 +696,7 @@ class Scanner {
} }
// Add or set author if not set // Add or set author if not set
if (matchData.author && !libraryItem.media.metadata.authorName || options.overrideDetails) { if (matchData.author && (!libraryItem.media.metadata.authorName || options.overrideDetails)) {
if (!Array.isArray(matchData.author)) matchData.author = [matchData.author] if (!Array.isArray(matchData.author)) matchData.author = [matchData.author]
const authorPayload = [] const authorPayload = []
for (let index = 0; index < matchData.author.length; index++) { for (let index = 0; index < matchData.author.length; index++) {
@ -714,7 +714,7 @@ class Scanner {
} }
// Add or set series if not set // Add or set series if not set
if (matchData.series && !libraryItem.media.metadata.seriesName || options.overrideDetails) { if (matchData.series && (!libraryItem.media.metadata.seriesName || options.overrideDetails)) {
if (!Array.isArray(matchData.series)) matchData.series = [{ series: matchData.series, volumeNumber: matchData.volumeNumber }] if (!Array.isArray(matchData.series)) matchData.series = [{ series: matchData.series, volumeNumber: matchData.volumeNumber }]
const seriesPayload = [] const seriesPayload = []
for (let index = 0; index < matchData.series.length; index++) { for (let index = 0; index < matchData.series.length; index++) {

View File

@ -125,3 +125,8 @@ module.exports.copyValue = (val) => {
module.exports.encodeUriPath = (path) => { module.exports.encodeUriPath = (path) => {
return path.replace(/\\/g, '/').replace(/%/g, '%25').replace(/#/g, '%23') return path.replace(/\\/g, '/').replace(/%/g, '%25').replace(/#/g, '%23')
} }
module.exports.toNumber = (val, fallback = 0) => {
if (isNaN(val) || val === null) return fallback
return Number(val)
}