mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2025-08-30 22:59:37 +02:00
Update users table info #94, Reorder libraries in config #95, Use dropdown for library menu #96, update mobi reader
This commit is contained in:
parent
9715c53332
commit
cd6e99b4c3
30 changed files with 1361 additions and 557 deletions
|
@ -1,25 +1,42 @@
|
|||
<template>
|
||||
<div class="bg-bg rounded-md shadow-lg border border-white border-opacity-5 p-4 mb-8">
|
||||
<div id="librariesTable" class="bg-bg rounded-md shadow-lg border border-white border-opacity-5 p-4 mb-8">
|
||||
<div class="flex items-center mb-2">
|
||||
<h1 class="text-xl">Libraries</h1>
|
||||
<div class="mx-2 w-7 h-7 flex items-center justify-center rounded-full cursor-pointer hover:bg-white hover:bg-opacity-10 text-center" @click="clickAddLibrary">
|
||||
<span class="material-icons" style="font-size: 1.4rem">add</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<template v-for="library in libraries">
|
||||
<modals-libraries-library-item :key="library.id" :library="library" :selected="currentLibraryId === library.id" :show-edit="true" @edit="editLibrary" @click="clickLibrary" />
|
||||
</template>
|
||||
<draggable v-model="libraryCopies" v-bind="dragOptions" class="list-group" draggable=".item" tag="div" @start="startDrag" @end="endDrag">
|
||||
<!-- <transition-group type="transition" :name="!drag ? 'flip-list' : null"> -->
|
||||
<template v-for="library in libraryCopies">
|
||||
<modals-libraries-library-item :key="library.id" :library="library" :selected="currentLibraryId === library.id" :show-edit="true" :dragging="drag" @edit="editLibrary" @click="setLibrary" class="item" />
|
||||
</template>
|
||||
<!-- </transition-group> -->
|
||||
</draggable>
|
||||
<modals-edit-library-modal v-model="showLibraryModal" :library="selectedLibrary" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import draggable from 'vuedraggable'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
draggable
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
libraryCopies: [],
|
||||
currentOrder: [],
|
||||
showLibraryModal: false,
|
||||
selectedLibrary: null
|
||||
selectedLibrary: null,
|
||||
drag: false,
|
||||
dragOptions: {
|
||||
animation: 200,
|
||||
group: 'description',
|
||||
ghostClass: 'ghost'
|
||||
},
|
||||
orderTimeout: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -30,11 +47,46 @@ export default {
|
|||
return this.currentLibrary ? this.currentLibrary.id : null
|
||||
},
|
||||
libraries() {
|
||||
return this.$store.state.libraries.libraries
|
||||
return this.$store.getters['libraries/getSortedLibraries']()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async clickLibrary(library) {
|
||||
startDrag() {
|
||||
this.drag = true
|
||||
clearTimeout(this.orderTimeout)
|
||||
},
|
||||
endDrag() {
|
||||
this.drag = false
|
||||
this.checkOrder()
|
||||
console.log('DRAG END')
|
||||
},
|
||||
checkOrder() {
|
||||
clearTimeout(this.orderTimeout)
|
||||
this.orderTimeout = setTimeout(() => {
|
||||
this.saveOrder()
|
||||
}, 500)
|
||||
},
|
||||
saveOrder() {
|
||||
var _newOrder = 1
|
||||
var currOrder = this.libraries.map((lib) => lib.id).join(',')
|
||||
var libraryOrderData = this.libraryCopies.map((library) => {
|
||||
return {
|
||||
newOrder: _newOrder++,
|
||||
oldOrder: library.displayOrder,
|
||||
id: library.id
|
||||
}
|
||||
})
|
||||
var newOrder = libraryOrderData.map((lib) => lib.id).join(',')
|
||||
if (currOrder !== newOrder) {
|
||||
this.$axios.$patch('/api/libraries/order', libraryOrderData).then((libraries) => {
|
||||
if (libraries && libraries.length) {
|
||||
this.$toast.success('Library order saved', { timeout: 1500 })
|
||||
this.$store.commit('libraries/set', libraries)
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
async setLibrary(library) {
|
||||
await this.$store.dispatch('libraries/fetch', library.id)
|
||||
this.$router.push(`/library/${library.id}`)
|
||||
},
|
||||
|
@ -46,7 +98,11 @@ export default {
|
|||
this.selectedLibrary = library
|
||||
this.showLibraryModal = true
|
||||
},
|
||||
init() {}
|
||||
init() {
|
||||
this.libraryCopies = this.libraries.map((lib) => {
|
||||
return { ...lib }
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
|
|
|
@ -11,17 +11,43 @@
|
|||
<table id="accounts">
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>Account Type</th>
|
||||
<th style="width: 200px">Created At</th>
|
||||
<th style="width: 100px"></th>
|
||||
<th class="w-20">Type</th>
|
||||
<th>Activity</th>
|
||||
<th class="w-32">Last Seen</th>
|
||||
<th class="w-32">Created</th>
|
||||
<th class="w-32"></th>
|
||||
</tr>
|
||||
<tr v-for="user in users" :key="user.id" :class="user.isActive ? '' : 'bg-error bg-opacity-20'">
|
||||
<td>
|
||||
{{ user.username }} <span class="text-xs text-gray-400 italic pl-4">({{ user.id }})</span>
|
||||
<div class="flex items-center">
|
||||
<span v-if="usersOnline[user.id]" class="w-3 h-3 text-sm mr-2 text-success animate-pulse"
|
||||
><svg viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" /></svg
|
||||
></span>
|
||||
<svg v-else class="w-3 h-3 mr-2 text-white text-opacity-20" viewBox="0 0 24 24">
|
||||
<path fill="currentColor" d="M12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" />
|
||||
</svg>
|
||||
{{ user.username }} <span v-show="$isDev" class="text-xs text-gray-400 italic pl-4">({{ user.id }})</span>
|
||||
</div>
|
||||
</td>
|
||||
<td>{{ user.type }}</td>
|
||||
<td class="text-sm font-mono">
|
||||
{{ new Date(user.createdAt).toISOString() }}
|
||||
<td class="text-sm">{{ user.type }}</td>
|
||||
<td>
|
||||
<div v-if="usersOnline[user.id] && usersOnline[user.id].stream && usersOnline[user.id].stream.audiobook && usersOnline[user.id].stream.audiobook.book">
|
||||
<p class="truncate text-xs">Reading: {{ usersOnline[user.id].stream.audiobook.book.title || '' }}</p>
|
||||
</div>
|
||||
<div v-else-if="user.audiobooks && getLastRead(user.audiobooks)">
|
||||
<p class="truncate text-xs">Last: {{ getLastRead(user.audiobooks) }}</p>
|
||||
</div>
|
||||
</td>
|
||||
<td class="text-xs font-mono">
|
||||
<ui-tooltip v-if="user.lastSeen" direction="top" :text="$formatDate(user.lastSeen, 'MMMM do, yyyy HH:mm')">
|
||||
{{ $dateDistanceFromNow(user.lastSeen) }}
|
||||
</ui-tooltip>
|
||||
</td>
|
||||
<td class="text-xs font-mono">
|
||||
<ui-tooltip direction="top" :text="$formatDate(user.createdAt, 'MMMM do, yyyy HH:mm')">
|
||||
{{ $formatDate(user.createdAt, 'MMM d, yyyy') }}
|
||||
</ui-tooltip>
|
||||
</td>
|
||||
<td>
|
||||
<div class="w-full flex justify-center">
|
||||
|
@ -47,8 +73,41 @@ export default {
|
|||
isDeletingUser: false
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
computed: {
|
||||
currentUserId() {
|
||||
return this.$store.state.user.user.id
|
||||
},
|
||||
userStream() {
|
||||
return this.$store.state.streamAudiobook
|
||||
},
|
||||
usersOnline() {
|
||||
var _users = this.$store.state.users.users
|
||||
|
||||
var currUserStream = null
|
||||
if (this.userStream) {
|
||||
currUserStream = {
|
||||
audiobook: this.userStream
|
||||
}
|
||||
}
|
||||
var usermap = {
|
||||
[this.currentUserId]: {
|
||||
online: true,
|
||||
stream: currUserStream
|
||||
}
|
||||
}
|
||||
_users.forEach((u) => (usermap[u.id] = { online: true, stream: u.stream }))
|
||||
return usermap
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getLastRead(audiobooks) {
|
||||
var abs = Object.values(audiobooks)
|
||||
if (abs.length) {
|
||||
abs = abs.sort((a, b) => a.lastUpdate - b.lastUpdate)
|
||||
return abs[0] && abs[0].audiobookTitle ? abs[0].audiobookTitle : null
|
||||
}
|
||||
return null
|
||||
},
|
||||
deleteUserClick(user) {
|
||||
if (this.isDeletingUser) return
|
||||
if (confirm(`Are you sure you want to permanently delete user "${user.username}"?`)) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue