mirror of
				https://github.com/advplyr/audiobookshelf.git
				synced 2025-11-03 19:07:00 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			232 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			232 lines
		
	
	
		
			6.9 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
<template>
 | 
						|
  <div class="w-full h-full relative">
 | 
						|
    <form class="w-full h-full" @submit.prevent="submitForm">
 | 
						|
      <div ref="formWrapper" class="px-4 py-6 details-form-wrapper w-full overflow-hidden overflow-y-auto">
 | 
						|
        <!-- <div v-if="userProgress" class="bg-success bg-opacity-40 rounded-md w-full px-4 py-1 mb-4 border border-success border-opacity-50">
 | 
						|
        <div class="w-full flex items-center">
 | 
						|
          <p>
 | 
						|
            Your progress: <span class="font-mono text-lg">{{ (userProgress * 100).toFixed(0) }}%</span>
 | 
						|
          </p>
 | 
						|
          <div class="flex-grow" />
 | 
						|
          <ui-btn v-if="!resettingProgress" small :padding-x="2" class="-mr-3" @click="resetProgress">Reset</ui-btn>
 | 
						|
        </div>
 | 
						|
      </div> -->
 | 
						|
 | 
						|
        <ui-text-input-with-label v-model="details.title" label="Title" />
 | 
						|
 | 
						|
        <ui-text-input-with-label v-model="details.subtitle" label="Subtitle" class="mt-2" />
 | 
						|
 | 
						|
        <div class="flex mt-2 -mx-1">
 | 
						|
          <div class="w-3/4 px-1">
 | 
						|
            <ui-text-input-with-label v-model="details.author" label="Author" />
 | 
						|
          </div>
 | 
						|
          <div class="flex-grow px-1">
 | 
						|
            <ui-text-input-with-label v-model="details.publishYear" type="number" label="Publish Year" />
 | 
						|
          </div>
 | 
						|
        </div>
 | 
						|
 | 
						|
        <div class="flex mt-2 -mx-1">
 | 
						|
          <div class="w-3/4 px-1">
 | 
						|
            <ui-input-dropdown v-model="details.series" label="Series" :items="series" />
 | 
						|
          </div>
 | 
						|
          <div class="flex-grow px-1">
 | 
						|
            <ui-text-input-with-label v-model="details.volumeNumber" label="Volume #" />
 | 
						|
          </div>
 | 
						|
        </div>
 | 
						|
 | 
						|
        <ui-textarea-with-label v-model="details.description" :rows="3" label="Description" class="mt-2" />
 | 
						|
 | 
						|
        <div class="flex mt-2 -mx-1">
 | 
						|
          <div class="w-1/2 px-1">
 | 
						|
            <ui-multi-select v-model="details.genres" label="Genres" :items="genres" />
 | 
						|
          </div>
 | 
						|
          <div class="flex-grow px-1">
 | 
						|
            <ui-multi-select v-model="newTags" label="Tags" :items="tags" />
 | 
						|
          </div>
 | 
						|
        </div>
 | 
						|
 | 
						|
        <div class="flex mt-2 -mx-1">
 | 
						|
          <div class="w-1/2 px-1">
 | 
						|
            <ui-text-input-with-label v-model="details.narrarator" label="Narrarator" />
 | 
						|
          </div>
 | 
						|
        </div>
 | 
						|
      </div>
 | 
						|
 | 
						|
      <div class="absolute bottom-0 left-0 w-full py-4 bg-bg" :class="isScrollable ? 'box-shadow-md-up' : 'box-shadow-sm-up border-t border-primary border-opacity-50'">
 | 
						|
        <div class="flex px-4">
 | 
						|
          <ui-btn v-if="userCanDelete" color="error" type="button" small @click.stop.prevent="deleteAudiobook">Remove</ui-btn>
 | 
						|
          <div class="flex-grow" />
 | 
						|
          <ui-btn type="submit">Submit</ui-btn>
 | 
						|
        </div>
 | 
						|
      </div>
 | 
						|
    </form>
 | 
						|
  </div>
 | 
						|
</template>
 | 
						|
 | 
						|
<script>
 | 
						|
