mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-07-07 09:51:37 +00:00
141 lines
5.5 KiB
Vue
141 lines
5.5 KiB
Vue
<template>
|
||
<div v-if="enabled">
|
||
<button class="rounded px-3 py-1 text-sm bg-primary text-white border border-primary hover:bg-primary/90 focus:outline-none focus:ring-2 focus:ring-primary/40" @click="onClick">Recommend</button>
|
||
|
||
<div v-if="open" class="fixed inset-0 z-50 flex items-center justify-center">
|
||
<div class="absolute inset-0 bg-black/60" @click="close"></div>
|
||
|
||
<div class="relative z-10 w-full max-w-md rounded-md border border-gray-700 bg-bg p-4">
|
||
<div class="flex items-center mb-3">
|
||
<h3 class="text-lg font-semibold">Recommend this</h3>
|
||
<button class="ml-auto text-gray-400 hover:text-gray-200" @click="close" aria-label="Close">✕</button>
|
||
</div>
|
||
|
||
<div v-if="tagsLoading" class="text-gray-400">Loading tags…</div>
|
||
<div v-else>
|
||
<label class="block text-sm mb-1">Tag</label>
|
||
<select v-model="form.tagId" class="w-full mb-3 rounded border px-2 py-1 bg-[#111827] text-gray-100 border-gray-600" style="color-scheme: dark">
|
||
<option disabled value="">Select a tag…</option>
|
||
<option v-for="t in tags" :key="t.id" :value="t.id" class="bg-[#111827] text-gray-100">
|
||
{{ t.label }}
|
||
</option>
|
||
</select>
|
||
|
||
<label class="block text-sm mb-1">Visibility</label>
|
||
<select v-model="form.visibility" class="w-full mb-3 rounded border px-2 py-1 bg-[#111827] text-gray-100 border-gray-600" style="color-scheme: dark">
|
||
<option value="public">public</option>
|
||
<option value="recipient-only">recipient-only</option>
|
||
</select>
|
||
|
||
<div v-if="form.visibility === 'recipient-only'">
|
||
<label class="block text-sm mb-1">Recipient (username)</label>
|
||
<input v-model.trim="form.recipient" class="w-full mb-3 rounded border px-2 py-1 bg-[#111827] text-gray-100 border-gray-600" placeholder="Type the recipient’s username" autocomplete="off" />
|
||
<p class="text-xs text-gray-400 -mt-2 mb-2">Enter the user’s <strong>username</strong>. We’ll resolve it on the server.</p>
|
||
</div>
|
||
|
||
<label class="block text-sm mb-1">Note <span class="opacity-60">(optional)</span></label>
|
||
<textarea v-model="form.note" rows="3" maxlength="1000" class="w-full rounded border border-gray-600 bg-transparent px-2 py-1" placeholder="Why should someone read/listen to this?"></textarea>
|
||
|
||
<div class="mt-4 flex items-center gap-2">
|
||
<button :disabled="submitting || !form.tagId || (form.visibility === 'recipient-only' && !form.recipient)" class="rounded px-3 py-1 text-sm bg-primary text-white border border-primary hover:bg-primary/90 disabled:opacity-50 focus:outline-none focus:ring-2 focus:ring-primary/40" @click="submit">
|
||
{{ submitting ? 'Posting…' : 'Post recommendation' }}
|
||
</button>
|
||
<span v-if="error" class="text-sm text-error">{{ error }}</span>
|
||
<span v-if="ok" class="text-sm text-success">Posted</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
export default {
|
||
name: 'RecommendButton',
|
||
props: { bookId: { type: String, required: true } },
|
||
data: () => ({
|
||
enabled: false,
|
||
user: null,
|
||
open: false,
|
||
tags: [],
|
||
tagsLoading: false,
|
||
submitting: false,
|
||
ok: false,
|
||
error: '',
|
||
form: { tagId: '', visibility: 'public', recipient: '', note: '' }
|
||
}),
|
||
mounted() {
|
||
const flag = this.$store.getters['getServerSetting']?.('recommendationsEnabled')
|
||
this.enabled = !!flag
|
||
this.user = this.$store?.state?.user?.user || null
|
||
|
||
if (!this.enabled) {
|
||
this.$axios
|
||
.$get('/api/recommendations/tags')
|
||
.then(() => {
|
||
this.enabled = true
|
||
})
|
||
.catch(() => {
|
||
this.enabled = false
|
||
})
|
||
}
|
||
},
|
||
methods: {
|
||
async onClick() {
|
||
if (!this.enabled) return
|
||
|
||
if (!this.user) {
|
||
const back = encodeURIComponent(this.$route.fullPath)
|
||
return this.$router.push(`/login?redirect=${back}`)
|
||
}
|
||
this.open = true
|
||
this.ok = false
|
||
this.error = ''
|
||
if (!this.tags.length) {
|
||
this.tagsLoading = true
|
||
try {
|
||
this.tags = await this.$axios.$get('/api/recommendations/tags')
|
||
} catch (e) {
|
||
this.error = e?.response?.data?.message || 'Failed to load tags'
|
||
} finally {
|
||
this.tagsLoading = false
|
||
}
|
||
}
|
||
},
|
||
close() {
|
||
this.open = false
|
||
this.form = { tagId: '', visibility: 'public', note: '' }
|
||
this.error = ''
|
||
this.ok = false
|
||
},
|
||
async submit() {
|
||
if (!this.form.tagId) return
|
||
if (this.form.visibility === 'recipient-only' && !this.form.recipient) {
|
||
this.error = 'Recipient is required'
|
||
return
|
||
}
|
||
this.submitting = true
|
||
this.error = ''
|
||
this.ok = false
|
||
try {
|
||
const payload = {
|
||
bookId: this.bookId,
|
||
tagId: this.form.tagId,
|
||
note: this.form.note || '',
|
||
visibility: this.form.visibility,
|
||
recipientUsername: this.form.visibility === 'recipient-only' ? this.form.recipient : null
|
||
}
|
||
const created = await this.$axios.$post('/api/recommendations', payload)
|
||
this.$emit('recommended', created)
|
||
this.ok = true
|
||
this.$toast?.success?.('Recommendation posted')
|
||
setTimeout(() => this.close(), 700)
|
||
} catch (e) {
|
||
this.error = e?.response?.data?.message || 'Failed to post'
|
||
} finally {
|
||
this.submitting = false
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|