kopia lustrzana https://dev.funkwhale.audio/funkwhale/funkwhale
environments/review-front-deve-otr6gc/deployments/13419
rodzic
bbdb3dcb9d
commit
b8f5f5d6ef
|
@ -38,6 +38,7 @@
|
|||
"sass": "1.53.0",
|
||||
"showdown": "2.1.0",
|
||||
"text-clipper": "2.2.0",
|
||||
"transliteration": "^2.3.5",
|
||||
"vue": "3.2.37",
|
||||
"vue-gettext": "2.1.12",
|
||||
"vue-plyr": "7.0.0",
|
||||
|
|
|
@ -122,16 +122,11 @@ const showUserModal = ref(false)
|
|||
const showLanguageModal = ref(false)
|
||||
const showThemeModal = ref(false)
|
||||
|
||||
// TODO (wvffle): Use current language this.$language.current
|
||||
const languageSelection = undefined
|
||||
// export default {
|
||||
// watch: {
|
||||
// languageSelection: function (v) {
|
||||
// this.$store.dispatch('ui/currentLanguage', v)
|
||||
// this.$refs.languageModal.closeModal()
|
||||
// }
|
||||
// },
|
||||
// }
|
||||
const gettext = useGettext()
|
||||
const languageSelection = ref(gettext.current)
|
||||
watch(languageSelection, (v) => {
|
||||
store.dispatch('ui/currentLanguage', v)
|
||||
})
|
||||
|
||||
watch(() => store.state.auth.authenticated, (authenticated) => {
|
||||
if (authenticated) {
|
||||
|
|
|
@ -1,3 +1,145 @@
|
|||
<script setup lang="ts">
|
||||
import type { Channel, BackendError } from '~/types'
|
||||
|
||||
import { slugify } from 'transliteration'
|
||||
import { reactive, computed, ref, watchEffect, watch } from 'vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
import axios from 'axios'
|
||||
import AttachmentInput from '~/components/common/AttachmentInput.vue'
|
||||
import TagsSelector from '~/components/library/TagsSelector.vue'
|
||||
|
||||
interface Props {
|
||||
object?: Channel | null
|
||||
step: number
|
||||
}
|
||||
|
||||
const emit = defineEmits(['category', 'submittable', 'loading', 'errored', 'created', 'updated'])
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
object: null,
|
||||
step: 1
|
||||
})
|
||||
|
||||
const { $pgettext } = useGettext()
|
||||
|
||||
const newValues = reactive({
|
||||
name: props.object?.artist?.name ?? '',
|
||||
username: props.object?.actor.preferred_username ?? '',
|
||||
tags: props.object?.artist?.tags ?? [],
|
||||
description: props.object?.artist?.description?.text ?? '',
|
||||
cover: props.object?.artist?.cover?.uuid ?? null,
|
||||
content_category: props.object?.artist?.content_category ?? 'podcast',
|
||||
metadata: { ...(props.object?.metadata ?? {}) }
|
||||
})
|
||||
|
||||
const creating = computed(() => props.object === null)
|
||||
const categoryChoices = computed(() => [
|
||||
{
|
||||
value: 'podcast',
|
||||
label: $pgettext('*/*/*', 'Podcasts'),
|
||||
helpText: $pgettext('Content/Channels/Help', 'Host your episodes and keep your community updated.')
|
||||
},
|
||||
{
|
||||
value: 'music',
|
||||
label: $pgettext('*/*/*', 'Artist discography'),
|
||||
helpText: $pgettext('Content/Channels/Help', 'Publish music you make as a nice discography of albums and singles.')
|
||||
}
|
||||
])
|
||||
|
||||
interface ITunesCategory {
|
||||
value: string
|
||||
label: string
|
||||
children: []
|
||||
}
|
||||
|
||||
interface MetadataChoices {
|
||||
itunes_category?: ITunesCategory[] | null
|
||||
language: {
|
||||
value: string
|
||||
label: string
|
||||
}[]
|
||||
}
|
||||
|
||||
const metadataChoices = ref({ itunes_category: null } as MetadataChoices)
|
||||
const itunesSubcategories = computed(() => {
|
||||
for (const element of metadataChoices.value.itunes_category ?? []) {
|
||||
if (element.value === newValues.metadata.itunes_category) {
|
||||
return element.children ?? []
|
||||
}
|
||||
}
|
||||
|
||||
return []
|
||||
})
|
||||
|
||||
const labels = computed(() => ({
|
||||
namePlaceholder: $pgettext('Content/Channel/Form.Field.Placeholder', 'Awesome channel name'),
|
||||
usernamePlaceholder: $pgettext('Content/Channel/Form.Field.Placeholder', 'awesomechannelname')
|
||||
}))
|
||||
|
||||
const submittable = computed(() =>
|
||||
newValues.content_category === 'podcast'
|
||||
? newValues.name && newValues.username && newValues.metadata.itunes_category && newValues.metadata.language
|
||||
: newValues.name && newValues.username
|
||||
)
|
||||
|
||||
watch(() => newValues.name, (name) => {
|
||||
if (creating.value) {
|
||||
newValues.username = slugify(name)
|
||||
}
|
||||
})
|
||||
|
||||
watch(() => newValues.metadata.itunes_category, () => {
|
||||
newValues.metadata.itunes_subcategory = null
|
||||
})
|
||||
|
||||
const isLoading = ref(false)
|
||||
const errors = ref([] as string[])
|
||||
|
||||
watchEffect(() => emit('category', newValues.content_category))
|
||||
watchEffect(() => emit('loading', isLoading.value))
|
||||
watchEffect(() => emit('submittable', submittable.value))
|
||||
|
||||
// TODO (wvffle): Add loader / Use Suspense
|
||||
const fetchMetadataChoices = async () => {
|
||||
try {
|
||||
const response = await axios.get('channels/metadata-choices')
|
||||
metadataChoices.value = response.data
|
||||
} catch (error) {
|
||||
errors.value = (error as BackendError).backendErrors
|
||||
}
|
||||
}
|
||||
|
||||
fetchMetadataChoices()
|
||||
|
||||
const submit = async () => {
|
||||
isLoading.value = true
|
||||
|
||||
const payload = {
|
||||
...newValues,
|
||||
description: newValues.description
|
||||
? {
|
||||
content_type: 'text/markdown',
|
||||
text: newValues.description
|
||||
}
|
||||
: null
|
||||
}
|
||||
|
||||
try {
|
||||
const request = () => creating.value
|
||||
? axios.post('channels/', payload)
|
||||
: axios.patch(`channels/${props.object?.uuid}`, payload)
|
||||
|
||||
const response = await request()
|
||||
emit(creating.value ? 'created' : 'updated', response.data)
|
||||
} catch (error) {
|
||||
errors.value = (error as BackendError).backendErrors
|
||||
emit('errored', errors.value)
|
||||
}
|
||||
|
||||
isLoading.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<form
|
||||
class="ui form"
|
||||
|
@ -78,8 +220,8 @@
|
|||
<input
|
||||
v-model="newValues.username"
|
||||
type="text"
|
||||
:required="creating || null"
|
||||
:disabled="!creating || null"
|
||||
:required="creating"
|
||||
:disabled="!creating"
|
||||
:placeholder="labels.usernamePlaceholder"
|
||||
>
|
||||
</div>
|
||||
|
@ -182,7 +324,7 @@
|
|||
id="itunes-category"
|
||||
v-model="newValues.metadata.itunes_subcategory"
|
||||
name="itunes-category"
|
||||
:disabled="!newValues.metadata.itunes_category || null"
|
||||
:disabled="!newValues.metadata.itunes_category"
|
||||
class="ui dropdown"
|
||||
>
|
||||
<option
|
||||
|
@ -241,171 +383,3 @@
|
|||
</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>
|
||||
|
|
|
@ -113,6 +113,14 @@ export interface Channel {
|
|||
subscriptions_count: number
|
||||
downloads_count: number
|
||||
content_category: ContentCategory
|
||||
|
||||
metadata?: {
|
||||
itunes_category?: unknown
|
||||
itunes_subcategory?: unknown
|
||||
language?: string
|
||||
owner_name: string
|
||||
owner_email: string
|
||||
}
|
||||
}
|
||||
|
||||
export type PrivacyLevel = 'everyone' | 'instance' | 'me'
|
||||
|
|
|
@ -6237,6 +6237,13 @@ tr46@^1.0.1:
|
|||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
transliteration@^2.3.5:
|
||||
version "2.3.5"
|
||||
resolved "https://registry.yarnpkg.com/transliteration/-/transliteration-2.3.5.tgz#8f92309575f69e4a8a525dab4ff705ebcf961c45"
|
||||
integrity sha512-HAGI4Lq4Q9dZ3Utu2phaWgtm3vB6PkLUFqWAScg/UW+1eZ/Tg6Exo4oC0/3VUol/w4BlefLhUUSVBr/9/ZGQOw==
|
||||
dependencies:
|
||||
yargs "^17.5.1"
|
||||
|
||||
ts-jest@28.0.5:
|
||||
version "28.0.5"
|
||||
resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-28.0.5.tgz#31776f768fba6dfc8c061d488840ed0c8eeac8b9"
|
||||
|
@ -6895,7 +6902,7 @@ yargs-parser@^21.0.0, yargs-parser@^21.0.1:
|
|||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35"
|
||||
integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==
|
||||
|
||||
yargs@^17.3.1:
|
||||
yargs@^17.3.1, yargs@^17.5.1:
|
||||
version "17.5.1"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.5.1.tgz#e109900cab6fcb7fd44b1d8249166feb0b36e58e"
|
||||
integrity sha512-t6YAJcxDkNX7NFYiVtKvWUz8l+PaKTLiL63mJYWR2GnHq2gjEWISzsLp9wg3aY36dY1j+gfIEL3pIF+XlJJfbA==
|
||||
|
|
Ładowanie…
Reference in New Issue