export default {
 | 
						|
  props: {
 | 
						|
    processing: Boolean,
 | 
						|
    audiobook: {
 | 
						|
      type: Object,
 | 
						|
      default: () => {}
 | 
						|
    }
 | 
						|
  },
 | 
						|
  data() {
 | 
						|
    return {
 | 
						|
      details: {
 | 
						|
        title: null,
 | 
						|
        subtitle: null,
 | 
						|
        description: null,
 | 
						|
        author: null,
 | 
						|
        narrarator: null,
 | 
						|
        series: null,
 | 
						|
        volumeNumber: null,
 | 
						|
        publishYear: null,
 | 
						|
        genres: []
 | 
						|
      },
 | 
						|
      newTags: [],
 | 
						|
      resettingProgress: false,
 | 
						|
      isScrollable: false
 | 
						|
    }
 | 
						|
  },
 | 
						|
  watch: {
 | 
						|
    audiobook: {
 | 
						|
      immediate: true,
 | 
						|
      handler(newVal) {
 | 
						|
        if (newVal) this.init()
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
  computed: {
 | 
						|
    isProcessing: {
 | 
						|
      get() {
 | 
						|
        return this.processing
 | 
						|
      },
 | 
						|
      set(val) {
 | 
						|
        this.$emit('update:processing', val)
 | 
						|
      }
 | 
						|
    },
 | 
						|
    audiobookId() {
 | 
						|
      return this.audiobook ? this.audiobook.id : null
 | 
						|
    },
 | 
						|
    book() {
 | 
						|
      return this.audiobook ? this.audiobook.book || {} : {}
 | 
						|
    },
 | 
						|
    userCanDelete() {
 | 
						|
      return this.$store.getters['user/getUserCanDelete']
 | 
						|
    },
 | 
						|
    genres() {
 | 
						|
      return this.$store.state.audiobooks.genres
 | 
						|
    },
 | 
						|
    tags() {
 | 
						|
      return this.$store.state.audiobooks.tags
 | 
						|
    },
 | 
						|
    series() {
 | 
						|
      return this.$store.state.audiobooks.series
 | 
						|
    }
 | 
						|
  },
 | 
						|
  methods: {
 | 
						|
    async submitForm() {
 | 
						|
      if (this.isProcessing) {
 | 
						|
        return
 | 
						|
      }
 | 
						|
      this.isProcessing = true
 | 
						|
      const updatePayload = {
 | 
						|
        book: this.details,
 | 
						|
        tags: this.newTags
 | 
						|
      }
 | 
						|
 | 
						|
      var updatedAudiobook = await this.$axios.$patch(`/api/audiobook/${this.audiobook.id}`, updatePayload).catch((error) => {
 | 
						|
        console.error('Failed to update', error)
 | 
						|
        return false
 | 
						|
      })
 | 
						|
      this.isProcessing = false
 | 
						|
      if (updatedAudiobook) {
 | 
						|
        this.$toast.success('Update Successful')
 | 
						|
        this.$emit('close')
 | 
						|
      }
 | 
						|
    },
 | 
						|
    init() {
 | 
						|
      this.details.title = this.book.title
 | 
						|
      this.details.subtitle = this.book.subtitle
 | 
						|
      this.details.description = this.book.description
 | 
						|
      this.details.author = this.book.author
 | 
						|
      this.details.narrarator = this.book.narrarator
 | 
						|
      this.details.genres = this.book.genres || []
 | 
						|
      this.details.series = this.book.series
 | 
						|
      this.details.volumeNumber = this.book.volumeNumber
 | 
						|
      this.details.publishYear = this.book.publishYear
 | 
						|
 | 
						|
      this.newTags = this.audiobook.tags || []
 | 
						|
    },
 | 
						|
    resetProgress() {
 | 
						|
      if (confirm(`Are you sure you want to reset your progress?`)) {
 | 
						|
        this.resettingProgress = true
 | 
						|
        this.$axios
 | 
						|
          .$delete(`/api/user/audiobook/${this.audiobookId}`)
 | 
						|
          .then(() => {
 | 
						|
            console.log('Progress reset complete')
 | 
						|
            this.$toast.success(`Your progress was reset`)
 | 
						|
            this.resettingProgress = false
 | 
						|
          })
 | 
						|
          .catch((error) => {
 | 
						|
            console.error('Progress reset failed', error)
 | 
						|
            this.resettingProgress = false
 | 
						|
          })
 | 
						|
      }
 | 
						|
    },
 | 
						|
    deleteAudiobook() {
 | 
						|
      if (confirm(`Are you sure you want to remove this audiobook?\n\n*Does not delete your files, only removes the audiobook from AudioBookshelf`)) {
 | 
						|
        this.isProcessing = true
 | 
						|
        this.$axios
 | 
						|
          .$delete(`/api/audiobook/${this.audiobookId}`)
 | 
						|
          .then(() => {
 | 
						|
            console.log('Audiobook removed')
 | 
						|
            this.$toast.success('Audiobook Removed')
 | 
						|
            this.$emit('close')
 | 
						|
            this.isProcessing = false
 | 
						|
          })
 | 
						|
          .catch((error) => {
 | 
						|
            console.error('Remove Audiobook failed', error)
 | 
						|
            this.isProcessing = false
 | 
						|
          })
 | 
						|
      }
 | 
						|
    },
 | 
						|
    checkIsScrollable() {
 | 
						|
      this.$nextTick(() => {
 | 
						|
        if (this.$refs.formWrapper) {
 | 
						|
          if (this.$refs.formWrapper.scrollHeight > this.$refs.formWrapper.clientHeight) {
 | 
						|
            this.isScrollable = true
 | 
						|
          } else {
 | 
						|
            this.isScrollable = false
 | 
						|
          }
 | 
						|
        }
 | 
						|
      })
 | 
						|
    },
 | 
						|
    setResizeObserver() {
 | 
						|
      try {
 | 
						|
        this.$nextTick(() => {
 | 
						|
          const resizeObserver = new ResizeObserver(() => {
 | 
						|
            this.checkIsScrollable()
 | 
						|
          })
 | 
						|
          resizeObserver.observe(this.$refs.formWrapper)
 | 
						|
        })
 | 
						|
      } catch (error) {
 | 
						|
        console.error('Failed to set resize observer')
 | 
						|
      }
 | 
						|
    }
 | 
						|
  },
 | 
						|
  mounted() {
 | 
						|
    // this.init()
 | 
						|
    this.setResizeObserver()
 | 
						|
  }
 | 
						|
}
 | 
						|
</script>
 | 
						|
 | 
						|
<style scoped>
 | 
						|
.details-form-wrapper {
 | 
						|
  height: calc(100% - 70px);
 | 
						|
  max-height: calc(100% - 70px);
 | 
						|
}
 | 
						|
</style> |