fix(front): [WIP] Upload Podcast
This commit is contained in:
parent
1911aa799d
commit
70192906ba
|
@ -1,5 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { ContentCategory, Channel, BackendError } from '~/types'
|
import type { ContentCategory, Channel, BackendError } from '~/types'
|
||||||
|
import type { paths } from '~/generated/types'
|
||||||
|
|
||||||
import { slugify } from 'transliteration'
|
import { slugify } from 'transliteration'
|
||||||
import { reactive, computed, ref, watchEffect, watch } from 'vue'
|
import { reactive, computed, ref, watchEffect, watch } from 'vue'
|
||||||
|
@ -25,7 +26,7 @@ interface Events {
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
object?: Channel | null
|
object?: Channel | null
|
||||||
step: number
|
step?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const emit = defineEmits<Events>()
|
const emit = defineEmits<Events>()
|
||||||
|
@ -51,6 +52,8 @@ const tagList = computed(() => ({
|
||||||
others: [].map(tag => ({ type: 'custom' as const, label: tag }))
|
others: [].map(tag => ({ type: 'custom' as const, label: tag }))
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
// If props has an object, then this form edits, else it creates
|
||||||
|
// TODO: rename to `process : 'creating' | 'editing'`
|
||||||
const creating = computed(() => props.object === null)
|
const creating = computed(() => props.object === null)
|
||||||
const categoryChoices = computed(() => [
|
const categoryChoices = computed(() => [
|
||||||
{
|
{
|
||||||
|
@ -121,8 +124,11 @@ watchEffect(() => emit('submittable', submittable.value))
|
||||||
// TODO (wvffle): Add loader / Use Suspense
|
// TODO (wvffle): Add loader / Use Suspense
|
||||||
const fetchMetadataChoices = async () => {
|
const fetchMetadataChoices = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('channels/metadata-choices')
|
const response = await axios.get<paths['/api/v2/channels/metadata-choices/']['get']['responses']['200']['content']['application/json']>('channels/metadata-choices/')
|
||||||
metadataChoices.value = response.data
|
// Old code expected as a response { language, itunes_category? }
|
||||||
|
// but actual response is Channel
|
||||||
|
if (response.data.metadata)
|
||||||
|
metadataChoices.value = response.data.metadata
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errors.value = (error as BackendError).backendErrors
|
errors.value = (error as BackendError).backendErrors
|
||||||
}
|
}
|
||||||
|
|
|
@ -67,11 +67,11 @@ const store = useStore()
|
||||||
const errors = ref([] as string[])
|
const errors = ref([] as string[])
|
||||||
|
|
||||||
const values = reactive<{
|
const values = reactive<{
|
||||||
channel: string; // Channel UUID
|
channelUuid: string | null; // Channel UUID
|
||||||
license: unknown;
|
license: string | null;
|
||||||
album: unknown;
|
album: unknown;
|
||||||
}>({
|
}>({
|
||||||
channel: props.channel?.uuid ?? null,
|
channelUuid: props.channel?.uuid ?? null,
|
||||||
license: null,
|
license: null,
|
||||||
album: null
|
album: null
|
||||||
})
|
})
|
||||||
|
@ -81,7 +81,7 @@ const files = ref([] as VueUploadItem[])
|
||||||
//
|
//
|
||||||
// Channels
|
// Channels
|
||||||
//
|
//
|
||||||
const availableChannels = ref<Channel[]>([])
|
const availableChannels = ref<Channel[] | null>(null)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
availableChannels>1? :=1 :=0
|
availableChannels>1? :=1 :=0
|
||||||
|
@ -110,15 +110,24 @@ const channelDropdownId = ref<Channel['artist']['id'] | null>(null)
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
|
|
||||||
const selectedChannel = computed(() =>
|
const selectedChannel = computed(() =>
|
||||||
|
// Deeplink / Preset channel
|
||||||
props.channel
|
props.channel
|
||||||
? props.channel
|
? props.channel
|
||||||
|
// Not yet loaded the available channels
|
||||||
|
: availableChannels.value === null
|
||||||
|
? null
|
||||||
|
// No channels available
|
||||||
: availableChannels.value.length === 0
|
: availableChannels.value.length === 0
|
||||||
? (createEmptyChannel(), null)
|
? (createEmptyChannel(), null)
|
||||||
|
// Exactly one available channel
|
||||||
: availableChannels.value.length === 1
|
: availableChannels.value.length === 1
|
||||||
? availableChannels.value[0]
|
? availableChannels.value[0]
|
||||||
: availableChannels.value.find(({ artist }) => artist.id === channelDropdownId.value)
|
// Multiple available channels
|
||||||
|
: availableChannels.value.find(({ artist }) => artist.id === channelDropdownId.value) || null
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const emptyChannelCreateRequest:components['schemas']['ChannelCreateRequest'] = {
|
const emptyChannelCreateRequest:components['schemas']['ChannelCreateRequest'] = {
|
||||||
name: store.state.auth.fullUsername,
|
name: store.state.auth.fullUsername,
|
||||||
username: store.state.auth.username,
|
username: store.state.auth.username,
|
||||||
|
@ -141,9 +150,16 @@ const createEmptyChannel = async () => {
|
||||||
const fetchChannels = async () => {
|
const fetchChannels = async () => {
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('channels/', { params: { scope: 'me' /* Ask Pablo: which param to filter for `music` | `podcast`?
|
const response = await axios.get<paths['/api/v2/channels/']['get']['responses']['200']['content']['application/json']>(
|
||||||
Answer: const isPodcast = computed(() => channel.value?.artist?.content_category === 'podcast')*/ } })
|
'channels/',
|
||||||
availableChannels.value = response.data.results
|
{ params: { scope: 'me' } }
|
||||||
|
)
|
||||||
|
availableChannels.value = response.data.results.filter(channel =>
|
||||||
|
props.filter === undefined
|
||||||
|
? true
|
||||||
|
: channel.artist?.content_category === props.filter
|
||||||
|
)
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errors.value = (error as BackendError).backendErrors
|
errors.value = (error as BackendError).backendErrors
|
||||||
}
|
}
|
||||||
|
@ -197,13 +213,13 @@ const remainingSpace = computed(() => Math.max(
|
||||||
//
|
//
|
||||||
const includeDraftUploads = ref()
|
const includeDraftUploads = ref()
|
||||||
const draftUploads = ref([] as Upload[])
|
const draftUploads = ref([] as Upload[])
|
||||||
whenever(() => values.channel !== null, async () => {
|
whenever(() => values.channelUuid !== null, async () => {
|
||||||
files.value = []
|
files.value = []
|
||||||
draftUploads.value = []
|
draftUploads.value = []
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('uploads', {
|
const response = await axios.get('uploads', {
|
||||||
params: { import_status: 'draft', channel: values.channel }
|
params: { import_status: 'draft', channel: values.channelUuid }
|
||||||
})
|
})
|
||||||
|
|
||||||
draftUploads.value = response.data.results as Upload[]
|
draftUploads.value = response.data.results as Upload[]
|
||||||
|
@ -465,8 +481,7 @@ defineExpose({
|
||||||
|
|
||||||
<div :class="['ui', 'required', 'field']">
|
<div :class="['ui', 'required', 'field']">
|
||||||
<label
|
<label
|
||||||
v-if="availableChannels.length === 1"
|
v-if="availableChannels !== null && availableChannels.length === 1"
|
||||||
for="channel-dropdown"
|
|
||||||
>
|
>
|
||||||
{{ `${t('components.channels.UploadForm.label.channel')}: ${selectedChannel?.artist.name}` }}
|
{{ `${t('components.channels.UploadForm.label.channel')}: ${selectedChannel?.artist.name}` }}
|
||||||
</label>
|
</label>
|
||||||
|
@ -477,7 +492,7 @@ defineExpose({
|
||||||
{{ t('components.channels.UploadForm.label.channel') }}
|
{{ t('components.channels.UploadForm.label.channel') }}
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
v-if="availableChannels.length > 1"
|
v-if="availableChannels !== null && availableChannels.length > 1"
|
||||||
id="channel-dropdown"
|
id="channel-dropdown"
|
||||||
v-model="channelDropdownId"
|
v-model="channelDropdownId"
|
||||||
class="dropdown"
|
class="dropdown"
|
||||||
|
@ -492,12 +507,13 @@ defineExpose({
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<album-select
|
<album-select
|
||||||
v-if="selectedChannel && albumSelection && albumSelection.albums.length > 0"
|
v-if="selectedChannel !== null && albumSelection && albumSelection.albums.length > 0"
|
||||||
v-model="albumSelection"
|
v-model="albumSelection"
|
||||||
:class="['ui', 'field']"
|
:class="['ui', 'field']"
|
||||||
/>
|
/>
|
||||||
<div>
|
<div>
|
||||||
<license-select
|
<license-select
|
||||||
|
v-if="values.license !== null"
|
||||||
v-model="values.license"
|
v-model="values.license"
|
||||||
:class="['ui', 'field']"
|
:class="['ui', 'field']"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -30,7 +30,7 @@ interface Events {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: number
|
id: number | string
|
||||||
}
|
}
|
||||||
|
|
||||||
const store = useStore()
|
const store = useStore()
|
||||||
|
@ -80,12 +80,10 @@ const fetchData = async () => {
|
||||||
|
|
||||||
artistCredit.value = albumResponse.data.artist_credit
|
artistCredit.value = albumResponse.data.artist_credit
|
||||||
|
|
||||||
|
// fetch the first artist of the album
|
||||||
const artistResponse = await axios.get(`artists/${albumResponse.data.artist_credit[0].artist.id}/`)
|
const artistResponse = await axios.get(`artists/${albumResponse.data.artist_credit[0].artist.id}/`)
|
||||||
|
|
||||||
artist.value = artistResponse.data
|
artist.value = artistResponse.data
|
||||||
if (artist.value?.channel) {
|
|
||||||
artist.value.channel.artist = artist.value
|
|
||||||
}
|
|
||||||
|
|
||||||
object.value = albumResponse.data
|
object.value = albumResponse.data
|
||||||
if (object.value) {
|
if (object.value) {
|
||||||
|
@ -216,6 +214,7 @@ const remove = async () => {
|
||||||
</Layout>
|
</Layout>
|
||||||
<Layout flex>
|
<Layout flex>
|
||||||
<PlayButton
|
<PlayButton
|
||||||
|
v-if="object.tracks"
|
||||||
split
|
split
|
||||||
:tracks="object.tracks"
|
:tracks="object.tracks"
|
||||||
:is-playable="object.is_playable"
|
:is-playable="object.is_playable"
|
||||||
|
|
|
@ -13,7 +13,7 @@ import Layout from '~/components/ui/Layout.vue'
|
||||||
import Button from '~/components/ui/Button.vue'
|
import Button from '~/components/ui/Button.vue'
|
||||||
import Spacer from '~/components/ui/Spacer.vue'
|
import Spacer from '~/components/ui/Spacer.vue'
|
||||||
import Input from '~/components/ui/Input.vue'
|
import Input from '~/components/ui/Input.vue'
|
||||||
import Textarea from '~/components/ui/Textarea.vue'
|
// import Textarea from '~/components/ui/Textarea.vue'
|
||||||
import Pills from '~/components/ui/Pills.vue'
|
import Pills from '~/components/ui/Pills.vue'
|
||||||
import Alert from '~/components/ui/Alert.vue'
|
import Alert from '~/components/ui/Alert.vue'
|
||||||
|
|
||||||
|
@ -268,12 +268,14 @@ const resetField = (fieldId: string) => {
|
||||||
</select>
|
</select>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="fieldConfig.type === 'content'">
|
<template v-else-if="fieldConfig.type === 'content'">
|
||||||
<Textarea
|
TEXTAREA
|
||||||
|
<b> v-model: </b>{{ values[fieldConfig.id].text }}
|
||||||
|
<!-- <Textarea
|
||||||
v-model="values[fieldConfig.id].text"
|
v-model="values[fieldConfig.id].text"
|
||||||
:label="fieldConfig.label"
|
:label="fieldConfig.label"
|
||||||
:field-id="fieldConfig.id"
|
:field-id="fieldConfig.id"
|
||||||
initial-lines="3"
|
initial-lines="3"
|
||||||
/>
|
/> -->
|
||||||
</template>
|
</template>
|
||||||
<!-- TODO: Style Attachment Input -->
|
<!-- TODO: Style Attachment Input -->
|
||||||
<template v-else-if="fieldConfig.type === 'attachment'">
|
<template v-else-if="fieldConfig.type === 'attachment'">
|
||||||
|
@ -318,7 +320,9 @@ const resetField = (fieldId: string) => {
|
||||||
</Button>
|
</Button>
|
||||||
</Layout>
|
</Layout>
|
||||||
<Spacer />
|
<Spacer />
|
||||||
<Textarea
|
TEXTAREA
|
||||||
|
<b> v-model: </b>{{ summary }}
|
||||||
|
<!-- <Textarea
|
||||||
id="change-summary"
|
id="change-summary"
|
||||||
v-model="summary"
|
v-model="summary"
|
||||||
name="change-summary"
|
name="change-summary"
|
||||||
|
@ -326,7 +330,7 @@ const resetField = (fieldId: string) => {
|
||||||
:label="t('components.library.EditForm.label.summary')"
|
:label="t('components.library.EditForm.label.summary')"
|
||||||
:placeholder="labels.summaryPlaceholder"
|
:placeholder="labels.summaryPlaceholder"
|
||||||
>
|
>
|
||||||
<Button
|
<Link
|
||||||
v-if="objectType === 'track'"
|
v-if="objectType === 'track'"
|
||||||
low-height
|
low-height
|
||||||
secondary
|
secondary
|
||||||
|
@ -334,8 +338,8 @@ const resetField = (fieldId: string) => {
|
||||||
:to="{name: 'library.tracks.detail', params: {id: object.id }}"
|
:to="{name: 'library.tracks.detail', params: {id: object.id }}"
|
||||||
>
|
>
|
||||||
{{ t('components.library.EditForm.button.cancel') }}
|
{{ t('components.library.EditForm.button.cancel') }}
|
||||||
</Button>
|
</Link>
|
||||||
</Textarea>
|
</Textarea> -->
|
||||||
<Button
|
<Button
|
||||||
:is-loading="isLoading"
|
:is-loading="isLoading"
|
||||||
primary
|
primary
|
||||||
|
|
|
@ -480,7 +480,7 @@ const updateSubscriptionCount = (delta: number) => {
|
||||||
v-if="object.artist?.tags && object.artist?.tags.length > 0"
|
v-if="object.artist?.tags && object.artist?.tags.length > 0"
|
||||||
:tags="object.artist.tags"
|
:tags="object.artist.tags"
|
||||||
:limit="5"
|
:limit="5"
|
||||||
show-more="true"
|
:show-more="true"
|
||||||
/>
|
/>
|
||||||
<Tabs>
|
<Tabs>
|
||||||
<Tab
|
<Tab
|
||||||
|
|
Loading…
Reference in New Issue