feat: use composables for Router

This commit is contained in:
Phan An 2022-11-18 19:44:20 +01:00
parent 246fff58d3
commit bc5081cd0f
No known key found for this signature in database
GPG key ID: A81E4477F0BB6FDC
37 changed files with 179 additions and 205 deletions

View file

@ -26,11 +26,11 @@
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent, nextTick, onMounted, provide, ref, watch } from 'vue' import { defineAsyncComponent, nextTick, onMounted, provide, ref, watch } from 'vue'
import { hideOverlay, requireInjection, showOverlay } from '@/utils' import { hideOverlay, showOverlay } from '@/utils'
import { commonStore, preferenceStore as preferences, queueStore } from '@/stores' import { commonStore, preferenceStore as preferences, queueStore } from '@/stores'
import { authService, socketListener, socketService, uploadService } from '@/services' import { authService, socketListener, socketService, uploadService } from '@/services'
import { CurrentSongKey, DialogBoxKey, MessageToasterKey, RouterKey } from '@/symbols' import { CurrentSongKey, DialogBoxKey, MessageToasterKey } from '@/symbols'
import { useNetworkStatus } from '@/composables' import { useNetworkStatus, useRouter } from '@/composables'
import DialogBox from '@/components/ui/DialogBox.vue' import DialogBox from '@/components/ui/DialogBox.vue'
import MessageToaster from '@/components/ui/MessageToaster.vue' import MessageToaster from '@/components/ui/MessageToaster.vue'
@ -62,6 +62,7 @@ const currentSong = ref<Song | null>(null)
const authenticated = ref(false) const authenticated = ref(false)
const showDropZone = ref(false) const showDropZone = ref(false)
const { isCurrentScreen } = useRouter()
const { offline } = useNetworkStatus() const { offline } = useNetworkStatus()
/** /**
@ -115,10 +116,8 @@ const init = async () => {
} }
} }
const router = requireInjection(RouterKey)
const onDragOver = (e: DragEvent) => { const onDragOver = (e: DragEvent) => {
showDropZone.value = Boolean(e.dataTransfer?.types.includes('Files')) && router.$currentRoute.value.screen !== 'Upload' showDropZone.value = Boolean(e.dataTransfer?.types.includes('Files')) && !isCurrentScreen('Upload')
} }
watch(() => queueStore.current, song => song && (currentSong.value = song)) watch(() => queueStore.current, song => song && (currentSong.value = song))

View file

@ -42,16 +42,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, toRef, toRefs } from 'vue' import { computed, toRef, toRefs } from 'vue'
import { eventBus, requireInjection, secondsToHumanReadable } from '@/utils' import { eventBus } from '@/utils'
import { albumStore, artistStore, commonStore, songStore } from '@/stores' import { albumStore, artistStore, commonStore, songStore } from '@/stores'
import { downloadService, playbackService } from '@/services' import { downloadService, playbackService } from '@/services'
import { useDraggable } from '@/composables' import { useDraggable, useRouter } from '@/composables'
import { RouterKey } from '@/symbols'
import ArtistAlbumCard from '@/components/ui/ArtistAlbumCard.vue' import ArtistAlbumCard from '@/components/ui/ArtistAlbumCard.vue'
const router = requireInjection(RouterKey) const { go } = useRouter()
const { startDragging } = useDraggable('album') const { startDragging } = useDraggable('album')
const props = withDefaults(defineProps<{ album: Album, layout?: ArtistAlbumCardLayout }>(), { layout: 'full' }) const props = withDefaults(defineProps<{ album: Album, layout?: ArtistAlbumCardLayout }>(), { layout: 'full' })
@ -59,13 +57,12 @@ const { album, layout } = toRefs(props)
const allowDownload = toRef(commonStore.state, 'allow_download') const allowDownload = toRef(commonStore.state, 'allow_download')
const duration = computed(() => secondsToHumanReadable(album.value.length))
const isStandardArtist = computed(() => artistStore.isStandard(album.value.artist_id)) const isStandardArtist = computed(() => artistStore.isStandard(album.value.artist_id))
const showing = computed(() => !albumStore.isUnknown(album.value)) const showing = computed(() => !albumStore.isUnknown(album.value))
const shuffle = async () => { const shuffle = async () => {
playbackService.queueAndPlay(await songStore.fetchForAlbum(album.value), true /* shuffled */) playbackService.queueAndPlay(await songStore.fetchForAlbum(album.value), true /* shuffled */)
router.go('queue') go('queue')
} }
const download = () => downloadService.fromAlbum(album.value) const download = () => downloadService.fromAlbum(album.value)

View file

