Resolve some TODOs

This commit is contained in:
wvffle 2022-08-30 20:23:17 +00:00 committed by Georg Krause
parent 74d1a0a03e
commit e7da8b5f43
98 changed files with 318 additions and 261 deletions

View File

@ -56,7 +56,6 @@ onMounted(async () => {
}) })
// Time ago // Time ago
// TODO (wvffle): Migrate to useTimeAgo
useIntervalFn(() => { useIntervalFn(() => {
// used to redraw ago dates every minute // used to redraw ago dates every minute
store.commit('ui/computeLastDate') store.commit('ui/computeLastDate')

View File

@ -1,18 +1,18 @@
<script setup lang="ts"> <script setup lang="ts">
import type { QueueItemSource } from '~/types' import type { QueueItemSource } from '~/types'
interface Events {
(e: 'play', index: number): void
(e: 'remove', index: number): void
}
interface Props { interface Props {
source: QueueItemSource source: QueueItemSource
index: number index: number
} }
interface Emits { defineEmits<Events>()
(e: 'play', index: number): void
(e: 'remove', index: number): void
}
defineProps<Props>() defineProps<Props>()
defineEmits<Emits>()
</script> </script>
<template> <template>

View File

@ -9,6 +9,10 @@ import { useGettext } from 'vue3-gettext'
type Type = 'rss' | 'artists' | 'both' type Type = 'rss' | 'artists' | 'both'
interface Events {
(e: 'subscribed', rss: object): void
}
interface Props { interface Props {
initialId?: string initialId?: string
initialType?: Type initialType?: Type
@ -17,6 +21,7 @@ interface Props {
standalone?: boolean standalone?: boolean
} }
const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
initialId: '', initialId: '',
initialType: 'artists', initialType: 'artists',
@ -118,7 +123,6 @@ const createFetch = async () => {
isLoading.value = false isLoading.value = false
} }
const emit = defineEmits(['subscribed'])
const store = useStore() const store = useStore()
const rssSubscribe = async () => { const rssSubscribe = async () => {

View File

@ -9,13 +9,17 @@ import axios from 'axios'
import SemanticModal from '~/components/semantic/Modal.vue' import SemanticModal from '~/components/semantic/Modal.vue'
interface Events {
(e: 'update:show', show: boolean): void
}
interface Props { interface Props {
show: boolean show: boolean
} }
const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const emit = defineEmits(['update:show'])
const show = useVModel(props, 'show', emit) const show = useVModel(props, 'show', emit)
const instanceUrl = ref('') const instanceUrl = ref('')

View File

@ -4,12 +4,17 @@ import { useVModel } from '@vueuse/core'
import { computed } from 'vue' import { computed } from 'vue'
import { useGettext } from 'vue3-gettext' import { useGettext } from 'vue3-gettext'
interface Events {
(e: 'update:show', show: boolean): void
}
interface Props { interface Props {
show: boolean show: boolean
} }
const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const emit = defineEmits(['update:show'])
const showRef = useVModel(props, 'show', emit) const showRef = useVModel(props, 'show', emit)
const { $pgettext } = useGettext() const { $pgettext } = useGettext()

View File

@ -7,16 +7,20 @@ import { computed, ref } from 'vue'
import { useGettext } from 'vue3-gettext' import { useGettext } from 'vue3-gettext'
import { arrayMove } from '~/utils' import { arrayMove } from '~/utils'
interface Events {
(e: 'update:modelValue', value: Form): void
}
interface Props { interface Props {
modelValue: Form modelValue: Form
signupApprovalEnabled?: boolean signupApprovalEnabled?: boolean
} }
const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
signupApprovalEnabled: false signupApprovalEnabled: false
}) })
const emit = defineEmits(['update:modelValue'])
const value = useVModel(props, 'modelValue', emit, { deep: true }) const value = useVModel(props, 'modelValue', emit, { deep: true })
const maxFields = ref(10) const maxFields = ref(10)
@ -140,7 +144,6 @@ const move = (idx: number, increment: number) => {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<!-- TODO (wvffle): Add random _id as :key -->
<tr <tr
v-for="(field, idx) in value.fields" v-for="(field, idx) in value.fields"
:key="idx" :key="idx"

View File

@ -1,16 +1,17 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Channel } from '~/types' import type { Channel } from '~/types'
import PlayButton from '~/components/audio/PlayButton.vue'
import TagsList from '~/components/tags/List.vue'
import { momentFormat } from '~/utils/filters' import { momentFormat } from '~/utils/filters'
import { useGettext } from 'vue3-gettext'
import { useStore } from '~/store' import { useStore } from '~/store'
import { computed } from 'vue' import { computed } from 'vue'
import { useGettext } from 'vue3-gettext'
import moment from 'moment' import moment from 'moment'
import PlayButton from '~/components/audio/PlayButton.vue'
import TagsList from '~/components/tags/List.vue'
interface Props { interface Props {
// TODO (wvffle) : Find type
object: Channel object: Channel
} }
@ -43,7 +44,7 @@ const updatedAgo = computed(() => moment(props.object.artist?.modification_date)
<div class="card app-card"> <div class="card app-card">
<div <div
v-lazy:background-image="imageUrl" v-lazy:background-image="imageUrl"
:class="['ui', 'head-image', {'circular': object.artist.content_category != 'podcast'}, {'padded': object.artist.content_category === 'podcast'}, 'image', {'default-cover': !object.artist.cover}]" :class="['ui', 'head-image', {'circular': object.artist?.content_category != 'podcast'}, {'padded': object.artist?.content_category === 'podcast'}, 'image', {'default-cover': !object.artist?.cover}]"
@click="$router.push({name: 'channels.detail', params: {id: urlId}})" @click="$router.push({name: 'channels.detail', params: {id: urlId}})"
> >
<play-button <play-button
@ -59,12 +60,12 @@ const updatedAgo = computed(() => moment(props.object.artist?.modification_date)
class="discrete link" class="discrete link"
:to="{name: 'channels.detail', params: {id: urlId}}" :to="{name: 'channels.detail', params: {id: urlId}}"
> >
{{ object.artist.name }} {{ object.artist?.name }}
</router-link> </router-link>
</strong> </strong>
<div class="description"> <div class="description">
<translate <translate
v-if="object.artist.content_category === 'podcast'" v-if="object.artist?.content_category === 'podcast'"
class="meta ellipsis" class="meta ellipsis"
translate-context="Content/Channel/Paragraph" translate-context="Content/Channel/Paragraph"
translate-plural="%{ count } episodes" translate-plural="%{ count } episodes"
@ -76,8 +77,8 @@ const updatedAgo = computed(() => moment(props.object.artist?.modification_date)
<translate <translate
v-else v-else
translate-context="*/*/*" translate-context="*/*/*"
:translate-params="{count: object.artist.tracks_count}" :translate-params="{count: object.artist?.tracks_count}"
:translate-n="object.artist.tracks_count" :translate-n="object.artist?.tracks_count"
translate-plural="%{ count } tracks" translate-plural="%{ count } tracks"
> >
%{ count } track %{ count } track
@ -87,7 +88,7 @@ const updatedAgo = computed(() => moment(props.object.artist?.modification_date)
:truncate-size="20" :truncate-size="20"
:limit="2" :limit="2"
:show-more="false" :show-more="false"
:tags="object.artist.tags ?? []" :tags="object.artist?.tags ?? []"
/> />
</div> </div>
</div> </div>
@ -96,7 +97,7 @@ const updatedAgo = computed(() => moment(props.object.artist?.modification_date)
v-translate="{ updatedAgo }" v-translate="{ updatedAgo }"
:translate-params="{ updatedAgo }" :translate-params="{ updatedAgo }"
class="meta ellipsis" class="meta ellipsis"
:datetime="object.artist.modification_date" :datetime="object.artist?.modification_date"
:title="updatedTitle" :title="updatedTitle"
> >
%{ updatedAgo } %{ updatedAgo }

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Cover, Track, BackendError } from '~/types' import type { Cover, Track, BackendResponse, BackendError } from '~/types'
import { clone } from 'lodash-es' import { clone } from 'lodash-es'
import { ref, watch } from 'vue' import { ref, watch } from 'vue'
@ -8,6 +8,10 @@ import axios from 'axios'
import PodcastTable from '~/components/audio/podcast/Table.vue' import PodcastTable from '~/components/audio/podcast/Table.vue'
import TrackTable from '~/components/audio/track/Table.vue' import TrackTable from '~/components/audio/track/Table.vue'
interface Events {
(e: 'fetched', data: BackendResponse<Track[]>): void
}
interface Props { interface Props {
filters: object filters: object
limit?: number limit?: number
@ -15,7 +19,7 @@ interface Props {
isPodcast: boolean isPodcast: boolean
} }
const emit = defineEmits(['fetched']) const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
limit: 10, limit: 10,
defaultCover: null defaultCover: null

View File

@ -8,7 +8,6 @@ import usePlayer from '~/composables/audio/usePlayer'
import { computed } from 'vue' import { computed } from 'vue'
interface Props { interface Props {
// TODO (wvffle): Is it correct type?
entry: Track entry: Track
defaultCover: Cover defaultCover: Cover
} }
@ -42,7 +41,7 @@ const duration = computed(() => props.entry.uploads.find(upload => upload.durati
@click="$router.push({name: 'library.tracks.detail', params: {id: entry.id}})" @click="$router.push({name: 'library.tracks.detail', params: {id: entry.id}})"
> >
<img <img
v-else-if="entry.artist.content_category === 'podcast' && defaultCover != undefined" v-else-if="entry.artist?.content_category === 'podcast' && defaultCover != undefined"
v-lazy="$store.getters['instance/absoluteUrl'](defaultCover.urls.medium_square_crop)" v-lazy="$store.getters['instance/absoluteUrl'](defaultCover.urls.medium_square_crop)"
class="channel-image image" class="channel-image image"
@click="$router.push({name: 'library.tracks.detail', params: {id: entry.id}})" @click="$router.push({name: 'library.tracks.detail', params: {id: entry.id}})"

View File

@ -1,5 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Channel, BackendError } from '~/types' import type { ContentCategory, Channel, BackendError, Tag } from '~/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'
@ -9,12 +9,21 @@ import axios from 'axios'
import AttachmentInput from '~/components/common/AttachmentInput.vue' import AttachmentInput from '~/components/common/AttachmentInput.vue'
import TagsSelector from '~/components/library/TagsSelector.vue' import TagsSelector from '~/components/library/TagsSelector.vue'
interface Events {
(e: 'category', contentCategory: ContentCategory): void
(e: 'submittable', value: boolean): void
(e: 'loading', value: boolean): void
(e: 'errored', errors: string[]): void
(e: 'created', channel: Channel): void
(e: 'updated', channel: Channel): void
}
interface Props { interface Props {
object?: Channel | null object?: Channel | null
step: number step: number
} }
const emit = defineEmits(['category', 'submittable', 'loading', 'errored', 'created', 'updated']) const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
object: null, object: null,
step: 1 step: 1
@ -25,7 +34,7 @@ const { $pgettext } = useGettext()
const newValues = reactive({ const newValues = reactive({
name: props.object?.artist?.name ?? '', name: props.object?.artist?.name ?? '',
username: props.object?.actor.preferred_username ?? '', username: props.object?.actor.preferred_username ?? '',
tags: props.object?.artist?.tags ?? [], tags: props.object?.artist?.tags?.map(name => ({ name } as Tag)) ?? [] as Tag[],
description: props.object?.artist?.description?.text ?? '', description: props.object?.artist?.description?.text ?? '',
cover: props.object?.artist?.cover?.uuid ?? null, cover: props.object?.artist?.cover?.uuid ?? null,
content_category: props.object?.artist?.content_category ?? 'podcast', content_category: props.object?.artist?.content_category ?? 'podcast',
@ -76,11 +85,11 @@ const labels = computed(() => ({
usernamePlaceholder: $pgettext('Content/Channel/Form.Field.Placeholder', 'awesomechannelname') usernamePlaceholder: $pgettext('Content/Channel/Form.Field.Placeholder', 'awesomechannelname')
})) }))
const submittable = computed(() => const submittable = computed(() => !!(
newValues.content_category === 'podcast' newValues.content_category === 'podcast'
? newValues.name && newValues.username && newValues.metadata.itunes_category && newValues.metadata.language ? newValues.name && newValues.username && newValues.metadata.itunes_category && newValues.metadata.language
: newValues.name && newValues.username : newValues.name && newValues.username
) ))
watch(() => newValues.name, (name) => { watch(() => newValues.name, (name) => {
if (creating.value) { if (creating.value) {
@ -130,7 +139,8 @@ const submit = async () => {
: axios.patch(`channels/${props.object?.uuid}`, payload) : axios.patch(`channels/${props.object?.uuid}`, payload)
const response = await request() const response = await request()
emit(creating.value ? 'created' : 'updated', response.data) if (creating.value) emit('created', response.data)
else emit('updated', response.data)
} catch (error) { } catch (error) {
errors.value = (error as BackendError).backendErrors errors.value = (error as BackendError).backendErrors
emit('errored', errors.value) emit('errored', errors.value)

View File

@ -1,18 +1,23 @@
<script setup lang="ts"> <script setup lang="ts">
import type { BackendError, Channel } from '~/types' import type { BackendError, BackendResponse, Channel } from '~/types'
import { clone } from 'lodash-es'
import { ref, reactive } from 'vue' import { ref, reactive } from 'vue'
import { clone } from 'lodash-es'
import axios from 'axios' import axios from 'axios'
import ChannelCard from '~/components/audio/ChannelCard.vue' import ChannelCard from '~/components/audio/ChannelCard.vue'
interface Events {
(e: 'fetched', channels: BackendResponse<Channel>): void
}
interface Props { interface Props {
filters: object filters: object
limit?: number limit?: number
} }
const emit = defineEmits(['fetched']) const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
limit: 5 limit: 5
}) })

View File

@ -4,10 +4,16 @@ import type { Library } from '~/types'
import { computed } from 'vue' import { computed } from 'vue'
import { useStore } from '~/store' import { useStore } from '~/store'
interface Events {
(e: 'unfollowed'): void
(e: 'followed'): void
}
interface Props { interface Props {
library: Library library: Library
} }
const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const store = useStore() const store = useStore()
@ -15,7 +21,6 @@ const follow = computed(() => store.getters['libraries/follow'](props.library.uu
const isPending = computed(() => follow.value && follow.value.approved === null) const isPending = computed(() => follow.value && follow.value.approved === null)
const isApproved = computed(() => follow.value && (follow.value?.approved === true || (isPending.value && props.library.privacy_level === 'everyone'))) const isApproved = computed(() => follow.value && (follow.value?.approved === true || (isPending.value && props.library.privacy_level === 'everyone')))
const emit = defineEmits(['followed', 'unfollowed'])
const toggle = () => { const toggle = () => {
if (isPending.value || isApproved.value) { if (isPending.value || isApproved.value) {
emit('unfollowed') emit('unfollowed')

View File

@ -152,7 +152,6 @@ const openMenu = () => {
<div class="menu"> <div class="menu">
<button <button
class="item basic" class="item basic"
data-ref="enqueue"
:disabled="!playable" :disabled="!playable"
:title="labels.addToQueue" :title="labels.addToQueue"
@click.stop.prevent="enqueue" @click.stop.prevent="enqueue"
@ -161,7 +160,6 @@ const openMenu = () => {
</button> </button>
<button <button
class="item basic" class="item basic"
data-ref="enqueueNext"
:disabled="!playable" :disabled="!playable"
:title="labels.playNext" :title="labels.playNext"
@click.stop.prevent="enqueueNext()" @click.stop.prevent="enqueueNext()"
@ -170,7 +168,6 @@ const openMenu = () => {
</button> </button>
<button <button
class="item basic" class="item basic"
data-ref="playNow"
:disabled="!playable" :disabled="!playable"
:title="labels.playNow" :title="labels.playNow"
@click.stop.prevent="enqueueNext(true)" @click.stop.prevent="enqueueNext(true)"
@ -213,7 +210,6 @@ const openMenu = () => {
<div class="divider" /> <div class="divider" />
<button <button
v-if="filterableArtist" v-if="filterableArtist"
data-ref="filterArtist"
class="item basic" class="item basic"
:disabled="!filterableArtist" :disabled="!filterableArtist"
:title="labels.hideArtist" :title="labels.hideArtist"
@ -226,7 +222,6 @@ const openMenu = () => {
v-for="obj in getReportableObjects({track, album, artist, playlist, account, channel})" v-for="obj in getReportableObjects({track, album, artist, playlist, account, channel})"
:key="obj.target.type + obj.target.id" :key="obj.target.type + obj.target.id"
class="item basic" class="item basic"
:data-ref="`report${obj.target.type}${obj.target.id}`"
@click.stop.prevent="report(obj)" @click.stop.prevent="report(obj)"
> >
<i class="share icon" /> {{ obj.label }} <i class="share icon" /> {{ obj.label }}

View File

@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
// TODO (wvffle): Move most of this stufff to usePlayer
import { useStore } from '~/store' import { useStore } from '~/store'
import VolumeControl from './VolumeControl.vue' import VolumeControl from './VolumeControl.vue'
import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue' import TrackFavoriteIcon from '~/components/favorites/TrackFavoriteIcon.vue'

View File

@ -12,7 +12,7 @@ import { useStore } from '~/store'
import onKeyboardShortcut from '~/composables/onKeyboardShortcut' import onKeyboardShortcut from '~/composables/onKeyboardShortcut'
interface Emits { interface Events {
(e: 'search'): void (e: 'search'): void
} }
@ -41,7 +41,7 @@ interface Result {
routerUrl: RouteLocationNamedRaw routerUrl: RouteLocationNamedRaw
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const search = ref() const search = ref()
const { focused } = useFocus(search) const { focused } = useFocus(search)

View File

@ -11,6 +11,10 @@ import usePlayOptions from '~/composables/audio/usePlayOptions'
import useReport from '~/composables/moderation/useReport' import useReport from '~/composables/moderation/useReport'
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
interface Events {
(e: 'update:show', value: boolean): void
}
interface Props extends PlayOptionsProps { interface Props extends PlayOptionsProps {
track: Track track: Track
index: number index: number
@ -30,6 +34,7 @@ interface Props extends PlayOptionsProps {
account?: Actor | null account?: Actor | null
} }
const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
isArtist: false, isArtist: false,
isAlbum: false isAlbum: false
@ -37,7 +42,6 @@ const props = withDefaults(defineProps<Props>(), {
const modal = ref() const modal = ref()
const emit = defineEmits(['update:show'])
const show = useVModel(props, 'show', emit) const show = useVModel(props, 'show', emit)
const { report, getReportableObjects } = useReport() const { report, getReportableObjects } = useReport()
@ -280,9 +284,7 @@ const labels = computed(() => ({
<div <div
v-for="obj in getReportableObjects({ track, album: track.album, artist: track.artist })" v-for="obj in getReportableObjects({ track, album: track.album, artist: track.artist })"
:key="obj.target.type + obj.target.id" :key="obj.target.type + obj.target.id"
:ref="`report${obj.target.type}${obj.target.id}`"
class="row" class="row"
:data-ref="`report${obj.target.type}${obj.target.id}`"
@click.stop.prevent="report(obj)" @click.stop.prevent="report(obj)"
> >
<div class="column"> <div class="column">

View File

@ -11,6 +11,10 @@ import usePlayOptions from '~/composables/audio/usePlayOptions'
import useReport from '~/composables/moderation/useReport' import useReport from '~/composables/moderation/useReport'
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
interface Events {
(e: 'update:show', value: boolean): void
}
interface Props extends PlayOptionsProps { interface Props extends PlayOptionsProps {
track: Track track: Track
index: number index: number
@ -30,6 +34,7 @@ interface Props extends PlayOptionsProps {
account?: Actor | null account?: Actor | null
} }
const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
isArtist: false, isArtist: false,
isAlbum: false isAlbum: false
@ -37,7 +42,6 @@ const props = withDefaults(defineProps<Props>(), {
const modal = ref() const modal = ref()
const emit = defineEmits(['update:show'])
const show = useVModel(props, 'show', emit) const show = useVModel(props, 'show', emit)
const { report, getReportableObjects } = useReport() const { report, getReportableObjects } = useReport()
@ -225,9 +229,7 @@ const labels = computed(() => ({
<div <div
v-for="obj in getReportableObjects({ track, album: track.album, artist: track.artist })" v-for="obj in getReportableObjects({ track, album: track.album, artist: track.artist })"
:key="obj.target.type + obj.target.id" :key="obj.target.type + obj.target.id"
:ref="`report${obj.target.type}${obj.target.id}`"
class="row" class="row"
:data-ref="`report${obj.target.type}${obj.target.id}`"
@click.stop.prevent="report(obj)" @click.stop.prevent="report(obj)"
> >
<div class="column"> <div class="column">

View File

@ -14,6 +14,11 @@ import TrackRow from '~/components/audio/track/Row.vue'
import useErrorHandler from '~/composables/useErrorHandler' import useErrorHandler from '~/composables/useErrorHandler'
interface Events {
(e: 'fetched'): void
(e: 'page-changed', page: number): void
}
interface Props { interface Props {
tracks?: Track[] tracks?: Track[]
@ -28,7 +33,6 @@ interface Props {
isAlbum?: boolean isAlbum?: boolean
isPodcast?: boolean isPodcast?: boolean
// TODO (wvffle): Find correct type
filters?: object filters?: object
nextUrl?: string | null nextUrl?: string | null
@ -41,6 +45,7 @@ interface Props {
unique?: boolean unique?: boolean
} }
const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
tracks: () => [], tracks: () => [],
@ -95,8 +100,6 @@ const labels = computed(() => ({
artist: $pgettext('*/*/*/Noun', 'Artist') artist: $pgettext('*/*/*/Noun', 'Artist')
})) }))
const emit = defineEmits(['fetched', 'page-changed'])
const isLoading = ref(false) const isLoading = ref(false)
const fetchData = async () => { const fetchData = async () => {
isLoading.value = true isLoading.value = true
@ -255,7 +258,7 @@ const updatePage = (page: number) => {
:total="totalTracks" :total="totalTracks"
:current=" tracks.length > 0 ? page : currentPage" :current=" tracks.length > 0 ? page : currentPage"
:paginate-by="paginateBy" :paginate-by="paginateBy"
@page-changed="updatePage" @update:current="updatePage"
/> />
</div> </div>
</div> </div>
@ -295,7 +298,7 @@ const updatePage = (page: number) => {
:total="totalTracks" :total="totalTracks"
:current="tracks.length > 0 ? page : currentPage" :current="tracks.length > 0 ? page : currentPage"
:compact="true" :compact="true"
@page-changed="updatePage" @update:current="updatePage"
/> />
</div> </div>
</div> </div>

View File

@ -1,7 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Track, Listening } from '~/types' import type { Track, Listening } from '~/types'
// TODO (wvffle): Fix websocket update (#1534)
import { ref, reactive, watch } from 'vue' import { ref, reactive, watch } from 'vue'
import { useStore } from '~/store' import { useStore } from '~/store'
import { clone } from 'lodash-es' import { clone } from 'lodash-es'
@ -14,7 +13,7 @@ import TagsList from '~/components/tags/List.vue'
import useErrorHandler from '~/composables/useErrorHandler' import useErrorHandler from '~/composables/useErrorHandler'
interface Emits { interface Events {
(e: 'count', count: number): void (e: 'count', count: number): void
} }
@ -28,7 +27,7 @@ interface Props {
websocketHandlers?: string[] websocketHandlers?: string[]
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
isActivity: true, isActivity: true,
showCount: false, showCount: false,

View File

@ -9,7 +9,7 @@ import { uniq } from 'lodash-es'
import useScopes from '~/composables/auth/useScopes' import useScopes from '~/composables/auth/useScopes'
interface Emits { interface Events {
(e: 'updated', application: Application): void (e: 'updated', application: Application): void
(e: 'created', application: Application): void (e: 'created', application: Application): void
} }
@ -19,7 +19,7 @@ interface Props {
defaults?: Partial<Application> defaults?: Partial<Application>
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
app: null, app: null,
defaults: () => ({}) defaults: () => ({})

View File

@ -315,9 +315,9 @@ fetchOwnedApps()
:key="f.id" :key="f.id"
class="field" class="field"
> >
<label :for="f.id">{{ sharedLabels.fields[f.id].label }}</label> <label :for="f.id">{{ sharedLabels.fields[f.id as FieldId].label }}</label>
<p v-if="sharedLabels.fields[f.id].help"> <p v-if="sharedLabels.fields[f.id as FieldId].help">
{{ sharedLabels.fields[f.id].help }} {{ sharedLabels.fields[f.id as FieldId].help }}
</p> </p>
<select <select
v-if="f.type === 'dropdown'" v-if="f.type === 'dropdown'"
@ -330,7 +330,7 @@ fetchOwnedApps()
:key="key" :key="key"
:value="c" :value="c"
> >
{{ sharedLabels.fields[f.id].choices[c] }} {{ sharedLabels.fields[f.id as FieldId].choices[c] }}
</option> </option>
</select> </select>
<content-form <content-form

View File

@ -4,7 +4,7 @@ import type { BackendError, Channel } from '~/types'
import { computed, watch, ref } from 'vue' import { computed, watch, ref } from 'vue'
import axios from 'axios' import axios from 'axios'
interface Emits { interface Events {
(e: 'submittable', value: boolean): void (e: 'submittable', value: boolean): void
(e: 'loading', value: boolean): void (e: 'loading', value: boolean): void
(e: 'created'): void (e: 'created'): void
@ -14,7 +14,7 @@ interface Props {
channel: Channel channel: Channel
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const title = ref('') const title = ref('')

View File

@ -5,7 +5,7 @@ import axios from 'axios'
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
import { reactive, ref, watch } from 'vue' import { reactive, ref, watch } from 'vue'
interface Emits { interface Events {
(e: 'update:modelValue', value: string): void (e: 'update:modelValue', value: string): void
} }
@ -14,7 +14,7 @@ interface Props {
channel: Channel | null channel: Channel | null
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
modelValue: null, modelValue: null,
channel: null channel: null

View File

@ -5,7 +5,7 @@ import { computed, reactive, ref } from 'vue'
import axios from 'axios' import axios from 'axios'
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
interface Emits { interface Events {
(e: 'update:modelValue', value: string): void (e: 'update:modelValue', value: string): void
} }
@ -13,7 +13,7 @@ interface Props {
modelValue: string | null modelValue: string | null
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
modelValue: null modelValue: null
}) })

View File

@ -7,7 +7,7 @@ import { computed } from 'vue'
import LoginModal from '~/components/common/LoginModal.vue' import LoginModal from '~/components/common/LoginModal.vue'
interface Emits { interface Events {
(e: 'unsubscribed'): void (e: 'unsubscribed'): void
(e: 'subscribed'): void (e: 'subscribed'): void
} }
@ -16,7 +16,7 @@ interface Props {
channel: Channel channel: Channel
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const { $pgettext } = useGettext() const { $pgettext } = useGettext()

View File

@ -18,7 +18,7 @@ import AlbumSelect from '~/components/channels/AlbumSelect.vue'
import useErrorHandler from '~/composables/useErrorHandler' import useErrorHandler from '~/composables/useErrorHandler'
interface Emits { interface Events {
(e: 'status', status: UploadStatus): void (e: 'status', status: UploadStatus): void
(e: 'step', step: 1 | 2 | 3): void (e: 'step', step: 1 | 2 | 3): void
} }
@ -47,7 +47,7 @@ interface UploadedFile extends VueUploadItem {
metadata: Record<string, string> metadata: Record<string, string>
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
channel: null channel: null
}) })
@ -55,7 +55,6 @@ const props = withDefaults(defineProps<Props>(), {
const { $pgettext } = useGettext() const { $pgettext } = useGettext()
const store = useStore() const store = useStore()
// TODO (wvffle): Find types in UploadMetadataForm.vue
const errors = ref([] as string[]) const errors = ref([] as string[])
const values = reactive({ const values = reactive({

View File

@ -6,8 +6,7 @@ import { ref, computed, watch } from 'vue'
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 Emits { interface Events {
// TODO (wvffle): Find correct type
(e: 'values', values: Record<string, string>): void (e: 'values', values: Record<string, string>): void
} }
@ -16,13 +15,12 @@ interface Props {
values?: Record<string, string> | null values?: Record<string, string> | null
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
values: null values: null
}) })
// TODO (wvffle): This is something like a Track, but `cover` is a plain uuid const newValues = ref({ ...(props.values ?? props.upload.import_metadata) } as Record<string, string>)
const newValues = ref({ ...(props.values ?? props.upload.import_metadata) } as any)
// computed: { // computed: {
// isLoading () { // isLoading () {

View File

@ -16,14 +16,14 @@ interface Action {
filterChackable?: (item: never) => boolean filterChackable?: (item: never) => boolean
} }
interface Emits { interface Events {
(e: 'action-launched', data: any): void (e: 'action-launched', data: any): void
(e: 'refresh'): void (e: 'refresh'): void
} }
interface Props { interface Props {
objectsData: { results: [], count: number } objectsData: { results: [], count: number }
actions: [Action] actions: Action[]
actionUrl: string actionUrl: string
idField?: string idField?: string
refreshable?: boolean refreshable?: boolean
@ -32,7 +32,7 @@ interface Props {
customObjects?: Record<string, unknown>[] customObjects?: Record<string, unknown>[]
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
idField: 'id', idField: 'id',
refreshable: false, refreshable: false,

View File

@ -3,10 +3,9 @@ import type { BackendError } from '~/types'
import { ref } from 'vue' import { ref } from 'vue'
// TODO (wvffle): Remove this component
import axios from 'axios' import axios from 'axios'
interface Emits { interface Events {
(e: 'action-done', data: any): void (e: 'action-done', data: any): void
(e: 'action-error', error: BackendError): void (e: 'action-error', error: BackendError): void
} }
@ -16,7 +15,7 @@ interface Props {
url: string url: string
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const isLoading = ref(false) const isLoading = ref(false)

View File

@ -7,6 +7,11 @@ import { reactive, ref, watch } from 'vue'
import { useStore } from '~/store' import { useStore } from '~/store'
import useFormData from '~/composables/useFormData' import useFormData from '~/composables/useFormData'
interface Events {
(e: 'update:modelValue', value: string | null): void
(e: 'delete'): void
}
interface Props { interface Props {
modelValue: string | null modelValue: string | null
imageClass?: string imageClass?: string
@ -15,6 +20,7 @@ interface Props {
initialValue?: string | undefined initialValue?: string | undefined
} }
const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
imageClass: '', imageClass: '',
required: false, required: false,
@ -22,7 +28,6 @@ const props = withDefaults(defineProps<Props>(), {
initialValue: undefined initialValue: undefined
}) })
const emit = defineEmits(['update:modelValue', 'delete'])
const value = useVModel(props, 'modelValue', emit) const value = useVModel(props, 'modelValue', emit)
const attachment = ref() const attachment = ref()

View File

@ -1,12 +1,16 @@
<script setup lang="ts"> <script setup lang="ts">
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
interface Events {
(e: 'update:modelValue', value: boolean): void
}
interface Props { interface Props {
modelValue: boolean modelValue: boolean
} }
const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const emit = defineEmits(['update:modelValue'])
const value = useVModel(props, 'modelValue', emit) const value = useVModel(props, 'modelValue', emit)
</script> </script>

View File

@ -4,7 +4,7 @@ import { useVModel, watchDebounced, useTextareaAutosize, syncRef } from '@vueuse
import { ref, computed, watchEffect, onMounted, nextTick } from 'vue' import { ref, computed, watchEffect, onMounted, nextTick } from 'vue'
import { useGettext } from 'vue3-gettext' import { useGettext } from 'vue3-gettext'
interface Emits { interface Events {
(e: 'update:modelValue', value: string): void (e: 'update:modelValue', value: string): void
} }
@ -17,7 +17,7 @@ interface Props {
charLimit?: number charLimit?: number
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
placeholder: undefined, placeholder: undefined,
autofocus: false, autofocus: false,

View File

@ -2,16 +2,17 @@
import SemanticModal from '~/components/semantic/Modal.vue' import SemanticModal from '~/components/semantic/Modal.vue'
import { ref } from 'vue' import { ref } from 'vue'
interface Events {
(e: 'confirm'): void
}
interface Props { interface Props {
action?: () => void action?: () => void
disabled?: boolean disabled?: boolean
// TODO (wvffle): Find correct type confirmColor?: 'danger' | 'success'
confirmColor?: 'danger'
} }
// TODO (wvffle): MOVE ALL defineEmits ABOVE defineProps const emit = defineEmits<Events>()
const emit = defineEmits()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
action: () => {}, action: () => {},
disabled: false, disabled: false,
@ -30,7 +31,7 @@ const confirm = () => {
<template> <template>
<button <button
:class="[{disabled: disabled}]" :class="[{disabled: disabled}]"
:disabled="disabled || null" :disabled="disabled"
@click.prevent.stop="showModal = true" @click.prevent.stop="showModal = true"
> >
<slot /> <slot />

View File

@ -3,16 +3,21 @@ import { useVModel } from '@vueuse/core'
import { computed } from 'vue' import { computed } from 'vue'
import { useGettext } from 'vue3-gettext' import { useGettext } from 'vue3-gettext'
interface Events {
(e: 'update:modelValue', value: string): void
(e: 'search', query: string): void
}
interface Props { interface Props {
modelValue: string modelValue: string
placeholder?: string placeholder?: string
} }
const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
placeholder: '' placeholder: ''
}) })
const emit = defineEmits(['update:modelValue', 'search'])
const value = useVModel(props, 'modelValue', emit) const value = useVModel(props, 'modelValue', emit)
const { $pgettext } = useGettext() const { $pgettext } = useGettext()

View File

@ -7,7 +7,7 @@ import { whenever } from '@vueuse/core'
import axios from 'axios' import axios from 'axios'
import clip from 'text-clipper' import clip from 'text-clipper'
interface Emits { interface Events {
(e: 'updated', data: unknown): void (e: 'updated', data: unknown): void
} }
@ -21,7 +21,7 @@ interface Props {
truncateLength?: number truncateLength?: number
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
content: null, content: null,
fieldName: 'description', fieldName: 'description',

View File

@ -5,11 +5,11 @@ import useThemeList from '~/composables/useThemeList'
import useTheme from '~/composables/useTheme' import useTheme from '~/composables/useTheme'
import { computed } from 'vue' import { computed } from 'vue'
interface Emits { interface Events {
(e: 'show:shortcuts-modal'): void (e: 'show:shortcuts-modal'): void
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const { $pgettext } = useGettext() const { $pgettext } = useGettext()
const themes = useThemeList() const themes = useThemeList()

View File

@ -6,11 +6,17 @@ import { useVModel } from '@vueuse/core'
import { computed } from 'vue' import { computed } from 'vue'
import { useGettext } from 'vue3-gettext' import { useGettext } from 'vue3-gettext'
interface Events {
(e: 'update:show', value: boolean): void
(e: 'showLanguageModalEvent'): void
(e: 'showThemeModalEvent'): void
}
interface Props { interface Props {
show: boolean show: boolean
} }
const emit = defineEmits(['update:show', 'showThemeModalEvent', 'showLanguageModalEvent']) const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const show = useVModel(props, 'show', emit) const show = useVModel(props, 'show', emit)
@ -53,8 +59,8 @@ const labels = computed(() => ({
class="header" class="header"
> >
<img <img
v-if="$store.state.auth.profile.avatar && $store.state.auth.profile.avatar.urls.medium_square_crop" v-if="$store.state.auth.profile?.avatar && $store.state.auth.profile?.avatar.urls.medium_square_crop"
v-lazy="$store.getters['instance/absoluteUrl']($store.state.auth.profile.avatar.urls.medium_square_crop)" v-lazy="$store.getters['instance/absoluteUrl']($store.state.auth.profile?.avatar.urls.medium_square_crop)"
alt="" alt=""
class="ui centered small circular image" class="ui centered small circular image"
> >
@ -80,7 +86,7 @@ const labels = computed(() => ({
<div <div
class="column" class="column"
role="button" role="button"
@click="[$emit('update:show', false), $emit('showLanguageModalEvent')]" @click="[$emit('update:show', false), emit('showLanguageModalEvent')]"
> >
<i class="language icon user-modal list-icon" /> <i class="language icon user-modal list-icon" />
<span class="user-modal list-item">{{ labels.language }}:</span> <span class="user-modal list-item">{{ labels.language }}:</span>
@ -94,12 +100,12 @@ const labels = computed(() => ({
<div <div
class="column" class="column"
role="button" role="button"
@click="[$emit('update:show', false), $emit('showThemeModalEvent')]" @click="[$emit('update:show', false), emit('showThemeModalEvent')]"
> >
<i class="palette icon user-modal list-icon" /> <i class="palette icon user-modal list-icon" />
<span class="user-modal list-item">{{ labels.theme }}:</span> <span class="user-modal list-item">{{ labels.theme }}:</span>
<div class="right floated"> <div class="right floated">
<span class="user-modal list-item"> {{ themes.find(x => x.key === theme).name }}</span> <span class="user-modal list-item"> {{ themes.find(x => x.key === theme)?.name }}</span>
<i class="action-hint chevron right icon user-modal" /> <i class="action-hint chevron right icon user-modal" />
</div> </div>
</div> </div>

View File

@ -33,7 +33,6 @@ const props = withDefaults(defineProps<Props>(), {
const store = useStore() const store = useStore()
// TODO (wvffle): Make sure everything is it's own type
const page = ref(+props.defaultPage) const page = ref(+props.defaultPage)
const orderingOptions: [OrderingField, keyof typeof sharedLabels.filters][] = [ const orderingOptions: [OrderingField, keyof typeof sharedLabels.filters][] = [

View File

@ -9,7 +9,7 @@ import LibraryCard from '~/views/content/remote/Card.vue'
import useErrorHandler from '~/composables/useErrorHandler' import useErrorHandler from '~/composables/useErrorHandler'
interface Emits { interface Events {
(e: 'loaded', libraries: Library[]): void (e: 'loaded', libraries: Library[]): void
} }
@ -17,7 +17,7 @@ interface Props {
url: string url: string
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const nextPage = ref() const nextPage = ref()

View File

@ -4,6 +4,10 @@ import { useGettext } from 'vue3-gettext'
import { useClipboard, useVModel } from '@vueuse/core' import { useClipboard, useVModel } from '@vueuse/core'
import { useStore } from '~/store' import { useStore } from '~/store'
interface Events {
(e: 'update:modelValue', value: string): void
}
interface Props { interface Props {
modelValue: string modelValue: string
defaultShow?: boolean defaultShow?: boolean
@ -11,12 +15,12 @@ interface Props {
fieldId: string fieldId: string
} }
const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
defaultShow: false, defaultShow: false,
copyButton: false copyButton: false
}) })
const emit = defineEmits(['update:modelValue'])
const value = useVModel(props, 'modelValue', emit) const value = useVModel(props, 'modelValue', emit)
const showPassword = ref(props.defaultShow) const showPassword = ref(props.defaultShow)

View File

@ -16,10 +16,15 @@ import AlbumDropdown from './AlbumDropdown.vue'
import useErrorHandler from '~/composables/useErrorHandler' import useErrorHandler from '~/composables/useErrorHandler'
interface Events {
(e: 'deleted'): void
}
interface Props { interface Props {
id: string id: string
} }
const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const object = ref<Album | null>(null) const object = ref<Album | null>(null)
@ -81,7 +86,6 @@ const fetchData = async () => {
watch(() => props.id, fetchData, { immediate: true }) watch(() => props.id, fetchData, { immediate: true })
watch(page, fetchData) watch(page, fetchData)
const emit = defineEmits(['deleted'])
const router = useRouter() const router = useRouter()
const remove = async () => { const remove = async () => {
isLoading.value = true isLoading.value = true

View File

@ -6,7 +6,7 @@ import ChannelEntries from '~/components/audio/ChannelEntries.vue'
import TrackTable from '~/components/audio/track/Table.vue' import TrackTable from '~/components/audio/track/Table.vue'
import PlayButton from '~/components/audio/PlayButton.vue' import PlayButton from '~/components/audio/PlayButton.vue'
interface Emits { interface Events {
(e: 'page-changed', page: number): void (e: 'page-changed', page: number): void
(e: 'libraries-loaded', libraries: Library[]): void (e: 'libraries-loaded', libraries: Library[]): void
} }
@ -23,7 +23,7 @@ interface Props {
totalTracks: number totalTracks: number
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
defineProps<Props>() defineProps<Props>()
const getDiscKey = (disc: Track[]) => disc.map(track => track.id).join('|') const getDiscKey = (disc: Track[]) => disc.map(track => track.id).join('|')

View File

@ -9,6 +9,10 @@ import { useGettext } from 'vue3-gettext'
import { getDomain } from '~/utils' import { getDomain } from '~/utils'
interface Events {
(e: 'remove'): void
}
interface Props { interface Props {
isLoading: boolean isLoading: boolean
artist: Artist | null artist: Artist | null
@ -19,6 +23,7 @@ interface Props {
isSerie: boolean isSerie: boolean
} }
const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const { report, getReportableObjects } = useReport() const { report, getReportableObjects } = useReport()
@ -35,7 +40,6 @@ const isEmbedable = computed(() => (props.isChannel && props.artist?.channel?.ac
const musicbrainzUrl = computed(() => props.object?.mbid ? `https://musicbrainz.org/release/${props.object.mbid}` : null) const musicbrainzUrl = computed(() => props.object?.mbid ? `https://musicbrainz.org/release/${props.object.mbid}` : null)
const discogsUrl = computed(() => `https://discogs.com/search/?type=release&title=${encodeURI(props.object?.title)}&artist=${encodeURI(props.object?.artist.name)}`) const discogsUrl = computed(() => `https://discogs.com/search/?type=release&title=${encodeURI(props.object?.title)}&artist=${encodeURI(props.object?.artist.name)}`)
const emit = defineEmits(['remove'])
const remove = () => emit('remove') const remove = () => emit('remove')
</script> </script>

View File

@ -37,7 +37,6 @@ const props = withDefaults(defineProps<Props>(), {
scope: 'all' scope: 'all'
}) })
// TODO (wvffle): Make sure everything is it's own type
const page = ref(+props.defaultPage) 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)

View File

@ -37,7 +37,6 @@ const props = withDefaults(defineProps<Props>(), {
scope: 'all' scope: 'all'
}) })
// TODO (wvffle): Make sure everything is it's own type
const page = ref(+props.defaultPage) 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)

View File

@ -13,7 +13,7 @@ import axios from 'axios'
import useEditConfigs from '~/composables/moderation/useEditConfigs' import useEditConfigs from '~/composables/moderation/useEditConfigs'
import useErrorHandler from '~/composables/useErrorHandler' import useErrorHandler from '~/composables/useErrorHandler'
interface Emits { interface Events {
(e: 'approved', isApproved: boolean): void (e: 'approved', isApproved: boolean): void
(e: 'deleted'): void (e: 'deleted'): void
} }
@ -23,7 +23,7 @@ interface Props {
currentState?: ReviewState currentState?: ReviewState
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
currentState: () => ({}) currentState: () => ({})
}) })
@ -41,7 +41,6 @@ const canDelete = computed(() => {
if (props.obj.is_applied || props.obj.is_approved) return false if (props.obj.is_applied || props.obj.is_approved) return false
if (!store.state.auth.authenticated) return false if (!store.state.auth.authenticated) return false
// TODO (wvffle): Is it better to compare ids? Is full_username unique?
return props.obj.created_by.full_username === store.state.auth.fullUsername return props.obj.created_by.full_username === store.state.auth.fullUsername
|| store.state.auth.availablePermissions.library || store.state.auth.availablePermissions.library
}) })

View File

@ -38,7 +38,6 @@ const canEdit = computed(() => {
if (!store.state.auth.authenticated) return false if (!store.state.auth.authenticated) return false
const isOwner = props.object.attributed_to const isOwner = props.object.attributed_to
// TODO (wvffle): Is it better to compare ids? Is full_username unique?
&& store.state.auth.fullUsername === props.object.attributed_to.full_username && store.state.auth.fullUsername === props.object.attributed_to.full_username
return isOwner || store.state.auth.availablePermissions.library return isOwner || store.state.auth.availablePermissions.library

View File

@ -20,7 +20,7 @@ import useWebSocketHandler from '~/composables/useWebSocketHandler'
import updateQueryString from '~/composables/updateQueryString' import updateQueryString from '~/composables/updateQueryString'
import useErrorHandler from '~/composables/useErrorHandler' import useErrorHandler from '~/composables/useErrorHandler'
interface Emits { interface Events {
(e: 'uploads-finished', delta: number):void (e: 'uploads-finished', delta: number):void
} }
@ -29,7 +29,7 @@ interface Props {
defaultImportReference?: string defaultImportReference?: string
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
defaultImportReference: '' defaultImportReference: ''
}) })

View File

@ -3,14 +3,19 @@ import type { FileSystem, FSEntry } from '~/types'
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
interface Events {
(e: 'update:modelValue', value: string[]): void
(e: 'import'): void
}
interface Props { interface Props {
data: FileSystem data: FileSystem
loading: boolean loading: boolean
modelValue: string[] modelValue: string[]
} }
const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const emit = defineEmits(['update:modelValue', 'import'])
const value = useVModel(props, 'modelValue', emit) const value = useVModel(props, 'modelValue', emit)
const handleClick = (entry: FSEntry) => { const handleClick = (entry: FSEntry) => {

View File

@ -10,12 +10,16 @@ interface ErrorEntry {
value: string value: string
} }
interface Events {
(e: 'update:show', value: boolean): void
}
interface Props { interface Props {
upload: Upload upload: Upload
show: boolean show: boolean
} }
const emit = defineEmits(['update:show']) const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const show = useVModel(props, 'show', emit) const show = useVModel(props, 'show', emit)

View File

@ -40,7 +40,6 @@ const props = withDefaults(defineProps<Props>(), {
scope: 'all' scope: 'all'
}) })
// TODO (wvffle): Make sure everything is it's own type
const page = ref(+props.defaultPage) 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)

View File

@ -34,7 +34,6 @@ const props = withDefaults(defineProps<Props>(), {
scope: 'all' scope: 'all'
}) })
// TODO (wvffle): Make sure everything is it's own type
const page = ref(+props.defaultPage) 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)

View File

@ -7,7 +7,7 @@ import { useStore } from '~/store'
import $ from 'jquery' import $ from 'jquery'
interface Emits { interface Events {
(e: 'update:modelValue', tags: Tag[]): void (e: 'update:modelValue', tags: Tag[]): void
} }
@ -15,7 +15,7 @@ interface Props {
modelValue: Tag[] modelValue: Tag[]
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const store = useStore() const store = useStore()

View File

@ -21,11 +21,17 @@ import useErrorHandler from '~/composables/useErrorHandler'
import useReport from '~/composables/moderation/useReport' import useReport from '~/composables/moderation/useReport'
import useLogger from '~/composables/useLogger' import useLogger from '~/composables/useLogger'
interface Events {
(e: 'deleted'): void
}
interface Props { interface Props {
id: string id: string
} }
const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const { report, getReportableObjects } = useReport() const { report, getReportableObjects } = useReport()
const track = ref<Track | null>(null) const track = ref<Track | null>(null)
@ -107,7 +113,6 @@ const fetchData = async () => {
watch(() => props.id, fetchData, { immediate: true }) watch(() => props.id, fetchData, { immediate: true })
const emit = defineEmits(['deleted'])
const remove = async () => { const remove = async () => {
isLoading.value = true isLoading.value = true
try { try {

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
// TODO (wvffle): SORT IMPORTS LIKE SO EVERYWHERE // TODO (wvffle): SORT IMPORTS LIKE SO EVERYWHERE
import type { Track } from '~/types'
import type { BuilderFilter, FilterConfig } from './Builder.vue' import type { BuilderFilter, FilterConfig } from './Builder.vue'
import type { Track } from '~/types'
import axios from 'axios' import axios from 'axios'
import $ from 'jquery' import $ from 'jquery'
@ -16,6 +16,14 @@ import TrackTable from '~/components/audio/track/Table.vue'
import useErrorHandler from '~/composables/useErrorHandler' import useErrorHandler from '~/composables/useErrorHandler'
type Filter = { candidates: { count: number, sample: Track[] } }
type ResponseType = { filters: Array<Filter> }
interface Events {
(e: 'update-config', index: number, name: string, value: number[]): void
(e: 'delete', index: number): void
}
interface Props { interface Props {
index: number index: number
@ -23,10 +31,7 @@ interface Props {
config: FilterConfig config: FilterConfig
} }
type Filter = { candidates: { count: number, sample: Track[] } } const emit = defineEmits<Events>()
type ResponseType = { filters: Array<Filter> }
const emit = defineEmits(['update-config', 'delete'])
const props = defineProps<Props>() const props = defineProps<Props>()
const store = useStore() const store = useStore()
@ -127,7 +132,6 @@ watch(exclude, fetchCandidates)
<div <div
v-for="f in filter.fields" v-for="f in filter.fields"
:key="f.name" :key="f.name"
:ref="f.name"
class="ui field" class="ui field"
> >
<div :class="['ui', 'search', 'selection', 'dropdown', {'autocomplete': f.autocomplete}, {'multiple': f.type === 'list'}]"> <div :class="['ui', 'search', 'selection', 'dropdown', {'autocomplete': f.autocomplete}, {'multiple': f.type === 'list'}]">

View File

@ -17,7 +17,6 @@ import useSmartSearch from '~/composables/useSmartSearch'
import useOrdering from '~/composables/useOrdering' import useOrdering from '~/composables/useOrdering'
interface Props extends SmartSearchProps, OrderingProps { interface Props extends SmartSearchProps, OrderingProps {
// TODO (wvffle): find object type
filters?: object filters?: object
// TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged // TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged
@ -32,7 +31,6 @@ const props = withDefaults(defineProps<Props>(), {
filters: () => ({}) filters: () => ({})
}) })
// TODO (wvffle): Make sure everything is it's own type
const page = ref(1) const page = ref(1)
type ResponseType = { count: number, results: any[] } type ResponseType = { count: number, results: any[] }
const result = ref<null | ResponseType>(null) const result = ref<null | ResponseType>(null)
@ -47,8 +45,6 @@ const orderingOptions: [OrderingField, keyof typeof sharedLabels.filters][] = [
] ]
const actionFilters = computed(() => ({ q: query.value, ...props.filters })) const actionFilters = computed(() => ({ q: query.value, ...props.filters }))
// TODO (wvffle): Find correct type
const actions: unknown[] = []
const isLoading = ref(false) const isLoading = ref(false)
const fetchData = async () => { const fetchData = async () => {
@ -176,7 +172,7 @@ const labels = computed(() => ({
<action-table <action-table
v-if="result" v-if="result"
:objects-data="result" :objects-data="result"
:actions="actions" :actions="[]"
action-url="manage/library/artists/action/" action-url="manage/library/artists/action/"
:filters="actionFilters" :filters="actionFilters"
@action-launched="fetchData" @action-launched="fetchData"

View File

@ -17,8 +17,6 @@ import useSmartSearch from '~/composables/useSmartSearch'
import useOrdering from '~/composables/useOrdering' import useOrdering from '~/composables/useOrdering'
interface Props extends SmartSearchProps, OrderingProps { interface Props extends SmartSearchProps, OrderingProps {
// TODO (wvffle): Remove from EVERY SINGLE component that does not use it at all
// TODO (wvffle): find object type
filters?: object filters?: object
// TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged // TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged
@ -35,7 +33,6 @@ const props = withDefaults(defineProps<Props>(), {
const search = ref() const search = ref()
// TODO (wvffle): Make sure everything is it's own type
const page = ref(1) const page = ref(1)
type ResponseType = { count: number, results: any[] } type ResponseType = { count: number, results: any[] }
const result = ref<null | ResponseType>(null) const result = ref<null | ResponseType>(null)

View File

@ -17,7 +17,6 @@ import useSmartSearch from '~/composables/useSmartSearch'
import useOrdering from '~/composables/useOrdering' import useOrdering from '~/composables/useOrdering'
interface Props extends SmartSearchProps, OrderingProps { interface Props extends SmartSearchProps, OrderingProps {
// TODO (wvffle): find object type
filters?: object filters?: object
// TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged // TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged
@ -34,7 +33,6 @@ const props = withDefaults(defineProps<Props>(), {
const search = ref() const search = ref()
// TODO (wvffle): Make sure everything is it's own type
const page = ref(1) const page = ref(1)
type ResponseType = { count: number, results: any[] } type ResponseType = { count: number, results: any[] }
const result = ref<null | ResponseType>(null) const result = ref<null | ResponseType>(null)

View File

@ -20,7 +20,6 @@ import useSmartSearch from '~/composables/useSmartSearch'
import useOrdering from '~/composables/useOrdering' import useOrdering from '~/composables/useOrdering'
interface Props extends SmartSearchProps, OrderingProps { interface Props extends SmartSearchProps, OrderingProps {
// TODO (wvffle): find object type
filters?: object filters?: object
// TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged // TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged
@ -38,7 +37,6 @@ const props = withDefaults(defineProps<Props>(), {
const configs = useEditConfigs() const configs = useEditConfigs()
const search = ref() const search = ref()
// TODO (wvffle): Make sure everything is it's own type
const page = ref(1) const page = ref(1)
type StateTarget = { id: number, type: keyof typeof targets } type StateTarget = { id: number, type: keyof typeof targets }

View File

@ -17,7 +17,6 @@ import useSmartSearch from '~/composables/useSmartSearch'
import useOrdering from '~/composables/useOrdering' import useOrdering from '~/composables/useOrdering'
interface Props extends SmartSearchProps, OrderingProps { interface Props extends SmartSearchProps, OrderingProps {
// TODO (wvffle): find object type
filters?: object filters?: object
// TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged // TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged
@ -34,7 +33,6 @@ const props = withDefaults(defineProps<Props>(), {
const search = ref() const search = ref()
// TODO (wvffle): Make sure everything is it's own type
const page = ref(1) const page = ref(1)
type ResponseType = { count: number, results: any[] } type ResponseType = { count: number, results: any[] }
const result = ref<null | ResponseType>(null) const result = ref<null | ResponseType>(null)

View File

@ -19,7 +19,6 @@ import useSmartSearch from '~/composables/useSmartSearch'
import useOrdering from '~/composables/useOrdering' import useOrdering from '~/composables/useOrdering'
interface Props extends SmartSearchProps, OrderingProps { interface Props extends SmartSearchProps, OrderingProps {
// TODO (wvffle): find object type
filters?: object filters?: object
// TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged // TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged
@ -36,7 +35,6 @@ const props = withDefaults(defineProps<Props>(), {
const search = ref() const search = ref()
// TODO (wvffle): Make sure everything is it's own type
const page = ref(1) const page = ref(1)
type ResponseType = { count: number, results: any[] } type ResponseType = { count: number, results: any[] }
const result = ref<null | ResponseType>(null) const result = ref<null | ResponseType>(null)

View File

@ -17,7 +17,6 @@ import useSmartSearch from '~/composables/useSmartSearch'
import useOrdering from '~/composables/useOrdering' import useOrdering from '~/composables/useOrdering'
interface Props extends SmartSearchProps, OrderingProps { interface Props extends SmartSearchProps, OrderingProps {
// TODO (wvffle): find object type
filters?: object filters?: object
// TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged // TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged
@ -34,7 +33,6 @@ const props = withDefaults(defineProps<Props>(), {
const search = ref() const search = ref()
// TODO (wvffle): Make sure everything is it's own type
const page = ref(1) const page = ref(1)
type ResponseType = { count: number, results: any[] } type ResponseType = { count: number, results: any[] }
const result = ref<null | ResponseType>(null) const result = ref<null | ResponseType>(null)

View File

@ -20,7 +20,6 @@ import useSmartSearch from '~/composables/useSmartSearch'
import useOrdering from '~/composables/useOrdering' import useOrdering from '~/composables/useOrdering'
interface Props extends SmartSearchProps, OrderingProps { interface Props extends SmartSearchProps, OrderingProps {
// TODO (wvffle): find object type
filters?: object filters?: object
// TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged // TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged
@ -37,7 +36,6 @@ const props = withDefaults(defineProps<Props>(), {
const search = ref() const search = ref()
// TODO (wvffle): Make sure everything is it's own type
const page = ref(1) const page = ref(1)
type ResponseType = { count: number, results: any[] } type ResponseType = { count: number, results: any[] }
const result = ref<null | ResponseType>(null) const result = ref<null | ResponseType>(null)

View File

@ -17,7 +17,6 @@ import useSmartSearch from '~/composables/useSmartSearch'
import useOrdering from '~/composables/useOrdering' import useOrdering from '~/composables/useOrdering'
interface Props extends SmartSearchProps, OrderingProps { interface Props extends SmartSearchProps, OrderingProps {
// TODO (wvffle): find object type
filters?: object filters?: object
// TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged // TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged
@ -34,7 +33,6 @@ const props = withDefaults(defineProps<Props>(), {
const search = ref() const search = ref()
// TODO (wvffle): Make sure everything is it's own type
const page = ref(1) const page = ref(1)
type ResponseType = { count: number, results: any[] } type ResponseType = { count: number, results: any[] }
const result = ref<null | ResponseType>(null) const result = ref<null | ResponseType>(null)

View File

@ -16,7 +16,6 @@ import useErrorHandler from '~/composables/useErrorHandler'
import useOrdering from '~/composables/useOrdering' import useOrdering from '~/composables/useOrdering'
interface Props extends OrderingProps { interface Props extends OrderingProps {
// TODO (wvffle): find object type
filters?: object filters?: object
allowListEnabled?: boolean allowListEnabled?: boolean
@ -29,7 +28,6 @@ const props = withDefaults(defineProps<Props>(), {
allowListEnabled: false allowListEnabled: false
}) })
// TODO (wvffle): Make sure everything is it's own type
const page = ref(1) const page = ref(1)
type ResponseType = { count: number, results: any[] } type ResponseType = { count: number, results: any[] }
const result = ref<null | ResponseType>(null) const result = ref<null | ResponseType>(null)

View File

@ -7,7 +7,7 @@ import { useGettext } from 'vue3-gettext'
import axios from 'axios' import axios from 'axios'
interface Emits { interface Events {
(e: 'save', data: InstancePolicy): void (e: 'save', data: InstancePolicy): void
(e: 'delete'): void (e: 'delete'): void
(e: 'cancel'): void (e: 'cancel'): void
@ -19,7 +19,7 @@ interface Props {
object?: InstancePolicy | null object?: InstancePolicy | null
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
object: null object: null
}) })

View File

@ -5,7 +5,7 @@ import axios from 'axios'
import { ref, computed } from 'vue' import { ref, computed } from 'vue'
import { useGettext } from 'vue3-gettext' import { useGettext } from 'vue3-gettext'
interface Emits { interface Events {
(e: 'created', note: Note): void (e: 'created', note: Note): void
} }
@ -13,7 +13,7 @@ interface Props {
target: Note target: Note
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const { $pgettext } = useGettext() const { $pgettext } = useGettext()

View File

@ -8,13 +8,16 @@ import axios from 'axios'
import useErrorHandler from '~/composables/useErrorHandler' import useErrorHandler from '~/composables/useErrorHandler'
interface Events {
(e: 'deleted', uuid: string): void
}
interface Props { interface Props {
notes: Note[] notes: Note[]
} }
const emit = defineEmits<Events>()
defineProps<Props>() defineProps<Props>()
const emit = defineEmits(['deleted'])
const isLoading = ref(false) const isLoading = ref(false)
const remove = async (note: Note) => { const remove = async (note: Note) => {
isLoading.value = true isLoading.value = true

View File

@ -16,7 +16,7 @@ import useReportConfigs from '~/composables/moderation/useReportConfigs'
import useErrorHandler from '~/composables/useErrorHandler' import useErrorHandler from '~/composables/useErrorHandler'
import useMarkdown from '~/composables/useMarkdown' import useMarkdown from '~/composables/useMarkdown'
interface Emits { interface Events {
(e: 'updated', updating: { type: string }): void (e: 'updated', updating: { type: string }): void
(e: 'handled', isHandled: boolean): void (e: 'handled', isHandled: boolean): void
} }
@ -25,7 +25,7 @@ interface Props {
initObj: Report initObj: Report
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const configs = useReportConfigs() const configs = useReportConfigs()

View File

@ -11,7 +11,7 @@ import NoteForm from '~/components/manage/moderation/NoteForm.vue'
import useErrorHandler from '~/composables/useErrorHandler' import useErrorHandler from '~/composables/useErrorHandler'
interface Emits { interface Events {
(e: 'handled', status: UserRequestStatus): void (e: 'handled', status: UserRequestStatus): void
} }
@ -19,7 +19,7 @@ interface Props {
initObj: UserRequest initObj: UserRequest
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const store = useStore() const store = useStore()

View File

@ -17,7 +17,6 @@ import useErrorHandler from '~/composables/useErrorHandler'
import useOrdering from '~/composables/useOrdering' import useOrdering from '~/composables/useOrdering'
interface Props extends OrderingProps { interface Props extends OrderingProps {
// TODO (wvffle): find object type
filters?: object filters?: object
// TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged // TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged
@ -28,7 +27,6 @@ const props = withDefaults(defineProps<Props>(), {
filters: () => ({}) filters: () => ({})
}) })
// TODO (wvffle): Make sure everything is it's own type
const page = ref(1) const page = ref(1)
type ResponseType = { count: number, results: any[] } type ResponseType = { count: number, results: any[] }
const result = ref<null | ResponseType>(null) const result = ref<null | ResponseType>(null)

View File

@ -16,7 +16,6 @@ import useErrorHandler from '~/composables/useErrorHandler'
import useOrdering from '~/composables/useOrdering' import useOrdering from '~/composables/useOrdering'
interface Props extends OrderingProps { interface Props extends OrderingProps {
// TODO (wvffle): find object type
filters?: object filters?: object
// TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged // TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged
@ -27,7 +26,6 @@ const props = withDefaults(defineProps<Props>(), {
filters: () => ({}) filters: () => ({})
}) })
// TODO (wvffle): Make sure everything is it's own type
const page = ref(1) const page = ref(1)
const query = ref('') const query = ref('')
type ResponseType = { count: number, results: any[] } type ResponseType = { count: number, results: any[] }
@ -58,8 +56,6 @@ const permissions = computed(() => [
const { $pgettext } = useGettext() const { $pgettext } = useGettext()
const actionFilters = computed(() => ({ q: query.value, ...props.filters })) const actionFilters = computed(() => ({ q: query.value, ...props.filters }))
// TODO (wvffle): Find correct type
const actions: unknown[] = []
const isLoading = ref(false) const isLoading = ref(false)
const fetchData = async () => { const fetchData = async () => {
@ -158,7 +154,7 @@ const labels = computed(() => ({
<action-table <action-table
v-if="result" v-if="result"
:objects-data="result" :objects-data="result"
:actions="actions" :actions="[]"
:action-url="'manage/library/uploads/action/'" :action-url="'manage/library/uploads/action/'"
:filters="actionFilters" :filters="actionFilters"
@action-launched="fetchData" @action-launched="fetchData"

View File

@ -4,6 +4,10 @@ import { useGettext } from 'vue3-gettext'
import useSharedLabels from '~/composables/locale/useSharedLabels' import useSharedLabels from '~/composables/locale/useSharedLabels'
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
interface Events {
(e: 'update:modelValue', value: string): void
}
interface Props { interface Props {
modelValue: string modelValue: string
all?: boolean all?: boolean
@ -13,6 +17,7 @@ interface Props {
restrictTo?: string[] // TODO (wvffle): Make sure its string list restrictTo?: string[] // TODO (wvffle): Make sure its string list
} }
const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
all: false, all: false,
label: false, label: false,
@ -21,7 +26,6 @@ const props = withDefaults(defineProps<Props>(), {
restrictTo: () => [] restrictTo: () => []
}) })
const emit = defineEmits(['update:modelValue'])
const value = useVModel(props, 'modelValue', emit) const value = useVModel(props, 'modelValue', emit)
const { $pgettext } = useGettext() const { $pgettext } = useGettext()

View File

@ -94,16 +94,12 @@ const handleAction = (handler?: () => void) => {
} }
const approveLibraryFollow = async (follow: LibraryFollow) => { const approveLibraryFollow = async (follow: LibraryFollow) => {
follow.isLoading = true
await axios.post(`federation/follows/library/${follow.uuid}/accept/`) await axios.post(`federation/follows/library/${follow.uuid}/accept/`)
follow.isLoading = false
follow.approved = true follow.approved = true
} }
const rejectLibraryFollow = async (follow: LibraryFollow) => { const rejectLibraryFollow = async (follow: LibraryFollow) => {
follow.isLoading = true
await axios.post(`federation/follows/library/${follow.uuid}/reject/`) await axios.post(`federation/follows/library/${follow.uuid}/reject/`)
follow.isLoading = false
follow.approved = false follow.approved = false
} }
</script> </script>

View File

@ -1,23 +1,31 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Playlist, Track, PlaylistTrack, BackendError, APIErrorResponse } from '~/types' import type { Playlist, Track, PlaylistTrack, BackendError, APIErrorResponse } from '~/types'
import { useStore } from '~/store'
import { useGettext } from 'vue3-gettext' import { useGettext } from 'vue3-gettext'
import { computed, ref } from 'vue'
import axios from 'axios'
import PlaylistForm from '~/components/playlists/Form.vue'
import draggable from 'vuedraggable'
import { useVModels } from '@vueuse/core' import { useVModels } from '@vueuse/core'
import { computed, ref } from 'vue'
import { useStore } from '~/store'
import draggable from 'vuedraggable'
import axios from 'axios'
import PlaylistForm from '~/components/playlists/Form.vue'
import useQueue from '~/composables/audio/useQueue' import useQueue from '~/composables/audio/useQueue'
interface Events {
(e: 'update:playlistTracks', value: PlaylistTrack[]): void
(e: 'update:playlist', value: Playlist): void
}
interface Props { interface Props {
playlist: Playlist | null playlist: Playlist | null
playlistTracks: PlaylistTrack[] playlistTracks: PlaylistTrack[]
} }
const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const emit = defineEmits(['update:playlist', 'update:playlistTracks'])
const { playlistTracks, playlist } = useVModels(props, emit) const { playlistTracks, playlist } = useVModels(props, emit)
const errors = ref([] as string[]) const errors = ref([] as string[])
@ -72,8 +80,6 @@ const responseHandlers = {
return this.errored(error) return this.errored(error)
} }
// TODO (wvffle): Test if it works
// if (errors.length === 1 && errors[0].code === 'tracks_already_exist_in_playlist') {
if (backendErrors.length === 1 && backendErrors[0] === 'Tracks already exist in playlist') { if (backendErrors.length === 1 && backendErrors[0] === 'Tracks already exist in playlist') {
duplicateTrackAddInfo.value = rawPayload ?? null duplicateTrackAddInfo.value = rawPayload ?? null
showDuplicateTrackAddConfirmation.value = true showDuplicateTrackAddConfirmation.value = true

View File

@ -1,14 +1,20 @@
<script setup lang="ts"> <script setup lang="ts">
import type { Playlist, PrivacyLevel, BackendError } from '~/types' import type { Playlist, PrivacyLevel, BackendError } from '~/types'
import $ from 'jquery'
import axios from 'axios'
import { useVModels, useCurrentElement } from '@vueuse/core' import { useVModels, useCurrentElement } from '@vueuse/core'
import { ref, computed, onMounted, nextTick } from 'vue'
import { useGettext } from 'vue3-gettext' import { useGettext } from 'vue3-gettext'
import { useStore } from '~/store' import { useStore } from '~/store'
import { ref, computed, onMounted, nextTick } from 'vue'
import useLogger from '~/composables/useLogger' import axios from 'axios'
import $ from 'jquery'
import useSharedLabels from '~/composables/locale/useSharedLabels' import useSharedLabels from '~/composables/locale/useSharedLabels'
import useLogger from '~/composables/useLogger'
interface Events {
(e: 'update:playlist', value: Playlist): void
}
interface Props { interface Props {
title?: boolean title?: boolean
@ -16,13 +22,13 @@ interface Props {
playlist?: Playlist | null playlist?: Playlist | null
} }
const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
title: true, title: true,
create: false, create: false,
playlist: null playlist: null
}) })
const emit = defineEmits(['update:playlist'])
const { playlist } = useVModels(props, emit) const { playlist } = useVModels(props, emit)
const logger = useLogger() const logger = useLogger()

View File

@ -5,6 +5,14 @@ import { computed, onBeforeUnmount, ref, watchEffect } from 'vue'
import { useVModel } from '@vueuse/core' import { useVModel } from '@vueuse/core'
import { useStore } from '~/store' import { useStore } from '~/store'
interface Events {
(e: 'update:show', show: boolean): void
(e: 'approved'): void
(e: 'deny'): void
(e: 'show'): void
(e: 'hide'): void
}
interface Props { interface Props {
show: boolean show: boolean
fullscreen?: boolean fullscreen?: boolean
@ -12,14 +20,13 @@ interface Props {
additionalClasses?: string[] additionalClasses?: string[]
} }
const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
fullscreen: true, fullscreen: true,
scrolling: false, scrolling: false,
additionalClasses: () => [] additionalClasses: () => []
}) })
const emit = defineEmits(['approved', 'deny', 'update:show', 'show', 'hide'])
const modal = ref() const modal = ref()
const { activate, deactivate, pause, unpause } = useFocusTrap(modal, { const { activate, deactivate, pause, unpause } = useFocusTrap(modal, {
allowOutsideClick: true allowOutsideClick: true

View File

@ -4,6 +4,10 @@ import { range, clamp } from 'lodash-es'
import { computed } from 'vue' import { computed } from 'vue'
import { useGettext } from 'vue3-gettext' import { useGettext } from 'vue3-gettext'
interface Events {
(e: 'update:current', page: number): void
}
interface Props { interface Props {
current?: number current?: number
paginateBy?: number paginateBy?: number
@ -11,13 +15,13 @@ interface Props {
compact?: boolean compact?: boolean
} }
const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
current: 1, current: 1,
paginateBy: 25, paginateBy: 25,
compact: false compact: false
}) })
const emit = defineEmits(['update:current', 'pageChanged'])
const current = useVModel(props, 'current', emit) const current = useVModel(props, 'current', emit)
const RANGE = 2 const RANGE = 2
@ -47,8 +51,6 @@ const setPage = (page: number) => {
} }
current.value = page current.value = page
// TODO (wvffle): Compat before change to v-model
emit('pageChanged', page)
} }
const { $pgettext } = useGettext() const { $pgettext } = useGettext()

View File

@ -7,7 +7,7 @@ import { ref, watchEffect, reactive } from 'vue'
import { RecycleScroller } from 'vue-virtual-scroller' import { RecycleScroller } from 'vue-virtual-scroller'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css' import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
interface Emits { interface Events {
(e: 'reorder', from: number, to: number): void (e: 'reorder', from: number, to: number): void
(e: 'visible'): void (e: 'visible'): void
(e: 'hidden'): void (e: 'hidden'): void
@ -18,7 +18,7 @@ interface Props {
size: number size: number
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const ghostContainer = ref() const ghostContainer = ref()

View File

@ -23,8 +23,6 @@ export interface PlayOptionsProps {
} }
export default (props: PlayOptionsProps) => { export default (props: PlayOptionsProps) => {
// TODO (wvffle): Test if we can defineProps in composable
const store = useStore() const store = useStore()
const { resume, pause, playing } = usePlayer() const { resume, pause, playing } = usePlayer()
const { currentTrack } = useQueue() const { currentTrack } = useQueue()
@ -95,7 +93,7 @@ export default (props: PlayOptionsProps) => {
const tracks: Track[] = [] const tracks: Track[] = []
// TODO (wvffle): There is no channel? // TODO (wvffle): Why is there no channel?
if (props.tracks?.length) { if (props.tracks?.length) {
tracks.push(...props.tracks) tracks.push(...props.tracks)
} else if (props.track) { } else if (props.track) {
@ -126,7 +124,6 @@ export default (props: PlayOptionsProps) => {
tracks.push(...await getTracksPage({ library: props.library.uuid, ordering: '-creation_date' })) tracks.push(...await getTracksPage({ library: props.library.uuid, ordering: '-creation_date' }))
} }
// TODO (wvffle): It was behind 250ms timeout, why?
isLoading.value = false isLoading.value = false
return tracks.filter(track => track.uploads?.length).map(markRaw) return tracks.filter(track => track.uploads?.length).map(markRaw)

View File

@ -16,7 +16,6 @@ export default (defaultQuery: MaybeRef<string>, updateUrl: MaybeRef<boolean>) =>
const tokens = ref([] as Token[]) const tokens = ref([] as Token[])
watch(query, (value) => { watch(query, (value) => {
// TODO (wvffle): Move normalizeQuery and parseTokens into the composable file
tokens.value = parseTokens(normalizeQuery(value)) tokens.value = parseTokens(normalizeQuery(value))
}, { immediate: true }) }, { immediate: true })

View File

@ -9,8 +9,6 @@ import useTheme from '~/composables/useTheme'
// NOTE: Set the theme as fast as possible // NOTE: Set the theme as fast as possible
useTheme() useTheme()
// TODO (wvffle): Make sure V_FOR_REF works
// Search pattern: v-for([^>]|\n)+?[^h]ref
const logger = useLogger() const logger = useLogger()
logger.info('Loading environment:', import.meta.env.MODE) logger.info('Loading environment:', import.meta.env.MODE)
logger.debug('Environment variables:', import.meta.env) logger.debug('Environment variables:', import.meta.env)
@ -49,11 +47,4 @@ Promise.all(modules).finally(() => {
}) })
// TODO (wvffle): Rename filters from useSharedLabels to filters from backend // TODO (wvffle): Rename filters from useSharedLabels to filters from backend
// TODO (wvffle): Check for mixin merging: https://v3-migration.vuejs.org/breaking-changes/data-option.html#mixin-merge-behavior-change= // TODO (wvffle): Migrate EmbedFrame.vue to <script setup lang="ts"> and remove allowJs from tsconfig.json
// TODO (wvffle): Use emits options: https://v3-migration.vuejs.org/breaking-changes/emits-option.html
// TODO (wvffle): Find all array watchers and make them deep
// TODO (wvffle): Migrate to <script setup lang="ts"> and remove allowJs from tsconfig.json
// TODO (wvffle): Replace `from '(../)+` with `from '~/`
// TODO (wvffle): Fix props not being available in template in IntelliJ Idea
// TODO (wvffle): Use navigation guards
// TODO (wvffle): Use computedEager whenever there is a cheap operation that can be executed eagerly

View File

@ -1,13 +1,12 @@
import type { RouteRecordRaw } from 'vue-router' import type { RouteRecordRaw } from 'vue-router'
import { requireLoggedOut, requireLoggedIn } from '../guards' import { requireLoggedOut, requireLoggedIn } from '~/router/guards'
export default [ export default [
{ {
path: '/login', path: '/login',
name: 'login', name: 'login',
component: () => import('~/views/auth/Login.vue'), component: () => import('~/views/auth/Login.vue'),
// TODO (wvffle): Use named routes EVERYWHERE
props: route => ({ next: route.query.next || '/library' }), props: route => ({ next: route.query.next || '/library' }),
beforeEnter: requireLoggedOut({ name: 'library.index' }) beforeEnter: requireLoggedOut({ name: 'library.index' })
}, },

View File

@ -7,7 +7,7 @@ import manage from './manage'
import store from '~/store' import store from '~/store'
import auth from './auth' import auth from './auth'
import user from './user' import user from './user'
import { requireLoggedIn } from '../guards' import { requireLoggedIn } from '~/router/guards'
export default [ export default [
{ {

View File

@ -246,7 +246,7 @@ export default [
// browse a single library via it's uuid // browse a single library via it's uuid
path: ':id([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})', path: ':id([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})',
props: true, props: true,
component: () => import('~/views/library/DetailBase.vue'), component: () => import('~/views/library/LibraryBase.vue'),
children: [ children: [
{ {
path: '', path: '',

View File

@ -9,13 +9,13 @@ export interface State {
frontSettings: FrontendSettings frontSettings: FrontendSettings
instanceUrl?: string instanceUrl?: string
knownInstances: string[] knownInstances: string[]
nodeinfo: unknown | null // TODO (wvffle): Get nodeinfo type from swagger automatically nodeinfo: unknown | null
settings: Settings settings: Settings
} }
interface FrontendSettings { interface FrontendSettings {
defaultServerUrl: string defaultServerUrl: string
additionalStylesheets: string[] // TODO (wvffle): Ensure it's not nullable additionalStylesheets: string[]
} }
interface InstanceSettings { interface InstanceSettings {

View File

@ -19,13 +19,11 @@ export interface CurrentRadio {
clientOnly: boolean clientOnly: boolean
session: null session: null
type: 'account' type: 'account'
// TODO (wvffle): Find correct type customRadioId: number
customRadioId: unknown
config: RadioConfig config: RadioConfig
objectId: ObjectId | null objectId: ObjectId | null
} }
// TODO (wvffle): Find correct type
export type RadioConfig = { type: 'tag', names: string[] } | { type: 'artist', ids: string[] } export type RadioConfig = { type: 'tag', names: string[] } | { type: 'artist', ids: string[] }
export interface PopulateQueuePayload { export interface PopulateQueuePayload {

View File

@ -24,7 +24,6 @@ export interface QueueItemSource {
duration: string duration: string
coverUrl: string coverUrl: string
// TODO (wvffle): Maybe use <translate> component to avoid passing the labels
labels: { labels: {
remove: string remove: string
selectTrack: string selectTrack: string
@ -53,6 +52,7 @@ export interface Artist {
description: Content description: Content
cover?: Cover cover?: Cover
channel?: Channel channel?: Channel
// TODO (wvffle): Check if it's Tag[] or string[]
tags: string[] tags: string[]
content_category: ContentCategory content_category: ContentCategory
@ -164,9 +164,6 @@ export interface LibraryFollow {
name: string name: string
type?: 'music.Library' | 'federation.LibraryFollow' type?: 'music.Library' | 'federation.LibraryFollow'
target: Library target: Library
// TODO (wvffle): Check if it's not added only on frontend side
isLoading?: boolean
} }
export interface Cover { export interface Cover {
@ -225,6 +222,11 @@ export interface BackendError extends AxiosError {
rawPayload?: APIErrorResponse rawPayload?: APIErrorResponse
} }
export interface BackendResponse<T> {
count: number
results: T[]
}
export interface RateLimitStatus { export interface RateLimitStatus {
limit: string limit: string
scope: string scope: string
@ -251,14 +253,14 @@ export interface FileSystem {
export interface FSLogs { export interface FSLogs {
status: 'pending' | 'started' status: 'pending' | 'started'
reference: unknown // TODO (wvffle): Find correct type reference: unknown
logs: string[] logs: string[]
} }
// Content stuff // Content stuff
export interface Content { export interface Content {
content_type: 'text/plain' | 'text/markdown' content_type: 'text/plain' | 'text/markdown'
text: string // TODO (wvffle): Ensure it's not nullable from backend side text: string
} }
// Form stuff // Form stuff
@ -311,8 +313,7 @@ export interface User {
id: string id: string
avatar?: Cover avatar?: Cover
email: string email: string
// TODO (wvffle): Is it really a string? Or maybe it's { text: string, content_type: string } summary: { text: string, content_type: string }
summary: string
username: string username: string
full_username: string full_username: string
instance_support_message_display_date: string instance_support_message_display_date: string
@ -361,7 +362,7 @@ export interface SettingsDataEntry {
export interface Note { export interface Note {
uuid: string uuid: string
type: 'request' | 'report' type: 'request' | 'report'
author?: Actor // TODO (wvffle): Check if is valid author?: Actor
summary?: string summary?: string
creation_date?: string creation_date?: string
} }

View File

@ -51,7 +51,6 @@ const paginateBy = ref(25)
const { $pgettext } = useGettext() const { $pgettext } = useGettext()
// TODO (wvffle): Check if can rename to Category
interface SearchType { interface SearchType {
id: QueryType id: QueryType
label: string label: string
@ -132,7 +131,6 @@ const updateQueryString = () => router.replace({
} }
}) })
// TODO (wvffle): Debounce all `fetchData` functions
const isLoading = ref(false) const isLoading = ref(false)
const search = async () => { const search = async () => {
if (!query.value) { if (!query.value) {
@ -358,10 +356,9 @@ const radioConfig = computed(() => {
<pagination <pagination
v-if="currentResults && currentResults.count > paginateBy" v-if="currentResults && currentResults.count > paginateBy"
:current="page" v-model:current="page"
:paginate-by="paginateBy" :paginate-by="paginateBy"
:total="currentResults.count" :total="currentResults.count"
@page-changed="page = $event"
/> />
</div> </div>
</section> </section>

View File

@ -19,7 +19,6 @@ import useSmartSearch from '~/composables/useSmartSearch'
import useOrdering from '~/composables/useOrdering' import useOrdering from '~/composables/useOrdering'
interface Props extends SmartSearchProps, OrderingProps { interface Props extends SmartSearchProps, OrderingProps {
// TODO (wvffle): find more types
mode?: 'card' mode?: 'card'
// TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged // TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged

View File

@ -18,10 +18,15 @@ import TagsList from '~/components/tags/List.vue'
import useErrorHandler from '~/composables/useErrorHandler' import useErrorHandler from '~/composables/useErrorHandler'
import useReport from '~/composables/moderation/useReport' import useReport from '~/composables/moderation/useReport'
interface Events {
(e: 'deleted'): void
}
interface Props { interface Props {
id: string id: string
} }
const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const { report, getReportableObjects } = useReport() const { report, getReportableObjects } = useReport()
const store = useStore() const store = useStore()
@ -100,7 +105,6 @@ watchEffect(() => {
} }
}) })
const emit = defineEmits(['deleted'])
const remove = async () => { const remove = async () => {
isLoading.value = true isLoading.value = true
try { try {

View File

@ -20,11 +20,13 @@ import useErrorHandler from '~/composables/useErrorHandler'
import useSmartSearch from '~/composables/useSmartSearch' import useSmartSearch from '~/composables/useSmartSearch'
import useOrdering from '~/composables/useOrdering' import useOrdering from '~/composables/useOrdering'
interface Events {
(e: 'fetch-start'): void
}
interface Props extends SmartSearchProps, OrderingProps { interface Props extends SmartSearchProps, OrderingProps {
// TODO (wvffle): find object type
filters?: object filters?: object
needsRefresh?: boolean needsRefresh?: boolean
// TODO (wvffle): find object type
customObjects?: any[] customObjects?: any[]
// TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged // TODO(wvffle): Remove after https://github.com/vuejs/core/pull/4512 is merged
@ -33,8 +35,7 @@ interface Props extends SmartSearchProps, OrderingProps {
updateUrl?: boolean updateUrl?: boolean
} }
const search = ref() const emit = defineEmits<Events>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
defaultQuery: '', defaultQuery: '',
updateUrl: false, updateUrl: false,
@ -43,6 +44,8 @@ const props = withDefaults(defineProps<Props>(), {
customObjects: () => [] customObjects: () => []
}) })
const search = ref()
// TODO (wvffle): Make sure everything is it's own type // TODO (wvffle): Make sure everything is it's own type
const page = ref(1) const page = ref(1)
type ResponseType = { count: number, results: any[] } type ResponseType = { count: number, results: any[] }
@ -83,8 +86,6 @@ const actions = computed(() => [
} }
]) ])
const emit = defineEmits(['fetch-start'])
const isLoading = ref(false) const isLoading = ref(false)
const fetchData = async () => { const fetchData = async () => {
emit('fetch-start') emit('fetch-start')

View File

@ -11,7 +11,7 @@ import useSharedLabels from '~/composables/locale/useSharedLabels'
const PRIVACY_LEVELS = ['me', 'instance', 'everyone'] as PrivacyLevel[] const PRIVACY_LEVELS = ['me', 'instance', 'everyone'] as PrivacyLevel[]
interface Emits { interface Events {
(e: 'updated', data: Library): void (e: 'updated', data: Library): void
(e: 'created', data: Library): void (e: 'created', data: Library): void
(e: 'deleted'): void (e: 'deleted'): void
@ -21,7 +21,7 @@ interface Props {
library?: Library library?: Library
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const props = defineProps<Props>() const props = defineProps<Props>()
const { $pgettext } = useGettext() const { $pgettext } = useGettext()

View File

@ -13,6 +13,10 @@ import RadioButton from '~/components/radios/Button.vue'
import useErrorHandler from '~/composables/useErrorHandler' import useErrorHandler from '~/composables/useErrorHandler'
import useReport from '~/composables/moderation/useReport' import useReport from '~/composables/moderation/useReport'
interface Emits {
(e: 'followed'): void
}
interface Props { interface Props {
initialLibrary: Library initialLibrary: Library
displayFollow?: boolean displayFollow?: boolean
@ -20,6 +24,7 @@ interface Props {
displayCopyFid?: boolean displayCopyFid?: boolean
} }
const emit = defineEmits<Emits>()
const props = withDefaults(defineProps<Props>(), { const props = withDefaults(defineProps<Props>(), {
displayFollow: true, displayFollow: true,
displayScan: true, displayScan: true,
@ -68,7 +73,6 @@ const launchScan = async () => {
} }
} }
const emit = defineEmits(['followed', 'deleted'])
const follow = async () => { const follow = async () => {
isLoadingFollow.value = true isLoadingFollow.value = true
try { try {
@ -76,7 +80,7 @@ const follow = async () => {
library.value.follow = response.data library.value.follow = response.data
emit('followed') emit('followed')
} catch (error) { } catch (error) {
// TODO (wvffle): ==> CORRECTLY HANDLED ERROR HERE <== console.error(error)
store.commit('ui/addMessage', { store.commit('ui/addMessage', {
// TODO (wvffle): Translate // TODO (wvffle): Translate
content: 'Cannot follow remote library: ' + error, content: 'Cannot follow remote library: ' + error,

View File

@ -6,11 +6,11 @@ import { computed, ref } from 'vue'
import axios from 'axios' import axios from 'axios'
interface Emits { interface Events {
(e: 'scanned', data: object): void (e: 'scanned', data: object): void
} }
const emit = defineEmits<Emits>() const emit = defineEmits<Events>()
const { $pgettext } = useGettext() const { $pgettext } = useGettext()

View File

@ -35,18 +35,13 @@ const fetchData = async () => {
fetchData() fetchData()
// TODO (wvffle): Find correct type
const updateApproved = async (follow: LibraryFollow, approved: boolean) => { const updateApproved = async (follow: LibraryFollow, approved: boolean) => {
follow.isLoading = true
try { try {
await axios.post(`federation/follows/library/${follow.uuid}/${approved ? 'accept' : 'reject'}/`) await axios.post(`federation/follows/library/${follow.uuid}/${approved ? 'accept' : 'reject'}/`)
follow.approved = approved follow.approved = approved
} catch (error) { } catch (error) {
useErrorHandler(error as Error) useErrorHandler(error as Error)
} }
follow.isLoading = false
} }
</script> </script>
@ -85,7 +80,7 @@ const updateApproved = async (follow: LibraryFollow, approved: boolean) => {
</div> </div>
</div> </div>
<table <table
v-else-if="follows?.count > 0" v-else-if="(follows ?? { count: 0 }).count > 0"
class="ui table" class="ui table"
> >
<thead> <thead>

View File

@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
// TODO (wvffle): Rename to LibraryBase
import type { Library } from '~/types' import type { Library } from '~/types'
import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router' import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'

View File

@ -32,7 +32,6 @@ const props = withDefaults(defineProps<Props>(), {
scope: 'all' scope: 'all'
}) })
// TODO (wvffle): Make sure everything is it's own type
const page = ref(+props.defaultPage) 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)