416 lines
13 KiB
Vue
416 lines
13 KiB
Vue
<template>
|
|
<form
|
|
class="ui form"
|
|
@submit.prevent.stop="submit"
|
|
>
|
|
<div
|
|
v-if="errors.length > 0"
|
|
role="alert"
|
|
class="ui negative message"
|
|
>
|
|
<h4 class="header">
|
|
<translate translate-context="Content/*/Error message.Title">
|
|
Error while saving channel
|
|
</translate>
|
|
</h4>
|
|
<ul class="list">
|
|
<li
|
|
v-for="(error, key) in errors"
|
|
:key="key"
|
|
>
|
|
{{ error }}
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
<template v-if="metadataChoices">
|
|
<fieldset
|
|
v-if="creating && step === 1"
|
|
class="ui grouped channel-type required field"
|
|
>
|
|
<legend>
|
|
<translate translate-context="Content/Channel/Paragraph">
|
|
What will this channel be used for?
|
|
</translate>
|
|
</legend>
|
|
<div class="ui hidden divider" />
|
|
<div class="field">
|
|
<div
|
|
v-for="(choice, key) in categoryChoices"
|
|
:key="key"
|
|
:class="['ui', 'radio', 'checkbox', {selected: choice.value == newValues.content_category}]"
|
|
>
|
|
<input
|
|
:id="`category-${choice.value}`"
|
|
v-model="newValues.content_category"
|
|
type="radio"
|
|
name="channel-category"
|
|
:value="choice.value"
|
|
>
|
|
<label :for="`category-${choice.value}`">
|
|
<span :class="['right floated', 'placeholder', 'image', {circular: choice.value === 'music'}]" />
|
|
<strong>{{ choice.label }}</strong>
|
|
<div class="ui small hidden divider" />
|
|
{{ choice.helpText }}
|
|
</label>
|
|
</div>
|
|
</div>
|
|
</fieldset>
|
|
<template v-if="!creating || step === 2">
|
|
<div class="ui required field">
|
|
<label for="channel-name">
|
|
<translate translate-context="Content/Channel/*">Name</translate>
|
|
</label>
|
|
<input
|
|
v-model="newValues.name"
|
|
type="text"
|
|
required
|
|
:placeholder="labels.namePlaceholder"
|
|
>
|
|
</div>
|
|
<div class="ui required field">
|
|
<label for="channel-username">
|
|
<translate translate-context="Content/Channel/*">Fediverse handle</translate>
|
|
</label>
|
|
<div class="ui left labeled input">
|
|
<div class="ui basic label">
|
|
@
|
|
</div>
|
|
<input
|
|
v-model="newValues.username"
|
|
type="text"
|
|
:required="creating"
|
|
:disabled="!creating"
|
|
:placeholder="labels.usernamePlaceholder"
|
|
>
|
|
</div>
|
|
<template v-if="creating">
|
|
<div class="ui small hidden divider" />
|
|
<p>
|
|
<translate translate-context="Content/Channels/Paragraph">
|
|
Used in URLs and to follow this channel in the Fediverse. It cannot be changed later.
|
|
</translate>
|
|
</p>
|
|
</template>
|
|
</div>
|
|
<div class="six wide column">
|
|
<attachment-input
|
|
v-model="newValues.cover"
|
|
:required="false"
|
|
:image-class="newValues.content_category === 'podcast' ? '' : 'circular'"
|
|
@delete="newValues.cover = null"
|
|
>
|
|
<translate
|
|
slot="label"
|
|
translate-context="Content/Channel/*"
|
|
>
|
|
Channel Picture
|
|
</translate>
|
|
</attachment-input>
|
|
</div>
|
|
<div class="ui small hidden divider" />
|
|
<div class="ui stackable grid row">
|
|
<div class="ten wide column">
|
|
<div class="ui field">
|
|
<label for="channel-tags">
|
|
<translate translate-context="*/*/*">Tags</translate>
|
|
</label>
|
|
<tags-selector
|
|
id="channel-tags"
|
|
v-model="newValues.tags"
|
|
:required="false"
|
|
/>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-if="newValues.content_category === 'podcast'"
|
|
class="six wide column"
|
|
>
|
|
<div class="ui required field">
|
|
<label for="channel-language">
|
|
<translate translate-context="*/*/*">Language</translate>
|
|
</label>
|
|
<select
|
|
id="channel-language"
|
|
v-model="newValues.metadata.language"
|
|
name="channel-language"
|
|
required
|
|
class="ui search selection dropdown"
|
|
>
|
|
<option
|
|
v-for="(v, key) in metadataChoices.language"
|
|
:key="key"
|
|
:value="v.value"
|
|
>
|
|
{{ v.label }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="ui small hidden divider" />
|
|
<div class="ui field">
|
|
<label for="channel-name">
|
|
<translate translate-context="*/*/*">Description</translate>
|
|
</label>
|
|
<content-form v-model="newValues.description" />
|
|
</div>
|
|
<div
|
|
v-if="newValues.content_category === 'podcast'"
|
|
class="ui two fields"
|
|
>
|
|
<div class="ui required field">
|
|
<label for="channel-itunes-category">
|
|
<translate translate-context="*/*/*">Category</translate>
|
|
</label>
|
|
<select
|
|
id="itunes-category"
|
|
v-model="newValues.metadata.itunes_category"
|
|
name="itunes-category"
|
|
required
|
|
class="ui dropdown"
|
|
>
|
|
<option
|
|
v-for="(v, key) in metadataChoices.itunes_category"
|
|
:key="key"
|
|
:value="v.value"
|
|
>
|
|
{{ v.label }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
<div class="ui field">
|
|
<label for="channel-itunes-category">
|
|
<translate translate-context="*/*/*">Subcategory</translate>
|
|
</label>
|
|
<select
|
|
id="itunes-category"
|
|
v-model="newValues.metadata.itunes_subcategory"
|
|
name="itunes-category"
|
|
:disabled="!newValues.metadata.itunes_category"
|
|
class="ui dropdown"
|
|
>
|
|
<option
|
|
v-for="(v, key) in itunesSubcategories"
|
|
:key="key"
|
|
:value="v"
|
|
>
|
|
{{ v }}
|
|
</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div
|
|
v-if="newValues.content_category === 'podcast'"
|
|
class="ui two fields"
|
|
>
|
|
<div class="ui field">
|
|
<label for="channel-itunes-email">
|
|
<translate translate-context="*/*/*">Owner e-mail address</translate>
|
|
</label>
|
|
<input
|
|
id="channel-itunes-email"
|
|
v-model="newValues.metadata.owner_email"
|
|
name="channel-itunes-email"
|
|
type="email"
|
|
>
|
|
</div>
|
|
<div class="ui field">
|
|
<label for="channel-itunes-name">
|
|
<translate translate-context="*/*/*">Owner name</translate>
|
|
</label>
|
|
<input
|
|
id="channel-itunes-name"
|
|
v-model="newValues.metadata.owner_name"
|
|
name="channel-itunes-name"
|
|
maxlength="255"
|
|
>
|
|
</div>
|
|
</div>
|
|
<p>
|
|
<translate translate-context="*/*/*">
|
|
Used for the itunes:email and itunes:name field required by certain platforms such as Spotify or iTunes.
|
|
</translate>
|
|
</p>
|
|
</template>
|
|
</template>
|
|
<div
|
|
v-else
|
|
class="ui active inverted dimmer"
|
|
>
|
|
<div class="ui text loader">
|
|
<translate translate-context="*/*/*">
|
|
Loading
|
|
</translate>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</template>
|
|
|
|
<script>
|
|
import axios from 'axios'
|
|
|
|
import AttachmentInput from '~/components/common/AttachmentInput.vue'
|
|
import TagsSelector from '~/components/library/TagsSelector.vue'
|
|
|
|
function slugify (text) {
|
|
return text.toString().toLowerCase()
|
|
.replace(/\s+/g, '') // Remove spaces
|
|
.replace(/[^\w]+/g, '') // Remove all non-word chars
|
|
}
|
|
|
|
export default {
|
|
components: {
|
|
AttachmentInput,
|
|
TagsSelector
|
|
},
|
|
props: {
|
|
object: { type: Object, required: false, default: null },
|
|
step: { type: Number, required: false, default: 1 }
|
|
},
|
|
data () {
|
|
const oldValues = {}
|
|
if (this.object) {
|
|
oldValues.metadata = { ...(this.object.metadata || {}) }
|
|
oldValues.name = this.object.artist.name
|
|
oldValues.description = this.object.artist.description
|
|
oldValues.cover = this.object.artist.cover
|
|
oldValues.tags = this.object.artist.tags
|
|
oldValues.content_category = this.object.artist.content_category
|
|
oldValues.username = this.object.actor.preferred_username
|
|
}
|
|
return {
|
|
isLoading: false,
|
|
errors: [],
|
|
metadataChoices: null,
|
|
newValues: {
|
|
name: oldValues.name || '',
|
|
username: oldValues.username || '',
|
|
tags: oldValues.tags || [],
|
|
description: (oldValues.description || {}).text || '',
|
|
cover: (oldValues.cover || {}).uuid || null,
|
|
content_category: oldValues.content_category || 'podcast',
|
|
metadata: oldValues.metadata || {}
|
|
}
|
|
}
|
|
},
|
|
computed: {
|
|
creating () {
|
|
return this.object === null
|
|
},
|
|
categoryChoices () {
|
|
return [
|
|
{
|
|
value: 'podcast',
|
|
label: this.$pgettext('*/*/*', 'Podcasts'),
|
|
helpText: this.$pgettext('Content/Channels/Help', 'Host your episodes and keep your community updated.')
|
|
},
|
|
{
|
|
value: 'music',
|
|
label: this.$pgettext('*/*/*', 'Artist discography'),
|
|
helpText: this.$pgettext('Content/Channels/Help', 'Publish music you make as a nice discography of albums and singles.')
|
|
}
|
|
]
|
|
},
|
|
itunesSubcategories () {
|
|
for (let index = 0; index < this.metadataChoices.itunes_category.length; index++) {
|
|
const element = this.metadataChoices.itunes_category[index]
|
|
if (element.value === this.newValues.metadata.itunes_category) {
|
|
return element.children || []
|
|
}
|
|
}
|
|
return []
|
|
},
|
|
labels () {
|
|
return {
|
|
namePlaceholder: this.$pgettext('Content/Channel/Form.Field.Placeholder', 'Awesome channel name'),
|
|
usernamePlaceholder: this.$pgettext('Content/Channel/Form.Field.Placeholder', 'awesomechannelname')
|
|
}
|
|
},
|
|
submittable () {
|
|
let v = this.newValues.name && this.newValues.username
|
|
if (this.newValues.content_category === 'podcast') {
|
|
v = v && this.newValues.metadata.itunes_category && this.newValues.metadata.language
|
|
}
|
|
return !!v
|
|
}
|
|
},
|
|
watch: {
|
|
'newValues.name' (v) {
|
|
if (this.creating) {
|
|
this.newValues.username = slugify(v)
|
|
}
|
|
},
|
|
'newValues.metadata.itunes_category' (v) {
|
|
this.newValues.metadata.itunes_subcategory = null
|
|
},
|
|
'newValues.content_category': {
|
|
handler (v) {
|
|
this.$emit('category', v)
|
|
},
|
|
immediate: true
|
|
},
|
|
isLoading: {
|
|
handler (v) {
|
|
this.$emit('loading', v)
|
|
},
|
|
immediate: true
|
|
},
|
|
submittable: {
|
|
handler (v) {
|
|
this.$emit('submittable', v)
|
|
},
|
|
immediate: true
|
|
}
|
|
},
|
|
|
|
created () {
|
|
this.fetchMetadataChoices()
|
|
},
|
|
methods: {
|
|
fetchMetadataChoices () {
|
|
const self = this
|
|
axios.get('channels/metadata-choices').then((response) => {
|
|
self.metadataChoices = response.data
|
|
}, error => {
|
|
self.errors = error.backendErrors
|
|
})
|
|
},
|
|
submit () {
|
|
this.isLoading = true
|
|
const self = this
|
|
const handler = this.creating ? axios.post : axios.patch
|
|
const url = this.creating ? 'channels/' : `channels/${this.object.uuid}`
|
|
const payload = {
|
|
name: this.newValues.name,
|
|
username: this.newValues.username,
|
|
tags: this.newValues.tags,
|
|
content_category: this.newValues.content_category,
|
|
cover: this.newValues.cover,
|
|
metadata: this.newValues.metadata
|
|
}
|
|
if (this.newValues.description) {
|
|
payload.description = {
|
|
content_type: 'text/markdown',
|
|
text: this.newValues.description
|
|
}
|
|
} else {
|
|
payload.description = null
|
|
}
|
|
|
|
handler(url, payload).then((response) => {
|
|
self.isLoading = false
|
|
if (self.creating) {
|
|
self.$emit('created', response.data)
|
|
} else {
|
|
self.$emit('updated', response.data)
|
|
}
|
|
}, error => {
|
|
self.isLoading = false
|
|
self.errors = error.backendErrors
|
|
self.$emit('errored', self.errors)
|
|
})
|
|
}
|
|
}
|
|
}
|
|
</script>
|