mirror of
https://github.com/advplyr/audiobookshelf.git
synced 2026-07-07 09:51:37 +00:00
Add recipient field for “recipient-only” visibility
This commit is contained in:
parent
c6e804bcae
commit
a5a22efeda
4 changed files with 35 additions and 12 deletions
BIN
Imagine Dragons - Believer/Imagine Dragons - Believer.mp3
Normal file
BIN
Imagine Dragons - Believer/Imagine Dragons - Believer.mp3
Normal file
Binary file not shown.
|
|
@ -27,11 +27,17 @@
|
||||||
<option value="recipient-only">recipient-only</option>
|
<option value="recipient-only">recipient-only</option>
|
||||||
</select>
|
</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>
|
<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>
|
<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">
|
<div class="mt-4 flex items-center gap-2">
|
||||||
<button :disabled="submitting || !form.tagId" 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">
|
<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' }}
|
{{ submitting ? 'Posting…' : 'Post recommendation' }}
|
||||||
</button>
|
</button>
|
||||||
<span v-if="error" class="text-sm text-error">{{ error }}</span>
|
<span v-if="error" class="text-sm text-error">{{ error }}</span>
|
||||||
|
|
@ -56,7 +62,7 @@ export default {
|
||||||
submitting: false,
|
submitting: false,
|
||||||
ok: false,
|
ok: false,
|
||||||
error: '',
|
error: '',
|
||||||
form: { tagId: '', visibility: 'public', note: '' }
|
form: { tagId: '', visibility: 'public', recipient: '', note: '' }
|
||||||
}),
|
}),
|
||||||
mounted() {
|
mounted() {
|
||||||
const flag = this.$store.getters['getServerSetting']?.('recommendationsEnabled')
|
const flag = this.$store.getters['getServerSetting']?.('recommendationsEnabled')
|
||||||
|
|
@ -104,6 +110,10 @@ export default {
|
||||||
},
|
},
|
||||||
async submit() {
|
async submit() {
|
||||||
if (!this.form.tagId) return
|
if (!this.form.tagId) return
|
||||||
|
if (this.form.visibility === 'recipient-only' && !this.form.recipient) {
|
||||||
|
this.error = 'Recipient is required'
|
||||||
|
return
|
||||||
|
}
|
||||||
this.submitting = true
|
this.submitting = true
|
||||||
this.error = ''
|
this.error = ''
|
||||||
this.ok = false
|
this.ok = false
|
||||||
|
|
@ -112,7 +122,8 @@ export default {
|
||||||
bookId: this.bookId,
|
bookId: this.bookId,
|
||||||
tagId: this.form.tagId,
|
tagId: this.form.tagId,
|
||||||
note: this.form.note || '',
|
note: this.form.note || '',
|
||||||
visibility: this.form.visibility
|
visibility: this.form.visibility,
|
||||||
|
recipientUsername: this.form.visibility === 'recipient-only' ? this.form.recipient : null
|
||||||
}
|
}
|
||||||
const created = await this.$axios.$post('/api/recommendations', payload)
|
const created = await this.$axios.$post('/api/recommendations', payload)
|
||||||
this.$emit('recommended', created)
|
this.$emit('recommended', created)
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ module.exports = (injectedManager) => {
|
||||||
const userId = req.user?.id
|
const userId = req.user?.id
|
||||||
if (!userId) return res.status(401).send('Unauthorized')
|
if (!userId) return res.status(401).send('Unauthorized')
|
||||||
|
|
||||||
const { bookId, tagId, tagSlug, recipientUserId, note, visibility } = req.body || {}
|
const { bookId, tagId, tagSlug, recipientUserId, recipientUsername, note, visibility } = req.body || {}
|
||||||
if (!bookId) return res.status(400).json({ message: 'bookId is required' })
|
if (!bookId) return res.status(400).json({ message: 'bookId is required' })
|
||||||
if (!tagId && !tagSlug) return res.status(400).json({ message: 'tagId or tagSlug is required' })
|
if (!tagId && !tagSlug) return res.status(400).json({ message: 'tagId or tagSlug is required' })
|
||||||
|
|
||||||
|
|
@ -24,6 +24,7 @@ module.exports = (injectedManager) => {
|
||||||
tagId,
|
tagId,
|
||||||
tagSlug,
|
tagSlug,
|
||||||
recipientUserId,
|
recipientUserId,
|
||||||
|
recipientUsername,
|
||||||
note,
|
note,
|
||||||
visibility
|
visibility
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -86,19 +86,30 @@ const RecommendationManager = {
|
||||||
return null
|
return null
|
||||||
},
|
},
|
||||||
|
|
||||||
async resolveRecipient(recipientUserId) {
|
async resolveRecipient({ recipientUserId, recipientUsername }) {
|
||||||
if (!recipientUserId) return null
|
const User = Database.userModel
|
||||||
const user = await Database.userModel.findByPk(recipientUserId)
|
if (recipientUserId) {
|
||||||
return user || null
|
const u = await User.findByPk(recipientUserId)
|
||||||
|
return u || null
|
||||||
|
}
|
||||||
|
if (recipientUsername) {
|
||||||
|
const u = await User.findOne({ where: { username: String(recipientUsername) } })
|
||||||
|
return u || null
|
||||||
|
}
|
||||||
|
return null
|
||||||
},
|
},
|
||||||
|
|
||||||
async createRecommendation({ userId, bookId, tagId, tagSlug, recipientUserId, note, visibility }) {
|
async createRecommendation({ userId, bookId, tagId, tagSlug, recipientUserId, recipientUsername, note, visibility }) {
|
||||||
const tag = await this.resolveTag({ tagId, tagSlug })
|
const tag = await this.resolveTag({ tagId, tagSlug })
|
||||||
if (!tag) return { error: { status: 400, message: 'Invalid tag' } }
|
if (!tag) return { error: { status: 400, message: 'Invalid tag' } }
|
||||||
|
|
||||||
const recip = await this.resolveRecipient(recipientUserId)
|
if (visibility === 'recipient-only' && !recipientUserId && !recipientUsername) {
|
||||||
if (recipientUserId && !recip) return { error: { status: 400, message: 'Recipient not found' } }
|
return { error: { status: 400, message: 'Recipient is required for recipient-only visibility' } }
|
||||||
|
}
|
||||||
|
const recip = await this.resolveRecipient({ recipientUserId, recipientUsername })
|
||||||
|
if (visibility === 'recipient-only' && !recip) {
|
||||||
|
return { error: { status: 400, message: 'Recipient not found' } }
|
||||||
|
}
|
||||||
const created = await Database.bookRecommendationModel.create({
|
const created = await Database.bookRecommendationModel.create({
|
||||||
bookId: String(bookId),
|
bookId: String(bookId),
|
||||||
tagId: tag.id,
|
tagId: tag.id,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue