Fix router warnings
This commit is contained in:
parent
dbe762d71c
commit
e608089557
|
@ -1,3 +1,51 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import Modal from '~/components/semantic/Modal.vue'
|
||||||
|
import ChannelUploadForm from '~/components/channels/UploadForm.vue'
|
||||||
|
import { humanSize } from '~/utils/filters'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useStore } from '~/store'
|
||||||
|
import { ref, computed } from 'vue'
|
||||||
|
import { useGettext } from 'vue3-gettext'
|
||||||
|
|
||||||
|
const store = useStore()
|
||||||
|
const router = useRouter()
|
||||||
|
router.beforeEach(() => store.commit('channels/showUploadModal', { show: false }))
|
||||||
|
|
||||||
|
const update = (value: boolean) => store.commit('channels/showUploadModal', { show: value })
|
||||||
|
|
||||||
|
const { $npgettext, $gettext } = useGettext()
|
||||||
|
|
||||||
|
const statusData = ref()
|
||||||
|
const statusInfo = computed(() => {
|
||||||
|
if (!statusData.value) {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
const info = []
|
||||||
|
if (statusData.value.totalSize) {
|
||||||
|
info.push(humanSize(statusData.value.totalSize))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusData.value.totalFiles) {
|
||||||
|
const msg = $npgettext('*/*/*', '%{ count } file', '%{ count } files', statusData.value.totalFiles)
|
||||||
|
info.push($gettext(msg, { count: statusData.value.totalFiles }))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusData.value.progress) {
|
||||||
|
info.push(`${statusData.value.progress}%`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusData.value.speed) {
|
||||||
|
info.push(`${humanSize(statusData.value.speed)}/s`)
|
||||||
|
}
|
||||||
|
|
||||||
|
return info
|
||||||
|
})
|
||||||
|
|
||||||
|
const step = ref(1)
|
||||||
|
const isLoading = ref(false)
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<modal
|
<modal
|
||||||
v-model:show="$store.state.channels.showUploadModal"
|
v-model:show="$store.state.channels.showUploadModal"
|
||||||
|
@ -37,7 +85,6 @@
|
||||||
@loading="isLoading = $event"
|
@loading="isLoading = $event"
|
||||||
@published="$store.commit('channels/publish', $event)"
|
@published="$store.commit('channels/publish', $event)"
|
||||||
@status="statusData = $event"
|
@status="statusData = $event"
|
||||||
@submittable="submittable = $event"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="actions">
|
<div class="actions">
|
||||||
|
@ -96,7 +143,7 @@
|
||||||
<button
|
<button
|
||||||
:class="['ui', 'primary button', {loading: isLoading}]"
|
:class="['ui', 'primary button', {loading: isLoading}]"
|
||||||
type="submit"
|
type="submit"
|
||||||
:disabled="!statusData || !statusData.canSubmit || null"
|
:disabled="!statusData?.canSubmit || undefined"
|
||||||
@click.prevent.stop="$refs.uploadForm.publish"
|
@click.prevent.stop="$refs.uploadForm.publish"
|
||||||
>
|
>
|
||||||
<translate translate-context="*/Channels/Button.Label">
|
<translate translate-context="*/Channels/Button.Label">
|
||||||
|
@ -107,7 +154,7 @@
|
||||||
ref="dropdown"
|
ref="dropdown"
|
||||||
v-dropdown
|
v-dropdown
|
||||||
class="ui floating dropdown icon button"
|
class="ui floating dropdown icon button"
|
||||||
:disabled="!statusData || !statusData.canSubmit || null"
|
:disabled="!statusData?.canSubmit || undefined"
|
||||||
>
|
>
|
||||||
<i class="dropdown icon" />
|
<i class="dropdown icon" />
|
||||||
<div class="menu">
|
<div class="menu">
|
||||||
|
@ -135,67 +182,3 @@
|
||||||
</div>
|
</div>
|
||||||
</modal>
|
</modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
|
||||||
import Modal from '~/components/semantic/Modal.vue'
|
|
||||||
import ChannelUploadForm from '~/components/channels/UploadForm.vue'
|
|
||||||
import { humanSize } from '~/utils/filters'
|
|
||||||
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Modal,
|
|
||||||
ChannelUploadForm
|
|
||||||
},
|
|
||||||
setup () {
|
|
||||||
const guard = () => {
|
|
||||||
this.$store.commit('channels/showUploadModal', { show: false })
|
|
||||||
}
|
|
||||||
|
|
||||||
onBeforeRouteUpdate(guard)
|
|
||||||
onBeforeRouteLeave(guard)
|
|
||||||
|
|
||||||
return { humanSize }
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
step: 1,
|
|
||||||
isLoading: false,
|
|
||||||
submittable: true,
|
|
||||||
statusData: null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
labels () {
|
|
||||||
return {}
|
|
||||||
},
|
|
||||||
statusInfo () {
|
|
||||||
if (!this.statusData) {
|
|
||||||
return []
|
|
||||||
}
|
|
||||||
const info = []
|
|
||||||
if (this.statusData.totalSize) {
|
|
||||||
info.push(humanSize(this.statusData.totalSize))
|
|
||||||
}
|
|
||||||
if (this.statusData.totalFiles) {
|
|
||||||
const msg = this.$npgettext('*/*/*', '%{ count } file', '%{ count } files', this.statusData.totalFiles)
|
|
||||||
info.push(
|
|
||||||
this.$gettextInterpolate(msg, { count: this.statusData.totalFiles })
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if (this.statusData.progress) {
|
|
||||||
info.push(`${this.statusData.progress}%`)
|
|
||||||
}
|
|
||||||
if (this.statusData.speed) {
|
|
||||||
info.push(`${humanSize(this.statusData.speed)}/s`)
|
|
||||||
}
|
|
||||||
return info
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
update (v) {
|
|
||||||
this.$store.commit('channels/showUploadModal', { show: v })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -26,7 +26,7 @@ const value = useVModel(props, 'modelValue', emit)
|
||||||
|
|
||||||
const attachment = ref()
|
const attachment = ref()
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
const errors = reactive<string[]>([])
|
const errors = reactive([] as Error[])
|
||||||
const attachmentId = Math.random().toString(36).substring(7)
|
const attachmentId = Math.random().toString(36).substring(7)
|
||||||
|
|
||||||
const input = ref()
|
const input = ref()
|
||||||
|
|
|
@ -1,3 +1,82 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { filter, sortBy, flow } from 'lodash-es'
|
||||||
|
|
||||||
|
import axios, { AxiosError } from 'axios'
|
||||||
|
import { useGettext } from 'vue3-gettext'
|
||||||
|
|
||||||
|
import Modal from '~/components/semantic/Modal.vue'
|
||||||
|
import PlaylistForm from '~/components/playlists/Form.vue'
|
||||||
|
import useLogger from '~/composables/useLogger'
|
||||||
|
import { useStore } from '~/store'
|
||||||
|
import { ref, computed, watch } from 'vue'
|
||||||
|
import { BackendError, Playlist } from '~/types'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
|
||||||
|
const logger = useLogger()
|
||||||
|
const store = useStore()
|
||||||
|
|
||||||
|
const showDuplicateTrackAddConfirmation = ref(false)
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
router.beforeEach(() => {
|
||||||
|
store.commit('playlists/showModal', false)
|
||||||
|
showDuplicateTrackAddConfirmation.value = false
|
||||||
|
})
|
||||||
|
|
||||||
|
const playlists = computed(() => store.state.playlists.playlists)
|
||||||
|
const track = computed(() => store.state.playlists.modalTrack)
|
||||||
|
|
||||||
|
const { $pgettext } = useGettext()
|
||||||
|
const labels = computed(() => ({
|
||||||
|
addToPlaylist: $pgettext('Popup/Playlist/Table.Button.Tooltip/Verb', 'Add to this playlist'),
|
||||||
|
filterPlaylistField: $pgettext('Popup/Playlist/Form/Placeholder', 'Enter playlist name')
|
||||||
|
}))
|
||||||
|
|
||||||
|
const playlistNameFilter = ref('')
|
||||||
|
|
||||||
|
const sortedPlaylists = computed(() => flow(
|
||||||
|
filter((playlist: Playlist) => playlist.name.match(new RegExp(playlistNameFilter.value, 'i')) !== null),
|
||||||
|
sortBy((playlist: Playlist) => { return playlist.modification_date })
|
||||||
|
)(playlists.value).reverse())
|
||||||
|
|
||||||
|
const formKey = ref(new Date().toString())
|
||||||
|
watch(() => store.state.playlists.showModal, () => {
|
||||||
|
formKey.value = new Date().toString()
|
||||||
|
showDuplicateTrackAddConfirmation.value = false
|
||||||
|
})
|
||||||
|
|
||||||
|
const lastSelectedPlaylist = ref(-1)
|
||||||
|
const errors = ref([] as AxiosError[])
|
||||||
|
const duplicateTrackAddInfo = ref({} as { playlist_name?: string })
|
||||||
|
|
||||||
|
const addToPlaylist = async (playlistId: number, allowDuplicates: boolean) => {
|
||||||
|
lastSelectedPlaylist.value = playlistId
|
||||||
|
|
||||||
|
try {
|
||||||
|
await axios.post(`playlists/${playlistId}/add`, {
|
||||||
|
tracks: [track.value?.id].filter(i => i),
|
||||||
|
allow_duplicates: allowDuplicates
|
||||||
|
})
|
||||||
|
|
||||||
|
logger.info('Successfully added track to playlist')
|
||||||
|
store.state.playlists.showModal = false
|
||||||
|
store.dispatch('playlists/fetchOwn')
|
||||||
|
} catch (error) {
|
||||||
|
if (error as BackendError) {
|
||||||
|
const { backendErrors } = error as BackendError
|
||||||
|
|
||||||
|
if (backendErrors.length === 1 && backendErrors[0].code === 'tracks_already_exist_in_playlist') {
|
||||||
|
duplicateTrackAddInfo.value = backendErrors[0] as unknown as { playlist_name: string }
|
||||||
|
showDuplicateTrackAddConfirmation.value = true
|
||||||
|
} else {
|
||||||
|
errors.value = backendErrors
|
||||||
|
showDuplicateTrackAddConfirmation.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<modal
|
<modal
|
||||||
v-model:show="$store.state.playlists.showModal"
|
v-model:show="$store.state.playlists.showModal"
|
||||||
|
@ -35,15 +114,15 @@
|
||||||
class="ui warning message"
|
class="ui warning message"
|
||||||
>
|
>
|
||||||
<p
|
<p
|
||||||
v-translate="{track: track.title, playlist: duplicateTrackAddInfo.playlist_name}"
|
v-translate="{track: track?.title, playlist: duplicateTrackAddInfo.playlist_name}"
|
||||||
translate-context="Popup/Playlist/Paragraph"
|
translate-context="Popup/Playlist/Paragraph"
|
||||||
:translate-params="{track: track.title, playlist: duplicateTrackAddInfo.playlist_name}"
|
:translate-params="{track: track?.title, playlist: duplicateTrackAddInfo.playlist_name}"
|
||||||
>
|
>
|
||||||
<strong>%{ track }</strong> is already in <strong>%{ playlist }</strong>.
|
<strong>%{ track }</strong> is already in <strong>%{ playlist }</strong>.
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
class="ui small basic cancel button"
|
class="ui small basic cancel button"
|
||||||
@click="duplicateTrackAddConfirm(false)"
|
@click="showDuplicateTrackAddConfirmation = false"
|
||||||
>
|
>
|
||||||
<translate translate-context="*/*/Button.Label/Verb">
|
<translate translate-context="*/*/Button.Label/Verb">
|
||||||
Cancel
|
Cancel
|
||||||
|
@ -189,98 +268,3 @@
|
||||||
</div>
|
</div>
|
||||||
</modal>
|
</modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
|
||||||
import { filter, sortBy, flow } from 'lodash-es'
|
|
||||||
|
|
||||||
import axios from 'axios'
|
|
||||||
import { mapState } from 'vuex'
|
|
||||||
|
|
||||||
import Modal from '~/components/semantic/Modal.vue'
|
|
||||||
import PlaylistForm from '~/components/playlists/Form.vue'
|
|
||||||
import { onBeforeRouteLeave, onBeforeRouteUpdate } from 'vue-router'
|
|
||||||
import useLogger from '~/composables/useLogger'
|
|
||||||
|
|
||||||
const logger = useLogger()
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
Modal,
|
|
||||||
PlaylistForm
|
|
||||||
},
|
|
||||||
setup () {
|
|
||||||
const guard = () => {
|
|
||||||
this.$store.commit('playlists/showModal', false)
|
|
||||||
this.showDuplicateTrackAddConfirmation = false
|
|
||||||
}
|
|
||||||
|
|
||||||
onBeforeRouteUpdate(guard)
|
|
||||||
onBeforeRouteLeave(guard)
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
formKey: String(new Date()),
|
|
||||||
errors: [],
|
|
||||||
playlistNameFilter: '',
|
|
||||||
duplicateTrackAddInfo: {},
|
|
||||||
showDuplicateTrackAddConfirmation: false,
|
|
||||||
lastSelectedPlaylist: -1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
...mapState({
|
|
||||||
playlists: state => state.playlists.playlists,
|
|
||||||
track: state => state.playlists.modalTrack
|
|
||||||
}),
|
|
||||||
labels () {
|
|
||||||
return {
|
|
||||||
addToPlaylist: this.$pgettext('Popup/Playlist/Table.Button.Tooltip/Verb', 'Add to this playlist'),
|
|
||||||
filterPlaylistField: this.$pgettext('Popup/Playlist/Form/Placeholder', 'Enter playlist name')
|
|
||||||
}
|
|
||||||
},
|
|
||||||
sortedPlaylists () {
|
|
||||||
const regexp = new RegExp(this.playlistNameFilter, 'i')
|
|
||||||
const p = flow(
|
|
||||||
filter((e) => e.name.match(regexp) !== null),
|
|
||||||
sortBy((e) => { return e.modification_date })
|
|
||||||
)(this.playlists)
|
|
||||||
p.reverse()
|
|
||||||
return p
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
'$store.state.playlists.showModal' () {
|
|
||||||
this.formKey = String(new Date())
|
|
||||||
this.showDuplicateTrackAddConfirmation = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
addToPlaylist (playlistId, allowDuplicate) {
|
|
||||||
const self = this
|
|
||||||
const payload = {
|
|
||||||
tracks: [this.track.id],
|
|
||||||
allow_duplicates: allowDuplicate
|
|
||||||
}
|
|
||||||
|
|
||||||
self.lastSelectedPlaylist = playlistId
|
|
||||||
|
|
||||||
return axios.post(`playlists/${playlistId}/add`, payload).then(response => {
|
|
||||||
logger.info('Successfully added track to playlist')
|
|
||||||
self.$store.state.playlists.showModal = false
|
|
||||||
self.$store.dispatch('playlists/fetchOwn')
|
|
||||||
}, error => {
|
|
||||||
if (error.backendErrors.length === 1 && error.backendErrors[0].code === 'tracks_already_exist_in_playlist') {
|
|
||||||
self.duplicateTrackAddInfo = error.backendErrors[0]
|
|
||||||
self.showDuplicateTrackAddConfirmation = true
|
|
||||||
} else {
|
|
||||||
self.errors = error.backendErrors
|
|
||||||
self.showDuplicateTrackAddConfirmation = false
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
duplicateTrackAddConfirm (v) {
|
|
||||||
this.showDuplicateTrackAddConfirmation = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { Module } from 'vuex'
|
import { Module } from 'vuex'
|
||||||
import { RootState } from '~/store/index'
|
import { RootState } from '~/store/index'
|
||||||
|
import { Playlist, Track } from '~/types'
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
playlists: any[]
|
playlists: Playlist[]
|
||||||
showModal: boolean
|
showModal: boolean
|
||||||
modalTrack: null
|
modalTrack: null | Track
|
||||||
}
|
}
|
||||||
|
|
||||||
const store: Module<State, RootState> = {
|
const store: Module<State, RootState> = {
|
||||||
|
|
|
@ -87,13 +87,19 @@ export interface License {
|
||||||
url: string
|
url: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface Playlist {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
modification_date: Date // TODO (wvffle): Find correct type
|
||||||
|
}
|
||||||
|
|
||||||
// API stuff
|
// API stuff
|
||||||
export interface APIErrorResponse {
|
export interface APIErrorResponse {
|
||||||
[key: string]: APIErrorResponse | string[]
|
[key: string]: APIErrorResponse | string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BackendError extends AxiosError {
|
export interface BackendError {
|
||||||
backendErrors: string[]
|
backendErrors: AxiosError[]
|
||||||
rawPayload?: object
|
rawPayload?: object
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue