mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-10-27 11:18:14 +01:00 
			
		
		
		
	Change: Bookmark UI #115
This commit is contained in:
		
							parent
							
								
									76f28971a9
								
							
						
					
					
						commit
						908b9da112
					
				| @ -27,7 +27,7 @@ | ||||
| 
 | ||||
|     <audio-player ref="audioPlayer" :chapters="chapters" :loading="isLoading" :bookmarks="bookmarks" @close="cancelStream" @updateTime="updateTime" @loaded="(d) => (totalDuration = d)" @showBookmarks="showBookmarks" @hook:mounted="audioPlayerMounted" /> | ||||
| 
 | ||||
|     <modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :audiobook-id="bookmarkAudiobookId" :current-time="bookmarkCurrentTime" @select="selectBookmark" @create="createBookmark" @update="updateBookmark" @delete="deleteBookmark" /> | ||||
|     <modals-bookmarks-modal v-model="showBookmarksModal" :bookmarks="bookmarks" :audiobook-id="bookmarkAudiobookId" :current-time="bookmarkCurrentTime" @select="selectBookmark" /> | ||||
|   </div> | ||||
| </template> | ||||
| 
 | ||||
| @ -58,7 +58,7 @@ export default { | ||||
|     }, | ||||
|     bookmarks() { | ||||
|       if (!this.userAudiobook) return [] | ||||
|       return this.userAudiobook.bookmarks || [] | ||||
|       return (this.userAudiobook.bookmarks || []).map((bm) => ({ ...bm })).sort((a, b) => a.time - b.time) | ||||
|     }, | ||||
|     isLoading() { | ||||
|       if (!this.streamAudiobook) return false | ||||
| @ -111,38 +111,12 @@ export default { | ||||
|       this.bookmarkCurrentTime = currentTime | ||||
|       this.showBookmarksModal = true | ||||
|     }, | ||||
|     // bookmarkCreated(time) { | ||||
|     //   if (time === this.bookmarkTimeProcessing) { | ||||
|     //     this.bookmarkTimeProcessing = 0 | ||||
|     //     this.$toast.success(`${this.$secondsToTimestamp(time)} Bookmarked`) | ||||
|     //   } | ||||
|     // }, | ||||
|     createBookmark(bookmark) { | ||||
|       // this.bookmarkTimeProcessing = bookmark.time | ||||
|       this.$root.socket.emit('create_bookmark', bookmark) | ||||
|       this.showBookmarksModal = false | ||||
|     }, | ||||
|     // bookmarkUpdated(time) { | ||||
|     //   if (time === this.bookmarkTimeProcessing) { | ||||
|     //     this.bookmarkTimeProcessing = 0 | ||||
|     //     this.$toast.success(`Bookmark @${this.$secondsToTimestamp(time)} Updated`) | ||||
|     //   } | ||||
|     // }, | ||||
|     updateBookmark(bookmark) { | ||||
|       // this.bookmarkTimeProcessing = bookmark.time | ||||
|       this.$root.socket.emit('update_bookmark', bookmark) | ||||
|       this.showBookmarksModal = false | ||||
|     }, | ||||
|     selectBookmark(bookmark) { | ||||
|       if (this.$refs.audioPlayer) { | ||||
|         this.$refs.audioPlayer.selectBookmark(bookmark) | ||||
|       } | ||||
|       this.showBookmarksModal = false | ||||
|     }, | ||||
|     deleteBookmark(bookmark) { | ||||
|       this.$root.socket.emit('delete_bookmark', bookmark) | ||||
|       this.showBookmarksModal = false | ||||
|     }, | ||||
|     filterByAuthor() { | ||||
|       if (this.$route.name !== 'index') { | ||||
|         this.$router.push(`/library/${this.libraryId || this.$store.state.libraries.currentLibraryId}/bookshelf`) | ||||
|  | ||||
| @ -1,38 +1,27 @@ | ||||
| <template> | ||||
|   <modals-modal v-model="show" name="bookmarks" :width="500" :height="'unset'"> | ||||
|     <div ref="container" class="w-full rounded-lg bg-primary box-shadow-md overflow-y-auto overflow-x-hidden" style="max-height: 80vh"> | ||||
|       <div class="w-full h-full px-6 py-6" v-show="showBookmarkTitleInput"> | ||||
|         <div class="flex mb-4 items-center"> | ||||
|           <div class="w-9 h-9 flex items-center justify-center rounded-full hover:bg-white hover:bg-opacity-10 cursor-pointer" @click="showBookmarkTitleInput = false"> | ||||
|             <span class="material-icons text-3xl">arrow_back</span> | ||||
|           </div> | ||||
|           <p class="text-xl pl-2">{{ selectedBookmark ? 'Edit Bookmark' : 'New Bookmark' }}</p> | ||||
|           <div class="flex-grow" /> | ||||
|           <p class="text-xl font-mono"> | ||||
|             {{ this.$secondsToTimestamp(currentTime) }} | ||||
|           </p> | ||||
|         </div> | ||||
|         <form @submit.prevent="submitBookmark"> | ||||
|           <ui-text-input-with-label v-model="newBookmarkTitle" label="Note" /> | ||||
|           <div class="flex justify-end mt-6"> | ||||
|             <ui-btn color="success" class="w-1/2" type="submit">{{ selectedBookmark ? 'Update' : 'Create' }} Bookmark</ui-btn> | ||||
|           </div> | ||||
|         </form> | ||||
|       </div> | ||||
|       <div class="w-full h-full" v-show="!showBookmarkTitleInput"> | ||||
|       <div v-if="show" class="w-full h-full"> | ||||
|         <template v-for="bookmark in bookmarks"> | ||||
|           <modals-bookmarks-bookmark-item :key="bookmark.id" :highlight="currentTime === bookmark.time" :bookmark="bookmark" @click="clickBookmark" @edit="editBookmark" @delete="deleteBookmark" /> | ||||
|           <modals-bookmarks-bookmark-item :key="bookmark.id" :highlight="currentTime === bookmark.time" :bookmark="bookmark" @click="clickBookmark" @update="submitUpdateBookmark" @delete="deleteBookmark" /> | ||||
|         </template> | ||||
|         <div v-if="!bookmarks.length" class="flex h-32 items-center justify-center"> | ||||
|           <p class="text-xl">No Bookmarks</p> | ||||
|         </div> | ||||
|         <div v-show="canCreateBookmark" class="flex px-4 py-2 items-center text-center justify-between border-b border-white border-opacity-10 bg-blue-500 bg-opacity-20 cursor-pointer text-white text-opacity-80 hover:bg-opacity-40 hover:text-opacity-100" @click="createBookmark"> | ||||
|           <span class="material-icons">add</span> | ||||
|           <p class="text-base pl-2">Create Bookmark</p> | ||||
|         <div class="w-full h-px bg-white bg-opacity-10" /> | ||||
|         <form @submit.prevent="submitCreateBookmark"> | ||||
|           <div v-show="canCreateBookmark" class="flex px-4 py-2 items-center text-center border-b border-white border-opacity-10 text-white text-opacity-80"> | ||||
|             <div class="w-16 text-center"> | ||||
|               <p class="text-sm font-mono"> | ||||
|                 {{ this.$secondsToTimestamp(currentTime) }} | ||||
|               </p> | ||||
|             </div> | ||||
|             <div class="flex-grow px-2"> | ||||
|               <ui-text-input v-model="newBookmarkTitle" placeholder="Note" class="w-full" /> | ||||
|             </div> | ||||
|             <ui-btn type="submit" color="success" :padding-x="4" class="h-10"><span class="material-icons -mt-px">add</span></ui-btn> | ||||
|           </div> | ||||
|         </form> | ||||
|       </div> | ||||
|     </div> | ||||
|   </modals-modal> | ||||
| @ -88,34 +77,34 @@ export default { | ||||
|     }, | ||||
|     deleteBookmark(bm) { | ||||
|       var bookmark = { ...bm, audiobookId: this.audiobookId } | ||||
|       this.$emit('delete', bookmark) | ||||
|       this.$root.socket.emit('delete_bookmark', bookmark) | ||||
|       this.show = false | ||||
|     }, | ||||
|     clickBookmark(bm) { | ||||
|       this.$emit('select', bm) | ||||
|     }, | ||||
|     createBookmark() { | ||||
|       this.selectedBookmark = null | ||||
|       this.newBookmarkTitle = this.$formatDate(Date.now(), 'MMM dd, yyyy HH:mm') | ||||
|       this.showBookmarkTitleInput = true | ||||
|     }, | ||||
|     submitBookmark() { | ||||
|       if (this.selectedBookmark) { | ||||
|         if (this.selectedBookmark.title !== this.newBookmarkTitle) { | ||||
|           var bookmark = { ...this.selectedBookmark } | ||||
|     submitUpdateBookmark(updatedBookmark) { | ||||
|       var bookmark = { ...updatedBookmark } | ||||
|       bookmark.audiobookId = this.audiobookId | ||||
|           bookmark.title = this.newBookmarkTitle | ||||
|           this.$emit('update', bookmark) | ||||
| 
 | ||||
|       this.$root.socket.emit('update_bookmark', bookmark) | ||||
|       this.show = false | ||||
|     }, | ||||
|     submitCreateBookmark() { | ||||
|       if (!this.newBookmarkTitle) { | ||||
|         this.newBookmarkTitle = this.$formatDate(Date.now(), 'MMM dd, yyyy HH:mm') | ||||
|       } | ||||
|       } else { | ||||
|       var bookmark = { | ||||
|         audiobookId: this.audiobookId, | ||||
|         title: this.newBookmarkTitle, | ||||
|         time: this.currentTime | ||||
|       } | ||||
|         this.$emit('create', bookmark) | ||||
|       } | ||||
|       this.$root.socket.emit('create_bookmark', bookmark) | ||||
| 
 | ||||
|       this.newBookmarkTitle = '' | ||||
|       this.showBookmarkTitleInput = false | ||||
| 
 | ||||
|       this.show = false | ||||
|     } | ||||
|   } | ||||
| } | ||||
|  | ||||
| @ -1,15 +1,30 @@ | ||||
| <template> | ||||
|   <div :key="bookmark.id" :id="`bookmark-row-${bookmark.id}`" class="flex items-center px-4 py-4 justify-start cursor-pointer hover:bg-bg relative" :class="highlight ? 'bg-bg bg-opacity-60' : ' bg-opacity-20'" @click="click" @mouseover="isHovering = true" @mouseleave="isHovering = false"> | ||||
|     <span class="material-icons" :class="highlight ? 'text-success' : 'text-white text-opacity-60'">{{ highlight ? 'bookmark' : 'bookmark_border' }}</span> | ||||
|   <div :key="bookmark.id" :id="`bookmark-row-${bookmark.id}`" class="flex items-center px-4 py-4 justify-start relative hover:bg-bg" :class="wrapperClass" @click="click" @mouseover="mouseover" @mouseleave="mouseleave"> | ||||
|     <!-- <span class="material-icons" :class="highlight ? 'text-success' : 'text-white text-opacity-80'">{{ highlight ? 'bookmark' : 'bookmark_border' }}</span> --> | ||||
|     <div class="w-12 min-w-12 text-center"> | ||||
|       <p class="text-sm font-mono text-white text-opacity-80"> | ||||
|         {{ this.$secondsToTimestamp(bookmark.time) }} | ||||
|       </p> | ||||
|     </div> | ||||
|     <div class="flex-grow overflow-hidden"> | ||||
|       <p class="pl-2 pr-2 truncate">{{ bookmark.title }}</p> | ||||
|       <template v-if="isEditing"> | ||||
|         <form @submit.prevent="submitUpdate"> | ||||
|           <div class="flex items-center"> | ||||
|             <div class="flex-grow pr-2"> | ||||
|               <ui-text-input v-model="newBookmarkTitle" placeholder="Note" class="w-full" /> | ||||
|             </div> | ||||
|     <div class="h-full flex items-center w-16 justify-end"> | ||||
|       <span class="font-mono text-sm text-gray-300">{{ $secondsToTimestamp(bookmark.time) }}</span> | ||||
|             <ui-btn type="submit" color="success" :padding-x="4" class="h-10"><span class="material-icons -mt-px">forward</span></ui-btn> | ||||
|             <div class="pl-2 flex items-center"> | ||||
|               <span class="material-icons text-3xl text-white text-opacity-70 hover:text-opacity-95 cursor-pointer" @click.stop.prevent="cancelEditing">close</span> | ||||
|             </div> | ||||
|     <div class="h-full flex items-center justify-end transform" :class="isHovering ? 'transition-transform translate-0 w-16' : 'translate-x-40 w-0'"> | ||||
|       <span class="material-icons text-lg mr-2 text-gray-200 hover:text-yellow-400" @click.stop="editClick">edit</span> | ||||
|       <span class="material-icons text-lg text-gray-200 hover:text-error cursor-pointer" @click.stop="deleteClick">delete</span> | ||||
|           </div> | ||||
|         </form> | ||||
|       </template> | ||||
|       <p v-else class="pl-2 pr-2 truncate">{{ bookmark.title }}</p> | ||||
|     </div> | ||||
|     <div v-if="!isEditing" class="h-full flex items-center justify-end transform" :class="isHovering ? 'transition-transform translate-0 w-16' : 'translate-x-40 w-0'"> | ||||
|       <span class="material-icons text-xl mr-2 text-gray-200 hover:text-yellow-400" @click.stop="editClick">edit</span> | ||||
|       <span class="material-icons text-xl text-gray-200 hover:text-error cursor-pointer" @click.stop="deleteClick">delete</span> | ||||
|     </div> | ||||
|   </div> | ||||
| </template> | ||||
| @ -25,19 +40,53 @@ export default { | ||||
|   }, | ||||
|   data() { | ||||
|     return { | ||||
|       isHovering: false | ||||
|       isHovering: false, | ||||
|       isEditing: false, | ||||
|       newBookmarkTitle: null | ||||
|     } | ||||
|   }, | ||||
|   computed: { | ||||
|     wrapperClass() { | ||||
|       var classes = [] | ||||
|       if (this.highlight) classes.push('bg-bg bg-opacity-60') | ||||
|       if (!this.isEditing) classes.push('cursor-pointer') | ||||
|       return classes.join(' ') | ||||
|     } | ||||
|   }, | ||||
|   computed: {}, | ||||
|   methods: { | ||||
|     click() { | ||||
|     mouseover() { | ||||
|       if (this.isEditing) return | ||||
|       this.isHovering = true | ||||
|     }, | ||||
|     mouseleave() { | ||||
|       this.isHovering = false | ||||
|     }, | ||||
|     click(e) { | ||||
|       if (this.isEditing) { | ||||
|         if (e) e.stopPropagation() | ||||
|         return | ||||
|       } | ||||
|       this.$emit('click', this.bookmark) | ||||
|     }, | ||||
|     deleteClick() { | ||||
|       if (this.isEditing) return | ||||
|       this.$emit('delete', this.bookmark) | ||||
|     }, | ||||
|     editClick() { | ||||
|       this.$emit('edit', this.bookmark) | ||||
|       this.newBookmarkTitle = this.bookmark.title | ||||
|       this.isEditing = true | ||||
|       this.isHovering = false | ||||
|     }, | ||||
|     cancelEditing() { | ||||
|       this.isEditing = false | ||||
|     }, | ||||
|     submitUpdate() { | ||||
|       if (this.newBookmarkTitle === this.bookmark.title) { | ||||
|         return this.cancelEditing() | ||||
|       } | ||||
|       var bookmark = { ...this.bookmark } | ||||
|       bookmark.title = this.newBookmarkTitle | ||||
|       this.$emit('update', bookmark) | ||||
|     } | ||||
|   }, | ||||
|   mounted() {} | ||||
|  | ||||
							
								
								
									
										8
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										8
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "audiobookshelf", | ||||
|   "version": "1.4.13", | ||||
|   "version": "1.6.2", | ||||
|   "lockfileVersion": 1, | ||||
|   "requires": true, | ||||
|   "dependencies": { | ||||
| @ -1222,9 +1222,9 @@ | ||||
|       "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" | ||||
|     }, | ||||
|     "njodb": { | ||||
|       "version": "0.4.21", | ||||
|       "resolved": "https://registry.npmjs.org/njodb/-/njodb-0.4.21.tgz", | ||||
|       "integrity": "sha512-3qLMzwIZUgT1yq2PCzJlT6FFK/zfLHz71QnFeE9ec4KKJH9abY4SXnmHVaWP7wVq+lY77wW1F+EeKG9gm8j6WA==", | ||||
|       "version": "0.4.22", | ||||
|       "resolved": "https://registry.npmjs.org/njodb/-/njodb-0.4.22.tgz", | ||||
|       "integrity": "sha512-/paIiYKICyV/z540d27dF54y3Tv/DgY7MY/jQxcMLALyFpdYY/xSu+o78nko/3FpGBt34KPPlNOwnzLsy+WuxA==", | ||||
|       "requires": { | ||||
|         "proper-lockfile": "^4.1.2" | ||||
|       } | ||||
|  | ||||
| @ -38,7 +38,7 @@ | ||||
|     "ip": "^1.1.5", | ||||
|     "jsonwebtoken": "^8.5.1", | ||||
|     "libgen": "^2.1.0", | ||||
|     "njodb": "^0.4.21", | ||||
|     "njodb": "^0.4.22", | ||||
|     "node-cron": "^3.0.0", | ||||
|     "node-dir": "^0.1.17", | ||||
|     "node-stream-zip": "^1.15.0", | ||||
|  | ||||
							
								
								
									
										10
									
								
								server/Db.js
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								server/Db.js
									
									
									
									
									
								
							| @ -170,7 +170,15 @@ class Db { | ||||
| 
 | ||||
|   updateEntity(entityName, entity) { | ||||
|     var entityDb = this.getEntityDb(entityName) | ||||
|     return entityDb.update((record) => record.id === entity.id, () => entity).then((results) => { | ||||
| 
 | ||||
|     var jsonEntity = entity | ||||
|     if (entity && entity.toJSON) { | ||||
|       jsonEntity = entity.toJSON() | ||||
|     } else { | ||||
|       console.log('Entity has no json', jsonEntity) | ||||
|     } | ||||
| 
 | ||||
|     return entityDb.update((record) => record.id === entity.id, () => jsonEntity).then((results) => { | ||||
|       Logger.debug(`[DB] Updated entity ${entityName}: ${results.updated}`) | ||||
|       var arrayKey = this.getEntityArrayKey(entityName) | ||||
|       this[arrayKey] = this[arrayKey].map(e => { | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user