Move *.js to *.ts
This commit is contained in:
parent
8ff0bb937b
commit
9e0596d136
|
@ -28,11 +28,7 @@ module.exports = {
|
|||
// NOTE: Handled by typescript
|
||||
'no-undef': 'off',
|
||||
'no-unused-vars': 'off',
|
||||
|
||||
// TODO (wvffle): Migrate to VUI
|
||||
// We're using `// @ts-ignore` in jQuery extensions
|
||||
// and gettext for vue 2
|
||||
'@typescript-eslint/ban-ts-comment': 'off',
|
||||
'no-use-before-define': 'off',
|
||||
|
||||
// TODO (wvffle): Enable these rules later
|
||||
'vue/multi-word-component-names': 'off',
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
const Album = {
|
||||
clean (album) {
|
||||
// we manually rebind the album and artist to each child track
|
||||
album.tracks = album.tracks.map((track) => {
|
||||
track.album = album
|
||||
return track
|
||||
})
|
||||
return album
|
||||
}
|
||||
}
|
||||
const Artist = {
|
||||
clean (artist) {
|
||||
// clean data as given by the API
|
||||
artist.albums = artist.albums.map((album) => {
|
||||
return Album.clean(album)
|
||||
})
|
||||
return artist
|
||||
}
|
||||
}
|
||||
export default {
|
||||
Artist: Artist,
|
||||
Album: Album
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
export default {
|
||||
formats: [
|
||||
// 'audio/ogg',
|
||||
'audio/mpeg'
|
||||
],
|
||||
formatsMap: {
|
||||
'audio/ogg': 'ogg',
|
||||
'audio/mpeg': 'mp3',
|
||||
'audio/x-flac': 'flac'
|
||||
}
|
||||
}
|
|
@ -1,23 +0,0 @@
|
|||
const DYNAMIC_RANGE = 40 // dB
|
||||
|
||||
export function toLinearVolumeScale (v) {
|
||||
if (v <= 0.0) {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
// (1.0; 0.0) -> (0; -DYNAMIC_RANGE) dB
|
||||
const dB = (v - 1) * DYNAMIC_RANGE
|
||||
|
||||
return Math.pow(10, dB / 20)
|
||||
}
|
||||
|
||||
export function toLogarithmicVolumeScale (v) {
|
||||
if (v <= 0.0) {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
const dB = 20 * Math.log10(v)
|
||||
|
||||
// (0; -DYNAMIC_RANGE) [dB] -> (1.0; 0.0)
|
||||
return 1 - (dB / -DYNAMIC_RANGE)
|
||||
}
|
|
@ -51,8 +51,8 @@
|
|||
</template>
|
||||
<content-form
|
||||
v-if="setting.fieldType === 'markdown'"
|
||||
v-model="values[setting.identifier]"
|
||||
v-bind="setting.fieldParams"
|
||||
v-model="values[setting.identifier]"
|
||||
/>
|
||||
<signup-form-builder
|
||||
v-else-if="setting.fieldType === 'formBuilder'"
|
||||
|
|
|
@ -321,7 +321,7 @@
|
|||
|
||||
<script>
|
||||
import { useStore, mapState, mapGetters, mapActions } from 'vuex'
|
||||
import { toLinearVolumeScale } from '~/audio/volume.js'
|
||||
import toLinearVolumeScale from '~/composables/audio/toLinearVolumeScale'
|
||||
import { Howl, Howler } from 'howler'
|
||||
import { throttle, reverse } from 'lodash-es'
|
||||
import axios from 'axios'
|
||||
|
|
|
@ -27,17 +27,15 @@
|
|||
class="ui center aligned basic segment desktop-and-up"
|
||||
>
|
||||
<pagination
|
||||
v-bind="$attrs"
|
||||
:total="total"
|
||||
:current="page"
|
||||
:paginate-by="paginateBy"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
:class="['track-table', 'ui', 'unstackable', 'grid', 'tablet-and-below']"
|
||||
>
|
||||
<div :class="['track-table', 'ui', 'unstackable', 'grid', 'tablet-and-below']">
|
||||
<div
|
||||
v-if="isLoading"
|
||||
class="ui inverted active dimmer"
|
||||
|
@ -66,10 +64,10 @@
|
|||
>
|
||||
<pagination
|
||||
v-if="paginateResults"
|
||||
v-bind="$attrs"
|
||||
:total="total"
|
||||
:current="page"
|
||||
:compact="true"
|
||||
v-bind="$attrs"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -121,7 +121,7 @@
|
|||
<script>
|
||||
import { uniq } from 'lodash-es'
|
||||
import axios from 'axios'
|
||||
import useSharedLabels from '../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
|
|
@ -145,7 +145,7 @@
|
|||
import axios from 'axios'
|
||||
|
||||
import { checkRedirectToLogin } from '~/utils'
|
||||
import useSharedLabels from '../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
export default {
|
||||
props: {
|
||||
clientId: { type: String, required: true },
|
||||
|
|
|
@ -717,7 +717,7 @@ import PasswordInput from '~/components/forms/PasswordInput.vue'
|
|||
import SubsonicTokenForm from '~/components/auth/SubsonicTokenForm.vue'
|
||||
import AttachmentInput from '~/components/common/AttachmentInput.vue'
|
||||
import useLogger from '~/composables/useLogger'
|
||||
import useSharedLabels from '../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
const logger = useLogger()
|
||||
|
||||
|
|
|
@ -77,7 +77,7 @@ const remove = async (uuid: string, sendEvent = true) => {
|
|||
const initialValue = ref(props.initialValue ?? props.modelValue)
|
||||
watch(value, (to, from) => {
|
||||
// NOTE: Remove old attachment if it's not the original one
|
||||
if (from !== initialValue.value) {
|
||||
if (from !== initialValue.value && typeof from === 'string') {
|
||||
remove(from, false)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,16 +1,33 @@
|
|||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue'
|
||||
import { useToggle } from '@vueuse/core'
|
||||
|
||||
interface Props {
|
||||
content: string
|
||||
length?: number
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
length: 150
|
||||
})
|
||||
|
||||
const [expanded, toggleExpanded] = useToggle(false)
|
||||
const truncated = computed(() => props.content.slice(0, props.length))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="expandable-wrapper">
|
||||
<div :class="['expandable-content', {expandable: truncated.length < content.length}, {expanded: isExpanded}]">
|
||||
<div :class="['expandable-content', { expandable: truncated.length < content.length, expanded }]">
|
||||
<slot>{{ content }}</slot>
|
||||
</div>
|
||||
<a
|
||||
v-if="truncated.length < content.length"
|
||||
role="button"
|
||||
@click.prevent="isExpanded = !isExpanded"
|
||||
@click.prevent="toggleExpanded()"
|
||||
>
|
||||
<br>
|
||||
<translate
|
||||
v-if="isExpanded"
|
||||
v-if="expanded"
|
||||
key="1"
|
||||
translate-context="*/*/Button,Label"
|
||||
>Show less</translate>
|
||||
|
@ -22,23 +39,3 @@
|
|||
</a>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
// import sanitize from "~/sanitize"
|
||||
|
||||
export default {
|
||||
props: {
|
||||
content: { type: String, required: true },
|
||||
length: { type: Number, default: 150, required: false }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isExpanded: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
truncated () {
|
||||
return this.content.substring(0, this.length)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -7,7 +7,8 @@ interface Props {
|
|||
duration: number
|
||||
}
|
||||
|
||||
const { duration } = toRefs(defineProps<Props>())
|
||||
const props = defineProps<Props>()
|
||||
const { duration } = toRefs(props)
|
||||
const parsedDuration = computed(() => time.parse(duration.value))
|
||||
</script>
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ onMounted(() => {
|
|||
...props.message
|
||||
}
|
||||
|
||||
// @ts-expect-error
|
||||
// @ts-expect-error toast is from semantic ui
|
||||
$('body').toast(params)
|
||||
$('.ui.toast.visible').last().attr('role', 'alert')
|
||||
})
|
||||
|
|
|
@ -142,7 +142,7 @@ import PaginationMixin from '~/components/mixins/Pagination.vue'
|
|||
import { checkRedirectToLogin } from '~/utils'
|
||||
import TrackTable from '~/components/audio/track/Table.vue'
|
||||
import useLogger from '~/composables/useLogger'
|
||||
import useSharedLabels from '../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
const logger = useLogger()
|
||||
|
||||
|
|
|
@ -157,7 +157,7 @@ import AlbumCard from '~/components/audio/album/Card.vue'
|
|||
import Pagination from '~/components/Pagination.vue'
|
||||
import TagsSelector from '~/components/library/TagsSelector.vue'
|
||||
import useLogger from '~/composables/useLogger'
|
||||
import useSharedLabels from '../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
const logger = useLogger()
|
||||
|
||||
|
|
|
@ -173,7 +173,7 @@ import ArtistCard from '~/components/audio/artist/Card.vue'
|
|||
import Pagination from '~/components/Pagination.vue'
|
||||
import TagsSelector from '~/components/library/TagsSelector.vue'
|
||||
import useLogger from '~/composables/useLogger'
|
||||
import useSharedLabels from '../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
const logger = useLogger()
|
||||
|
||||
|
|
|
@ -219,7 +219,7 @@
|
|||
import axios from 'axios'
|
||||
import { diffWordsWithSpace } from 'diff'
|
||||
|
||||
import edits from '~/edits.js'
|
||||
import useEditConfigs from '~/composables/moderation/useEditConfigs'
|
||||
|
||||
function castValue (value) {
|
||||
if (value === null || value === undefined) {
|
||||
|
@ -233,15 +233,28 @@ export default {
|
|||
obj: { type: Object, required: true },
|
||||
currentState: { type: Object, required: false, default: function () { return { } } }
|
||||
},
|
||||
setup () {
|
||||
return { configs: useEditConfigs() }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isLoading: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
configs: edits.getConfigs,
|
||||
canApprove: edits.getCanApprove,
|
||||
canDelete: edits.getCanDelete,
|
||||
canApprove () {
|
||||
if (this.obj.is_applied) return false
|
||||
if (!this.$store.state.auth.authenticated) return false
|
||||
return this.$store.state.auth.availablePermissions.library
|
||||
},
|
||||
canDelete () {
|
||||
if (this.obj.is_applied || this.obj.is_approved) return false
|
||||
if (!this.$store.state.auth.authenticated) return false
|
||||
|
||||
// TODO (wvffle): Is it better to compare ids? Is full_username unique?
|
||||
return this.obj.created_by.full_username === this.$store.state.auth.fullUsername ||
|
||||
this.$store.state.auth.availablePermissions.library
|
||||
},
|
||||
previousState () {
|
||||
if (this.obj.is_applied) {
|
||||
// mutation was applied, we use the previous state that is stored
|
||||
|
@ -279,7 +292,7 @@ export default {
|
|||
const fields = Object.keys(payload)
|
||||
const self = this
|
||||
return fields.map((f) => {
|
||||
const fieldConfig = edits.getFieldConfig(self.configs, this.obj.target.type, f)
|
||||
const fieldConfig = configs[this.obj.target.type].fields.find(({ id }) => id === f)
|
||||
const dummyRepr = (v) => { return v }
|
||||
const getValueRepr = fieldConfig.getValueRepr || dummyRepr
|
||||
const d = {
|
||||
|
|
|
@ -1,5 +1,46 @@
|
|||
<script setup lang="ts">
|
||||
import axios from 'axios'
|
||||
import useEditConfigs, { EditObject, EditObjectType } from '~/composables/moderation/useEditConfigs'
|
||||
import EditCard from '~/components/library/EditCard.vue'
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
interface Props {
|
||||
object: EditObject
|
||||
objectType: EditObjectType
|
||||
editId: number
|
||||
}
|
||||
|
||||
const props = defineProps<Props>()
|
||||
|
||||
const configs = useEditConfigs()
|
||||
const config = computed(() => configs[props.objectType])
|
||||
|
||||
const currentState = computed(() => config.value.fields.reduce((state: Record<string, unknown>, field) => {
|
||||
state[field.id] = { value: field.getValue(props.object) }
|
||||
return state
|
||||
}, {}))
|
||||
|
||||
const isLoading = ref(false)
|
||||
const obj = ref()
|
||||
const fetchData = async () => {
|
||||
isLoading.value = true
|
||||
|
||||
try {
|
||||
const response = await axios.get(`mutations/${props.editId}/`)
|
||||
obj.value = response.data
|
||||
} catch (error) {
|
||||
// TODO (wvffle): Handle error
|
||||
} finally {
|
||||
isLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
fetchData()
|
||||
// TODO (wvffle): Check if we want to watch for editId change and refetch data
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section :class="['ui', 'vertical', 'stripe', {loading: isLoading}, 'segment']">
|
||||
<section :class="['ui', 'vertical', 'stripe', { loading: isLoading }, 'segment']">
|
||||
<div class="ui text container">
|
||||
<edit-card
|
||||
v-if="obj"
|
||||
|
@ -9,50 +50,3 @@
|
|||
</div>
|
||||
</section>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import edits from '~/edits.js'
|
||||
import EditCard from '~/components/library/EditCard.vue'
|
||||
export default {
|
||||
components: {
|
||||
EditCard
|
||||
},
|
||||
props: {
|
||||
object: { type: Object, required: true },
|
||||
objectType: { type: String, required: true },
|
||||
editId: { type: Number, required: true }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isLoading: true,
|
||||
obj: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
configs: edits.getConfigs,
|
||||
config: edits.getConfig,
|
||||
currentState () {
|
||||
const self = this
|
||||
const s = {}
|
||||
this.config.fields.forEach(f => {
|
||||
s[f.id] = { value: f.getValue(self.object) }
|
||||
})
|
||||
return s
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.fetchData()
|
||||
},
|
||||
methods: {
|
||||
fetchData () {
|
||||
const self = this
|
||||
this.isLoading = true
|
||||
axios.get(`mutations/${this.editId}/`).then(response => {
|
||||
self.obj = response.data
|
||||
self.isLoading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -249,7 +249,8 @@ import AttachmentInput from '~/components/common/AttachmentInput.vue'
|
|||
import EditList from '~/components/library/EditList.vue'
|
||||
import EditCard from '~/components/library/EditCard.vue'
|
||||
import TagsSelector from '~/components/library/TagsSelector.vue'
|
||||
import edits from '~/edits.js'
|
||||
import useEditConfigs from '~/composables/useEditConfigs'
|
||||
import { computed } from 'vue/dist/vue'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -263,6 +264,16 @@ export default {
|
|||
object: { type: Object, required: true },
|
||||
licenses: { type: Array, required: true }
|
||||
},
|
||||
setup (props) {
|
||||
const configs = useEditConfigs()
|
||||
const config = computed(() => configs[props.objectType])
|
||||
const currentState = computed(() => config.value.fields.reduce((state/*: Record<string, unknown> */, field) => {
|
||||
state[field.id] = { value: field.getValue(props.object) }
|
||||
return state
|
||||
}, {}))
|
||||
|
||||
return { config, currentState, configs }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isLoading: false,
|
||||
|
@ -275,10 +286,15 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
configs: edits.getConfigs,
|
||||
config: edits.getConfig,
|
||||
currentState: edits.getCurrentState,
|
||||
canEdit: edits.getCanEdit,
|
||||
canEdit () {
|
||||
if (!this.$store.state.auth.authenticated) return false
|
||||
|
||||
const isOwner = this.object.attributed_to &&
|
||||
// TODO (wvffle): Is it better to compare ids? Is full_username unique?
|
||||
this.$store.state.auth.fullUsername === this.object.attributed_to.full_username
|
||||
|
||||
return isOwner || this.$store.state.auth.availablePermissions.library
|
||||
},
|
||||
labels () {
|
||||
return {
|
||||
summaryPlaceholder: this.$pgettext('*/*/Placeholder', 'A short summary describing your changes.')
|
||||
|
|
|
@ -207,7 +207,7 @@ import TagsSelector from '~/components/library/TagsSelector.vue'
|
|||
import Modal from '~/components/semantic/Modal.vue'
|
||||
import RemoteSearchForm from '~/components/RemoteSearchForm.vue'
|
||||
import useLogger from '~/composables/useLogger'
|
||||
import useSharedLabels from '../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
const logger = useLogger()
|
||||
|
||||
|
|
|
@ -182,7 +182,7 @@ import PaginationMixin from '~/components/mixins/Pagination.vue'
|
|||
import RadioCard from '~/components/radios/Card.vue'
|
||||
import Pagination from '~/components/Pagination.vue'
|
||||
import useLogger from '~/composables/useLogger'
|
||||
import useSharedLabels from '../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
const logger = useLogger()
|
||||
|
||||
|
|
|
@ -202,12 +202,12 @@
|
|||
import axios from 'axios'
|
||||
import { merge } from 'lodash-es'
|
||||
import time from '~/utils/time'
|
||||
import { normalizeQuery, parseTokens } from '~/search'
|
||||
import { normalizeQuery, parseTokens } from '~/utils/search'
|
||||
import Pagination from '~/components/Pagination.vue'
|
||||
import ActionTable from '~/components/common/ActionTable.vue'
|
||||
import OrderingMixin from '~/components/mixins/Ordering.vue'
|
||||
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
|
||||
import useSharedLabels from '../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -187,12 +187,12 @@
|
|||
import axios from 'axios'
|
||||
import { merge } from 'lodash-es'
|
||||
import time from '~/utils/time'
|
||||
import { normalizeQuery, parseTokens } from '~/search'
|
||||
import { normalizeQuery, parseTokens } from '~/utils/search'
|
||||
import Pagination from '~/components/Pagination.vue'
|
||||
import ActionTable from '~/components/common/ActionTable.vue'
|
||||
import OrderingMixin from '~/components/mixins/Ordering.vue'
|
||||
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -186,12 +186,12 @@
|
|||
import axios from 'axios'
|
||||
import { merge } from 'lodash-es'
|
||||
import time from '~/utils/time'
|
||||
import { normalizeQuery, parseTokens } from '~/search'
|
||||
import { normalizeQuery, parseTokens } from '~/utils/search'
|
||||
import Pagination from '~/components/Pagination.vue'
|
||||
import ActionTable from '~/components/common/ActionTable.vue'
|
||||
import OrderingMixin from '~/components/mixins/Ordering.vue'
|
||||
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -136,11 +136,11 @@ import time from '~/utils/time'
|
|||
import Pagination from '~/components/Pagination.vue'
|
||||
import OrderingMixin from '~/components/mixins/Ordering.vue'
|
||||
import EditCard from '~/components/library/EditCard.vue'
|
||||
import { normalizeQuery, parseTokens } from '~/search'
|
||||
import { normalizeQuery, parseTokens } from '~/utils/search'
|
||||
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
|
||||
|
||||
import edits from '~/edits'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useEditConfigs from '~/composables/useEditConfigs'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
@ -153,7 +153,8 @@ export default {
|
|||
},
|
||||
setup () {
|
||||
const sharedLabels = useSharedLabels()
|
||||
return { sharedLabels }
|
||||
const configs = useEditConfigs()
|
||||
return { sharedLabels, configs }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
|
@ -244,7 +245,10 @@ export default {
|
|||
response.data.results.forEach((e) => {
|
||||
self.targets[k][e.id] = {
|
||||
payload: e,
|
||||
currentState: edits.getCurrentStateForObj(e, edits.getConfigs.bind(self)()[k])
|
||||
currentState: configs[k].fields.reduce((state/*: Record<string, unknown> */, field) => {
|
||||
state[field.id] = { value: field.getValue(e) }
|
||||
return state
|
||||
}, {})
|
||||
}
|
||||
})
|
||||
}, error => {
|
||||
|
|
|
@ -217,12 +217,12 @@
|
|||
import axios from 'axios'
|
||||
import { merge } from 'lodash-es'
|
||||
import time from '~/utils/time'
|
||||
import { normalizeQuery, parseTokens } from '~/search'
|
||||
import { normalizeQuery, parseTokens } from '~/utils/search'
|
||||
import Pagination from '~/components/Pagination.vue'
|
||||
import ActionTable from '~/components/common/ActionTable.vue'
|
||||
import OrderingMixin from '~/components/mixins/Ordering.vue'
|
||||
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -148,14 +148,14 @@
|
|||
import axios from 'axios'
|
||||
import { merge } from 'lodash-es'
|
||||
import time from '~/utils/time'
|
||||
import { normalizeQuery, parseTokens } from '~/search'
|
||||
import { normalizeQuery, parseTokens } from '~/utils/search'
|
||||
import Pagination from '~/components/Pagination.vue'
|
||||
import ActionTable from '~/components/common/ActionTable.vue'
|
||||
import OrderingMixin from '~/components/mixins/Ordering.vue'
|
||||
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
|
||||
import ImportStatusModal from '~/components/library/ImportStatusModal.vue'
|
||||
import { truncate } from '~/utils/filters'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -200,12 +200,12 @@
|
|||
import axios from 'axios'
|
||||
import { merge } from 'lodash-es'
|
||||
import time from '~/utils/time'
|
||||
import { normalizeQuery, parseTokens } from '~/search'
|
||||
import { normalizeQuery, parseTokens } from '~/utils/search'
|
||||
import Pagination from '~/components/Pagination.vue'
|
||||
import ActionTable from '~/components/common/ActionTable.vue'
|
||||
import OrderingMixin from '~/components/mixins/Ordering.vue'
|
||||
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -309,14 +309,14 @@
|
|||
import axios from 'axios'
|
||||
import { merge } from 'lodash-es'
|
||||
import time from '~/utils/time'
|
||||
import { normalizeQuery, parseTokens } from '~/search'
|
||||
import { normalizeQuery, parseTokens } from '~/utils/search'
|
||||
import Pagination from '~/components/Pagination.vue'
|
||||
import ActionTable from '~/components/common/ActionTable.vue'
|
||||
import OrderingMixin from '~/components/mixins/Ordering.vue'
|
||||
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
|
||||
import ImportStatusModal from '~/components/library/ImportStatusModal.vue'
|
||||
import { humanSize, truncate } from '~/utils/filters'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -173,12 +173,12 @@
|
|||
import axios from 'axios'
|
||||
import { merge } from 'lodash-es'
|
||||
import time from '~/utils/time'
|
||||
import { normalizeQuery, parseTokens } from '~/search'
|
||||
import { normalizeQuery, parseTokens } from '~/utils/search'
|
||||
import Pagination from '~/components/Pagination.vue'
|
||||
import ActionTable from '~/components/common/ActionTable.vue'
|
||||
import OrderingMixin from '~/components/mixins/Ordering.vue'
|
||||
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -187,7 +187,7 @@ import time from '~/utils/time'
|
|||
import Pagination from '~/components/Pagination.vue'
|
||||
import ActionTable from '~/components/common/ActionTable.vue'
|
||||
import OrderingMixin from '~/components/mixins/Ordering.vue'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -396,7 +396,7 @@ import NoteForm from '~/components/manage/moderation/NoteForm.vue'
|
|||
import NotesThread from '~/components/manage/moderation/NotesThread.vue'
|
||||
import ReportCategoryDropdown from '~/components/moderation/ReportCategoryDropdown.vue'
|
||||
import InstancePolicyModal from '~/components/manage/moderation/InstancePolicyModal.vue'
|
||||
import entities from '~/entities'
|
||||
import useReportConfigs from '~/composables/moderation/useReportConfigs.ts'
|
||||
import { setUpdate } from '~/utils'
|
||||
import showdown from 'showdown'
|
||||
|
||||
|
@ -418,6 +418,9 @@ export default {
|
|||
initObj: { type: Object, required: true },
|
||||
currentState: { type: String, required: false, default: '' }
|
||||
},
|
||||
setup () {
|
||||
return { configs: useReportConfigs() }
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
obj: this.initObj,
|
||||
|
@ -430,7 +433,6 @@ export default {
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
configs: entities.getConfigs,
|
||||
previousState () {
|
||||
if (this.obj.is_applied) {
|
||||
// mutation was applied, we use the previous state that is stored
|
||||
|
@ -466,15 +468,13 @@ export default {
|
|||
const payload = this.obj.target_state
|
||||
const fields = this.configs[this.target.type].moderatedFields
|
||||
return fields.map((fieldConfig) => {
|
||||
const dummyRepr = (v) => { return v }
|
||||
const getValueRepr = fieldConfig.getValueRepr || dummyRepr
|
||||
const d = {
|
||||
const getValueRepr = fieldConfig.getValueRepr ?? (i => i)
|
||||
return {
|
||||
id: fieldConfig.id,
|
||||
label: fieldConfig.label,
|
||||
value: payload[fieldConfig.id],
|
||||
repr: castValue(getValueRepr(payload[fieldConfig.id]))
|
||||
}
|
||||
return d
|
||||
})
|
||||
},
|
||||
target () {
|
||||
|
|
|
@ -161,7 +161,7 @@ import { merge } from 'lodash-es'
|
|||
import Pagination from '~/components/Pagination.vue'
|
||||
import ActionTable from '~/components/common/ActionTable.vue'
|
||||
import OrderingMixin from '~/components/mixins/Ordering.vue'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -204,7 +204,7 @@ import time from '~/utils/time'
|
|||
import Pagination from '~/components/Pagination.vue'
|
||||
import ActionTable from '~/components/common/ActionTable.vue'
|
||||
import OrderingMixin from '~/components/mixins/Ordering.vue'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<script>
|
||||
|
||||
import { normalizeQuery, parseTokens, compileTokens } from '~/search'
|
||||
import { normalizeQuery, parseTokens, compileTokens } from '~/utils/search'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { computed } from 'vue'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
import useSharedLabels from '~/composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
import { useVModel } from '@vueuse/core'
|
||||
|
||||
interface Props {
|
||||
|
|
|
@ -102,7 +102,7 @@ import $ from 'jquery'
|
|||
import axios from 'axios'
|
||||
|
||||
import useLogger from '~/composables/useLogger'
|
||||
import useSharedLabels from '~/composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
const logger = useLogger()
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ const show = useVModel(props, 'show', emit)
|
|||
|
||||
const control = ref()
|
||||
const initModal = () => {
|
||||
// @ts-expect-error
|
||||
// @ts-expect-error modal is from semantic ui
|
||||
control.value = $(modal.value).modal({
|
||||
duration: 100,
|
||||
onApprove: () => emit('approved'),
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
export const DYNAMIC_RANGE = 40 // dB
|
||||
|
||||
export default (volume: number) => {
|
||||
if (volume <= 0.0) {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
// (1.0; 0.0) -> (0; -DYNAMIC_RANGE) dB
|
||||
const dB = (volume - 1) * DYNAMIC_RANGE
|
||||
return Math.pow(10, dB / 20)
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
import { gettext } from '~/init/locale'
|
||||
import { Album, Artist, Content, Track } from '~/types'
|
||||
|
||||
interface ConfigField {
|
||||
id: string
|
||||
label: string
|
||||
type: 'content' | 'attachment' | 'tags' | 'text' | 'license'
|
||||
inputType?: 'text' | 'number'
|
||||
required: boolean
|
||||
getValue: (obj: EditObject) => unknown
|
||||
getValueRepr?: (obj: any) => string
|
||||
}
|
||||
|
||||
export type EditObject = Artist | Album | Track
|
||||
export type EditObjectType = 'artist' | 'album' | 'track'
|
||||
type Configs = Record<EditObjectType, { fields: ConfigField[] }>
|
||||
|
||||
const { $pgettext } = gettext
|
||||
const getContentValueRepr = (val: Content) => val.text
|
||||
|
||||
const description: ConfigField = {
|
||||
id: 'description',
|
||||
type: 'content',
|
||||
required: true,
|
||||
label: $pgettext('*/*/*/Noun', 'Description'),
|
||||
getValue: (obj) => obj.description ?? { text: '', content_type: 'text/markdown' },
|
||||
getValueRepr: getContentValueRepr
|
||||
}
|
||||
|
||||
const cover: ConfigField = {
|
||||
id: 'cover',
|
||||
type: 'attachment',
|
||||
required: false,
|
||||
label: $pgettext('Content/*/*/Noun', 'Cover'),
|
||||
getValue: (obj) => obj.cover?.uuid ?? null
|
||||
}
|
||||
|
||||
const tags: ConfigField = {
|
||||
id: 'tags',
|
||||
type: 'tags',
|
||||
required: true,
|
||||
label: $pgettext('*/*/*/Noun', 'Tags'),
|
||||
getValue: (obj) => { return obj.tags },
|
||||
getValueRepr: (tags: string[]) => tags.slice().sort().join('\n')
|
||||
}
|
||||
|
||||
// TODO: Get params from typescript type somehow?
|
||||
export default (): Configs => ({
|
||||
artist: {
|
||||
fields: [
|
||||
{
|
||||
id: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: $pgettext('*/*/*/Noun', 'Name'),
|
||||
getValue: (artist) => (artist as Artist).name
|
||||
},
|
||||
description,
|
||||
cover,
|
||||
tags
|
||||
]
|
||||
},
|
||||
album: {
|
||||
fields: [
|
||||
{
|
||||
id: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: $pgettext('*/*/*/Noun', 'Title'),
|
||||
getValue: (album) => (album as Album).title
|
||||
},
|
||||
description,
|
||||
{
|
||||
id: 'release_date',
|
||||
type: 'text',
|
||||
required: false,
|
||||
label: $pgettext('Content/*/*/Noun', 'Release date'),
|
||||
getValue: (album) => (album as Album).release_date
|
||||
},
|
||||
cover,
|
||||
tags
|
||||
]
|
||||
},
|
||||
track: {
|
||||
fields: [
|
||||
{
|
||||
id: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: $pgettext('*/*/*/Noun', 'Title'),
|
||||
getValue: (track) => (track as Track).title
|
||||
},
|
||||
description,
|
||||
cover,
|
||||
{
|
||||
id: 'position',
|
||||
type: 'text',
|
||||
inputType: 'number',
|
||||
required: false,
|
||||
label: $pgettext('*/*/*/Short, Noun', 'Position'),
|
||||
getValue: (track) => (track as Track).position
|
||||
},
|
||||
{
|
||||
id: 'copyright',
|
||||
type: 'text',
|
||||
required: false,
|
||||
label: $pgettext('Content/Track/*/Noun', 'Copyright'),
|
||||
getValue: (track) => (track as Track).copyright
|
||||
},
|
||||
{
|
||||
id: 'license',
|
||||
type: 'license',
|
||||
required: false,
|
||||
label: $pgettext('Content/*/*/Noun', 'License'),
|
||||
getValue: (track) => (track as Track).license
|
||||
},
|
||||
tags
|
||||
]
|
||||
}
|
||||
})
|
|
@ -0,0 +1,183 @@
|
|||
import { gettext } from '~/init/locale'
|
||||
import { RouteLocationRaw } from 'vue-router'
|
||||
|
||||
interface ModeratedField {
|
||||
id: string
|
||||
label: string
|
||||
getValueRepr?: (obj: any) => string
|
||||
}
|
||||
|
||||
export interface Entity {
|
||||
label: string
|
||||
icon: string
|
||||
getDeleteUrl?: (object: any) => string
|
||||
urls: {
|
||||
getDetail?: (object: any) => RouteLocationRaw
|
||||
getAdminDetail?: (object: any) => RouteLocationRaw
|
||||
}
|
||||
moderatedFields: ModeratedField[]
|
||||
}
|
||||
|
||||
export type EntityObjectType = 'artist' | 'album' | 'track' | 'library' | 'playlist' | 'account' | 'channel'
|
||||
type Configs = Record<EntityObjectType, Entity>
|
||||
|
||||
const { $pgettext } = gettext
|
||||
|
||||
const tags: ModeratedField = {
|
||||
id: 'tags',
|
||||
label: $pgettext('*/*/*/Noun', 'Tags'),
|
||||
getValueRepr: (tags: string[]) => tags.slice().sort().join('\n')
|
||||
}
|
||||
|
||||
const name: ModeratedField = {
|
||||
id: 'name',
|
||||
label: $pgettext('*/*/*/Noun', 'Name')
|
||||
}
|
||||
|
||||
const creationDate: ModeratedField = {
|
||||
id: 'creation_date',
|
||||
label: $pgettext('Content/*/*/Noun', 'Creation date')
|
||||
}
|
||||
|
||||
const musicBrainzId: ModeratedField = {
|
||||
id: 'mbid',
|
||||
label: $pgettext('*/*/*/Noun', 'MusicBrainz ID')
|
||||
}
|
||||
|
||||
const visibility: ModeratedField = {
|
||||
id: 'privacy_level',
|
||||
label: $pgettext('*/*/*', 'Visibility')
|
||||
}
|
||||
|
||||
export default (): Configs => ({
|
||||
artist: {
|
||||
label: $pgettext('*/*/*/Noun', 'Artist'),
|
||||
icon: 'users',
|
||||
getDeleteUrl: (obj) => {
|
||||
return `manage/library/artists/${obj.id}/`
|
||||
},
|
||||
urls: {
|
||||
getDetail: (obj) => ({ name: 'library.artists.detail', params: { id: obj.id } }),
|
||||
getAdminDetail: (obj) => ({ name: 'manage.library.artists.detail', params: { id: obj.id } })
|
||||
},
|
||||
moderatedFields: [
|
||||
name,
|
||||
creationDate,
|
||||
tags,
|
||||
musicBrainzId
|
||||
]
|
||||
},
|
||||
album: {
|
||||
label: $pgettext('*/*/*', 'Album'),
|
||||
icon: 'play',
|
||||
getDeleteUrl: (obj) => {
|
||||
return `manage/library/albums/${obj.id}/`
|
||||
},
|
||||
urls: {
|
||||
getDetail: (obj) => ({ name: 'library.albums.detail', params: { id: obj.id } }),
|
||||
getAdminDetail: (obj) => ({ name: 'manage.library.albums.detail', params: { id: obj.id } })
|
||||
},
|
||||
moderatedFields: [
|
||||
{
|
||||
id: 'title',
|
||||
label: $pgettext('*/*/*/Noun', 'Title')
|
||||
},
|
||||
creationDate,
|
||||
{
|
||||
id: 'release_date',
|
||||
label: $pgettext('Content/*/*/Noun', 'Release date')
|
||||
},
|
||||
tags,
|
||||
musicBrainzId
|
||||
]
|
||||
},
|
||||
track: {
|
||||
label: $pgettext('*/*/*/Noun', 'Track'),
|
||||
icon: 'music',
|
||||
getDeleteUrl: (obj) => {
|
||||
return `manage/library/tracks/${obj.id}/`
|
||||
},
|
||||
urls: {
|
||||
getDetail: (obj) => ({ name: 'library.tracks.detail', params: { id: obj.id } }),
|
||||
getAdminDetail: (obj) => ({ name: 'manage.library.tracks.detail', params: { id: obj.id } })
|
||||
},
|
||||
moderatedFields: [
|
||||
{
|
||||
id: 'title',
|
||||
label: $pgettext('*/*/*/Noun', 'Title')
|
||||
},
|
||||
{
|
||||
id: 'position',
|
||||
label: $pgettext('*/*/*/Short, Noun', 'Position')
|
||||
},
|
||||
{
|
||||
id: 'copyright',
|
||||
label: $pgettext('Content/Track/*/Noun', 'Copyright')
|
||||
},
|
||||
{
|
||||
id: 'license',
|
||||
label: $pgettext('Content/*/*/Noun', 'License')
|
||||
},
|
||||
tags,
|
||||
musicBrainzId
|
||||
]
|
||||
},
|
||||
library: {
|
||||
label: $pgettext('*/*/*/Noun', 'Library'),
|
||||
icon: 'book',
|
||||
getDeleteUrl: (obj) => {
|
||||
return `manage/library/libraries/${obj.uuid}/`
|
||||
},
|
||||
urls: {
|
||||
getAdminDetail: (obj) => ({ name: 'manage.library.libraries.detail', params: { id: obj.uuid } })
|
||||
},
|
||||
moderatedFields: [
|
||||
name,
|
||||
{
|
||||
id: 'description',
|
||||
label: $pgettext('*/*/*/Noun', 'Description')
|
||||
},
|
||||
visibility
|
||||
]
|
||||
},
|
||||
playlist: {
|
||||
label: $pgettext('*/*/*', 'Playlist'),
|
||||
icon: 'list',
|
||||
urls: {
|
||||
getDetail: (obj) => ({ name: 'library.playlists.detail', params: { id: obj.id } })
|
||||
// getAdminDetail: (obj) => ({name: 'manage.playlists.detail', params: {id: obj.id}}}
|
||||
},
|
||||
moderatedFields: [
|
||||
name,
|
||||
visibility
|
||||
]
|
||||
},
|
||||
account: {
|
||||
label: $pgettext('*/*/*/Noun', 'Account'),
|
||||
icon: 'user',
|
||||
urls: {
|
||||
getDetail: (obj) => ({ name: 'profile.full.overview', params: { username: obj.preferred_username, domain: obj.domain } }),
|
||||
getAdminDetail: (obj) => ({ name: 'manage.moderation.accounts.detail', params: { id: `${obj.preferred_username}@${obj.domain}` } })
|
||||
},
|
||||
moderatedFields: [
|
||||
name,
|
||||
{
|
||||
id: 'summary',
|
||||
label: $pgettext('*/*/*/Noun', 'Bio')
|
||||
}
|
||||
]
|
||||
},
|
||||
channel: {
|
||||
label: $pgettext('*/*/*', 'Channel'),
|
||||
icon: 'stream',
|
||||
urls: {
|
||||
getDetail: (obj) => ({ name: 'channels.detail', params: { id: obj.uuid } }),
|
||||
getAdminDetail: (obj) => ({ name: 'manage.channels.detail', params: { id: obj.uuid } })
|
||||
},
|
||||
moderatedFields: [
|
||||
name,
|
||||
creationDate,
|
||||
tags
|
||||
]
|
||||
}
|
||||
})
|
|
@ -1,191 +0,0 @@
|
|||
function getTagsValueRepr (val) {
|
||||
if (!val) {
|
||||
return ''
|
||||
}
|
||||
return val.slice().sort().join('\n')
|
||||
}
|
||||
|
||||
function getContentValueRepr (val) {
|
||||
return val.text
|
||||
}
|
||||
|
||||
export default {
|
||||
getConfigs () {
|
||||
const description = {
|
||||
id: 'description',
|
||||
type: 'content',
|
||||
required: true,
|
||||
label: this.$pgettext('*/*/*/Noun', 'Description'),
|
||||
getValue: (obj) => { return obj.description || { text: null, content_type: 'text/markdown' } },
|
||||
getValueRepr: getContentValueRepr
|
||||
}
|
||||
const cover = {
|
||||
id: 'cover',
|
||||
type: 'attachment',
|
||||
required: false,
|
||||
label: this.$pgettext('Content/*/*/Noun', 'Cover'),
|
||||
getValue: (obj) => {
|
||||
if (obj.cover) {
|
||||
return obj.cover.uuid
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
artist: {
|
||||
fields: [
|
||||
{
|
||||
id: 'name',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: this.$pgettext('*/*/*/Noun', 'Name'),
|
||||
getValue: (obj) => { return obj.name }
|
||||
},
|
||||
description,
|
||||
cover,
|
||||
{
|
||||
id: 'tags',
|
||||
type: 'tags',
|
||||
required: true,
|
||||
label: this.$pgettext('*/*/*/Noun', 'Tags'),
|
||||
getValue: (obj) => { return obj.tags },
|
||||
getValueRepr: getTagsValueRepr
|
||||
}
|
||||
]
|
||||
},
|
||||
album: {
|
||||
fields: [
|
||||
{
|
||||
id: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: this.$pgettext('*/*/*/Noun', 'Title'),
|
||||
getValue: (obj) => { return obj.title }
|
||||
},
|
||||
description,
|
||||
{
|
||||
id: 'release_date',
|
||||
type: 'text',
|
||||
required: false,
|
||||
label: this.$pgettext('Content/*/*/Noun', 'Release date'),
|
||||
getValue: (obj) => { return obj.release_date }
|
||||
},
|
||||
cover,
|
||||
{
|
||||
id: 'tags',
|
||||
type: 'tags',
|
||||
required: true,
|
||||
label: this.$pgettext('*/*/*/Noun', 'Tags'),
|
||||
getValue: (obj) => { return obj.tags },
|
||||
getValueRepr: getTagsValueRepr
|
||||
}
|
||||
]
|
||||
},
|
||||
track: {
|
||||
fields: [
|
||||
{
|
||||
id: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
label: this.$pgettext('*/*/*/Noun', 'Title'),
|
||||
getValue: (obj) => { return obj.title }
|
||||
},
|
||||
description,
|
||||
cover,
|
||||
{
|
||||
id: 'position',
|
||||
type: 'text',
|
||||
inputType: 'number',
|
||||
required: false,
|
||||
label: this.$pgettext('*/*/*/Short, Noun', 'Position'),
|
||||
getValue: (obj) => { return obj.position }
|
||||
},
|
||||
{
|
||||
id: 'copyright',
|
||||
type: 'text',
|
||||
required: false,
|
||||
label: this.$pgettext('Content/Track/*/Noun', 'Copyright'),
|
||||
getValue: (obj) => { return obj.copyright }
|
||||
},
|
||||
{
|
||||
id: 'license',
|
||||
type: 'license',
|
||||
required: false,
|
||||
label: this.$pgettext('Content/*/*/Noun', 'License'),
|
||||
getValue: (obj) => { return obj.license }
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
type: 'tags',
|
||||
required: true,
|
||||
label: this.$pgettext('*/*/*/Noun', 'Tags'),
|
||||
getValue: (obj) => { return obj.tags },
|
||||
getValueRepr: getTagsValueRepr
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getConfig () {
|
||||
return this.configs[this.objectType]
|
||||
},
|
||||
getFieldConfig (configs, type, fieldId) {
|
||||
const c = configs[type]
|
||||
return c.fields.filter((f) => {
|
||||
return f.id === fieldId
|
||||
})[0]
|
||||
},
|
||||
getCurrentState () {
|
||||
const self = this
|
||||
const s = {}
|
||||
this.config.fields.forEach(f => {
|
||||
s[f.id] = { value: f.getValue(self.object) }
|
||||
})
|
||||
return s
|
||||
},
|
||||
getCurrentStateForObj (obj, config) {
|
||||
const s = {}
|
||||
config.fields.forEach(f => {
|
||||
s[f.id] = { value: f.getValue(obj) }
|
||||
})
|
||||
return s
|
||||
},
|
||||
|
||||
getCanDelete () {
|
||||
if (this.obj.is_applied || this.obj.is_approved) {
|
||||
return false
|
||||
}
|
||||
if (!this.$store.state.auth.authenticated) {
|
||||
return false
|
||||
}
|
||||
return (
|
||||
this.obj.created_by.full_username === this.$store.state.auth.fullUsername ||
|
||||
this.$store.state.auth.availablePermissions.library
|
||||
)
|
||||
},
|
||||
getCanApprove () {
|
||||
if (this.obj.is_applied) {
|
||||
return false
|
||||
}
|
||||
if (!this.$store.state.auth.authenticated) {
|
||||
return false
|
||||
}
|
||||
return this.$store.state.auth.availablePermissions.library
|
||||
},
|
||||
getCanEdit () {
|
||||
if (!this.$store.state.auth.authenticated) {
|
||||
return false
|
||||
}
|
||||
|
||||
const libraryPermission = this.$store.state.auth.availablePermissions.library
|
||||
const objData = this.object || {}
|
||||
let isOwner = false
|
||||
if (objData.attributed_to) {
|
||||
isOwner = this.$store.state.auth.fullUsername === objData.attributed_to.full_username
|
||||
}
|
||||
return libraryPermission || isOwner
|
||||
}
|
||||
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
import EmbedFrame from './EmbedFrame.vue'
|
||||
import { createApp } from 'vue'
|
||||
|
||||
// @ts-expect-error
|
||||
// @ts-expect-error vue-plyr has no types defined
|
||||
import VuePlyr from 'vue-plyr'
|
||||
|
||||
const app = createApp(EmbedFrame)
|
||||
|
|
|
@ -1,245 +0,0 @@
|
|||
function getTagsValueRepr (val) {
|
||||
if (!val) {
|
||||
return ''
|
||||
}
|
||||
return val.slice().sort().join('\n')
|
||||
}
|
||||
|
||||
export default {
|
||||
getConfigs () {
|
||||
return {
|
||||
artist: {
|
||||
label: this.$pgettext('*/*/*/Noun', 'Artist'),
|
||||
icon: 'users',
|
||||
getDeleteUrl: (obj) => {
|
||||
return `manage/library/artists/${obj.id}/`
|
||||
},
|
||||
urls: {
|
||||
getDetail: (obj) => { return { name: 'library.artists.detail', params: { id: obj.id } } },
|
||||
getAdminDetail: (obj) => { return { name: 'manage.library.artists.detail', params: { id: obj.id } } }
|
||||
},
|
||||
moderatedFields: [
|
||||
{
|
||||
id: 'name',
|
||||
label: this.$pgettext('*/*/*/Noun', 'Name'),
|
||||
getValue: (obj) => { return obj.name }
|
||||
},
|
||||
{
|
||||
id: 'creation_date',
|
||||
label: this.$pgettext('Content/*/*/Noun', 'Creation date'),
|
||||
getValue: (obj) => { return obj.creation_date }
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
type: 'tags',
|
||||
label: this.$pgettext('*/*/*/Noun', 'Tags'),
|
||||
getValue: (obj) => { return obj.tags },
|
||||
getValueRepr: getTagsValueRepr
|
||||
},
|
||||
{
|
||||
id: 'mbid',
|
||||
label: this.$pgettext('*/*/*/Noun', 'MusicBrainz ID'),
|
||||
getValue: (obj) => { return obj.mbid }
|
||||
}
|
||||
]
|
||||
},
|
||||
album: {
|
||||
label: this.$pgettext('*/*/*', 'Album'),
|
||||
icon: 'play',
|
||||
getDeleteUrl: (obj) => {
|
||||
return `manage/library/albums/${obj.id}/`
|
||||
},
|
||||
urls: {
|
||||
getDetail: (obj) => { return { name: 'library.albums.detail', params: { id: obj.id } } },
|
||||
getAdminDetail: (obj) => { return { name: 'manage.library.albums.detail', params: { id: obj.id } } }
|
||||
},
|
||||
moderatedFields: [
|
||||
{
|
||||
id: 'title',
|
||||
label: this.$pgettext('*/*/*/Noun', 'Title'),
|
||||
getValue: (obj) => { return obj.title }
|
||||
},
|
||||
{
|
||||
id: 'creation_date',
|
||||
label: this.$pgettext('Content/*/*/Noun', 'Creation date'),
|
||||
getValue: (obj) => { return obj.creation_date }
|
||||
},
|
||||
{
|
||||
id: 'release_date',
|
||||
label: this.$pgettext('Content/*/*/Noun', 'Release date'),
|
||||
getValue: (obj) => { return obj.release_date }
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
type: 'tags',
|
||||
required: true,
|
||||
label: this.$pgettext('*/*/*/Noun', 'Tags'),
|
||||
getValue: (obj) => { return obj.tags },
|
||||
getValueRepr: getTagsValueRepr
|
||||
},
|
||||
{
|
||||
id: 'mbid',
|
||||
label: this.$pgettext('*/*/*/Noun', 'MusicBrainz ID'),
|
||||
getValue: (obj) => { return obj.mbid }
|
||||
}
|
||||
]
|
||||
},
|
||||
track: {
|
||||
label: this.$pgettext('*/*/*/Noun', 'Track'),
|
||||
icon: 'music',
|
||||
getDeleteUrl: (obj) => {
|
||||
return `manage/library/tracks/${obj.id}/`
|
||||
},
|
||||
urls: {
|
||||
getDetail: (obj) => { return { name: 'library.tracks.detail', params: { id: obj.id } } },
|
||||
getAdminDetail: (obj) => { return { name: 'manage.library.tracks.detail', params: { id: obj.id } } }
|
||||
},
|
||||
moderatedFields: [
|
||||
{
|
||||
id: 'title',
|
||||
label: this.$pgettext('*/*/*/Noun', 'Title'),
|
||||
getValue: (obj) => { return obj.title }
|
||||
},
|
||||
{
|
||||
id: 'position',
|
||||
label: this.$pgettext('*/*/*/Short, Noun', 'Position'),
|
||||
getValue: (obj) => { return obj.position }
|
||||
},
|
||||
{
|
||||
id: 'copyright',
|
||||
label: this.$pgettext('Content/Track/*/Noun', 'Copyright'),
|
||||
getValue: (obj) => { return obj.copyright }
|
||||
},
|
||||
{
|
||||
id: 'license',
|
||||
label: this.$pgettext('Content/*/*/Noun', 'License'),
|
||||
getValue: (obj) => { return obj.license }
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
label: this.$pgettext('*/*/*/Noun', 'Tags'),
|
||||
getValue: (obj) => { return obj.tags },
|
||||
getValueRepr: getTagsValueRepr
|
||||
},
|
||||
{
|
||||
id: 'mbid',
|
||||
label: this.$pgettext('*/*/*/Noun', 'MusicBrainz ID'),
|
||||
getValue: (obj) => { return obj.mbid }
|
||||
}
|
||||
]
|
||||
},
|
||||
library: {
|
||||
label: this.$pgettext('*/*/*/Noun', 'Library'),
|
||||
icon: 'book',
|
||||
getDeleteUrl: (obj) => {
|
||||
return `manage/library/libraries/${obj.uuid}/`
|
||||
},
|
||||
urls: {
|
||||
getAdminDetail: (obj) => { return { name: 'manage.library.libraries.detail', params: { id: obj.uuid } } }
|
||||
},
|
||||
moderatedFields: [
|
||||
{
|
||||
id: 'name',
|
||||
label: this.$pgettext('*/*/*/Noun', 'Name'),
|
||||
getValue: (obj) => { return obj.name }
|
||||
},
|
||||
{
|
||||
id: 'description',
|
||||
label: this.$pgettext('*/*/*/Noun', 'Description'),
|
||||
getValue: (obj) => { return obj.position }
|
||||
},
|
||||
{
|
||||
id: 'privacy_level',
|
||||
label: this.$pgettext('*/*/*', 'Visibility'),
|
||||
getValue: (obj) => { return obj.privacy_level }
|
||||
}
|
||||
]
|
||||
},
|
||||
playlist: {
|
||||
label: this.$pgettext('*/*/*', 'Playlist'),
|
||||
icon: 'list',
|
||||
urls: {
|
||||
getDetail: (obj) => { return { name: 'library.playlists.detail', params: { id: obj.id } } }
|
||||
// getAdminDetail: (obj) => { return {name: 'manage.playlists.detail', params: {id: obj.id}}}
|
||||
},
|
||||
moderatedFields: [
|
||||
{
|
||||
id: 'name',
|
||||
label: this.$pgettext('*/*/*/Noun', 'Name'),
|
||||
getValue: (obj) => { return obj.name }
|
||||
},
|
||||
{
|
||||
id: 'privacy_level',
|
||||
label: this.$pgettext('*/*/*', 'Visibility'),
|
||||
getValue: (obj) => { return obj.privacy_level }
|
||||
}
|
||||
]
|
||||
},
|
||||
account: {
|
||||
label: this.$pgettext('*/*/*/Noun', 'Account'),
|
||||
icon: 'user',
|
||||
urls: {
|
||||
getDetail: (obj) => { return { name: 'profile.full.overview', params: { username: obj.preferred_username, domain: obj.domain } } },
|
||||
getAdminDetail: (obj) => { return { name: 'manage.moderation.accounts.detail', params: { id: `${obj.preferred_username}@${obj.domain}` } } }
|
||||
},
|
||||
moderatedFields: [
|
||||
{
|
||||
id: 'name',
|
||||
label: this.$pgettext('*/*/*/Noun', 'Name'),
|
||||
getValue: (obj) => { return obj.name }
|
||||
},
|
||||
{
|
||||
id: 'summary',
|
||||
label: this.$pgettext('*/*/*/Noun', 'Bio'),
|
||||
getValue: (obj) => { return obj.summary }
|
||||
}
|
||||
]
|
||||
},
|
||||
channel: {
|
||||
label: this.$pgettext('*/*/*', 'Channel'),
|
||||
icon: 'stream',
|
||||
urls: {
|
||||
getDetail: (obj) => { return { name: 'channels.detail', params: { id: obj.uuid } } },
|
||||
getAdminDetail: (obj) => { return { name: 'manage.channels.detail', params: { id: obj.uuid } } }
|
||||
},
|
||||
moderatedFields: [
|
||||
{
|
||||
id: 'name',
|
||||
label: this.$pgettext('*/*/*/Noun', 'Name'),
|
||||
getValue: (obj) => { return obj.name }
|
||||
},
|
||||
{
|
||||
id: 'creation_date',
|
||||
label: this.$pgettext('Content/*/*/Noun', 'Creation date'),
|
||||
getValue: (obj) => { return obj.creation_date }
|
||||
},
|
||||
{
|
||||
id: 'tags',
|
||||
type: 'tags',
|
||||
label: this.$pgettext('*/*/*/Noun', 'Tags'),
|
||||
getValue: (obj) => { return obj.tags },
|
||||
getValueRepr: getTagsValueRepr
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
getConfig () {
|
||||
return this.configs[this.objectType]
|
||||
},
|
||||
getFieldConfig (configs, type, fieldId) {
|
||||
const c = configs[type]
|
||||
return c.fields.filter((f) => {
|
||||
return f.id === fieldId
|
||||
})[0]
|
||||
},
|
||||
getCurrentStateForObj (obj, config) {
|
||||
const s = {}
|
||||
config.fields.forEach(f => {
|
||||
s[f.id] = { value: f.getValue(obj) }
|
||||
})
|
||||
return s
|
||||
}
|
||||
|
||||
}
|
|
@ -7,14 +7,14 @@ export const install: InitModule = ({ app, store }) => {
|
|||
})
|
||||
|
||||
app.directive('dropdown', function (el, binding) {
|
||||
// @ts-expect-error
|
||||
// @ts-expect-error dropdown is from semantic ui
|
||||
jQuery(el).dropdown({
|
||||
selectOnKeydown: false,
|
||||
action (text: string, value: string, $el: JQuery<HTMLElement>) {
|
||||
// used to ensure focusing the dropdown and clicking via keyboard
|
||||
// works as expected
|
||||
$el[0]?.click()
|
||||
// @ts-expect-error
|
||||
// @ts-expect-error dropdown is from semantic ui
|
||||
jQuery(el).find('.ui.dropdown').dropdown('hide')
|
||||
},
|
||||
...(binding.value || {})
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import router from '~/router'
|
||||
import store from '~/store'
|
||||
// @ts-expect-error typescript does not know about configureCompat
|
||||
import { configureCompat, createApp, defineAsyncComponent, h } from 'vue'
|
||||
import useLogger from '~/composables/useLogger'
|
||||
import useTheme from '~/composables/useTheme'
|
||||
|
@ -49,10 +50,8 @@ Promise.all(modules).finally(() => {
|
|||
logger.info('Everything loaded!')
|
||||
})
|
||||
|
||||
// TODO (wvffle): Migrate to pinia
|
||||
// TODO (wvffle): Check for mixin merging: https://v3-migration.vuejs.org/breaking-changes/data-option.html#mixin-merge-behavior-change=
|
||||
// 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>
|
||||
// TODO (wvffle): Replace `from '(../)+` with `from '~/`
|
||||
// TODO (wvffle): Remove `allowJs` from tsconfig.json
|
||||
|
|
|
@ -1,43 +0,0 @@
|
|||
import sanitizeHtml from 'sanitize-html'
|
||||
|
||||
const allowedTags = [
|
||||
'h3',
|
||||
'h4',
|
||||
'h5',
|
||||
'h6',
|
||||
'blockquote',
|
||||
'p',
|
||||
'a',
|
||||
'ul',
|
||||
'ol',
|
||||
'nl',
|
||||
'li',
|
||||
'b',
|
||||
'i',
|
||||
'strong',
|
||||
'em',
|
||||
'strike',
|
||||
'code',
|
||||
'hr',
|
||||
'br',
|
||||
'div',
|
||||
'table',
|
||||
'thead',
|
||||
'caption',
|
||||
'tbody',
|
||||
'tr',
|
||||
'th',
|
||||
'td',
|
||||
'pre'
|
||||
]
|
||||
const allowedAttributes = {
|
||||
a: ['href', 'name', 'target'],
|
||||
// We don't currently allow img itself by default, but this
|
||||
// would make sense if we did. You could add srcset here,
|
||||
// and if you do the URL is checked for safety
|
||||
img: ['src']
|
||||
}
|
||||
|
||||
export default function sanitize (input) {
|
||||
return sanitizeHtml(input, { allowedAttributes, allowedTags })
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
export function normalizeQuery (query) {
|
||||
// given a string such as 'this is "my query" go', returns
|
||||
// an array of tokens like this: ['this', 'is', 'my query', 'go']
|
||||
if (!query) {
|
||||
return []
|
||||
}
|
||||
return query.match(/\\?.|^$/g).reduce((p, c) => {
|
||||
if (c === '"') {
|
||||
p.quote ^= 1
|
||||
} else if (!p.quote && c === ' ') {
|
||||
p.a.push('')
|
||||
} else {
|
||||
p.a[p.a.length - 1] += c.replace(/\\(.)/, '$1')
|
||||
}
|
||||
return p
|
||||
}, { a: [''] }).a
|
||||
}
|
||||
|
||||
export function parseTokens (tokens) {
|
||||
// given an array of tokens as returned by normalizeQuery,
|
||||
// returns a list of objects such as [
|
||||
// {
|
||||
// field: 'status',
|
||||
// value: 'pending'
|
||||
// },
|
||||
// {
|
||||
// field: null,
|
||||
// value: 'hello'
|
||||
// }
|
||||
// ]
|
||||
return tokens.map(t => {
|
||||
// we split the token on ":"
|
||||
const parts = t.split(/:(.+)/)
|
||||
if (parts.length === 1) {
|
||||
// no field specified
|
||||
return { field: null, value: t }
|
||||
}
|
||||
// first item is the field, second is the value, possibly quoted
|
||||
const field = parts[0]
|
||||
let rawValue = parts[1]
|
||||
|
||||
// we remove surrounding quotes if any
|
||||
if (rawValue[0] === '"') {
|
||||
rawValue = rawValue.substring(1)
|
||||
}
|
||||
if (rawValue.slice(-1) === '"') {
|
||||
rawValue = rawValue.substring(0, rawValue.length - 1)
|
||||
}
|
||||
return { field, value: rawValue }
|
||||
})
|
||||
}
|
||||
|
||||
export function compileTokens (tokens) {
|
||||
// given a list of tokens as returned by parseTokens,
|
||||
// returns a string query
|
||||
const parts = tokens.map(t => {
|
||||
let v = t.value
|
||||
const k = t.field
|
||||
if (v.indexOf(' ') > -1) {
|
||||
v = `"${v}"`
|
||||
}
|
||||
if (k) {
|
||||
return `${k}:${v}`
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
})
|
||||
return parts.join(' ')
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
import { Store } from 'vuex'
|
||||
|
||||
declare module '@vue/runtime-core' {
|
||||
interface ComponentCustomProperties {
|
||||
$store: Store<any>
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
import { createStore } from 'vuex'
|
||||
import { createStore, Store } from 'vuex'
|
||||
import createPersistedState from 'vuex-persistedstate'
|
||||
|
||||
import favorites from './favorites'
|
||||
|
@ -12,8 +12,14 @@ import radios from './radios'
|
|||
import player from './player'
|
||||
import playlists from './playlists'
|
||||
import ui from './ui'
|
||||
import { InjectionKey } from 'vue'
|
||||
|
||||
export default createStore({
|
||||
export interface RootState {
|
||||
|
||||
}
|
||||
|
||||
export const key: InjectionKey<Store<RootState>> = Symbol('vuex state injection key')
|
||||
export default createStore<RootState>({
|
||||
modules: {
|
||||
ui,
|
||||
auth,
|
||||
|
|
|
@ -33,20 +33,41 @@ export type ContentCategory = 'podcast'
|
|||
|
||||
export interface Artist {
|
||||
id: string
|
||||
|
||||
name: string
|
||||
description: Content
|
||||
cover?: Cover
|
||||
tags: string[]
|
||||
|
||||
content_category: ContentCategory
|
||||
albums: Album[]
|
||||
}
|
||||
|
||||
export interface Album {
|
||||
id: string
|
||||
|
||||
title: string
|
||||
description: Content
|
||||
release_date?: string
|
||||
cover?: Cover
|
||||
tags: string[]
|
||||
|
||||
artist: Artist
|
||||
tracks_count: number
|
||||
title: string
|
||||
tracks: Track[]
|
||||
}
|
||||
|
||||
export interface Track {
|
||||
id: string
|
||||
|
||||
title: string
|
||||
description: Content
|
||||
cover?: Cover
|
||||
position?: number
|
||||
copyright?: string
|
||||
license?: License
|
||||
tags: string[]
|
||||
|
||||
album?: Album
|
||||
artist?: Artist
|
||||
}
|
||||
|
@ -56,6 +77,16 @@ export interface Channel {
|
|||
artist?: Artist
|
||||
}
|
||||
|
||||
export interface Cover {
|
||||
uuid: string
|
||||
}
|
||||
|
||||
export interface License {
|
||||
code: string
|
||||
name: string
|
||||
url: string
|
||||
}
|
||||
|
||||
// API stuff
|
||||
export interface APIErrorResponse {
|
||||
[key: string]: APIErrorResponse | string[]
|
||||
|
@ -111,12 +142,13 @@ export interface FileSystem {
|
|||
content: FSEntry[]
|
||||
}
|
||||
|
||||
// Form stuff
|
||||
export interface FormHelpText {
|
||||
content_type: string
|
||||
text?: string
|
||||
// Content stuff
|
||||
export interface Content {
|
||||
content_type: 'text/plain' | 'text/markdown'
|
||||
text: string // TODO (wvffle): Ensure it's not nullable from backend side
|
||||
}
|
||||
|
||||
// Form stuff
|
||||
export interface FormField {
|
||||
label: string
|
||||
input_type: 'short_text' | 'long_text'
|
||||
|
@ -125,7 +157,7 @@ export interface FormField {
|
|||
|
||||
export interface Form {
|
||||
fields: FormField[]
|
||||
help_text: FormHelpText
|
||||
help_text: Content
|
||||
}
|
||||
|
||||
// Yet uncategorized stuff
|
||||
|
@ -135,9 +167,3 @@ export interface Actor {
|
|||
is_local: boolean
|
||||
domain: string
|
||||
}
|
||||
|
||||
export interface License {
|
||||
code: string
|
||||
name: string
|
||||
url: string
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
export function hashCode (str) { // java String#hashCode
|
||||
// java String#hashCode
|
||||
export function hashCode (str: string) {
|
||||
let hash = 0
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
hash = str.charCodeAt(i) + ((hash << 5) - hash)
|
||||
|
@ -6,7 +7,7 @@ export function hashCode (str) { // java String#hashCode
|
|||
return hash
|
||||
}
|
||||
|
||||
export function intToRGB (i) {
|
||||
export function intToRGB (i: number) {
|
||||
const c = (i & 0x00FFFFFF).toString(16).toUpperCase()
|
||||
return '00000'.substring(0, 6 - c.length) + c
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
interface Token {
|
||||
field: string | null
|
||||
value: string
|
||||
}
|
||||
|
||||
export function normalizeQuery (query: string): string[] {
|
||||
// given a string such as 'this is "my query" go', returns
|
||||
// an array of tokens like this: ['this', 'is', 'my query', 'go']
|
||||
if (!query) return []
|
||||
|
||||
const match = query.match(/\\?.|^$/g)
|
||||
if (!match) return []
|
||||
|
||||
const { tokens } = match.reduce((state, c) => {
|
||||
if (c === '"') {
|
||||
state.quote ^= 1
|
||||
} else if (!state.quote && c === ' ') {
|
||||
state.tokens.push('')
|
||||
} else {
|
||||
state.tokens[state.tokens.length - 1] += c.replace(/\\(.)/, '$1')
|
||||
}
|
||||
|
||||
return state
|
||||
}, { tokens: [''], quote: 0 })
|
||||
|
||||
return tokens
|
||||
}
|
||||
|
||||
const unquote = (str: string) => {
|
||||
if (str[0] === '"') str = str.slice(1)
|
||||
if (str[str.length - 1] === '"') str = str.slice(0, -1)
|
||||
return str
|
||||
}
|
||||
|
||||
export function parseTokens (normalizedQuery: string[]): Token[] {
|
||||
// given an array of tokens as returned by normalizeQuery,
|
||||
// returns a list of objects such as [
|
||||
// {
|
||||
// field: 'status',
|
||||
// value: 'pending'
|
||||
// },
|
||||
// {
|
||||
// field: null,
|
||||
// value: 'hello'
|
||||
// }
|
||||
// ]
|
||||
return normalizedQuery.map(t => {
|
||||
// we split the token on ":"
|
||||
const parts = t.split(/:(.+)/)
|
||||
if (parts.length === 1) {
|
||||
// no field specified
|
||||
return { field: null, value: t }
|
||||
}
|
||||
|
||||
// first item is the field, second is the value, possibly quoted
|
||||
const [field, value] = parts
|
||||
|
||||
// we remove surrounding quotes if any
|
||||
return { field, value: unquote(value) }
|
||||
})
|
||||
}
|
||||
|
||||
export function compileTokens (tokens: Token[]) {
|
||||
// given a list of tokens as returned by parseTokens,
|
||||
// returns a string query
|
||||
const parts = tokens.map(token => {
|
||||
const { field } = token
|
||||
let { value } = token
|
||||
|
||||
if (value.includes(' ')) {
|
||||
value = `"${value}"`
|
||||
}
|
||||
|
||||
if (field) {
|
||||
return `${field}:${value}`
|
||||
}
|
||||
|
||||
return value
|
||||
})
|
||||
|
||||
return parts.join(' ')
|
||||
}
|
|
@ -366,7 +366,7 @@
|
|||
import axios from 'axios'
|
||||
import { humanSize, truncate } from '~/utils/filters'
|
||||
import useLogger from '~/composables/useLogger'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
const logger = useLogger()
|
||||
|
||||
|
|
|
@ -386,7 +386,7 @@ import axios from 'axios'
|
|||
import ImportStatusModal from '~/components/library/ImportStatusModal.vue'
|
||||
import time from '~/utils/time'
|
||||
import { humanSize, truncate } from '~/utils/filters'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -133,9 +133,9 @@ import Pagination from '~/components/Pagination.vue'
|
|||
import OrderingMixin from '~/components/mixins/Ordering.vue'
|
||||
import ReportCard from '~/components/manage/moderation/ReportCard.vue'
|
||||
import ReportCategoryDropdown from '~/components/moderation/ReportCategoryDropdown.vue'
|
||||
import { normalizeQuery, parseTokens } from '~/search'
|
||||
import { normalizeQuery, parseTokens } from '~/utils/search'
|
||||
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -130,9 +130,9 @@ import time from '~/utils/time'
|
|||
import Pagination from '~/components/Pagination.vue'
|
||||
import OrderingMixin from '~/components/mixins/Ordering.vue'
|
||||
import UserRequestCard from '~/components/manage/moderation/UserRequestCard.vue'
|
||||
import { normalizeQuery, parseTokens } from '~/search'
|
||||
import { normalizeQuery, parseTokens } from '~/utils/search'
|
||||
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
|
||||
<script>
|
||||
import { humanSize } from '~/utils/filters'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
props: { library: { type: Object, required: true } },
|
||||
|
|
|
@ -268,7 +268,7 @@
|
|||
import axios from 'axios'
|
||||
import { merge } from 'lodash-es'
|
||||
import time from '~/utils/time'
|
||||
import { normalizeQuery, parseTokens } from '~/search'
|
||||
import { normalizeQuery, parseTokens } from '~/utils/search'
|
||||
|
||||
import Pagination from '~/components/Pagination.vue'
|
||||
import ActionTable from '~/components/common/ActionTable.vue'
|
||||
|
@ -276,7 +276,7 @@ import OrderingMixin from '~/components/mixins/Ordering.vue'
|
|||
import SmartSearchMixin from '~/components/mixins/SmartSearch.vue'
|
||||
import ImportStatusModal from '~/components/library/ImportStatusModal.vue'
|
||||
import { humanSize, truncate } from '~/utils/filters'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
components: {
|
||||
|
|
|
@ -121,7 +121,7 @@
|
|||
|
||||
<script>
|
||||
import axios from 'axios'
|
||||
import useSharedLabels from '../../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
export default {
|
||||
props: { library: { type: Object, default: null } },
|
||||
|
|
|
@ -213,7 +213,7 @@
|
|||
<script>
|
||||
import axios from 'axios'
|
||||
import { humanSize } from '~/utils/filters'
|
||||
import { compileTokens } from '~/search'
|
||||
import { compileTokens } from '~/utils/search'
|
||||
|
||||
export default {
|
||||
data () {
|
||||
|
|
|
@ -144,7 +144,7 @@ import OrderingMixin from '~/components/mixins/Ordering.vue'
|
|||
import PaginationMixin from '~/components/mixins/Pagination.vue'
|
||||
import PlaylistCardList from '~/components/playlists/CardList.vue'
|
||||
import Pagination from '~/components/Pagination.vue'
|
||||
import useSharedLabels from '../../composables/useSharedLabels'
|
||||
import useSharedLabels from '~/composables/locale/useSharedLabels'
|
||||
|
||||
const FETCH_URL = 'playlists/'
|
||||
|
||||
|
|
|
@ -14,8 +14,6 @@
|
|||
"skipLibCheck": true,
|
||||
"lib": ["dom", "esnext", "webworker"],
|
||||
|
||||
"allowJs": true,
|
||||
|
||||
"noUnusedLocals": true,
|
||||
"strictNullChecks": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
|
@ -33,6 +31,5 @@
|
|||
"vueCompilerOptions": {
|
||||
"experimentalCompatMode": 2
|
||||
},
|
||||
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.vue"],
|
||||
"references": [{ "path": "./tsconfig.node.json" }]
|
||||
"include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.vue", "vite.config.ts"]
|
||||
}
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node"
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
Loading…
Reference in New Issue