@ -18,12 +18,11 @@
import { computed, ref, toRef } from 'vue' import { computed, ref, toRef } from 'vue'
import { albumStore, artistStore, commonStore, songStore } from '@/stores' import { albumStore, artistStore, commonStore, songStore } from '@/stores'
import { downloadService, playbackService } from '@/services' import { downloadService, playbackService } from '@/services'
import { useContextMenu } from '@/composables' import { useContextMenu, useRouter } from '@/composables'
import { eventBus, requireInjection } from '@/utils' import { eventBus } from '@/utils'
import { RouterKey } from '@/symbols'
const { go } = useRouter()
const { context, base, ContextMenuBase, open, trigger } = useContextMenu() const { context, base, ContextMenuBase, open, trigger } = useContextMenu()
const router = requireInjection(RouterKey)
const album = ref<Album>() const album = ref<Album>()
const allowDownload = toRef(commonStore.state, 'allow_download') const allowDownload = toRef(commonStore.state, 'allow_download')
@ -36,12 +35,12 @@ const isStandardArtist = computed(() => {
const play = () => trigger(async () => { const play = () => trigger(async () => {
playbackService.queueAndPlay(await songStore.fetchForAlbum(album.value!)) playbackService.queueAndPlay(await songStore.fetchForAlbum(album.value!))
router.go('queue') go('queue')
}) })
const shuffle = () => trigger(async () => { const shuffle = () => trigger(async () => {
playbackService.queueAndPlay(await songStore.fetchForAlbum(album.value!), true) playbackService.queueAndPlay(await songStore.fetchForAlbum(album.value!), true)
router.go('queue') go('queue')
}) })
const viewAlbumDetails = () => trigger(() => router.go(`album/${album.value!.id}`)) const viewAlbumDetails = () => trigger(() => router.go(`album/${album.value!.id}`))

View file

@ -34,26 +34,23 @@
<script lang="ts" setup> <script lang="ts" setup>
import { faCirclePlay } from '@fortawesome/free-solid-svg-icons' import { faCirclePlay } from '@fortawesome/free-solid-svg-icons'
import { computed, defineAsyncComponent, ref, toRefs, watch } from 'vue' import { computed, defineAsyncComponent, ref, toRefs, watch } from 'vue'
import { useThirdPartyServices } from '@/composables'
import { songStore } from '@/stores' import { songStore } from '@/stores'
import { mediaInfoService, playbackService } from '@/services' import { mediaInfoService, playbackService } from '@/services'
import { RouterKey } from '@/symbols' import { useRouter, useThirdPartyServices } from '@/composables'
import AlbumThumbnail from '@/components/ui/AlbumArtistThumbnail.vue' import AlbumThumbnail from '@/components/ui/AlbumArtistThumbnail.vue'
import { requireInjection } from '@/utils'
const router = requireInjection(RouterKey)
const TrackList = defineAsyncComponent(() => import('@/components/album/AlbumTrackList.vue')) const TrackList = defineAsyncComponent(() => import('@/components/album/AlbumTrackList.vue'))
const props = withDefaults(defineProps<{ album: Album, mode?: MediaInfoDisplayMode }>(), { mode: 'aside' }) const props = withDefaults(defineProps<{ album: Album, mode?: MediaInfoDisplayMode }>(), { mode: 'aside' })
const { album, mode } = toRefs(props) const { album, mode } = toRefs(props)
const { go } = useRouter()
const { useLastfm } = useThirdPartyServices()
const info = ref<AlbumInfo | null>(null) const info = ref<AlbumInfo | null>(null)
const showingFullWiki = ref(false) const showingFullWiki = ref(false)
const { useLastfm } = useThirdPartyServices()
watch(album, async () => { watch(album, async () => {
showingFullWiki.value = false showingFullWiki.value = false
info.value = null info.value = null
@ -65,7 +62,7 @@ const showFull = computed(() => !showSummary.value)
const play = async () => { const play = async () => {
playbackService.queueAndPlay(await songStore.fetchForAlbum(album.value)) playbackService.queueAndPlay(await songStore.fetchForAlbum(album.value))
router.go('queue') go('queue')
} }
</script> </script>

View file

@ -39,16 +39,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, toRef, toRefs } from 'vue' import { computed, toRef, toRefs } from 'vue'
import { eventBus, requireInjection } from '@/utils' import { eventBus } from '@/utils'
import { artistStore, commonStore, songStore } from '@/stores' import { artistStore, commonStore, songStore } from '@/stores'
import { downloadService, playbackService } from '@/services' import { downloadService, playbackService } from '@/services'
import { useDraggable } from '@/composables' import { useDraggable, useRouter } from '@/composables'
import { RouterKey } from '@/symbols'
import ArtistAlbumCard from '@/components/ui/ArtistAlbumCard.vue' import ArtistAlbumCard from '@/components/ui/ArtistAlbumCard.vue'
const router = requireInjection(RouterKey) const { go } = useRouter()
const { startDragging } = useDraggable('artist') const { startDragging } = useDraggable('artist')
const props = withDefaults(defineProps<{ artist: Artist, layout?: ArtistAlbumCardLayout }>(), { layout: 'full' }) const props = withDefaults(defineProps<{ artist: Artist, layout?: ArtistAlbumCardLayout }>(), { layout: 'full' })
@ -60,7 +58,7 @@ const showing = computed(() => artistStore.isStandard(artist.value))
const shuffle = async () => { const shuffle = async () => {
playbackService.queueAndPlay(await songStore.fetchForArtist(artist.value), true /* shuffled */) playbackService.queueAndPlay(await songStore.fetchForArtist(artist.value), true /* shuffled */)
router.go('queue') go('queue')
} }
const download = () => downloadService.fromArtist(artist.value) const download = () => downloadService.fromArtist(artist.value)

View file

@ -19,12 +19,11 @@
import { computed, ref, toRef } from 'vue' import { computed, ref, toRef } from 'vue'
import { artistStore, commonStore, songStore } from '@/stores' import { artistStore, commonStore, songStore } from '@/stores'
import { downloadService, playbackService } from '@/services' import { downloadService, playbackService } from '@/services'
import { useContextMenu } from '@/composables' import { useContextMenu, useRouter } from '@/composables'
import { RouterKey } from '@/symbols' import { eventBus } from '@/utils'
import { eventBus, requireInjection } from '@/utils'
const { go } = useRouter()
const { context, base, ContextMenuBase, open, trigger } = useContextMenu() const { context, base, ContextMenuBase, open, trigger } = useContextMenu()
const router = requireInjection(RouterKey)
const artist = ref<Artist>() const artist = ref<Artist>()
const allowDownload = toRef(commonStore.state, 'allow_download') const allowDownload = toRef(commonStore.state, 'allow_download')
@ -36,15 +35,15 @@ const isStandardArtist = computed(() =>
const play = () => trigger(async () => { const play = () => trigger(async () => {
playbackService.queueAndPlay(await songStore.fetchForArtist(artist.value!)) playbackService.queueAndPlay(await songStore.fetchForArtist(artist.value!))
router.go('queue') go('queue')
}) })
const shuffle = () => trigger(async () => { const shuffle = () => trigger(async () => {
playbackService.queueAndPlay(await songStore.fetchForArtist(artist.value!), true) playbackService.queueAndPlay(await songStore.fetchForArtist(artist.value!), true)
router.go('queue') go('queue')
}) })
const viewArtistDetails = () => trigger(() => router.go(`artist/${artist.value!.id}`)) const viewArtistDetails = () => trigger(() => go(`artist/${artist.value!.id}`))
const download = () => trigger(() => downloadService.fromArtist(artist.value!)) const download = () => trigger(() => downloadService.fromArtist(artist.value!))
eventBus.on('ARTIST_CONTEXT_MENU_REQUESTED', async (e, _artist) => { eventBus.on('ARTIST_CONTEXT_MENU_REQUESTED', async (e, _artist) => {

View file

@ -33,18 +33,15 @@
import { faCirclePlay } from '@fortawesome/free-solid-svg-icons' import { faCirclePlay } from '@fortawesome/free-solid-svg-icons'
import { computed, ref, toRefs, watch } from 'vue' import { computed, ref, toRefs, watch } from 'vue'
import { mediaInfoService, playbackService } from '@/services' import { mediaInfoService, playbackService } from '@/services'
import { useThirdPartyServices } from '@/composables' import { useRouter, useThirdPartyServices } from '@/composables'
import { songStore } from '@/stores' import { songStore } from '@/stores'
import { RouterKey } from '@/symbols'
import { requireInjection } from '@/utils'
import ArtistThumbnail from '@/components/ui/AlbumArtistThumbnail.vue' import ArtistThumbnail from '@/components/ui/AlbumArtistThumbnail.vue'
const router = requireInjection(RouterKey)
const props = withDefaults(defineProps<{ artist: Artist, mode?: MediaInfoDisplayMode }>(), { mode: 'aside' }) const props = withDefaults(defineProps<{ artist: Artist, mode?: MediaInfoDisplayMode }>(), { mode: 'aside' })
const { artist, mode } = toRefs(props) const { artist, mode } = toRefs(props)
const { go } = useRouter()
const { useLastfm } = useThirdPartyServices() const { useLastfm } = useThirdPartyServices()
const info = ref<ArtistInfo | null>(null) const info = ref<ArtistInfo | null>(null)
@ -61,7 +58,7 @@ const showFull = computed(() => !showSummary.value)
const play = async () => { const play = async () => {
playbackService.queueAndPlay(await songStore.fetchForArtist(artist.value)) playbackService.queueAndPlay(await songStore.fetchForArtist(artist.value))
router.go('queue') go('queue')
} }
</script> </script>

View file

@ -36,8 +36,8 @@
import { defineAsyncComponent, onMounted, ref, toRef } from 'vue' import { defineAsyncComponent, onMounted, ref, toRef } from 'vue'
import { requireInjection } from '@/utils' import { requireInjection } from '@/utils'
import { preferenceStore } from '@/stores' import { preferenceStore } from '@/stores'
import { useThirdPartyServices } from '@/composables' import { useRouter, useThirdPartyServices } from '@/composables'
import { CurrentSongKey, RouterKey } from '@/symbols' import { CurrentSongKey } from '@/symbols'
import HomeScreen from '@/components/screens/HomeScreen.vue' import HomeScreen from '@/components/screens/HomeScreen.vue'
import QueueScreen from '@/components/screens/QueueScreen.vue' import QueueScreen from '@/components/screens/QueueScreen.vue'
@ -64,16 +64,16 @@ const NotFoundScreen = defineAsyncComponent(() => import('@/components/screens/N
const VisualizerScreen = defineAsyncComponent(() => import('@/components/screens/VisualizerScreen.vue')) const VisualizerScreen = defineAsyncComponent(() => import('@/components/screens/VisualizerScreen.vue'))
const { useYouTube } = useThirdPartyServices() const { useYouTube } = useThirdPartyServices()
const { resolveRoute, onRouteChanged } = useRouter()
const router = requireInjection(RouterKey)
const currentSong = requireInjection(CurrentSongKey, ref(null)) const currentSong = requireInjection(CurrentSongKey, ref(null))
const showAlbumArtOverlay = toRef(preferenceStore.state, 'showAlbumArtOverlay') const showAlbumArtOverlay = toRef(preferenceStore.state, 'showAlbumArtOverlay')
const screen = ref<ScreenName>('Home') const screen = ref<ScreenName>('Home')
router.onRouteChanged(route => (screen.value = route.screen)) onRouteChanged(route => (screen.value = route.screen))
onMounted(() => router.resolve()) onMounted(() => resolveRoute())
</script> </script>
<style lang="scss"> <style lang="scss">

View file

@ -98,10 +98,9 @@ import {
} from '@fortawesome/free-solid-svg-icons' } from '@fortawesome/free-solid-svg-icons'
import { faYoutube } from '@fortawesome/free-brands-svg-icons' import { faYoutube } from '@fortawesome/free-brands-svg-icons'
import { ref } from 'vue' import { ref } from 'vue'
import { eventBus, requireInjection } from '@/utils' import { eventBus } from '@/utils'
import { queueStore } from '@/stores' import { queueStore } from '@/stores'
import { useAuthorization, useDroppable, useThirdPartyServices } from '@/composables' import { useAuthorization, useDroppable, useRouter, useThirdPartyServices } from '@/composables'
import { RouterKey } from '@/symbols'
import PlaylistList from '@/components/playlist/PlaylistSidebarList.vue' import PlaylistList from '@/components/playlist/PlaylistSidebarList.vue'
import SearchForm from '@/components/ui/SearchForm.vue' import SearchForm from '@/components/ui/SearchForm.vue'
@ -110,6 +109,7 @@ const mobileShowing = ref(false)
const activeScreen = ref<ScreenName>() const activeScreen = ref<ScreenName>()
const droppableToQueue = ref(false) const droppableToQueue = ref(false)
const { onRouteChanged } = useRouter()
const { acceptsDrop, resolveDroppedSongs } = useDroppable(['songs', 'album', 'artist', 'playlist']) const { acceptsDrop, resolveDroppedSongs } = useDroppable(['songs', 'album', 'artist', 'playlist'])
const { useYouTube } = useThirdPartyServices() const { useYouTube } = useThirdPartyServices()
const { isAdmin } = useAuthorization() const { isAdmin } = useAuthorization()
@ -138,9 +138,7 @@ const onQueueDrop = async (event: DragEvent) => {
const closeIfMobile = () => (mobileShowing.value = false) const closeIfMobile = () => (mobileShowing.value = false)
const router = requireInjection(RouterKey) onRouteChanged(route => {
router.onRouteChanged(route => {
mobileShowing.value = false mobileShowing.value = false
activeScreen.value = route.screen activeScreen.value = route.screen
}) })

View file

@ -30,16 +30,15 @@
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue' import { ref } from 'vue'
import { playlistStore } from '@/stores' import { playlistStore } from '@/stores'
import { logger, requireInjection } from '@/utils' import { logger } from '@/utils'
import { useDialogBox, useMessageToaster } from '@/composables' import { useDialogBox, useMessageToaster, useRouter } from '@/composables'
import { RouterKey } from '@/symbols'
import SoundBars from '@/components/ui/SoundBars.vue' import SoundBars from '@/components/ui/SoundBars.vue'
import Btn from '@/components/ui/Btn.vue' import Btn from '@/components/ui/Btn.vue'
const { toastSuccess } = useMessageToaster() const { toastSuccess } = useMessageToaster()
const { showConfirmDialog, showErrorDialog } = useDialogBox() const { showConfirmDialog, showErrorDialog } = useDialogBox()
const router = requireInjection(RouterKey) const { go } = useRouter()
const loading = ref(false) const loading = ref(false)
const name = ref('') const name = ref('')
@ -54,7 +53,7 @@ const submit = async () => {
const playlist = await playlistStore.store(name.value) const playlist = await playlistStore.store(name.value)
close() close()
toastSuccess(`Playlist "${playlist.name}" created.`) toastSuccess(`Playlist "${playlist.name}" created.`)
router.go(`playlist/${playlist.id}`) go(`playlist/${playlist.id}`)
} catch (error) { } catch (error) {
showErrorDialog('Something went wrong. Please try again.', 'Error') showErrorDialog('Something went wrong. Please try again.', 'Error')
logger.error(error) logger.error(error)

View file

@ -14,15 +14,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { useContextMenu } from '@/composables' import { eventBus } from '@/utils'
import { eventBus, requireInjection } from '@/utils'
import { playlistStore, songStore } from '@/stores' import { playlistStore, songStore } from '@/stores'
import { playbackService } from '@/services' import { playbackService } from '@/services'
import { RouterKey } from '@/symbols' import { useContextMenu, useRouter } from '@/composables'
const { go } = useRouter()
const { context, base, ContextMenuBase, open, trigger } = useContextMenu() const { context, base, ContextMenuBase, open, trigger } = useContextMenu()
const router = requireInjection(RouterKey)
const folder = ref<PlaylistFolder>() const folder = ref<PlaylistFolder>()
const playlistsInFolder = computed(() => folder.value ? playlistStore.byFolder(folder.value) : []) const playlistsInFolder = computed(() => folder.value ? playlistStore.byFolder(folder.value) : [])
@ -30,12 +29,12 @@ const playable = computed(() => playlistsInFolder.value.length > 0)
const play = () => trigger(async () => { const play = () => trigger(async () => {
playbackService.queueAndPlay(await songStore.fetchForPlaylistFolder(folder.value!)) playbackService.queueAndPlay(await songStore.fetchForPlaylistFolder(folder.value!))
router.go('queue') go('queue')
}) })
const shuffle = () => trigger(async () => { const shuffle = () => trigger(async () => {
playbackService.queueAndPlay(await songStore.fetchForPlaylistFolder(folder.value!), true) playbackService.queueAndPlay(await songStore.fetchForPlaylistFolder(folder.value!), true)
router.go('queue') go('queue')
}) })
const rename = () => trigger(() => eventBus.emit('MODAL_SHOW_EDIT_PLAYLIST_FOLDER_FORM', folder.value)) const rename = () => trigger(() => eventBus.emit('MODAL_SHOW_EDIT_PLAYLIST_FOLDER_FORM', folder.value))

View file

@ -30,15 +30,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { faBoltLightning, faClockRotateLeft, faFile, faHeart, faMusic } from '@fortawesome/free-solid-svg-icons' import { faBoltLightning, faClockRotateLeft, faFile, faHeart, faMusic } from '@fortawesome/free-solid-svg-icons'
import { computed, ref, toRefs } from 'vue' import { computed, ref, toRefs } from 'vue'
import { eventBus, requireInjection } from '@/utils' import { eventBus } from '@/utils'
import { favoriteStore } from '@/stores' import { favoriteStore } from '@/stores'
import { RouterKey } from '@/symbols' import { useDraggable, useDroppable, usePlaylistManagement, useRouter } from '@/composables'
import { useDraggable, useDroppable, usePlaylistManagement } from '@/composables'
const { onRouteChanged } = useRouter()
const { startDragging } = useDraggable('playlist') const { startDragging } = useDraggable('playlist')
const { acceptsDrop, resolveDroppedSongs } = useDroppable(['songs', 'album', 'artist']) const { acceptsDrop, resolveDroppedSongs } = useDroppable(['songs', 'album', 'artist'])
const router = requireInjection(RouterKey)
const droppable = ref(false) const droppable = ref(false)
const { addSongsToPlaylist } = usePlaylistManagement() const { addSongsToPlaylist } = usePlaylistManagement()
@ -108,7 +107,7 @@ const onDrop = async (event: DragEvent) => {
return false return false
} }
router.onRouteChanged(route => { onRouteChanged(route => {
switch (route.screen) { switch (route.screen) {
case 'Favorites': case 'Favorites':
active.value = isFavoriteList(list.value) active.value = isFavoriteList(list.value)

View file

@ -40,9 +40,8 @@
import { faPlus } from '@fortawesome/free-solid-svg-icons' import { faPlus } from '@fortawesome/free-solid-svg-icons'
import { ref } from 'vue' import { ref } from 'vue'
import { playlistStore } from '@/stores' import { playlistStore } from '@/stores'
import { logger, requireInjection } from '@/utils' import { logger } from '@/utils'
import { useDialogBox, useMessageToaster, useSmartPlaylistForm } from '@/composables' import { useDialogBox, useMessageToaster, useRouter, useSmartPlaylistForm } from '@/composables'
import { RouterKey } from '@/symbols'
const { const {
Btn, Btn,
@ -57,7 +56,7 @@ const {
const { toastSuccess } = useMessageToaster() const { toastSuccess } = useMessageToaster()
const { showConfirmDialog, showErrorDialog } = useDialogBox() const { showConfirmDialog, showErrorDialog } = useDialogBox()
const router = requireInjection(RouterKey) const { go } = useRouter()
const name = ref('') const name = ref('')
@ -80,7 +79,7 @@ const submit = async () => {
const playlist = await playlistStore.store(name.value, [], collectedRuleGroups.value) const playlist = await playlistStore.store(name.value, [], collectedRuleGroups.value)
close() close()
toastSuccess(`Playlist "${playlist.name}" created.`) toastSuccess(`Playlist "${playlist.name}" created.`)
router.go(`playlist/${playlist.id}`) go(`playlist/${playlist.id}`)
} catch (error) { } catch (error) {
showErrorDialog('Something went wrong. Please try again.') showErrorDialog('Something went wrong. Please try again.')
logger.error(error) logger.error(error)

View file

@ -88,11 +88,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineAsyncComponent, onMounted, ref, toRef, watch } from 'vue' import { computed, defineAsyncComponent, onMounted, ref, toRef, watch } from 'vue'
import { eventBus, logger, pluralize, requireInjection } from '@/utils' import { eventBus, logger, pluralize } from '@/utils'
import { albumStore, artistStore, commonStore, songStore } from '@/stores' import { albumStore, artistStore, commonStore, songStore } from '@/stores'
import { downloadService } from '@/services' import { downloadService } from '@/services'
import { useDialogBox, useSongList } from '@/composables' import { useDialogBox, useRouter, useSongList } from '@/composables'
import { RouterKey } from '@/symbols'
import ScreenHeader from '@/components/ui/ScreenHeader.vue' import ScreenHeader from '@/components/ui/ScreenHeader.vue'
import AlbumThumbnail from '@/components/ui/AlbumArtistThumbnail.vue' import AlbumThumbnail from '@/components/ui/AlbumArtistThumbnail.vue'
@ -108,7 +107,7 @@ const AlbumCard = defineAsyncComponent(() => import('@/components/album/AlbumCar
const AlbumCardSkeleton = defineAsyncComponent(() => import('@/components/ui/skeletons/ArtistAlbumCardSkeleton.vue')) const AlbumCardSkeleton = defineAsyncComponent(() => import('@/components/ui/skeletons/ArtistAlbumCardSkeleton.vue'))
const { showErrorDialog } = useDialogBox() const { showErrorDialog } = useDialogBox()
const router = requireInjection(RouterKey) const { getRouteParam, go, onRouteChanged } = useRouter()
const albumId = ref<number>() const albumId = ref<number>()
const album = ref<Album>() const album = ref<Album>()
@ -175,12 +174,12 @@ watch(albumId, async id => {
} }
}) })
onMounted(async () => (albumId.value = parseInt(router.$currentRoute.value.params!.id))) onMounted(async () => (albumId.value = parseInt(getRouteParam('id')!)))
router.onRouteChanged(route => route.screen === 'Album' && (albumId.value = parseInt(route.params!.id))) onRouteChanged(route => route.screen === 'Album' && (albumId.value = parseInt(getRouteParam('id')!)))
// if the current album has been deleted, go back to the list // if the current album has been deleted, go back to the list
eventBus.on('SONGS_UPDATED', () => albumStore.byId(albumId.value) || router.go('albums')) eventBus.on('SONGS_UPDATED', () => albumStore.byId(albumId.value) || go('albums'))
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View file

@ -36,11 +36,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref, toRef } from 'vue' import { computed, ref, toRef } from 'vue'
import { logger, pluralize, requireInjection, secondsToHumanReadable } from '@/utils' import { logger, pluralize, secondsToHumanReadable } from '@/utils'
import { commonStore, queueStore, songStore } from '@/stores' import { commonStore, queueStore, songStore } from '@/stores'
import { playbackService } from '@/services' import { playbackService } from '@/services'
import { useMessageToaster, useScreen, useSongList } from '@/composables' import { useMessageToaster, useRouter, useScreen, useSongList } from '@/composables'
import { RouterKey } from '@/symbols'
import ScreenHeader from '@/components/ui/ScreenHeader.vue' import ScreenHeader from '@/components/ui/ScreenHeader.vue'
import SongListSkeleton from '@/components/ui/skeletons/SongListSkeleton.vue' import SongListSkeleton from '@/components/ui/skeletons/SongListSkeleton.vue'
@ -66,7 +65,7 @@ const {
} = useSongList(toRef(songStore.state, 'songs')) } = useSongList(toRef(songStore.state, 'songs'))
const { toastError } = useMessageToaster() const { toastError } = useMessageToaster()
const router = requireInjection(RouterKey) const { go } = useRouter()
let initialized = false let initialized = false
const loading = ref(false) const loading = ref(false)
@ -108,7 +107,7 @@ const playAll = async (shuffle: boolean) => {
await queueStore.fetchInOrder(sortField, sortOrder) await queueStore.fetchInOrder(sortField, sortOrder)
} }
router.go('queue') go('queue')
await playbackService.playFirstInQueue() await playbackService.playFirstInQueue()
} }

View file

@ -85,11 +85,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, defineAsyncComponent, onMounted, ref, toRef, watch } from 'vue' import { computed, defineAsyncComponent, onMounted, ref, toRef, watch } from 'vue'
import { eventBus, logger, pluralize, requireInjection } from '@/utils' import { eventBus, logger, pluralize } from '@/utils'
import { albumStore, artistStore, commonStore, songStore } from '@/stores' import { albumStore, artistStore, commonStore, songStore } from '@/stores'
import { downloadService } from '@/services' import { downloadService } from '@/services'
import { useDialogBox, useSongList, useThirdPartyServices } from '@/composables' import { useDialogBox, useRouter, useSongList, useThirdPartyServices } from '@/composables'
import { RouterKey } from '@/symbols'
import ScreenHeader from '@/components/ui/ScreenHeader.vue' import ScreenHeader from '@/components/ui/ScreenHeader.vue'
import ArtistThumbnail from '@/components/ui/AlbumArtistThumbnail.vue' import ArtistThumbnail from '@/components/ui/AlbumArtistThumbnail.vue'
@ -105,7 +104,7 @@ type Tab = 'Songs' | 'Albums' | 'Info'
const activeTab = ref<Tab>('Songs') const activeTab = ref<Tab>('Songs')
const { showErrorDialog } = useDialogBox() const { showErrorDialog } = useDialogBox()
const router = requireInjection(RouterKey) const { getRouteParam, go } = useRouter()
const artistId = ref<number>() const artistId = ref<number>()
const artist = ref<Artist>() const artist = ref<Artist>()
@ -165,10 +164,10 @@ watch(artistId, async id => {
const download = () => downloadService.fromArtist(artist.value!) const download = () => downloadService.fromArtist(artist.value!)
onMounted(() => (artistId.value = parseInt(router.$currentRoute.value.params!.id))) onMounted(() => (artistId.value = parseInt(getRouteParam('id')!)))
// if the current artist has been deleted, go back to the list // if the current artist has been deleted, go back to the list
eventBus.on('SONGS_UPDATED', () => artistStore.byId(artist.value!.id) || router.go('artists')) eventBus.on('SONGS_UPDATED', () => artistStore.byId(artist.value!.id) || go('artists'))
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View file

@ -42,11 +42,10 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, ref, watch } from 'vue' import { computed, onMounted, ref, watch } from 'vue'
import { faTags } from '@fortawesome/free-solid-svg-icons' import { faTags } from '@fortawesome/free-solid-svg-icons'
import { eventBus, logger, pluralize, requireInjection, secondsToHumanReadable } from '@/utils' import { eventBus, logger, pluralize, secondsToHumanReadable } from '@/utils'
import { playbackService } from '@/services' import { playbackService } from '@/services'
import { genreStore, songStore } from '@/stores' import { genreStore, songStore } from '@/stores'
import { useDialogBox, useSongList } from '@/composables' import { useDialogBox, useRouter, useSongList } from '@/composables'
import { RouterKey } from '@/symbols'
import ScreenHeader from '@/components/ui/ScreenHeader.vue' import ScreenHeader from '@/components/ui/ScreenHeader.vue'
import ScreenEmptyState from '@/components/ui/ScreenEmptyState.vue' import ScreenEmptyState from '@/components/ui/ScreenEmptyState.vue'
@ -70,7 +69,7 @@ const {
} = useSongList(ref<Song[]>([])) } = useSongList(ref<Song[]>([]))
const { showErrorDialog } = useDialogBox() const { showErrorDialog } = useDialogBox()
const router = requireInjection(RouterKey) const { getRouteParam, go, onRouteChanged } = useRouter()
let sortField: SongListSortField = 'title' let sortField: SongListSortField = 'title'
let sortOrder: SortOrder = 'asc' let sortOrder: SortOrder = 'asc'
@ -125,9 +124,9 @@ const refresh = async () => {
await fetch() await fetch()
} }
const getNameFromRoute = () => router.$currentRoute.value.params?.name || null const getNameFromRoute = () => getRouteParam('name') ?? null
router.onRouteChanged(route => { onRouteChanged(route => {
if (route.screen !== 'Genre') return if (route.screen !== 'Genre') return
name.value = getNameFromRoute() name.value = getNameFromRoute()
}) })
@ -142,7 +141,7 @@ const playAll = async () => {
playbackService.queueAndPlay(await songStore.fetchRandomForGenre(genre.value!, randomSongCount)) playbackService.queueAndPlay(await songStore.fetchRandomForGenre(genre.value!, randomSongCount))
} }
router.go('queue') go('queue')
} }
onMounted(() => (name.value = getNameFromRoute())) onMounted(() => (name.value = getNameFromRoute()))

View file

@ -67,17 +67,16 @@
import { faFile } from '@fortawesome/free-regular-svg-icons' import { faFile } from '@fortawesome/free-regular-svg-icons'
import { differenceBy } from 'lodash' import { differenceBy } from 'lodash'
import { ref, toRef, watch } from 'vue' import { ref, toRef, watch } from 'vue'
import { eventBus, pluralize, requireInjection } from '@/utils' import { eventBus, pluralize } from '@/utils'
import { commonStore, playlistStore, songStore } from '@/stores' import { commonStore, playlistStore, songStore } from '@/stores'
import { downloadService } from '@/services' import { downloadService } from '@/services'
import { usePlaylistManagement, useSongList } from '@/composables' import { usePlaylistManagement, useRouter, useSongList } from '@/composables'
import { RouterKey } from '@/symbols'
import ScreenHeader from '@/components/ui/ScreenHeader.vue' import ScreenHeader from '@/components/ui/ScreenHeader.vue'
import ScreenEmptyState from '@/components/ui/ScreenEmptyState.vue' import ScreenEmptyState from '@/components/ui/ScreenEmptyState.vue'
import SongListSkeleton from '@/components/ui/skeletons/SongListSkeleton.vue' import SongListSkeleton from '@/components/ui/skeletons/SongListSkeleton.vue'
const router = requireInjection(RouterKey) const { onRouteChanged, triggerNotFound } = useRouter()
const playlistId = ref<number>() const playlistId = ref<number>()
const playlist = ref<Playlist>() const playlist = ref<Playlist>()
@ -129,10 +128,10 @@ watch(playlistId, async id => {
if (!id) return if (!id) return
playlist.value = playlistStore.byId(id) playlist.value = playlistStore.byId(id)
playlist.value ? await fetchSongs() : await router.triggerNotFound() playlist.value ? await fetchSongs() : await triggerNotFound()
}) })
router.onRouteChanged(route => route.screen === 'Playlist' && (playlistId.value = parseInt(route.params!.id))) onRouteChanged(route => route.screen === 'Playlist' && (playlistId.value = parseInt(route.params!.id)))
eventBus.on('PLAYLIST_UPDATED', async updated => updated.id === playlistId.value && await fetchSongs()) eventBus.on('PLAYLIST_UPDATED', async updated => updated.id === playlistId.value && await fetchSongs())
.on('PLAYLIST_SONGS_REMOVED', async (playlist, removed) => { .on('PLAYLIST_SONGS_REMOVED', async (playlist, removed) => {

View file

@ -51,17 +51,16 @@
<script lang="ts" setup> <script lang="ts" setup>
import { faCoffee } from '@fortawesome/free-solid-svg-icons' import { faCoffee } from '@fortawesome/free-solid-svg-icons'
import { computed, ref, toRef } from 'vue' import { computed, ref, toRef } from 'vue'
import { eventBus, logger, pluralize, requireInjection } from '@/utils' import { eventBus, logger, pluralize } from '@/utils'
import { commonStore, queueStore, songStore } from '@/stores' import { commonStore, queueStore, songStore } from '@/stores'
import { playbackService } from '@/services' import { playbackService } from '@/services'
import { useDialogBox, useSongList } from '@/composables' import { useDialogBox, useRouter, useSongList } from '@/composables'
import { RouterKey } from '@/symbols'
import ScreenHeader from '@/components/ui/ScreenHeader.vue' import ScreenHeader from '@/components/ui/ScreenHeader.vue'
import ScreenEmptyState from '@/components/ui/ScreenEmptyState.vue' import ScreenEmptyState from '@/components/ui/ScreenEmptyState.vue'
import SongListSkeleton from '@/components/ui/skeletons/SongListSkeleton.vue' import SongListSkeleton from '@/components/ui/skeletons/SongListSkeleton.vue'
const router = requireInjection(RouterKey) const { go } = useRouter()
const { showErrorDialog } = useDialogBox() const { showErrorDialog } = useDialogBox()
const controlConfig: Partial<SongListControlsConfig> = { clearQueue: true } const controlConfig: Partial<SongListControlsConfig> = { clearQueue: true }
@ -88,7 +87,7 @@ const libraryNotEmpty = computed(() => commonStore.state.song_count > 0)
const playAll = async (shuffle = true) => { const playAll = async (shuffle = true) => {
playbackService.queueAndPlay(songs.value, shuffle) playbackService.queueAndPlay(songs.value, shuffle)
router.go('queue') go('queue')
} }
const shuffleSome = async () => { const shuffleSome = async () => {

View file

@ -31,16 +31,15 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref } from 'vue' import { computed, ref } from 'vue'
import { settingStore } from '@/stores' import { settingStore } from '@/stores'
import { forceReloadWindow, hideOverlay, parseValidationError, requireInjection, showOverlay } from '@/utils' import { forceReloadWindow, hideOverlay, parseValidationError, showOverlay } from '@/utils'
import { useDialogBox, useMessageToaster } from '@/composables' import { useDialogBox, useMessageToaster, useRouter } from '@/composables'
import { RouterKey } from '@/symbols'
import ScreenHeader from '@/components/ui/ScreenHeader.vue' import ScreenHeader from '@/components/ui/ScreenHeader.vue'
import Btn from '@/components/ui/Btn.vue' import Btn from '@/components/ui/Btn.vue'
const { toastSuccess } = useMessageToaster() const { toastSuccess } = useMessageToaster()
const { showConfirmDialog, showErrorDialog } = useDialogBox() const { showConfirmDialog, showErrorDialog } = useDialogBox()
const router = requireInjection(RouterKey) const { go } = useRouter()
const mediaPath = ref(settingStore.state.media_path) const mediaPath = ref(settingStore.state.media_path)
const originalMediaPath = mediaPath.value const originalMediaPath = mediaPath.value
@ -61,7 +60,7 @@ const save = async () => {
await settingStore.update({ media_path: mediaPath.value }) await settingStore.update({ media_path: mediaPath.value })
toastSuccess('Settings saved.') toastSuccess('Settings saved.')
// Make sure we're back to home first. // Make sure we're back to home first.
router.go('home') go('home')
forceReloadWindow() forceReloadWindow()
} catch (err: any) { } catch (err: any) {
const msg = err.response.status === 422 ? parseValidationError(err.response.data)[0] : 'Unknown error.' const msg = err.response.status === 422 ? parseValidationError(err.response.data)[0] : 'Unknown error.'

View file

@ -33,19 +33,18 @@
<script lang="ts" setup> <script lang="ts" setup>
import { toRef, toRefs } from 'vue' import { toRef, toRefs } from 'vue'
import { overviewStore } from '@/stores' import { overviewStore } from '@/stores'
import { RouterKey } from '@/symbols' import { useRouter } from '@/composables'
import { requireInjection } from '@/utils'
import Btn from '@/components/ui/Btn.vue' import Btn from '@/components/ui/Btn.vue'
import SongCard from '@/components/song/SongCard.vue' import SongCard from '@/components/song/SongCard.vue'
import SongCardSkeleton from '@/components/ui/skeletons/SongCardSkeleton.vue' import SongCardSkeleton from '@/components/ui/skeletons/SongCardSkeleton.vue'
const router = requireInjection(RouterKey) const { go } = useRouter()
const props = withDefaults(defineProps<{ loading?: boolean }>(), { loading: false }) const props = withDefaults(defineProps<{ loading?: boolean }>(), { loading: false })
const { loading } = toRefs(props) const { loading } = toRefs(props)
const songs = toRef(overviewStore.state, 'recentlyPlayed') const songs = toRef(overviewStore.state, 'recentlyPlayed')
const goToRecentlyPlayedScreen = () => router.go('recently-played') const goToRecentlyPlayedScreen = () => go('recently-played')
</script> </script>

View file

@ -86,9 +86,9 @@
import { faSearch } from '@fortawesome/free-solid-svg-icons' import { faSearch } from '@fortawesome/free-solid-svg-icons'
import { intersectionBy } from 'lodash' import { intersectionBy } from 'lodash'
import { ref, toRef } from 'vue' import { ref, toRef } from 'vue'
import { eventBus, requireInjection } from '@/utils' import { eventBus } from '@/utils'
import { searchStore } from '@/stores' import { searchStore } from '@/stores'
import { RouterKey } from '@/symbols' import { useRouter } from '@/composables'
import ScreenHeader from '@/components/ui/ScreenHeader.vue' import ScreenHeader from '@/components/ui/ScreenHeader.vue'
import ScreenEmptyState from '@/components/ui/ScreenEmptyState.vue' import ScreenEmptyState from '@/components/ui/ScreenEmptyState.vue'
@ -99,13 +99,13 @@ import SongCard from '@/components/song/SongCard.vue'
import SongCardSkeleton from '@/components/ui/skeletons/SongCardSkeleton.vue' import SongCardSkeleton from '@/components/ui/skeletons/SongCardSkeleton.vue'
import ArtistAlbumCardSkeleton from '@/components/ui/skeletons/ArtistAlbumCardSkeleton.vue' import ArtistAlbumCardSkeleton from '@/components/ui/skeletons/ArtistAlbumCardSkeleton.vue'
const router = requireInjection(RouterKey) const { go } = useRouter()
const excerpt = toRef(searchStore.state, 'excerpt') const excerpt = toRef(searchStore.state, 'excerpt')
const q = ref('') const q = ref('')
const searching = ref(false) const searching = ref(false)
const goToSongResults = () => router.go(`search/songs/?q=${q.value}`) const goToSongResults = () => go(`search/songs/?q=${q.value}`)
const doSearch = async () => { const doSearch = async () => {
searching.value = true searching.value = true

View file

@ -30,14 +30,13 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, onMounted, ref, toRef } from 'vue' import { computed, onMounted, ref, toRef } from 'vue'
import { searchStore } from '@/stores' import { searchStore } from '@/stores'
import { useSongList } from '@/composables' import { useRouter, useSongList } from '@/composables'
import { pluralize, requireInjection } from '@/utils' import { pluralize } from '@/utils'
import { RouterKey } from '@/symbols'
import ScreenHeader from '@/components/ui/ScreenHeader.vue' import ScreenHeader from '@/components/ui/ScreenHeader.vue'
import SongListSkeleton from '@/components/ui/skeletons/SongListSkeleton.vue' import SongListSkeleton from '@/components/ui/skeletons/SongListSkeleton.vue'
const router = requireInjection(RouterKey) const { getRouteParam } = useRouter()
const q = ref('') const q = ref('')
const { const {
@ -65,7 +64,7 @@ const loading = ref(false)
searchStore.resetSongResultState() searchStore.resetSongResultState()
onMounted(async () => { onMounted(async () => {
q.value = router.$currentRoute.value.params?.q || '' q.value = getRouteParam('q') || ''
if (!q.value) return if (!q.value) return
loading.value = true loading.value = true

View file

@ -68,15 +68,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, nextTick, ref, toRef, toRefs, watch } from 'vue' import { computed, nextTick, ref, toRef, toRefs, watch } from 'vue'
import { pluralize, requireInjection } from '@/utils' import { pluralize } from '@/utils'
import { playlistStore, queueStore } from '@/stores' import { playlistStore, queueStore } from '@/stores'
import { useMessageToaster, useSongMenuMethods } from '@/composables' import { useMessageToaster, useRouter, useSongMenuMethods } from '@/composables'
import { RouterKey } from '@/symbols'
import Btn from '@/components/ui/Btn.vue' import Btn from '@/components/ui/Btn.vue'
const { toastSuccess } = useMessageToaster() const { toastSuccess } = useMessageToaster()
const router = requireInjection(RouterKey) const { go } = useRouter()
const props = defineProps<{ songs: Song[], config: AddToMenuConfig }>() const props = defineProps<{ songs: Song[], config: AddToMenuConfig }>()
const { songs, config } = toRefs(props) const { songs, config } = toRefs(props)
@ -119,7 +118,7 @@ const createNewPlaylistFromSongs = async () => {
// Activate the new playlist right away // Activate the new playlist right away
await nextTick() await nextTick()
router.go(`playlist/${playlist.id}`) go(`playlist/${playlist.id}`)
close() close()
} }

View file

@ -56,7 +56,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, ref, toRef } from 'vue' import { computed, ref, toRef } from 'vue'
import { arrayify, copyText, eventBus, pluralize, requireInjection } from '@/utils' import { arrayify, copyText, eventBus, pluralize } from '@/utils'
import { commonStore, favoriteStore, playlistStore, queueStore, songStore, userStore } from '@/stores' import { commonStore, favoriteStore, playlistStore, queueStore, songStore, userStore } from '@/stores'
import { downloadService, playbackService } from '@/services' import { downloadService, playbackService } from '@/services'
import { import {
@ -65,13 +65,13 @@ import {
useDialogBox, useDialogBox,
useMessageToaster, useMessageToaster,
usePlaylistManagement, usePlaylistManagement,
useRouter,
useSongMenuMethods useSongMenuMethods
} from '@/composables' } from '@/composables'
import { RouterKey } from '@/symbols'
const { toastSuccess } = useMessageToaster() const { toastSuccess } = useMessageToaster()
const { showConfirmDialog } = useDialogBox() const { showConfirmDialog } = useDialogBox()
const router = requireInjection(RouterKey) const { go, getRouteParam, isCurrentScreen } = useRouter()
const songs = ref<Song[]>([]) const songs = ref<Song[]>([])
@ -98,13 +98,13 @@ const firstSongPlaying = computed(() => songs.value.length ? songs.value[0].play
const normalPlaylists = computed(() => playlists.value.filter(playlist => !playlist.is_smart)) const normalPlaylists = computed(() => playlists.value.filter(playlist => !playlist.is_smart))
const canBeRemovedFromPlaylist = computed(() => { const canBeRemovedFromPlaylist = computed(() => {
if (router.$currentRoute.value.screen !== 'Playlist') return false if (!isCurrentScreen('Playlist')) return false
const playlist = playlistStore.byId(parseInt(router.$currentRoute.value.params!.id)) const playlist = playlistStore.byId(parseInt(getRouteParam('id')!))
return playlist && !playlist.is_smart return playlist && !playlist.is_smart
}) })
const isQueueScreen = computed(() => router.$currentRoute.value.screen === 'Queue') const isQueueScreen = computed(() => isCurrentScreen('Queue'))
const isFavoritesScreen = computed(() => router.$currentRoute.value.screen === 'Favorites') const isFavoritesScreen = computed(() => isCurrentScreen('Favorites'))
const doPlayback = () => trigger(() => { const doPlayback = () => trigger(() => {
if (!songs.value.length) return if (!songs.value.length) return
@ -126,12 +126,12 @@ const doPlayback = () => trigger(() => {
}) })
const openEditForm = () => trigger(() => songs.value.length && eventBus.emit('MODAL_SHOW_EDIT_SONG_FORM', songs.value)) const openEditForm = () => trigger(() => songs.value.length && eventBus.emit('MODAL_SHOW_EDIT_SONG_FORM', songs.value))
const viewAlbumDetails = (albumId: number) => trigger(() => router.go(`album/${albumId}`)) const viewAlbumDetails = (albumId: number) => trigger(() => go(`album/${albumId}`))
const viewArtistDetails = (artistId: number) => trigger(() => router.go(`artist/${artistId}`)) const viewArtistDetails = (artistId: number) => trigger(() => go(`artist/${artistId}`))
const download = () => trigger(() => downloadService.fromSongs(songs.value)) const download = () => trigger(() => downloadService.fromSongs(songs.value))
const removeFromPlaylist = () => trigger(async () => { const removeFromPlaylist = () => trigger(async () => {
const playlist = playlistStore.byId(parseInt(router.$currentRoute.value.params!.id)) const playlist = playlistStore.byId(parseInt(getRouteParam('id')!))
if (!playlist) return if (!playlist) return
await removeSongsFromPlaylist(playlist, songs.value) await removeSongsFromPlaylist(playlist, songs.value)

View file

@ -27,14 +27,13 @@ import { orderBy } from 'lodash'
import { computed, ref, toRef, toRefs } from 'vue' import { computed, ref, toRef, toRefs } from 'vue'
import { albumStore, artistStore, queueStore, songStore, userStore } from '@/stores' import { albumStore, artistStore, queueStore, songStore, userStore } from '@/stores'
import { playbackService } from '@/services' import { playbackService } from '@/services'
import { defaultCover, fileReader, logger, requireInjection } from '@/utils' import { defaultCover, fileReader, logger } from '@/utils'
import { useAuthorization, useMessageToaster } from '@/composables' import { useAuthorization, useMessageToaster, useRouter } from '@/composables'
import { RouterKey } from '@/symbols'
const VALID_IMAGE_TYPES = ['image/jpeg', 'image/gif', 'image/png', 'image/webp'] const VALID_IMAGE_TYPES = ['image/jpeg', 'image/gif', 'image/png', 'image/webp']
const { toastSuccess } = useMessageToaster() const { toastSuccess } = useMessageToaster()
const router = requireInjection(RouterKey) const { go } = useRouter()
const props = defineProps<{ entity: Album | Artist }>() const props = defineProps<{ entity: Album | Artist }>()
const { entity } = toRefs(props) const { entity } = toRefs(props)
@ -70,7 +69,7 @@ const playOrQueue = async (event: KeyboardEvent) => {
} }
playbackService.queueAndPlay(songs) playbackService.queueAndPlay(songs)
router.go('queue') go('queue')
} }
const onDragEnter = () => (droppable.value = allowsUpload.value) const onDragEnter = () => (droppable.value = allowsUpload.value)

View file

@ -11,9 +11,10 @@ import { computed, ref } from 'vue'
import { playbackService } from '@/services' import { playbackService } from '@/services'
import { commonStore, favoriteStore, queueStore, recentlyPlayedStore, songStore } from '@/stores' import { commonStore, favoriteStore, queueStore, recentlyPlayedStore, songStore } from '@/stores'
import { requireInjection } from '@/utils' import { requireInjection } from '@/utils'
import { CurrentSongKey, RouterKey } from '@/symbols' import { useRouter } from '@/composables'
import { CurrentSongKey } from '@/symbols'
const router = requireInjection(RouterKey) const { getCurrentScreen, getRouteParam, go } = useRouter()
const song = requireInjection(CurrentSongKey, ref(null)) const song = requireInjection(CurrentSongKey, ref(null))
const libraryEmpty = computed(() => commonStore.state.song_count === 0) const libraryEmpty = computed(() => commonStore.state.song_count === 0)
@ -26,15 +27,15 @@ const initiatePlayback = async () => {
let songs: Song[] let songs: Song[]
switch (router.$currentRoute.value.screen) { switch (getCurrentScreen()) {
case 'Album': case 'Album':
songs = await songStore.fetchForAlbum(parseInt(router.$currentRoute.value.params!.id)) songs = await songStore.fetchForAlbum(parseInt(getRouteParam('id')!))
break break
case 'Artist': case 'Artist':
songs = await songStore.fetchForArtist(parseInt(router.$currentRoute.value.params!.id)) songs = await songStore.fetchForArtist(parseInt(getRouteParam('id')!))
break break
case 'Playlist': case 'Playlist':
songs = await songStore.fetchForPlaylist(parseInt(router.$currentRoute.value.params!.id)) songs = await songStore.fetchForPlaylist(parseInt(getRouteParam('id')!))
break break
case 'Favorites': case 'Favorites':
songs = await favoriteStore.fetch() songs = await favoriteStore.fetch()
@ -48,7 +49,7 @@ const initiatePlayback = async () => {
} }
playbackService.queueAndPlay(songs) playbackService.queueAndPlay(songs)
router.go('queue') go('queue')
} }
</script> </script>

View file

@ -29,12 +29,12 @@ import isMobile from 'ismobilejs'
import { faSearch } from '@fortawesome/free-solid-svg-icons' import { faSearch } from '@fortawesome/free-solid-svg-icons'
import { ref } from 'vue' import { ref } from 'vue'
import { debounce } from 'lodash' import { debounce } from 'lodash'
import { eventBus, requireInjection } from '@/utils' import { eventBus } from '@/utils'
import { RouterKey } from '@/symbols' import { useRouter } from '@/composables'
const placeholder = isMobile.any ? 'Search' : 'Press F to search' const placeholder = isMobile.any ? 'Search' : 'Press F to search'
const router = requireInjection(RouterKey) const { go } = useRouter()
const input = ref<HTMLInputElement>() const input = ref<HTMLInputElement>()
const q = ref('') const q = ref('')
@ -50,10 +50,10 @@ if (process.env.NODE_ENV !== 'test') {
const onSubmit = () => { const onSubmit = () => {
eventBus.emit('TOGGLE_SIDEBAR') eventBus.emit('TOGGLE_SIDEBAR')
router.go('search') go('search')
} }
const maybeGoToSearchScreen = () => isMobile.any || router.go('search') const maybeGoToSearchScreen = () => isMobile.any || go('search')
eventBus.on('FOCUS_SEARCH_FIELD', () => { eventBus.on('FOCUS_SEARCH_FIELD', () => {
input.value?.focus() input.value?.focus()

View file

@ -11,10 +11,9 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, toRefs } from 'vue' import { computed, toRefs } from 'vue'
import { youTubeService } from '@/services' import { youTubeService } from '@/services'
import { RouterKey } from '@/symbols' import { useRouter } from '@/composables'
import { requireInjection } from '@/utils'
const router = requireInjection(RouterKey) const { go } = useRouter()
const props = defineProps<{ video: YouTubeVideo }>() const props = defineProps<{ video: YouTubeVideo }>()
const { video } = toRefs(props) const { video } = toRefs(props)
@ -23,7 +22,7 @@ const url = computed(() => `https://youtu.be/${video.value.id.videoId}`)
const play = () => { const play = () => {
youTubeService.play(video.value) youTubeService.play(video.value)
router.go('youtube') go('youtube')
} }
</script> </script>

View file

@ -32,9 +32,8 @@
import { faCircleCheck, faShield } from '@fortawesome/free-solid-svg-icons' import { faCircleCheck, faShield } from '@fortawesome/free-solid-svg-icons'
import { computed, toRefs } from 'vue' import { computed, toRefs } from 'vue'
import { userStore } from '@/stores' import { userStore } from '@/stores'
import { eventBus, requireInjection } from '@/utils' import { eventBus } from '@/utils'
import { useAuthorization, useDialogBox, useMessageToaster } from '@/composables' import { useAuthorization, useDialogBox, useMessageToaster, useRouter } from '@/composables'
import { RouterKey } from '@/symbols'
import Btn from '@/components/ui/Btn.vue' import Btn from '@/components/ui/Btn.vue'
@ -43,13 +42,13 @@ const { user } = toRefs(props)
const { toastSuccess } = useMessageToaster() const { toastSuccess } = useMessageToaster()
const { showConfirmDialog } = useDialogBox() const { showConfirmDialog } = useDialogBox()
const router = requireInjection(RouterKey) const { go } = useRouter()
const { currentUser } = useAuthorization() const { currentUser } = useAuthorization()
const isCurrentUser = computed(() => user.value.id === currentUser.value.id) const isCurrentUser = computed(() => user.value.id === currentUser.value.id)
const edit = () => isCurrentUser.value ? router.go('profile') : eventBus.emit('MODAL_SHOW_EDIT_USER_FORM', user.value) const edit = () => isCurrentUser.value ? go('profile') : eventBus.emit('MODAL_SHOW_EDIT_USER_FORM', user.value)
const confirmDelete = async () => const confirmDelete = async () =>
await showConfirmDialog(`Youre about to unperson ${user.value.name}. Are you sure?`) && await destroy() await showConfirmDialog(`Youre about to unperson ${user.value.name}. Are you sure?`) && await destroy()

View file

@ -5,27 +5,26 @@
import { defineComponent } from 'vue' import { defineComponent } from 'vue'
import { authService } from '@/services' import { authService } from '@/services'
import { playlistFolderStore, playlistStore, userStore } from '@/stores' import { playlistFolderStore, playlistStore, userStore } from '@/stores'
import { eventBus, forceReloadWindow, requireInjection } from '@/utils' import { eventBus, forceReloadWindow } from '@/utils'
import { RouterKey } from '@/symbols' import { useDialogBox, useMessageToaster, useRouter } from '@/composables'
import { useDialogBox, useMessageToaster } from '@/composables'
export const GlobalEventListeners = defineComponent({ export const GlobalEventListeners = defineComponent({
setup (props, { slots }) { setup (props, { slots }) {
const { toastSuccess } = useMessageToaster() const { toastSuccess } = useMessageToaster()
const { showConfirmDialog } = useDialogBox() const { showConfirmDialog } = useDialogBox()
const router = requireInjection(RouterKey) const { go } = useRouter()
eventBus.on('PLAYLIST_DELETE', async playlist => { eventBus.on('PLAYLIST_DELETE', async playlist => {
if (await showConfirmDialog(`Delete the playlist "${playlist.name}"?`)) { if (await showConfirmDialog(`Delete the playlist "${playlist.name}"?`)) {
await playlistStore.delete(playlist) await playlistStore.delete(playlist)
toastSuccess(`Playlist "${playlist.name}" deleted.`) toastSuccess(`Playlist "${playlist.name}" deleted.`)
router.go('home') go('home')
} }
}).on('PLAYLIST_FOLDER_DELETE', async folder => { }).on('PLAYLIST_FOLDER_DELETE', async folder => {
if (await showConfirmDialog(`Delete the playlist folder "${folder.name}"?`)) { if (await showConfirmDialog(`Delete the playlist folder "${folder.name}"?`)) {
await playlistFolderStore.delete(folder) await playlistFolderStore.delete(folder)
toastSuccess(`Playlist folder "${folder.name}" deleted.`) toastSuccess(`Playlist folder "${folder.name}" deleted.`)
router.go('home') go('home')
} }
}).on('LOG_OUT', async () => { }).on('LOG_OUT', async () => {
await userStore.logout() await userStore.logout()

View file

@ -14,3 +14,4 @@ export * from './useFloatingUi'
export * from './useMessageToaster' export * from './useMessageToaster'
export * from './useDialogBox' export * from './useDialogBox'
export * from './useSmartPlaylistForm' export * from './useSmartPlaylistForm'
export * from './useRouter'

View file

@ -0,0 +1,16 @@
import { RouterKey } from '@/symbols'
import { requireInjection } from '@/utils'
export const useRouter = () => {
const router = requireInjection(RouterKey)
return {
go: router.go.bind(router),
onRouteChanged: router.onRouteChanged.bind(router),
resolveRoute: router.resolve.bind(router),
triggerNotFound: router.triggerNotFound.bind(router),
getRouteParam: (name: string) => router.$currentRoute.value?.params?.[name],
getCurrentScreen: () => router.$currentRoute.value?.screen,
isCurrentScreen: (...screens: ScreenName[]) => screens.includes(router.$currentRoute.value?.screen!)
}
}

View file

@ -1,9 +1,8 @@
import { RouterKey } from '@/symbols' import { useRouter } from '@/composables'
import { requireInjection } from '@/utils'
export const useScreen = (screen: ScreenName) => { export const useScreen = (screen: ScreenName) => {
const router = requireInjection(RouterKey) const { onRouteChanged } = useRouter()
const onScreenActivated = (cb: Closure) => router.onRouteChanged(route => route.screen === screen && cb()) const onScreenActivated = (cb: Closure) => onRouteChanged(route => route.screen === screen && cb())
return { return {
onScreenActivated onScreenActivated

View file

@ -3,16 +3,10 @@ import isMobile from 'ismobilejs'
import { computed, reactive, Ref, ref } from 'vue' import { computed, reactive, Ref, ref } from 'vue'
import { playbackService } from '@/services' import { playbackService } from '@/services'
import { queueStore, songStore } from '@/stores' import { queueStore, songStore } from '@/stores'
import { eventBus, provideReadonly, requireInjection } from '@/utils' import { eventBus, provideReadonly } from '@/utils'
import { useRouter } from '@/composables'
import { import { SelectedSongsKey, SongListConfigKey, SongListSortFieldKey, SongListSortOrderKey, SongsKey } from '@/symbols'
RouterKey,
SelectedSongsKey,
SongListConfigKey,
SongListSortFieldKey,
SongListSortOrderKey,
SongsKey
} from '@/symbols'
import ControlsToggle from '@/components/ui/ScreenControlsToggle.vue' import ControlsToggle from '@/components/ui/ScreenControlsToggle.vue'
import SongList from '@/components/song/SongList.vue' import SongList from '@/components/song/SongList.vue'
@ -21,9 +15,9 @@ import ThumbnailStack from '@/components/ui/ThumbnailStack.vue'
export const useSongList = (songs: Ref<Song[]>, config: Partial<SongListConfig> = {}) => { export const useSongList = (songs: Ref<Song[]>, config: Partial<SongListConfig> = {}) => {
config = reactive(config) config = reactive(config)
const router = requireInjection(RouterKey) const { isCurrentScreen, go, onRouteChanged } = useRouter()
router.onRouteChanged(route => { onRouteChanged(route => {
config.reorderable = route.screen === 'Queue' config.reorderable = route.screen === 'Queue'
config.sortable = !['Queue', 'RecentlyPlayed', 'Search.Songs'].includes(route.screen) config.sortable = !['Queue', 'RecentlyPlayed', 'Search.Songs'].includes(route.screen)
}) })
@ -51,7 +45,7 @@ export const useSongList = (songs: Ref<Song[]>, config: Partial<SongListConfig>
const playAll = (shuffle: boolean) => { const playAll = (shuffle: boolean) => {
playbackService.queueAndPlay(getSongsToPlay(), shuffle) playbackService.queueAndPlay(getSongsToPlay(), shuffle)
router.go('queue') go('queue')
} }
const playSelected = (shuffle: boolean) => playbackService.queueAndPlay(selectedSongs.value, shuffle) const playSelected = (shuffle: boolean) => playbackService.queueAndPlay(selectedSongs.value, shuffle)
@ -73,13 +67,13 @@ export const useSongList = (songs: Ref<Song[]>, config: Partial<SongListConfig>
await playbackService.play(selectedSongs.value[0]) await playbackService.play(selectedSongs.value[0])
} }
router.go('queue') go('queue')
} }
const sortField = ref<SongListSortField | null>(((): SongListSortField | null => { const sortField = ref<SongListSortField | null>(((): SongListSortField | null => {
if (!config.sortable) return null if (!config.sortable) return null
if (router.$currentRoute.value.screen === 'Album' || router.$currentRoute.value.screen === 'Artist') return 'track' if (isCurrentScreen('Artist', 'Album')) return 'track'
if (router.$currentRoute.value.screen === 'Search.Songs') return null if (isCurrentScreen('Search.Songs', 'Queue', 'RecentlyPlayed')) return null
return 'title' return 'title'
})()) })())

View file

@ -1,17 +1,15 @@
import isMobile from 'ismobilejs' import isMobile from 'ismobilejs'
import { computed, toRef } from 'vue' import { computed, toRef } from 'vue'
import { useAuthorization, useMessageToaster } from '@/composables'
import { settingStore } from '@/stores' import { settingStore } from '@/stores'
import { acceptedMediaTypes } from '@/config' import { acceptedMediaTypes } from '@/config'
import { UploadFile, uploadService } from '@/services' import { UploadFile, uploadService } from '@/services'
import { getAllFileEntries, pluralize, requireInjection } from '@/utils' import { getAllFileEntries, pluralize } from '@/utils'
import { RouterKey } from '@/symbols' import { useAuthorization, useMessageToaster, useRouter } from '@/composables'
export const useUpload = () => { export const useUpload = () => {
const { isAdmin } = useAuthorization() const { isAdmin } = useAuthorization()
const { toastSuccess, toastWarning } = useMessageToaster() const { toastSuccess, toastWarning } = useMessageToaster()
const { go, isCurrentRoute } = useRouter()
const router = requireInjection(RouterKey)
const mediaPath = toRef(settingStore.state, 'media_path') const mediaPath = toRef(settingStore.state, 'media_path')
@ -47,7 +45,7 @@ export const useUpload = () => {
if (queuedFiles.length) { if (queuedFiles.length) {
toastSuccess(`Queued ${pluralize(queuedFiles, 'file')} for upload`) toastSuccess(`Queued ${pluralize(queuedFiles, 'file')} for upload`)
router.$currentRoute.value.screen === 'Upload' || router.go('upload') isCurrentRoute('Upload') || go('upload')
} else { } else {
toastWarning('No files applicable for upload') toastWarning('No files applicable for upload')
} }

View file

@ -6,7 +6,6 @@ import Router from '@/router'
export type ReadonlyInjectionKey<T> = InjectionKey<[Readonly<T> | DeepReadonly<T>, Closure]> export type ReadonlyInjectionKey<T> = InjectionKey<[Readonly<T> | DeepReadonly<T>, Closure]>
export const RouterKey: InjectionKey<Router> = Symbol('Router') export const RouterKey: InjectionKey<Router> = Symbol('Router')
export const DialogBoxKey: InjectionKey<Ref<InstanceType<typeof DialogBox>>> = Symbol('DialogBox') export const DialogBoxKey: InjectionKey<Ref<InstanceType<typeof DialogBox>>> = Symbol('DialogBox')
export const MessageToasterKey: InjectionKey<Ref<InstanceType<typeof MessageToaster>>> = Symbol('MessageToaster') export const MessageToasterKey: InjectionKey<Ref<InstanceType<typeof MessageToaster>>> = Symbol('MessageToaster')