Migrate some components
This commit is contained in:
parent
45740d510e
commit
2f2409f9f2
|
@ -1,3 +1,81 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { BackendError } from '~/types'
|
||||||
|
|
||||||
|
import { useGettext } from 'vue3-gettext'
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
import { useStore } from '~/store'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
import PasswordInput from '~/components/forms/PasswordInput.vue'
|
||||||
|
|
||||||
|
const { $pgettext } = useGettext()
|
||||||
|
const store = useStore()
|
||||||
|
|
||||||
|
const subsonicEnabled = computed(() => store.state.instance.settings.subsonic.enabled.value)
|
||||||
|
const labels = computed(() => ({
|
||||||
|
subsonicField: $pgettext('Content/Password/Input.label', 'Your subsonic API password')
|
||||||
|
}))
|
||||||
|
|
||||||
|
const errors = ref([] as string[])
|
||||||
|
const success = ref(false)
|
||||||
|
const isLoading = ref(false)
|
||||||
|
const token = ref()
|
||||||
|
const fetchToken = async () => {
|
||||||
|
success.value = false
|
||||||
|
errors.value = []
|
||||||
|
isLoading.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.get(`users/${store.state.auth.username}/subsonic-token/`)
|
||||||
|
token.value = response.data.subsonic_api_token
|
||||||
|
} catch (error) {
|
||||||
|
errors.value = (error as BackendError).backendErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const showToken = ref(false)
|
||||||
|
const successMessage = ref('')
|
||||||
|
const requestNewToken = async () => {
|
||||||
|
successMessage.value = $pgettext('Content/Settings/Message', 'Password updated')
|
||||||
|
success.value = false
|
||||||
|
errors.value = []
|
||||||
|
isLoading.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.post(`users/${store.state.auth.username}/subsonic-token/`)
|
||||||
|
showToken.value = true
|
||||||
|
token.value = response.data.subsonic_api_token
|
||||||
|
success.value = true
|
||||||
|
} catch (error) {
|
||||||
|
errors.value = (error as BackendError).backendErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const disable = async () => {
|
||||||
|
successMessage.value = $pgettext('Content/Settings/Message', 'Access disabled')
|
||||||
|
success.value = false
|
||||||
|
errors.value = []
|
||||||
|
isLoading.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.delete(`users/${store.state.auth.username}/subsonic-token/`)
|
||||||
|
token.value = null
|
||||||
|
success.value = true
|
||||||
|
showToken.value = false
|
||||||
|
} catch (error) {
|
||||||
|
errors.value = (error as BackendError).backendErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
fetchToken()
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<form
|
<form
|
||||||
class="ui form"
|
class="ui form"
|
||||||
|
@ -154,86 +232,3 @@
|
||||||
</template>
|
</template>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
|
||||||
import axios from 'axios'
|
|
||||||
import PasswordInput from '~/components/forms/PasswordInput.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
PasswordInput
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
token: null,
|
|
||||||
errors: [],
|
|
||||||
success: false,
|
|
||||||
isLoading: false,
|
|
||||||
successMessage: '',
|
|
||||||
showToken: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
subsonicEnabled () {
|
|
||||||
return this.$store.state.instance.settings.subsonic.enabled.value
|
|
||||||
},
|
|
||||||
labels () {
|
|
||||||
return {
|
|
||||||
subsonicField: this.$pgettext('Content/Password/Input.label', 'Your subsonic API password')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created () {
|
|
||||||
this.fetchToken()
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
fetchToken () {
|
|
||||||
this.success = false
|
|
||||||
this.errors = []
|
|
||||||
this.isLoading = true
|
|
||||||
const self = this
|
|
||||||
const url = `users/${this.$store.state.auth.username}/subsonic-token/`
|
|
||||||
return axios.get(url).then(response => {
|
|
||||||
self.token = response.data.subsonic_api_token
|
|
||||||
self.isLoading = false
|
|
||||||
}, error => {
|
|
||||||
self.isLoading = false
|
|
||||||
self.errors = error.backendErrors
|
|
||||||
})
|
|
||||||
},
|
|
||||||
requestNewToken () {
|
|
||||||
this.successMessage = this.$pgettext('Content/Settings/Message', 'Password updated')
|
|
||||||
this.success = false
|
|
||||||
this.errors = []
|
|
||||||
this.isLoading = true
|
|
||||||
const self = this
|
|
||||||
const url = `users/${this.$store.state.auth.username}/subsonic-token/`
|
|
||||||
return axios.post(url, {}).then(response => {
|
|
||||||
self.showToken = true
|
|
||||||
self.token = response.data.subsonic_api_token
|
|
||||||
self.isLoading = false
|
|
||||||
self.success = true
|
|
||||||
}, error => {
|
|
||||||
self.isLoading = false
|
|
||||||
self.errors = error.backendErrors
|
|
||||||
})
|
|
||||||
},
|
|
||||||
disable () {
|
|
||||||
this.successMessage = this.$pgettext('Content/Settings/Message', 'Access disabled')
|
|
||||||
this.success = false
|
|
||||||
this.errors = []
|
|
||||||
this.isLoading = true
|
|
||||||
const self = this
|
|
||||||
const url = `users/${this.$store.state.auth.username}/subsonic-token/`
|
|
||||||
return axios.delete(url).then(response => {
|
|
||||||
self.isLoading = false
|
|
||||||
self.token = null
|
|
||||||
self.success = true
|
|
||||||
}, error => {
|
|
||||||
self.isLoading = false
|
|
||||||
self.errors = error.backendErrors
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1,3 +1,53 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { BackendError, Channel } from '~/types'
|
||||||
|
|
||||||
|
import { computed, watch, ref } from 'vue'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'submittable', value: boolean): void
|
||||||
|
(e: 'loading', value: boolean): void
|
||||||
|
(e: 'created'): void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
channel: Channel
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const title = ref('')
|
||||||
|
|
||||||
|
const errors = ref([] as string[])
|
||||||
|
const isLoading = ref(false)
|
||||||
|
const submit = async () => {
|
||||||
|
isLoading.value = true
|
||||||
|
errors.value = []
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.post('albums/', {
|
||||||
|
title: title.value,
|
||||||
|
artist: props.channel.artist?.id
|
||||||
|
})
|
||||||
|
|
||||||
|
emit('created')
|
||||||
|
} catch (error) {
|
||||||
|
errors.value = (error as BackendError).backendErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
const submittable = computed(() => title.value.length > 0)
|
||||||
|
watch(submittable, (value) => emit('submittable', value))
|
||||||
|
watch(isLoading, (value) => emit('loading', value))
|
||||||
|
|
||||||
|
defineExpose({
|
||||||
|
submit
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<form
|
<form
|
||||||
:class="['ui', {loading: isLoading}, 'form']"
|
:class="['ui', {loading: isLoading}, 'form']"
|
||||||
|
@ -27,63 +77,9 @@
|
||||||
<translate translate-context="*/*/*/Noun">Title</translate>
|
<translate translate-context="*/*/*/Noun">Title</translate>
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
v-model="values.title"
|
v-model="title"
|
||||||
type="text"
|
type="text"
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {},
|
|
||||||
props: {
|
|
||||||
channel: { type: Object, required: true }
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
errors: [],
|
|
||||||
isLoading: false,
|
|
||||||
values: {
|
|
||||||
title: ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
submittable () {
|
|
||||||
return this.values.title.length > 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
submittable (v) {
|
|
||||||
this.$emit('submittable', v)
|
|
||||||
},
|
|
||||||
isLoading (v) {
|
|
||||||
this.$emit('loading', v)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
|
|
||||||
submit () {
|
|
||||||
const self = this
|
|
||||||
self.isLoading = true
|
|
||||||
self.errors = []
|
|
||||||
const payload = {
|
|
||||||
...this.values,
|
|
||||||
artist: this.channel.artist.id
|
|
||||||
}
|
|
||||||
return axios.post('albums/', payload).then(
|
|
||||||
response => {
|
|
||||||
self.isLoading = false
|
|
||||||
self.$emit('created')
|
|
||||||
},
|
|
||||||
error => {
|
|
||||||
self.errors = error.backendErrors
|
|
||||||
self.isLoading = false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1,3 +1,43 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { Channel } from '~/types'
|
||||||
|
|
||||||
|
import { useGettext } from 'vue3-gettext'
|
||||||
|
import { useStore } from '~/store'
|
||||||
|
import { computed } from 'vue'
|
||||||
|
|
||||||
|
import LoginModal from '~/components/common/LoginModal.vue'
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'unsubscribed'): void
|
||||||
|
(e: 'subscribed'): void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
channel: Channel
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const { $pgettext } = useGettext()
|
||||||
|
const store = useStore()
|
||||||
|
|
||||||
|
const isSubscribed = computed(() => store.getters['channels/isSubscribed'](props.channel.uuid))
|
||||||
|
const title = computed(() => isSubscribed.value
|
||||||
|
? $pgettext('Content/Channel/Button/Verb', 'Subscribe')
|
||||||
|
: $pgettext('Content/Channel/Button/Verb', 'Unsubscribe')
|
||||||
|
)
|
||||||
|
|
||||||
|
const message = computed(() => ({
|
||||||
|
authMessage: $pgettext('Popup/Message/Paragraph', 'You need to be logged in to subscribe to this channel')
|
||||||
|
}))
|
||||||
|
|
||||||
|
const toggle = async () => {
|
||||||
|
await store.dispatch('channels/toggle', props.channel.uuid)
|
||||||
|
emit(isSubscribed.value ? 'unsubscribed' : 'subscribed')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button
|
<button
|
||||||
v-if="$store.state.auth.authenticated"
|
v-if="$store.state.auth.authenticated"
|
||||||
|
@ -5,18 +45,7 @@
|
||||||
@click.stop="toggle"
|
@click.stop="toggle"
|
||||||
>
|
>
|
||||||
<i class="heart icon" />
|
<i class="heart icon" />
|
||||||
<translate
|
{{ title }}
|
||||||
v-if="isSubscribed"
|
|
||||||
translate-context="Content/Track/Button.Message"
|
|
||||||
>
|
|
||||||
Unsubscribe
|
|
||||||
</translate>
|
|
||||||
<translate
|
|
||||||
v-else
|
|
||||||
translate-context="Content/Track/*/Verb"
|
|
||||||
>
|
|
||||||
Subscribe
|
|
||||||
</translate>
|
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-else
|
v-else
|
||||||
|
@ -24,57 +53,14 @@
|
||||||
@click="$refs.loginModal.show = true"
|
@click="$refs.loginModal.show = true"
|
||||||
>
|
>
|
||||||
<i class="heart icon" />
|
<i class="heart icon" />
|
||||||
<translate translate-context="Content/Track/*/Verb">
|
{{ title }}
|
||||||
Subscribe
|
|
||||||
</translate>
|
|
||||||
<login-modal
|
<login-modal
|
||||||
ref="loginModal"
|
ref="loginModal"
|
||||||
class="small"
|
class="small"
|
||||||
:next-route="$route.fullPath"
|
:next-route="$route.fullPath"
|
||||||
:message="message.authMessage"
|
:message="message.authMessage"
|
||||||
:cover="channel.artist.cover"
|
:cover="channel.artist.cover!"
|
||||||
@created="$refs.loginModal.show = false;"
|
@created="$refs.loginModal.show = false;"
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
|
||||||
import LoginModal from '~/components/common/LoginModal.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
LoginModal
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
channel: { type: Object, required: true }
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
title () {
|
|
||||||
if (this.isSubscribed) {
|
|
||||||
return this.$pgettext('Content/Channel/Button/Verb', 'Subscribe')
|
|
||||||
} else {
|
|
||||||
return this.$pgettext('Content/Channel/Button/Verb', 'Unsubscribe')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isSubscribed () {
|
|
||||||
return this.$store.getters['channels/isSubscribed'](this.channel.uuid)
|
|
||||||
},
|
|
||||||
message () {
|
|
||||||
return {
|
|
||||||
authMessage: this.$pgettext('Popup/Message/Paragraph', 'You need to be logged in to subscribe to this channel')
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
toggle () {
|
|
||||||
if (this.isSubscribed) {
|
|
||||||
this.$emit('unsubscribed')
|
|
||||||
} else {
|
|
||||||
this.$emit('subscribed')
|
|
||||||
}
|
|
||||||
this.$store.dispatch('channels/toggle', this.channel.uuid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -230,6 +230,7 @@
|
||||||
</template>
|
</template>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import $ from 'jquery'
|
import $ from 'jquery'
|
||||||
|
@ -247,6 +248,8 @@ function setIfEmpty (obj, k, v) {
|
||||||
obj[k] = v
|
obj[k] = v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO (wvffle): Find types in UploadMetadataForm.vue
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
AlbumSelect,
|
AlbumSelect,
|
||||||
|
|
|
@ -1,3 +1,38 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { Upload, Track } from '~/types'
|
||||||
|
|
||||||
|
import { ref, computed, watch } from 'vue'
|
||||||
|
|
||||||
|
import TagsSelector from '~/components/library/TagsSelector.vue'
|
||||||
|
import AttachmentInput from '~/components/common/AttachmentInput.vue'
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
// TODO (wvffle): Find correct type
|
||||||
|
(e: 'values', values: any): void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
upload: Upload
|
||||||
|
values?: Track | null
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
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 any)
|
||||||
|
|
||||||
|
// computed: {
|
||||||
|
// isLoading () {
|
||||||
|
// return !!this.metadata
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
const isLoading = computed(() => !props.upload)
|
||||||
|
watch(newValues, (values) => emit('values', values), { immediate: true })
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div :class="['ui', {loading: isLoading}, 'form']">
|
<div :class="['ui', {loading: isLoading}, 'form']">
|
||||||
<div class="ui required field">
|
<div class="ui required field">
|
||||||
|
@ -52,37 +87,3 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
|
||||||
import TagsSelector from '~/components/library/TagsSelector.vue'
|
|
||||||
import AttachmentInput from '~/components/common/AttachmentInput.vue'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
TagsSelector,
|
|
||||||
AttachmentInput
|
|
||||||
},
|
|
||||||
props: {
|
|
||||||
upload: { type: Object, required: true },
|
|
||||||
values: { type: Object, required: true }
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
newValues: { ...this.values } || this.upload.import_metadata
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
isLoading () {
|
|
||||||
return !!this.metadata
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
newValues: {
|
|
||||||
handler (v) {
|
|
||||||
this.$emit('values', v)
|
|
||||||
},
|
|
||||||
immediate: true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1,3 +1,39 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { BackendError } from '~/types'
|
||||||
|
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
// TODO (wvffle): Remove this component
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
(e: 'action-done', data: any): void
|
||||||
|
(e: 'action-error', error: BackendError): void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
method: 'get' | 'post' | 'put' | 'patch' | 'delete'
|
||||||
|
url: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const isLoading = ref(false)
|
||||||
|
const ajaxCall = async () => {
|
||||||
|
isLoading.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios[props.method](props.url)
|
||||||
|
emit('action-done', response.data)
|
||||||
|
} catch (error) {
|
||||||
|
emit('action-error', error as BackendError)
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<button
|
<button
|
||||||
:class="['ui', {loading: isLoading}, 'button']"
|
:class="['ui', {loading: isLoading}, 'button']"
|
||||||
|
@ -6,31 +42,3 @@
|
||||||
<slot />
|
<slot />
|
||||||
</button>
|
</button>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
url: { type: String, required: true },
|
|
||||||
method: { type: String, required: true }
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
isLoading: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
ajaxCall () {
|
|
||||||
const self = this
|
|
||||||
this.isLoading = true
|
|
||||||
axios[this.method](this.url).then(response => {
|
|
||||||
self.$emit('action-done', response.data)
|
|
||||||
self.isLoading = false
|
|
||||||
}, error => {
|
|
||||||
self.isLoading = false
|
|
||||||
self.$emit('action-error', error)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1,3 +1,86 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import type { BackendError } from '~/types'
|
||||||
|
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import { whenever } from '@vueuse/core'
|
||||||
|
|
||||||
|
import axios from 'axios'
|
||||||
|
import clip from 'text-clipper'
|
||||||
|
|
||||||
|
interface Emits {
|
||||||
|
// TODO (wvffle): Find correct type
|
||||||
|
(e: 'updated', data: unknown): void
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
content?: { text?: string, html?: string } | null
|
||||||
|
fieldName?: string
|
||||||
|
updateUrl?: string
|
||||||
|
canUpdate?: boolean
|
||||||
|
fetchHtml?: boolean
|
||||||
|
permissive?: boolean
|
||||||
|
truncateLength?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const emit = defineEmits<Emits>()
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
content: null,
|
||||||
|
fieldName: 'description',
|
||||||
|
updateUrl: '',
|
||||||
|
canUpdate: true,
|
||||||
|
fetchHtml: false,
|
||||||
|
permissive: false,
|
||||||
|
truncateLength: 500
|
||||||
|
})
|
||||||
|
|
||||||
|
const preview = ref('')
|
||||||
|
const fetchPreview = async () => {
|
||||||
|
const response = await axios.post('text-preview/', { text: props.content?.text ?? '', permissive: props.permissive })
|
||||||
|
preview.value = response.data.rendered
|
||||||
|
}
|
||||||
|
|
||||||
|
whenever(() => props.fetchHtml, fetchPreview)
|
||||||
|
|
||||||
|
const truncatedHtml = computed(() => clip(props.content?.html ?? '', props.truncateLength, {
|
||||||
|
html: true,
|
||||||
|
maxLines: 3
|
||||||
|
}))
|
||||||
|
|
||||||
|
const showMore = ref(false)
|
||||||
|
const html = computed(() => props.fetchHtml
|
||||||
|
? preview.value
|
||||||
|
: props.truncateLength > 0 && !showMore.value
|
||||||
|
? truncatedHtml.value
|
||||||
|
: props.content?.html ?? ''
|
||||||
|
)
|
||||||
|
|
||||||
|
const isTruncated = computed(() => props.truncateLength > 0 && truncatedHtml.value.length < (props.content?.html ?? '').length)
|
||||||
|
|
||||||
|
const isUpdating = ref(false)
|
||||||
|
const text = ref(props.content?.text ?? '')
|
||||||
|
const isLoading = ref(false)
|
||||||
|
const errors = ref([] as string[])
|
||||||
|
const submit = async () => {
|
||||||
|
errors.value = []
|
||||||
|
isLoading.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await axios.patch(props.updateUrl, {
|
||||||
|
[props.fieldName]: text.value
|
||||||
|
? { content_type: 'text/markdown', text: text.value }
|
||||||
|
: null
|
||||||
|
})
|
||||||
|
|
||||||
|
emit('updated', response.data)
|
||||||
|
isUpdating.value = false
|
||||||
|
} catch (error) {
|
||||||
|
errors.value = (error as BackendError).backendErrors
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<template v-if="content && !isUpdating">
|
<template v-if="content && !isUpdating">
|
||||||
|
@ -60,7 +143,7 @@
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<content-form
|
<content-form
|
||||||
v-model="newText"
|
v-model="text"
|
||||||
:autofocus="true"
|
:autofocus="true"
|
||||||
/>
|
/>
|
||||||
<a
|
<a
|
||||||
|
@ -72,7 +155,7 @@
|
||||||
<button
|
<button
|
||||||
:class="['ui', {'loading': isLoading}, 'right', 'floated', 'button']"
|
:class="['ui', {'loading': isLoading}, 'right', 'floated', 'button']"
|
||||||
type="submit"
|
type="submit"
|
||||||
:disabled="isLoading || null"
|
:disabled="isLoading"
|
||||||
>
|
>
|
||||||
<translate translate-context="Content/Channels/Button.Label/Verb">
|
<translate translate-context="Content/Channels/Button.Label/Verb">
|
||||||
Update description
|
Update description
|
||||||
|
@ -82,80 +165,3 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
|
||||||
import axios from 'axios'
|
|
||||||
import clip from 'text-clipper'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
props: {
|
|
||||||
content: { type: Object, required: false, default: null },
|
|
||||||
fieldName: { type: String, required: false, default: 'description' },
|
|
||||||
updateUrl: { required: false, type: String, default: '' },
|
|
||||||
canUpdate: { required: false, default: true, type: Boolean },
|
|
||||||
fetchHtml: { required: false, default: false, type: Boolean },
|
|
||||||
permissive: { required: false, default: false, type: Boolean },
|
|
||||||
truncateLength: { required: false, default: 500, type: Number }
|
|
||||||
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
isUpdating: false,
|
|
||||||
showMore: false,
|
|
||||||
newText: (this.content || { text: '' }).text,
|
|
||||||
isLoading: false,
|
|
||||||
errors: [],
|
|
||||||
preview: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
html () {
|
|
||||||
if (this.fetchHtml) {
|
|
||||||
return this.preview
|
|
||||||
}
|
|
||||||
if (this.truncateLength > 0 && !this.showMore) {
|
|
||||||
return this.truncatedHtml
|
|
||||||
}
|
|
||||||
return this.content.html
|
|
||||||
},
|
|
||||||
truncatedHtml () {
|
|
||||||
return clip(this.content.html, this.truncateLength, { html: true, maxLines: 3 })
|
|
||||||
},
|
|
||||||
isTruncated () {
|
|
||||||
return this.truncateLength > 0 && this.truncatedHtml.length < this.content.html.length
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async created () {
|
|
||||||
if (this.fetchHtml) {
|
|
||||||
await this.fetchPreview()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async fetchPreview () {
|
|
||||||
const response = await axios.post('text-preview/', { text: this.content.text, permissive: this.permissive })
|
|
||||||
this.preview = response.data.rendered
|
|
||||||
},
|
|
||||||
submit () {
|
|
||||||
const self = this
|
|
||||||
this.isLoading = true
|
|
||||||
this.errors = []
|
|
||||||
const payload = {}
|
|
||||||
payload[this.fieldName] = null
|
|
||||||
if (this.newText) {
|
|
||||||
payload[this.fieldName] = {
|
|
||||||
content_type: 'text/markdown',
|
|
||||||
text: this.newText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
axios.patch(this.updateUrl, payload).then((response) => {
|
|
||||||
self.$emit('updated', response.data)
|
|
||||||
self.isLoading = false
|
|
||||||
self.isUpdating = false
|
|
||||||
}, error => {
|
|
||||||
self.errors = error.backendErrors
|
|
||||||
self.isLoading = false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -307,6 +307,8 @@ export interface Upload {
|
||||||
detail: object
|
detail: object
|
||||||
error_code: string
|
error_code: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import_metadata?: Track
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileSystem Logs
|
// FileSystem Logs
|
||||||
|
|
Loading…
Reference in New Issue