Resolve most type conflicts
This commit is contained in:
parent
e7da8b5f43
commit
436a76928f
|
@ -41,7 +41,7 @@ defineProps<Props>()
|
||||||
>
|
>
|
||||||
<strong>{{ source.track.title }}</strong><br>
|
<strong>{{ source.track.title }}</strong><br>
|
||||||
<span>
|
<span>
|
||||||
{{ source.track.artist.name }}
|
{{ source.track.artist?.name }}
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -219,10 +219,10 @@ onMounted(() => {
|
||||||
<div class="item">
|
<div class="item">
|
||||||
<div class="ui user-dropdown dropdown">
|
<div class="ui user-dropdown dropdown">
|
||||||
<img
|
<img
|
||||||
v-if="$store.state.auth.authenticated && $store.state.auth.profile.avatar && $store.state.auth.profile.avatar.urls.medium_square_crop"
|
v-if="$store.state.auth.authenticated && $store.state.auth.profile?.avatar && $store.state.auth.profile?.avatar.urls.medium_square_crop"
|
||||||
class="ui avatar image"
|
class="ui avatar image"
|
||||||
alt=""
|
alt=""
|
||||||
:src="$store.getters['instance/absoluteUrl']($store.state.auth.profile.avatar.urls.medium_square_crop)"
|
:src="$store.getters['instance/absoluteUrl']($store.state.auth.profile?.avatar.urls.medium_square_crop)"
|
||||||
>
|
>
|
||||||
<actor-avatar
|
<actor-avatar
|
||||||
v-else-if="$store.state.auth.authenticated"
|
v-else-if="$store.state.auth.authenticated"
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { useClipboard } from '@vueuse/core'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
type: string
|
type: string
|
||||||
id: string
|
id: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
|
@ -234,7 +234,7 @@ const updatePage = (page: number) => {
|
||||||
|
|
||||||
<track-row
|
<track-row
|
||||||
v-for="(track, index) in allTracks"
|
v-for="(track, index) in allTracks"
|
||||||
:key="track.id + track.position"
|
:key="track.id + (track.position ?? 0)"
|
||||||
:data-track-id="track.id"
|
:data-track-id="track.id"
|
||||||
:data-track-position="track.position"
|
:data-track-position="track.position"
|
||||||
:track="track"
|
:track="track"
|
||||||
|
|
|
@ -42,16 +42,15 @@ const submit = async () => {
|
||||||
isLoading.value = true
|
isLoading.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const event = props.app !== null
|
const isUpdating = props.app !== null
|
||||||
? 'updated'
|
const request = isUpdating
|
||||||
: 'created'
|
|
||||||
|
|
||||||
const request = props.app !== null
|
|
||||||
? () => axios.patch(`oauth/apps/${props.app?.client_id}/`, fields)
|
? () => axios.patch(`oauth/apps/${props.app?.client_id}/`, fields)
|
||||||
: () => axios.post('oauth/apps/', fields)
|
: () => axios.post('oauth/apps/', fields)
|
||||||
|
|
||||||
const response = await request()
|
const response = await request()
|
||||||
emit(event, response.data as Application)
|
|
||||||
|
if (isUpdating) emit('updated', response.data as Application)
|
||||||
|
else emit('created', response.data as Application)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
errors.value = (error as BackendError).backendErrors
|
errors.value = (error as BackendError).backendErrors
|
||||||
}
|
}
|
||||||
|
|
|
@ -126,7 +126,7 @@ const submitAndScan = async () => {
|
||||||
</translate>
|
</translate>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="plugin.conf?.length > 0">
|
<template v-if="(plugin.conf?.length ?? 0) > 0">
|
||||||
<template
|
<template
|
||||||
v-for="field in plugin.conf"
|
v-for="field in plugin.conf"
|
||||||
:key="field.name"
|
:key="field.name"
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { BackendError, Application } from '~/types'
|
import type { BackendError, Application, PrivacyLevel } from '~/types'
|
||||||
|
import type { $ElementType } from 'utility-types'
|
||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import $ from 'jquery'
|
import $ from 'jquery'
|
||||||
|
@ -18,9 +19,9 @@ import PasswordInput from '~/components/forms/PasswordInput.vue'
|
||||||
|
|
||||||
const SETTINGS_ORDER: FieldId[] = ['summary', 'privacy_level']
|
const SETTINGS_ORDER: FieldId[] = ['summary', 'privacy_level']
|
||||||
|
|
||||||
type FieldId = 'summary' | 'privacy_level'
|
type Field = { id: 'summary', type: 'content', value: { text: string, content_type: 'text/markdown' } }
|
||||||
type Field = { id: string, type: 'content', value: { text: string, content_type: 'text/markdown' } }
|
| { id: 'privacy_level', type: 'dropdown', choices: PrivacyLevel[], value: string }
|
||||||
| { id: string, type: 'dropdown', choices: string[], value: string }
|
type FieldId = $ElementType<Field, 'id'>
|
||||||
|
|
||||||
interface Settings {
|
interface Settings {
|
||||||
success: boolean
|
success: boolean
|
||||||
|
@ -157,7 +158,9 @@ const avatar = ref({ uuid: null, ...(store.state.auth.profile?.avatar ?? {}) })
|
||||||
const initialAvatar = avatar.value.uuid ?? undefined
|
const initialAvatar = avatar.value.uuid ?? undefined
|
||||||
const avatarErrors = ref([] as string[])
|
const avatarErrors = ref([] as string[])
|
||||||
const isLoadingAvatar = ref(false)
|
const isLoadingAvatar = ref(false)
|
||||||
const submitAvatar = async (uuid: string) => {
|
const submitAvatar = async (uuid: string | null) => {
|
||||||
|
if (!uuid) return
|
||||||
|
|
||||||
isLoadingAvatar.value = true
|
isLoadingAvatar.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -315,9 +318,9 @@ fetchOwnedApps()
|
||||||
:key="f.id"
|
:key="f.id"
|
||||||
class="field"
|
class="field"
|
||||||
>
|
>
|
||||||
<label :for="f.id">{{ sharedLabels.fields[f.id as FieldId].label }}</label>
|
<label :for="f.id">{{ sharedLabels.fields[f.id].label }}</label>
|
||||||
<p v-if="sharedLabels.fields[f.id as FieldId].help">
|
<p v-if="sharedLabels.fields[f.id].help">
|
||||||
{{ sharedLabels.fields[f.id as FieldId].help }}
|
{{ sharedLabels.fields[f.id].help }}
|
||||||
</p>
|
</p>
|
||||||
<select
|
<select
|
||||||
v-if="f.type === 'dropdown'"
|
v-if="f.type === 'dropdown'"
|
||||||
|
@ -330,7 +333,7 @@ fetchOwnedApps()
|
||||||
:key="key"
|
:key="key"
|
||||||
:value="c"
|
:value="c"
|
||||||
>
|
>
|
||||||
{{ sharedLabels.fields[f.id as FieldId].choices[c] }}
|
{{ sharedLabels.fields[f.id].choices?.[c] }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
<content-form
|
<content-form
|
||||||
|
@ -833,7 +836,7 @@ fetchOwnedApps()
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<translate
|
<translate
|
||||||
:translate-params="{email: $store.state.auth.profile.email}"
|
:translate-params="{email: $store.state.auth.profile?.email}"
|
||||||
translate-context="Content/Settings/Paragraph'"
|
translate-context="Content/Settings/Paragraph'"
|
||||||
>
|
>
|
||||||
Your current e-mail address is %{ email }.
|
Your current e-mail address is %{ email }.
|
||||||
|
|
|
@ -48,7 +48,7 @@ const payload = reactive({
|
||||||
password1: '',
|
password1: '',
|
||||||
email: '',
|
email: '',
|
||||||
invitation: props.defaultInvitation,
|
invitation: props.defaultInvitation,
|
||||||
request_fields: {}
|
request_fields: {} as Record<string, string | number | string[]>
|
||||||
})
|
})
|
||||||
|
|
||||||
const submitted = ref(false)
|
const submitted = ref(false)
|
||||||
|
|
|
@ -19,6 +19,7 @@ watch(show, () => {
|
||||||
submittable.value = false
|
submittable.value = false
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const albumForm = ref()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -28,7 +29,7 @@ watch(show, () => {
|
||||||
>
|
>
|
||||||
<h4 class="header">
|
<h4 class="header">
|
||||||
<translate
|
<translate
|
||||||
v-if="channel.content_category === 'podcasts'"
|
v-if="channel.content_category === 'podcast'"
|
||||||
translate-context="Popup/Channels/Title/Verb"
|
translate-context="Popup/Channels/Title/Verb"
|
||||||
>
|
>
|
||||||
New series
|
New series
|
||||||
|
@ -58,7 +59,7 @@ watch(show, () => {
|
||||||
<button
|
<button
|
||||||
:class="['ui', 'primary', {loading: isLoading}, 'button']"
|
:class="['ui', 'primary', {loading: isLoading}, 'button']"
|
||||||
:disabled="!submittable"
|
:disabled="!submittable"
|
||||||
@click.stop.prevent="$refs.albumForm.submit()"
|
@click.stop.prevent="albumForm.submit()"
|
||||||
>
|
>
|
||||||
<translate translate-context="*/*/Button.Label">
|
<translate translate-context="*/*/Button.Label">
|
||||||
Create
|
Create
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
import type { Channel } from '~/types'
|
import type { Channel } from '~/types'
|
||||||
|
|
||||||
import { useGettext } from 'vue3-gettext'
|
import { useGettext } from 'vue3-gettext'
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
import { useStore } from '~/store'
|
import { useStore } from '~/store'
|
||||||
import { computed } from 'vue'
|
|
||||||
|
|
||||||
import LoginModal from '~/components/common/LoginModal.vue'
|
import LoginModal from '~/components/common/LoginModal.vue'
|
||||||
|
|
||||||
|
@ -34,8 +34,12 @@ const message = computed(() => ({
|
||||||
|
|
||||||
const toggle = async () => {
|
const toggle = async () => {
|
||||||
await store.dispatch('channels/toggle', props.channel.uuid)
|
await store.dispatch('channels/toggle', props.channel.uuid)
|
||||||
emit(isSubscribed.value ? 'unsubscribed' : 'subscribed')
|
|
||||||
|
if (isSubscribed.value) emit('unsubscribed')
|
||||||
|
else emit('subscribed')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const loginModal = ref()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -50,7 +54,7 @@ const toggle = async () => {
|
||||||
<button
|
<button
|
||||||
v-else
|
v-else
|
||||||
:class="['ui', 'pink', 'icon', 'labeled', 'button']"
|
:class="['ui', 'pink', 'icon', 'labeled', 'button']"
|
||||||
@click="$refs.loginModal.show = true"
|
@click="loginModal.show = true"
|
||||||
>
|
>
|
||||||
<i class="heart icon" />
|
<i class="heart icon" />
|
||||||
{{ title }}
|
{{ title }}
|
||||||
|
@ -59,8 +63,8 @@ const toggle = async () => {
|
||||||
class="small"
|
class="small"
|
||||||
:next-route="$route.fullPath"
|
:next-route="$route.fullPath"
|
||||||
:message="message.authMessage"
|
:message="message.authMessage"
|
||||||
:cover="channel.artist.cover!"
|
:cover="channel.artist?.cover!"
|
||||||
@created="$refs.loginModal.show = false;"
|
@created="loginModal.show = false"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -579,9 +579,8 @@ const labels = computed(() => ({
|
||||||
</div>
|
</div>
|
||||||
<upload-metadata-form
|
<upload-metadata-form
|
||||||
v-if="selectedUpload"
|
v-if="selectedUpload"
|
||||||
|
v-model:values="uploadImportData[selectedUploadId]"
|
||||||
:upload="selectedUpload"
|
:upload="selectedUpload"
|
||||||
:values="uploadImportData[selectedUploadId]"
|
|
||||||
@values="uploadImportData.selectedUploadId = $event"
|
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
v-if="step === 2"
|
v-if="step === 2"
|
||||||
|
|
|
@ -1,34 +1,35 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Upload } from '~/types'
|
import type { Upload, Tag } from '~/types'
|
||||||
|
|
||||||
import { ref, computed, watch } from 'vue'
|
import { reactive, computed, watch } from 'vue'
|
||||||
|
import { useVModel } from '@vueuse/core'
|
||||||
|
|
||||||
import TagsSelector from '~/components/library/TagsSelector.vue'
|
import TagsSelector from '~/components/library/TagsSelector.vue'
|
||||||
import AttachmentInput from '~/components/common/AttachmentInput.vue'
|
import AttachmentInput from '~/components/common/AttachmentInput.vue'
|
||||||
|
|
||||||
interface Events {
|
interface Events {
|
||||||
(e: 'values', values: Record<string, string>): void
|
(e: 'update:values', values: Values): void
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
upload: Upload
|
upload: Upload
|
||||||
values?: Record<string, string> | null
|
values: Values
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Values = (Record<string, string> & { tags?: Tag[] }) | null
|
||||||
|
|
||||||
const emit = defineEmits<Events>()
|
const emit = defineEmits<Events>()
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
values: null
|
values: null
|
||||||
})
|
})
|
||||||
|
|
||||||
const newValues = ref({ ...(props.values ?? props.upload.import_metadata) } as Record<string, string>)
|
const newValues = reactive<Exclude<Values, null>>({
|
||||||
|
...(props.values ?? props.upload.import_metadata),
|
||||||
|
tags: (props.values ?? props.upload.import_metadata)?.tags ?? [] as Tag[]
|
||||||
|
})
|
||||||
|
|
||||||
// computed: {
|
|
||||||
// isLoading () {
|
|
||||||
// return !!this.metadata
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
const isLoading = computed(() => !props.upload)
|
const isLoading = computed(() => !props.upload)
|
||||||
watch(newValues, (values) => emit('values', values), { immediate: true })
|
watch(newValues, (values) => emit('update:values', values), { immediate: true })
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -44,7 +45,7 @@ watch(newValues, (values) => emit('values', values), { immediate: true })
|
||||||
</div>
|
</div>
|
||||||
<attachment-input
|
<attachment-input
|
||||||
v-model="newValues.cover"
|
v-model="newValues.cover"
|
||||||
@delete="newValues.cover = null"
|
@delete="newValues.cover = ''"
|
||||||
>
|
>
|
||||||
<translate translate-context="Content/Channel/*">
|
<translate translate-context="Content/Channel/*">
|
||||||
Track Picture
|
Track Picture
|
||||||
|
|
|
@ -13,7 +13,7 @@ interface Action {
|
||||||
allowAll?: boolean
|
allowAll?: boolean
|
||||||
confirmColor?: string
|
confirmColor?: string
|
||||||
confirmationMessage?: string
|
confirmationMessage?: string
|
||||||
filterChackable?: (item: never) => boolean
|
filterChackable?: (item: any) => boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Events {
|
interface Events {
|
||||||
|
@ -22,7 +22,7 @@ interface Events {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
objectsData: { results: [], count: number }
|
objectsData: { results: any[], count: number }
|
||||||
actions: Action[]
|
actions: Action[]
|
||||||
actionUrl: string
|
actionUrl: string
|
||||||
idField?: string
|
idField?: string
|
||||||
|
|
|
@ -24,6 +24,7 @@ onMounted(() => {
|
||||||
...props.message
|
...props.message
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @ts-expect-error fomantic ui
|
||||||
$('body').toast(params)
|
$('body').toast(params)
|
||||||
$('.ui.toast.visible').last().attr('role', 'alert')
|
$('.ui.toast.visible').last().attr('role', 'alert')
|
||||||
})
|
})
|
||||||
|
|
|
@ -21,7 +21,7 @@ interface Events {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string
|
id: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const emit = defineEmits<Events>()
|
const emit = defineEmits<Events>()
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Album, Library } from '~/types'
|
import type { EditObjectType } from '~/composables/moderation/useEditConfigs'
|
||||||
|
import type { Album, Library, Actor } from '~/types'
|
||||||
|
|
||||||
import { useStore } from '~/store'
|
import { useStore } from '~/store'
|
||||||
|
|
||||||
import EditForm from '~/components/library/EditForm.vue'
|
import EditForm from '~/components/library/EditForm.vue'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
objectType: string
|
objectType: EditObjectType
|
||||||
object: Album
|
object: Album & { attributed_to: Actor }
|
||||||
libraries: Library[]
|
libraries: Library[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,7 +47,6 @@ const canEdit = store.state.auth.availablePermissions.library
|
||||||
v-else
|
v-else
|
||||||
:object-type="objectType"
|
:object-type="objectType"
|
||||||
:object="object"
|
:object="object"
|
||||||
:can-edit="canEdit"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -41,7 +41,7 @@ const page = ref(+props.defaultPage)
|
||||||
type ResponseType = { count: number, results: any[] }
|
type ResponseType = { count: number, results: any[] }
|
||||||
const result = ref<null | ResponseType>(null)
|
const result = ref<null | ResponseType>(null)
|
||||||
const query = ref(props.defaultQuery)
|
const query = ref(props.defaultQuery)
|
||||||
const tags = reactive(props.defaultTags.slice())
|
const tags = reactive(props.defaultTags.map(name => ({ name })))
|
||||||
|
|
||||||
const orderingOptions: [OrderingField, keyof typeof sharedLabels.filters][] = [
|
const orderingOptions: [OrderingField, keyof typeof sharedLabels.filters][] = [
|
||||||
['creation_date', 'creation_date'],
|
['creation_date', 'creation_date'],
|
||||||
|
@ -59,7 +59,7 @@ const updateQueryString = () => router.replace({
|
||||||
query: {
|
query: {
|
||||||
query: query.value,
|
query: query.value,
|
||||||
page: page.value,
|
page: page.value,
|
||||||
tag: tags,
|
tag: tags.map(({ name }) => name),
|
||||||
paginateBy: paginateBy.value,
|
paginateBy: paginateBy.value,
|
||||||
ordering: orderingString.value
|
ordering: orderingString.value
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,25 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Track, Album, Artist, Library } from '~/types'
|
import type { Track, Album, Artist, Library } from '~/types'
|
||||||
|
|
||||||
|
import { computed, ref, watch } from 'vue'
|
||||||
import { useGettext } from 'vue3-gettext'
|
import { useGettext } from 'vue3-gettext'
|
||||||
import axios from 'axios'
|
import { useRouter } from 'vue-router'
|
||||||
import PlayButton from '~/components/audio/PlayButton.vue'
|
|
||||||
import EmbedWizard from '~/components/audio/EmbedWizard.vue'
|
|
||||||
import SemanticModal from '~/components/semantic/Modal.vue'
|
|
||||||
import RadioButton from '~/components/radios/Button.vue'
|
|
||||||
import TagsList from '~/components/tags/List.vue'
|
|
||||||
import useReport from '~/composables/moderation/useReport'
|
|
||||||
import useLogger from '~/composables/useLogger'
|
|
||||||
import { getDomain } from '~/utils'
|
import { getDomain } from '~/utils'
|
||||||
import { useStore } from '~/store'
|
import { useStore } from '~/store'
|
||||||
import { useRouter } from 'vue-router'
|
|
||||||
import { computed, ref, watch } from 'vue'
|
import axios from 'axios'
|
||||||
|
|
||||||
|
import EmbedWizard from '~/components/audio/EmbedWizard.vue'
|
||||||
|
import SemanticModal from '~/components/semantic/Modal.vue'
|
||||||
|
import PlayButton from '~/components/audio/PlayButton.vue'
|
||||||
|
import RadioButton from '~/components/radios/Button.vue'
|
||||||
|
import TagsList from '~/components/tags/List.vue'
|
||||||
|
|
||||||
|
import useReport from '~/composables/moderation/useReport'
|
||||||
|
import useLogger from '~/composables/useLogger'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string
|
id: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { EditObjectType } from '~/composables/moderation/useEditConfigs'
|
||||||
import type { Artist, Library } from '~/types'
|
import type { Artist, Library } from '~/types'
|
||||||
|
|
||||||
import { useStore } from '~/store'
|
import { useStore } from '~/store'
|
||||||
|
@ -6,7 +7,7 @@ import { useStore } from '~/store'
|
||||||
import EditForm from '~/components/library/EditForm.vue'
|
import EditForm from '~/components/library/EditForm.vue'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
objectType: string
|
objectType: EditObjectType
|
||||||
object: Artist
|
object: Artist
|
||||||
libraries: Library[]
|
libraries: Library[]
|
||||||
}
|
}
|
||||||
|
@ -46,7 +47,6 @@ const canEdit = store.state.auth.availablePermissions.library
|
||||||
v-else
|
v-else
|
||||||
:object-type="objectType"
|
:object-type="objectType"
|
||||||
:object="object"
|
:object="object"
|
||||||
:can-edit="canEdit"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -41,7 +41,7 @@ const page = ref(+props.defaultPage)
|
||||||
type ResponseType = { count: number, results: any[] }
|
type ResponseType = { count: number, results: any[] }
|
||||||
const result = ref<null | ResponseType>(null)
|
const result = ref<null | ResponseType>(null)
|
||||||
const query = ref(props.defaultQuery)
|
const query = ref(props.defaultQuery)
|
||||||
const tags = reactive(props.defaultTags.slice())
|
const tags = reactive(props.defaultTags.map(name => ({ name })))
|
||||||
const excludeCompilation = ref(true)
|
const excludeCompilation = ref(true)
|
||||||
|
|
||||||
const orderingOptions: [OrderingField, keyof typeof sharedLabels.filters][] = [
|
const orderingOptions: [OrderingField, keyof typeof sharedLabels.filters][] = [
|
||||||
|
@ -59,7 +59,7 @@ const updateQueryString = () => router.replace({
|
||||||
query: {
|
query: {
|
||||||
query: query.value,
|
query: query.value,
|
||||||
page: page.value,
|
page: page.value,
|
||||||
tag: tags,
|
tag: tags.map(({ name }) => name),
|
||||||
paginateBy: paginateBy.value,
|
paginateBy: paginateBy.value,
|
||||||
ordering: orderingString.value,
|
ordering: orderingString.value,
|
||||||
content_category: 'music',
|
content_category: 'music',
|
||||||
|
|
|
@ -19,10 +19,12 @@ import EditCard from '~/components/library/EditCard.vue'
|
||||||
interface Props {
|
interface Props {
|
||||||
objectType: EditObjectType
|
objectType: EditObjectType
|
||||||
object: EditObject
|
object: EditObject
|
||||||
licenses: License[]
|
licenses?: License[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
licenses: () => []
|
||||||
|
})
|
||||||
|
|
||||||
const { $pgettext } = useGettext()
|
const { $pgettext } = useGettext()
|
||||||
const configs = useEditConfigs()
|
const configs = useEditConfigs()
|
||||||
|
|
|
@ -53,7 +53,7 @@ const labels = computed(() => ({
|
||||||
'Invalid file type, ensure you are uploading an audio file. Supported file extensions are %{ extensions }',
|
'Invalid file type, ensure you are uploading an audio file. Supported file extensions are %{ extensions }',
|
||||||
{ extensions: supportedExtensions.value.join(', ') }
|
{ extensions: supportedExtensions.value.join(', ') }
|
||||||
)
|
)
|
||||||
}
|
} as Record<string, string>
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const uploads = reactive({
|
const uploads = reactive({
|
||||||
|
@ -113,7 +113,7 @@ const fetchStatus = async () => {
|
||||||
|
|
||||||
uploads[status as keyof typeof uploads] = response.data.count
|
uploads[status as keyof typeof uploads] = response.data.count
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
useErrorHandler(error as Error)
|
useErrorHandler(error as Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -473,7 +473,7 @@ useEventListener(window, 'beforeunload', (event) => {
|
||||||
<span
|
<span
|
||||||
v-if="typeof file.error === 'string' && file.error"
|
v-if="typeof file.error === 'string' && file.error"
|
||||||
class="ui tooltip"
|
class="ui tooltip"
|
||||||
:data-tooltip="labels.tooltips[file.error as keyof typeof labels.tooltips]"
|
:data-tooltip="labels.tooltips[file.error]"
|
||||||
>
|
>
|
||||||
<span class="ui danger icon label">
|
<span class="ui danger icon label">
|
||||||
<i class="question circle outline icon" /> {{ file.error }}
|
<i class="question circle outline icon" /> {{ file.error }}
|
||||||
|
|
|
@ -44,7 +44,7 @@ const page = ref(+props.defaultPage)
|
||||||
type ResponseType = { count: number, results: any[] }
|
type ResponseType = { count: number, results: any[] }
|
||||||
const result = ref<null | ResponseType>(null)
|
const result = ref<null | ResponseType>(null)
|
||||||
const query = ref(props.defaultQuery)
|
const query = ref(props.defaultQuery)
|
||||||
const tags = reactive(props.defaultTags.slice())
|
const tags = reactive(props.defaultTags.map(name => ({ name })))
|
||||||
const showSubscribeModal = ref(false)
|
const showSubscribeModal = ref(false)
|
||||||
|
|
||||||
const orderingOptions: [OrderingField, keyof typeof sharedLabels.filters][] = [
|
const orderingOptions: [OrderingField, keyof typeof sharedLabels.filters][] = [
|
||||||
|
@ -62,7 +62,7 @@ const updateQueryString = () => router.replace({
|
||||||
query: {
|
query: {
|
||||||
query: query.value,
|
query: query.value,
|
||||||
page: page.value,
|
page: page.value,
|
||||||
tag: tags,
|
tag: tags.map(({ name }) => name),
|
||||||
paginateBy: paginateBy.value,
|
paginateBy: paginateBy.value,
|
||||||
ordering: orderingString.value,
|
ordering: orderingString.value,
|
||||||
content_category: 'podcast',
|
content_category: 'podcast',
|
||||||
|
|
|
@ -26,7 +26,7 @@ interface Events {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string
|
id: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const emit = defineEmits<Events>()
|
const emit = defineEmits<Events>()
|
||||||
|
|
|
@ -20,7 +20,7 @@ type Filter = { candidates: { count: number, sample: Track[] } }
|
||||||
type ResponseType = { filters: Array<Filter> }
|
type ResponseType = { filters: Array<Filter> }
|
||||||
|
|
||||||
interface Events {
|
interface Events {
|
||||||
(e: 'update-config', index: number, name: string, value: number[]): void
|
(e: 'update-config', index: number, name: string, value: number[] | boolean): void
|
||||||
(e: 'delete', index: number): void
|
(e: 'delete', index: number): void
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import type { EditObjectType } from '~/composables/moderation/useEditConfigs'
|
||||||
import type { RouteWithPreferences, OrderingField } from '~/store/ui'
|
import type { RouteWithPreferences, OrderingField } from '~/store/ui'
|
||||||
import type { SmartSearchProps } from '~/composables/useSmartSearch'
|
import type { SmartSearchProps } from '~/composables/useSmartSearch'
|
||||||
import type { OrderingProps } from '~/composables/useOrdering'
|
import type { OrderingProps } from '~/composables/useOrdering'
|
||||||
|
import type { ReviewState, Review } from '~/types'
|
||||||
|
|
||||||
import { ref, reactive, watch, computed } from 'vue'
|
import { ref, reactive, watch, computed } from 'vue'
|
||||||
import { useGettext } from 'vue3-gettext'
|
import { useGettext } from 'vue3-gettext'
|
||||||
|
@ -39,9 +40,8 @@ const search = ref()
|
||||||
|
|
||||||
const page = ref(1)
|
const page = ref(1)
|
||||||
|
|
||||||
type StateTarget = { id: number, type: keyof typeof targets }
|
type StateTarget = Review['target']
|
||||||
type ResponseResult = { uuid: string, is_approved: boolean, target?: StateTarget }
|
type ResponseType = { count: number, results: Review[] }
|
||||||
type ResponseType = { count: number, results: ResponseResult[] }
|
|
||||||
const result = ref<null | ResponseType>(null)
|
const result = ref<null | ResponseType>(null)
|
||||||
|
|
||||||
const { onSearch, query, addSearchToken, getTokenValue } = useSmartSearch(props.defaultQuery, props.updateUrl)
|
const { onSearch, query, addSearchToken, getTokenValue } = useSmartSearch(props.defaultQuery, props.updateUrl)
|
||||||
|
@ -53,20 +53,23 @@ const orderingOptions: [OrderingField, keyof typeof sharedLabels.filters][] = [
|
||||||
]
|
]
|
||||||
|
|
||||||
interface TargetType {
|
interface TargetType {
|
||||||
payload: ResponseResult
|
payload: Review
|
||||||
currentState: Record<EditObjectType, { value: unknown }>
|
currentState: Record<EditObjectType, { value: unknown }>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Targets = Exclude<StateTarget, undefined>['type']
|
||||||
const targets = reactive({
|
const targets = reactive({
|
||||||
track: {} as Record<string, TargetType>
|
track: {}
|
||||||
})
|
}) as Record<Targets, Record<string, TargetType>>
|
||||||
|
|
||||||
const fetchTargets = async () => {
|
const fetchTargets = async () => {
|
||||||
// we request target data via the API so we can display previous state
|
// we request target data via the API so we can display previous state
|
||||||
// additionnal data next to the edit card
|
// additionnal data next to the edit card
|
||||||
type Config = { url: string, ids: number[] }
|
type Config = { url: string, ids: number[] }
|
||||||
const typesAndIds: Record<keyof typeof targets, Config> = {
|
const typesAndIds: Record<Targets, Config> = {
|
||||||
track: { url: 'tracks/', ids: [] }
|
artist: { url: 'artists/', ids: [] },
|
||||||
|
track: { url: 'tracks/', ids: [] },
|
||||||
|
album: { url: 'albums/', ids: [] }
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const res of result.value?.results ?? []) {
|
for (const res of result.value?.results ?? []) {
|
||||||
|
@ -85,7 +88,7 @@ const fetchTargets = async () => {
|
||||||
id: uniq(config.ids),
|
id: uniq(config.ids),
|
||||||
hidden: 'null'
|
hidden: 'null'
|
||||||
}
|
}
|
||||||
}).catch(() => {
|
}).catch((error) => {
|
||||||
useErrorHandler(error as Error)
|
useErrorHandler(error as Error)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -147,8 +150,8 @@ const handle = (type: 'delete' | 'approved', id: string, value: boolean) => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const getCurrentState = (target?: StateTarget): object => {
|
const getCurrentState = (target?: StateTarget): ReviewState => {
|
||||||
if (!target) return {}
|
if (!target || !(target.type in targets)) return {}
|
||||||
return targets[target.type]?.[target.id]?.currentState ?? {}
|
return targets[target.type]?.[target.id]?.currentState ?? {}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -97,8 +97,7 @@ const labels = computed(() => ({
|
||||||
searchPlaceholder: $pgettext('Content/Search/Input.Placeholder', 'Search by name')
|
searchPlaceholder: $pgettext('Content/Search/Input.Placeholder', 'Search by name')
|
||||||
}))
|
}))
|
||||||
|
|
||||||
// TODO (wvffle): Check if we can remove the prop
|
const detailedUpload = ref()
|
||||||
const detailedUpload = ref({})
|
|
||||||
const showUploadDetailModal = ref(false)
|
const showUploadDetailModal = ref(false)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
@ -157,6 +156,7 @@ const showUploadDetailModal = ref(false)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<import-status-modal
|
<import-status-modal
|
||||||
|
v-if="detailedUpload"
|
||||||
v-model:show="showUploadDetailModal"
|
v-model:show="showUploadDetailModal"
|
||||||
:upload="detailedUpload"
|
:upload="detailedUpload"
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import type { ImportStatus, PrivacyLevel, Upload, BackendResponse } from '~/types'
|
||||||
import type { RouteWithPreferences, OrderingField } from '~/store/ui'
|
import type { RouteWithPreferences, OrderingField } from '~/store/ui'
|
||||||
import type { SmartSearchProps } from '~/composables/useSmartSearch'
|
import type { SmartSearchProps } from '~/composables/useSmartSearch'
|
||||||
import type { ImportStatus, PrivacyLevel, Upload } from '~/types'
|
|
||||||
import type { OrderingProps } from '~/composables/useOrdering'
|
import type { OrderingProps } from '~/composables/useOrdering'
|
||||||
|
|
||||||
import { humanSize, truncate } from '~/utils/filters'
|
import { humanSize, truncate } from '~/utils/filters'
|
||||||
|
@ -37,8 +37,7 @@ const props = withDefaults(defineProps<Props>(), {
|
||||||
const search = ref()
|
const search = ref()
|
||||||
|
|
||||||
const page = ref(1)
|
const page = ref(1)
|
||||||
type ResponseType = { count: number, results: any[] }
|
const result = ref<BackendResponse<Upload>>()
|
||||||
const result = ref<null | ResponseType>(null)
|
|
||||||
|
|
||||||
const { onSearch, query, addSearchToken, getTokenValue } = useSmartSearch(props.defaultQuery, props.updateUrl)
|
const { onSearch, query, addSearchToken, getTokenValue } = useSmartSearch(props.defaultQuery, props.updateUrl)
|
||||||
const { onOrderingUpdate, orderingString, paginateBy, ordering, orderingDirection } = useOrdering(props.orderingConfigName)
|
const { onOrderingUpdate, orderingString, paginateBy, ordering, orderingDirection } = useOrdering(props.orderingConfigName)
|
||||||
|
@ -84,7 +83,7 @@ const fetchData = async () => {
|
||||||
result.value = response.data
|
result.value = response.data
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
useErrorHandler(error as Error)
|
useErrorHandler(error as Error)
|
||||||
result.value = null
|
result.value = undefined
|
||||||
} finally {
|
} finally {
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
}
|
}
|
||||||
|
@ -104,7 +103,7 @@ const displayName = (upload: Upload): string => {
|
||||||
return upload.filename ?? upload.source ?? upload.uuid
|
return upload.filename ?? upload.source ?? upload.uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
const detailedUpload = ref({})
|
const detailedUpload = ref<Upload>()
|
||||||
const showUploadDetailModal = ref(false)
|
const showUploadDetailModal = ref(false)
|
||||||
|
|
||||||
const getImportStatusChoice = (importStatus: ImportStatus) => {
|
const getImportStatusChoice = (importStatus: ImportStatus) => {
|
||||||
|
@ -229,8 +228,9 @@ const getPrivacyLevelChoice = (privacyLevel: PrivacyLevel) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- TODO (wvffle): Check if :upload shouldn't be v-modl:upload -->
|
<!-- TODO (wvffle): Check if :upload shouldn't be v-model:upload -->
|
||||||
<import-status-modal
|
<import-status-modal
|
||||||
|
v-if="detailedUpload"
|
||||||
v-model:show="showUploadDetailModal"
|
v-model:show="showUploadDetailModal"
|
||||||
:upload="detailedUpload"
|
:upload="detailedUpload"
|
||||||
/>
|
/>
|
||||||
|
@ -296,9 +296,7 @@ const getPrivacyLevelChoice = (privacyLevel: PrivacyLevel) => {
|
||||||
</translate>
|
</translate>
|
||||||
</th>
|
</th>
|
||||||
</template>
|
</template>
|
||||||
<template
|
<template #row-cells="scope">
|
||||||
#row-cells="scope"
|
|
||||||
>
|
|
||||||
<td>
|
<td>
|
||||||
<router-link :to="{name: 'manage.library.uploads.detail', params: {id: scope.obj.uuid }}">
|
<router-link :to="{name: 'manage.library.uploads.detail', params: {id: scope.obj.uuid }}">
|
||||||
{{ truncate(displayName(scope.obj), 30, undefined, true) }}
|
{{ truncate(displayName(scope.obj), 30, undefined, true) }}
|
||||||
|
@ -369,7 +367,7 @@ const getPrivacyLevelChoice = (privacyLevel: PrivacyLevel) => {
|
||||||
</a>
|
</a>
|
||||||
<button
|
<button
|
||||||
class="ui tiny basic icon button"
|
class="ui tiny basic icon button"
|
||||||
:title="sharedLabels.fields.import_status.detailTitle"
|
:title="sharedLabels.fields.import_status.label"
|
||||||
@click="detailedUpload = scope.obj; showUploadDetailModal = true"
|
@click="detailedUpload = scope.obj; showUploadDetailModal = true"
|
||||||
>
|
>
|
||||||
<i class="question circle outline icon" />
|
<i class="question circle outline icon" />
|
||||||
|
|
|
@ -326,7 +326,7 @@ const handleRemovedNote = (uuid: string) => {
|
||||||
<router-link
|
<router-link
|
||||||
v-if="target && configs[target.type].urls.getDetail"
|
v-if="target && configs[target.type].urls.getDetail"
|
||||||
class="ui basic button"
|
class="ui basic button"
|
||||||
:to="configs[target.type].urls.getDetail(obj.target_state)"
|
:to="configs[target.type].urls.getDetail?.(obj.target_state) ?? '/'"
|
||||||
>
|
>
|
||||||
<i class="eye icon" />
|
<i class="eye icon" />
|
||||||
<translate translate-context="Content/Moderation/Link">
|
<translate translate-context="Content/Moderation/Link">
|
||||||
|
@ -336,7 +336,7 @@ const handleRemovedNote = (uuid: string) => {
|
||||||
<router-link
|
<router-link
|
||||||
v-if="target && configs[target.type].urls.getAdminDetail"
|
v-if="target && configs[target.type].urls.getAdminDetail"
|
||||||
class="ui basic button"
|
class="ui basic button"
|
||||||
:to="configs[target.type].urls.getAdminDetail(obj.target_state)"
|
:to="configs[target.type].urls.getAdminDetail?.(obj.target_state) ?? '/'"
|
||||||
>
|
>
|
||||||
<i class="wrench icon" />
|
<i class="wrench icon" />
|
||||||
<translate translate-context="Content/Moderation/Link">
|
<translate translate-context="Content/Moderation/Link">
|
||||||
|
@ -391,10 +391,10 @@ const handleRemovedNote = (uuid: string) => {
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<instance-policy-modal
|
<instance-policy-modal
|
||||||
v-if="!obj.target_owner.is_local"
|
v-if="!obj.target_owner?.is_local"
|
||||||
class="right floated mini basic"
|
class="right floated mini basic"
|
||||||
type="actor"
|
type="actor"
|
||||||
:target="obj.target_owner.full_username"
|
:target="obj.target_owner?.full_username ?? ''"
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -9,7 +9,7 @@ interface Props {
|
||||||
customRadioId?: number | null
|
customRadioId?: number | null
|
||||||
type?: string
|
type?: string
|
||||||
clientOnly?: boolean
|
clientOnly?: boolean
|
||||||
objectId?: ObjectId | string | null
|
objectId?: ObjectId | number | string | null
|
||||||
radioConfig?: RadioConfig | null
|
radioConfig?: RadioConfig | null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,7 +31,10 @@ const running = computed(() => {
|
||||||
&& store.state.radios.current?.customRadioId === props.customRadioId
|
&& store.state.radios.current?.customRadioId === props.customRadioId
|
||||||
&& (
|
&& (
|
||||||
typeof props.objectId === 'string'
|
typeof props.objectId === 'string'
|
||||||
|| store.state.radios.current?.objectId?.fullUsername === props.objectId?.fullUsername
|
|| (
|
||||||
|
typeof props.objectId !== 'number'
|
||||||
|
&& store.state.radios.current?.objectId?.fullUsername === props.objectId?.fullUsername
|
||||||
|
)
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -6,12 +6,12 @@ import { reactive, watchEffect, ref } from 'vue'
|
||||||
const MAX_PRELOADED = 3
|
const MAX_PRELOADED = 3
|
||||||
|
|
||||||
export interface CachedSound {
|
export interface CachedSound {
|
||||||
id: string
|
id: number
|
||||||
date: Date
|
date: Date
|
||||||
howl: Howl
|
howl: Howl
|
||||||
}
|
}
|
||||||
|
|
||||||
const soundCache = reactive(new Map<string, CachedSound>())
|
const soundCache = reactive(new Map<number, CachedSound>())
|
||||||
const cleaningCache = ref(false)
|
const cleaningCache = ref(false)
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
|
|
|
@ -22,7 +22,7 @@ export default () => ({
|
||||||
} as Record<PrivacyLevel, string>
|
} as Record<PrivacyLevel, string>
|
||||||
},
|
},
|
||||||
import_status: {
|
import_status: {
|
||||||
detailTitle: $pgettext('Content/Library/Link.Title', 'Click to display more information about the import process for this upload'),
|
label: $pgettext('Content/Library/Link.Title', 'Click to display more information about the import process for this upload'),
|
||||||
choices: {
|
choices: {
|
||||||
skipped: {
|
skipped: {
|
||||||
label: $pgettext('Content/Library/*', 'Skipped'),
|
label: $pgettext('Content/Library/*', 'Skipped'),
|
||||||
|
@ -57,7 +57,8 @@ export default () => ({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
summary: {
|
summary: {
|
||||||
label: $pgettext('Content/Account/*', 'Bio')
|
label: $pgettext('Content/Account/*', 'Bio'),
|
||||||
|
help: undefined
|
||||||
},
|
},
|
||||||
content_category: {
|
content_category: {
|
||||||
label: $pgettext('Content/*/Dropdown.Label/Noun', 'Content category'),
|
label: $pgettext('Content/*/Dropdown.Label/Noun', 'Content category'),
|
||||||
|
|
|
@ -23,7 +23,7 @@ interface ReportableObject {
|
||||||
_obj: Objects[keyof Objects]
|
_obj: Objects[keyof Objects]
|
||||||
|
|
||||||
full_username?: string
|
full_username?: string
|
||||||
id?: string
|
id?: number
|
||||||
uuid?: string
|
uuid?: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +0,0 @@
|
||||||
import jQuery from 'jquery'
|
|
||||||
|
|
||||||
// NOTE: Workaround for fomantic-ui-css
|
|
||||||
if (import.meta.env.DEV) {
|
|
||||||
window.$ = window.jQuery = jQuery
|
|
||||||
}
|
|
||||||
|
|
||||||
export default jQuery
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
/// <reference lib="webworker" />
|
||||||
|
|
||||||
import { cleanupOutdatedCaches, precacheAndRoute } from 'workbox-precaching'
|
import { cleanupOutdatedCaches, precacheAndRoute } from 'workbox-precaching'
|
||||||
import { NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies'
|
import { NetworkFirst, StaleWhileRevalidate } from 'workbox-strategies'
|
||||||
import { ExpirationPlugin } from 'workbox-expiration'
|
import { ExpirationPlugin } from 'workbox-expiration'
|
||||||
|
|
|
@ -31,7 +31,7 @@ export interface ContentFilter {
|
||||||
creation_date: Date
|
creation_date: Date
|
||||||
target: {
|
target: {
|
||||||
type: 'artist'
|
type: 'artist'
|
||||||
id: string
|
id: number
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ export interface ThemeEntry {
|
||||||
export type ContentCategory = 'podcast' | 'music'
|
export type ContentCategory = 'podcast' | 'music'
|
||||||
|
|
||||||
export interface Artist {
|
export interface Artist {
|
||||||
id: string
|
id: number
|
||||||
fid: string
|
fid: string
|
||||||
mbid?: string
|
mbid?: string
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ export interface Artist {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Album {
|
export interface Album {
|
||||||
id: string
|
id: number
|
||||||
fid: string
|
fid: string
|
||||||
mbid?: string
|
mbid?: string
|
||||||
|
|
||||||
|
@ -84,7 +84,7 @@ export interface Album {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Track {
|
export interface Track {
|
||||||
id: string
|
id: number
|
||||||
fid: string
|
fid: string
|
||||||
mbid?: string
|
mbid?: string
|
||||||
|
|
||||||
|
@ -111,7 +111,7 @@ export interface Track {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Channel {
|
export interface Channel {
|
||||||
id: string
|
id: number
|
||||||
uuid: string
|
uuid: string
|
||||||
artist?: Artist
|
artist?: Artist
|
||||||
actor: Actor
|
actor: Actor
|
||||||
|
@ -134,7 +134,7 @@ export interface Channel {
|
||||||
export type PrivacyLevel = 'everyone' | 'instance' | 'me'
|
export type PrivacyLevel = 'everyone' | 'instance' | 'me'
|
||||||
|
|
||||||
export interface Library {
|
export interface Library {
|
||||||
id: string
|
id: number
|
||||||
uuid: string
|
uuid: string
|
||||||
fid?: string
|
fid?: string
|
||||||
name: string
|
name: string
|
||||||
|
@ -182,7 +182,7 @@ export interface License {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Playlist {
|
export interface Playlist {
|
||||||
id: string
|
id: number
|
||||||
name: string
|
name: string
|
||||||
modification_date: string
|
modification_date: string
|
||||||
user: User
|
user: User
|
||||||
|
@ -206,7 +206,7 @@ export interface Radio {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Listening {
|
export interface Listening {
|
||||||
id: string
|
id: number
|
||||||
track: Track
|
track: Track
|
||||||
user: User
|
user: User
|
||||||
actor: Actor
|
actor: Actor
|
||||||
|
@ -277,6 +277,7 @@ export interface Form {
|
||||||
|
|
||||||
// Upload stuff
|
// Upload stuff
|
||||||
export interface Upload {
|
export interface Upload {
|
||||||
|
id: number
|
||||||
uuid: string
|
uuid: string
|
||||||
filename?: string
|
filename?: string
|
||||||
source?: string
|
source?: string
|
||||||
|
@ -293,12 +294,12 @@ export interface Upload {
|
||||||
error_code: string
|
error_code: string
|
||||||
}
|
}
|
||||||
|
|
||||||
import_metadata?: Record<string, string>
|
import_metadata?: Record<string, string> & { tags?: Tag[] }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Profile stuff
|
// Profile stuff
|
||||||
export interface Actor {
|
export interface Actor {
|
||||||
id: string
|
id: number
|
||||||
fid?: string
|
fid?: string
|
||||||
name?: string
|
name?: string
|
||||||
icon?: Cover
|
icon?: Cover
|
||||||
|
@ -310,7 +311,7 @@ export interface Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface User {
|
export interface User {
|
||||||
id: string
|
id: number
|
||||||
avatar?: Cover
|
avatar?: Cover
|
||||||
email: string
|
email: string
|
||||||
summary: { text: string, content_type: string }
|
summary: { text: string, content_type: string }
|
||||||
|
@ -403,7 +404,7 @@ export interface Plugin {
|
||||||
export type EntityObjectType = 'artist' | 'album' | 'track' | 'library' | 'playlist' | 'account' | 'channel'
|
export type EntityObjectType = 'artist' | 'album' | 'track' | 'library' | 'playlist' | 'account' | 'channel'
|
||||||
|
|
||||||
export interface ReportTarget {
|
export interface ReportTarget {
|
||||||
id: string
|
id: number
|
||||||
type: EntityObjectType
|
type: EntityObjectType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import type { ListenWSEvent } from '~/types'
|
import type { CurrentRadio, PopulateQueuePayload } from '~/store/radios'
|
||||||
|
import type { ListenWS } from '~/composables/useWebSocketHandler'
|
||||||
import type { RootState } from '~/store'
|
import type { RootState } from '~/store'
|
||||||
import type { Store } from 'vuex'
|
import type { Store } from 'vuex'
|
||||||
import type { CurrentRadio, PopulateQueuePayload } from '~/store/radios'
|
|
||||||
|
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
import useLogger from '~/composables/useLogger'
|
import useLogger from '~/composables/useLogger'
|
||||||
|
|
||||||
const logger = useLogger()
|
const logger = useLogger()
|
||||||
|
@ -14,7 +15,7 @@ export const CLIENT_RADIOS = {
|
||||||
account: {
|
account: {
|
||||||
offset: 1,
|
offset: 1,
|
||||||
populateQueue ({ current, dispatch, playNow }: PopulateQueuePayload) {
|
populateQueue ({ current, dispatch, playNow }: PopulateQueuePayload) {
|
||||||
const params = { scope: `actor:${current.objectId.fullUsername}`, ordering: '-creation_date', page_size: 1, page: this.offset }
|
const params = { scope: `actor:${current.objectId?.fullUsername}`, ordering: '-creation_date', page_size: 1, page: this.offset }
|
||||||
axios.get('history/listenings', { params }).then(async (response) => {
|
axios.get('history/listenings', { params }).then(async (response) => {
|
||||||
const latest = response.data.results[0]
|
const latest = response.data.results[0]
|
||||||
if (!latest) {
|
if (!latest) {
|
||||||
|
@ -35,9 +36,9 @@ export const CLIENT_RADIOS = {
|
||||||
stop () {
|
stop () {
|
||||||
this.offset = 1
|
this.offset = 1
|
||||||
},
|
},
|
||||||
handleListen (current: CurrentRadio, event: ListenWSEvent, store: Store<RootState>) {
|
handleListen (current: CurrentRadio, event: ListenWS, store: Store<RootState>) {
|
||||||
// TODO: handle actors from other pods
|
// TODO: handle actors from other pods
|
||||||
if (event.actor.local_id === current.objectId.username) {
|
if (event.actor.local_id === current.objectId?.username) {
|
||||||
axios.get(`tracks/${event.object.local_id}`).then(async (response) => {
|
axios.get(`tracks/${event.object.local_id}`).then(async (response) => {
|
||||||
if (response.data.uploads.length > 0) {
|
if (response.data.uploads.length > 0) {
|
||||||
await store.dispatch('queue/append', { track: response.data })
|
await store.dispatch('queue/append', { track: response.data })
|
||||||
|
|
|
@ -12,7 +12,7 @@ import TagsList from '~/components/tags/List.vue'
|
||||||
import useErrorHandler from '~/composables/useErrorHandler'
|
import useErrorHandler from '~/composables/useErrorHandler'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string
|
id: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
|
@ -15,7 +15,7 @@ import useLogger from '~/composables/useLogger'
|
||||||
const PRIVACY_LEVELS = ['me', 'instance', 'everyone'] as PrivacyLevel[]
|
const PRIVACY_LEVELS = ['me', 'instance', 'everyone'] as PrivacyLevel[]
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string
|
id: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
|
@ -14,7 +14,7 @@ import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||||
import useErrorHandler from '~/composables/useErrorHandler'
|
import useErrorHandler from '~/composables/useErrorHandler'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string
|
id: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
@ -258,7 +258,7 @@ const showUploadDetailModal = ref(false)
|
||||||
{{ importStatus }}
|
{{ importStatus }}
|
||||||
<button
|
<button
|
||||||
class="ui tiny basic icon button"
|
class="ui tiny basic icon button"
|
||||||
:title="sharedLabels.fields.import_status.detailTitle"
|
:title="sharedLabels.fields.import_status.label"
|
||||||
@click="showUploadDetailModal = true"
|
@click="showUploadDetailModal = true"
|
||||||
>
|
>
|
||||||
<i class="question circle outline icon" />
|
<i class="question circle outline icon" />
|
||||||
|
|
|
@ -38,7 +38,7 @@ const allPermissions = computed(() => [
|
||||||
|
|
||||||
const isLoadingPolicy = ref(false)
|
const isLoadingPolicy = ref(false)
|
||||||
const policy = ref()
|
const policy = ref()
|
||||||
const fetchPolicy = async (id: string) => {
|
const fetchPolicy = async (id: number) => {
|
||||||
isLoadingPolicy.value = true
|
isLoadingPolicy.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -28,7 +28,7 @@ const labels = computed(() => ({
|
||||||
|
|
||||||
const isLoadingPolicy = ref(false)
|
const isLoadingPolicy = ref(false)
|
||||||
const policy = ref()
|
const policy = ref()
|
||||||
const fetchPolicy = async (id: string) => {
|
const fetchPolicy = async (id: number) => {
|
||||||
isLoadingPolicy.value = true
|
isLoadingPolicy.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -18,6 +18,9 @@ const showCreateModal = ref(false)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const submittable = ref(false)
|
const submittable = ref(false)
|
||||||
const category = ref('podcast')
|
const category = ref('podcast')
|
||||||
|
|
||||||
|
const modalContent = ref()
|
||||||
|
const createForm = ref()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -108,7 +111,7 @@ const category = ref('podcast')
|
||||||
@loading="loading = $event"
|
@loading="loading = $event"
|
||||||
@submittable="submittable = $event"
|
@submittable="submittable = $event"
|
||||||
@category="category = $event"
|
@category="category = $event"
|
||||||
@errored="$refs.modalContent.scrollTop = 0"
|
@errored="modalContent.scrollTop = 0"
|
||||||
@created="$router.push({name: 'channels.detail', params: {id: $event.actor.preferred_username}})"
|
@created="$router.push({name: 'channels.detail', params: {id: $event.actor.preferred_username}})"
|
||||||
/>
|
/>
|
||||||
<div class="ui hidden divider" />
|
<div class="ui hidden divider" />
|
||||||
|
@ -145,7 +148,7 @@ const category = ref('podcast')
|
||||||
:class="['ui', 'primary button', { loading }]"
|
:class="['ui', 'primary button', { loading }]"
|
||||||
type="submit"
|
type="submit"
|
||||||
:disabled="!submittable && !loading"
|
:disabled="!submittable && !loading"
|
||||||
@click.prevent.stop="$refs.createForm.submit"
|
@click.prevent.stop="createForm.submit"
|
||||||
>
|
>
|
||||||
<translate translate-context="*/Channels/Button.Label">
|
<translate translate-context="*/Channels/Button.Label">
|
||||||
Create channel
|
Create channel
|
||||||
|
|
|
@ -23,7 +23,7 @@ interface Events {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string
|
id: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const emit = defineEmits<Events>()
|
const emit = defineEmits<Events>()
|
||||||
|
|
|
@ -124,7 +124,7 @@ const labels = computed(() => ({
|
||||||
showStatus: $pgettext('Content/Library/Button.Label/Verb', 'Show information about the upload status for this track')
|
showStatus: $pgettext('Content/Library/Button.Label/Verb', 'Show information about the upload status for this track')
|
||||||
}))
|
}))
|
||||||
|
|
||||||
const detailedUpload = ref({})
|
const detailedUpload = ref()
|
||||||
const showUploadDetailModal = ref(false)
|
const showUploadDetailModal = ref(false)
|
||||||
|
|
||||||
const getImportStatusChoice = (importStatus: ImportStatus) => {
|
const getImportStatusChoice = (importStatus: ImportStatus) => {
|
||||||
|
@ -235,6 +235,7 @@ const getImportStatusChoice = (importStatus: ImportStatus) => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<import-status-modal
|
<import-status-modal
|
||||||
|
v-if="detailedUpload"
|
||||||
v-model:show="showUploadDetailModal"
|
v-model:show="showUploadDetailModal"
|
||||||
:upload="detailedUpload"
|
:upload="detailedUpload"
|
||||||
/>
|
/>
|
||||||
|
@ -352,7 +353,7 @@ const getImportStatusChoice = (importStatus: ImportStatus) => {
|
||||||
>{{ getImportStatusChoice(scope.obj.import_status).label }}</a>
|
>{{ getImportStatusChoice(scope.obj.import_status).label }}</a>
|
||||||
<button
|
<button
|
||||||
class="ui tiny basic icon button"
|
class="ui tiny basic icon button"
|
||||||
:title="sharedLabels.fields.import_status.detailTitle"
|
:title="sharedLabels.fields.import_status.label"
|
||||||
:aria-label="labels.showStatus"
|
:aria-label="labels.showStatus"
|
||||||
@click="detailedUpload = scope.obj; showUploadDetailModal = true"
|
@click="detailedUpload = scope.obj; showUploadDetailModal = true"
|
||||||
>
|
>
|
||||||
|
|
|
@ -83,7 +83,6 @@ const libraryCreated = (library: Library) => {
|
||||||
</a>
|
</a>
|
||||||
<library-form
|
<library-form
|
||||||
v-if="!hiddenForm"
|
v-if="!hiddenForm"
|
||||||
:library="null"
|
|
||||||
@created="libraryCreated"
|
@created="libraryCreated"
|
||||||
/>
|
/>
|
||||||
<div class="ui hidden divider" />
|
<div class="ui hidden divider" />
|
||||||
|
|
|
@ -16,7 +16,7 @@ import useErrorHandler from '~/composables/useErrorHandler'
|
||||||
import useReport from '~/composables/moderation/useReport'
|
import useReport from '~/composables/moderation/useReport'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string
|
id: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
|
@ -17,7 +17,7 @@ import PlayButton from '~/components/audio/PlayButton.vue'
|
||||||
import useErrorHandler from '~/composables/useErrorHandler'
|
import useErrorHandler from '~/composables/useErrorHandler'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string
|
id: number
|
||||||
defaultEdit?: boolean
|
defaultEdit?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import Pagination from '~/components/vui/Pagination.vue'
|
||||||
import useErrorHandler from '~/composables/useErrorHandler'
|
import useErrorHandler from '~/composables/useErrorHandler'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
id: string
|
id: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
Loading…
Reference in New Issue