Skip to content
Snippets Groups Projects
ChannelForm.vue 12.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • Georg Krause's avatar
    Georg Krause committed
      <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>
    
    Georg Krause's avatar
    Georg Krause committed
            <li
              v-for="(error, key) in errors"
              :key="key"
            >
              {{ error }}
            </li>
    
          </ul>
        </div>
        <template v-if="metadataChoices">
    
    Georg Krause's avatar
    Georg Krause committed
          <fieldset
            v-if="creating && step === 1"
            class="ui grouped channel-type required field"
          >
    
            <legend>
    
    Georg Krause's avatar
    Georg Krause committed
              <translate translate-context="Content/Channel/Paragraph">
                What will this channel be used for?
              </translate>
    
            </legend>
    
    Georg Krause's avatar
    Georg Krause committed
            <div class="ui hidden divider" />
    
    Georg Krause's avatar
    Georg Krause committed
              <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}`">
    
    Georg Krause's avatar
    Georg Krause committed
                  <span :class="['right floated', 'placeholder', 'image', {circular: choice.value === 'music'}]" />
    
                  <strong>{{ choice.label }}</strong>
    
    Georg Krause's avatar
    Georg Krause committed
                  <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>
    
    Georg Krause's avatar
    Georg Krause committed
              <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">
    
    Georg Krause's avatar
    Georg Krause committed
                <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">
    
    Georg Krause's avatar
    Georg Krause committed
                <div class="ui small hidden divider" />
    
    Georg Krause's avatar
    Georg Krause committed
                  <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'"
    
    Georg Krause's avatar
    Georg Krause committed
                @delete="newValues.cover = null"
              >
                <translate
                  slot="label"
                  translate-context="Content/Channel/*"
                >
                  Channel Picture
                </translate>
    
              </attachment-input>
            </div>
    
    Georg Krause's avatar
    Georg Krause committed
            <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"
    
    Georg Krause's avatar
    Georg Krause committed
                    v-model="newValues.tags"
                    :required="false"
                  />
    
    Georg Krause's avatar
    Georg Krause committed
              <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"
    
    Georg Krause's avatar
    Georg Krause committed
                    name="channel-language"
    
    Georg Krause's avatar
    Georg Krause committed
                    class="ui search selection dropdown"
                  >
                    <option
                      v-for="(v, key) in metadataChoices.language"
                      :key="key"
                      :value="v.value"
                    >
                      {{ v.label }}
                    </option>
    
    Georg Krause's avatar
    Georg Krause committed
            <div class="ui small hidden divider" />
    
            <div class="ui field">
              <label for="channel-name">
                <translate translate-context="*/*/*">Description</translate>
              </label>
    
    Georg Krause's avatar
    Georg Krause committed
              <content-form v-model="newValues.description" />
    
    Georg Krause's avatar
    Georg Krause committed
            <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"
    
    Georg Krause's avatar
    Georg Krause committed
                  name="itunes-category"
    
    Georg Krause's avatar
    Georg Krause committed
                  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"
    
    Georg Krause's avatar
    Georg Krause committed
                  name="itunes-category"
    
                  :disabled="!newValues.metadata.itunes_category"
    
    Georg Krause's avatar
    Georg Krause committed
                  class="ui dropdown"
                >
                  <option
                    v-for="(v, key) in itunesSubcategories"
                    :key="key"
                    :value="v"
                  >
                    {{ v }}
                  </option>
    
    Georg Krause's avatar
    Georg Krause committed
            <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>
    
    Georg Krause's avatar
    Georg Krause committed
                  v-model="newValues.metadata.owner_email"
                  name="channel-itunes-email"
    
              </div>
              <div class="ui field">
                <label for="channel-itunes-name">
                  <translate translate-context="*/*/*">Owner name</translate>
                </label>
                <input
                  id="channel-itunes-name"
    
    Georg Krause's avatar
    Georg Krause committed
                  v-model="newValues.metadata.owner_name"
                  name="channel-itunes-name"
    
    Georg Krause's avatar
    Georg Krause committed
              <translate translate-context="*/*/*">
                Used for the itunes:email and itunes:name field required by certain platforms such as Spotify or iTunes.
              </translate>
    
    Georg Krause's avatar
    Georg Krause committed
        <div
          v-else
          class="ui active inverted dimmer"
        >
    
          <div class="ui text loader">
    
    Georg Krause's avatar
    Georg Krause committed
            <translate translate-context="*/*/*">
              Loading
            </translate>
    
          </div>
        </div>
      </form>
    </template>
    
    <script>
    import axios from 'axios'
    
    
    Ciaran Ainsworth's avatar
    Ciaran Ainsworth committed
    import AttachmentInput from '@/components/common/AttachmentInput.vue'
    import TagsSelector from '@/components/library/TagsSelector.vue'
    
    Georg Krause's avatar
    Georg Krause committed
    function slugify (text) {
    
      return text.toString().toLowerCase()
    
    Georg Krause's avatar
    Georg Krause committed
        .replace(/\s+/g, '') // Remove spaces
        .replace(/[^\w]+/g, '') // Remove all non-word chars
    
    }
    
    export default {
      components: {
        AttachmentInput,
        TagsSelector
      },
    
    Georg Krause's avatar
    Georg Krause committed
      props: {
        object: { type: Object, required: false, default: null },
        step: { type: Number, required: false, default: 1 }
    
    Georg Krause's avatar
    Georg Krause committed
        const oldValues = {}
    
    Georg Krause's avatar
    Georg Krause committed
          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: {
    
    Georg Krause's avatar
    Georg Krause committed
            name: oldValues.name || '',
            username: oldValues.username || '',
    
            tags: oldValues.tags || [],
    
    Georg Krause's avatar
    Georg Krause committed
            description: (oldValues.description || {}).text || '',
    
            cover: (oldValues.cover || {}).uuid || null,
    
    Georg Krause's avatar
    Georg Krause committed
            content_category: oldValues.content_category || 'podcast',
            metadata: oldValues.metadata || {}
    
          }
        }
      },
      computed: {
        creating () {
          return this.object === null
        },
        categoryChoices () {
          return [
            {
    
    Georg Krause's avatar
    Georg Krause committed
              value: 'podcast',
              label: this.$pgettext('*/*/*', 'Podcasts'),
              helpText: this.$pgettext('Content/Channels/Help', 'Host your episodes and keep your community updated.')
    
    Georg Krause's avatar
    Georg Krause committed
              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++) {
    
    Georg Krause's avatar
    Georg Krause committed
            const element = this.metadataChoices.itunes_category[index]
    
            if (element.value === this.newValues.metadata.itunes_category) {
              return element.children || []
            }
          }
          return []
        },
        labels () {
          return {
    
    Georg Krause's avatar
    Georg Krause committed
            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
        }
      },
    
    Georg Krause's avatar
    Georg Krause committed
      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 () {
    
    Georg Krause's avatar
    Georg Krause committed
          const self = this
    
          axios.get('channels/metadata-choices').then((response) => {
            self.metadataChoices = response.data
          }, error => {
            self.errors = error.backendErrors
          })
        },
        submit () {
          this.isLoading = true
    
    Georg Krause's avatar
    Georg Krause committed
          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,
    
    Georg Krause's avatar
    Georg Krause committed
            metadata: this.newValues.metadata
    
          }
          if (this.newValues.description) {
            payload.description = {
              content_type: 'text/markdown',
    
    Georg Krause's avatar
    Georg Krause committed
              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>