koel/resources/assets/js/App.vue

179 lines
6.6 KiB
Vue
Raw Normal View History

2022-04-15 14:24:30 +00:00
<template>
2022-12-02 16:17:37 +00:00
<Overlay ref="overlay" />
<DialogBox ref="dialog" />
<MessageToaster ref="toaster" />
<GlobalEventListeners />
2024-03-16 18:11:08 +00:00
<OfflineNotification v-if="!online" />
2022-07-10 15:59:26 +00:00
2024-04-04 22:20:42 +00:00
<main
v-if="layout === 'main' && initialized"
class="absolute md:relative top-0 h-full md:h-screen pt-k-header-height md:pt-0 w-full md:w-auto flex flex-col justify-end"
@dragend="onDragEnd"
@dragover="onDragOver"
@drop="onDrop"
>
2022-12-02 16:17:37 +00:00
<Hotkeys />
<MainWrapper />
<AppFooter />
<SupportKoel />
<SongContextMenu />
<AlbumContextMenu />
<ArtistContextMenu />
<PlaylistContextMenu />
<PlaylistFolderContextMenu />
<CreateNewPlaylistContextMenu />
2024-02-24 15:37:01 +00:00
<DropZone v-show="showDropZone" @close="showDropZone = false" />
2024-04-04 22:20:42 +00:00
</main>
2022-04-15 14:24:30 +00:00
2023-08-20 22:35:58 +00:00
<LoginForm v-if="layout === 'auth'" @loggedin="onUserLoggedIn" />
<AcceptInvitation v-if="layout === 'invitation'" />
2024-02-25 19:32:53 +00:00
<ResetPasswordForm v-if="layout === 'reset-password'" />
2022-04-15 14:24:30 +00:00
</template>
<script lang="ts" setup>
2024-04-04 20:13:35 +00:00
import { defineAsyncComponent, onMounted, provide, ref, watch } from 'vue'
2024-03-16 18:11:08 +00:00
import { useOnline } from '@vueuse/core'
2022-10-13 15:18:47 +00:00
import { commonStore, preferenceStore as preferences, queueStore } from '@/stores'
import { authService, socketListener, socketService, uploadService } from '@/services'
import { CurrentSongKey, DialogBoxKey, MessageToasterKey, OverlayKey } from '@/symbols'
2024-04-04 22:20:42 +00:00
import { useRouter, useOverlay } from '@/composables'
2022-04-15 14:24:30 +00:00
import DialogBox from '@/components/ui/DialogBox.vue'
2024-04-04 22:20:42 +00:00
import MessageToaster from '@/components/ui/message-toaster/MessageToaster.vue'
import Overlay from '@/components/ui/Overlay.vue'
import OfflineNotification from '@/components/ui/OfflineNotification.vue'
// Do not dynamic-import app footer, as it contains the <audio> element
// that is necessary to properly initialize the playService and equalizer.
import AppFooter from '@/components/layout/app-footer/index.vue'
2022-04-15 14:24:30 +00:00
// GlobalEventListener must NOT be lazy-loaded, so that it can handle LOG_OUT event properly.
2024-03-16 18:11:08 +00:00
import GlobalEventListeners from '@/components/utils/GlobalEventListeners.vue'
const Hotkeys = defineAsyncComponent(() => import('@/components/utils/HotkeyListener.vue'))
const LoginForm = defineAsyncComponent(() => import('@/components/auth/LoginForm.vue'))
const MainWrapper = defineAsyncComponent(() => import('@/components/layout/main-wrapper/index.vue'))
const AlbumContextMenu = defineAsyncComponent(() => import('@/components/album/AlbumContextMenu.vue'))
const ArtistContextMenu = defineAsyncComponent(() => import('@/components/artist/ArtistContextMenu.vue'))
const PlaylistContextMenu = defineAsyncComponent(() => import('@/components/playlist/PlaylistContextMenu.vue'))
const PlaylistFolderContextMenu = defineAsyncComponent(() => import('@/components/playlist/PlaylistFolderContextMenu.vue'))
const SongContextMenu = defineAsyncComponent(() => import('@/components/song/SongContextMenu.vue'))
2024-04-04 22:20:42 +00:00
const CreateNewPlaylistContextMenu = defineAsyncComponent(() => import('@/components/playlist/CreatePlaylistContextMenu.vue'))
2022-04-24 08:29:14 +00:00
const SupportKoel = defineAsyncComponent(() => import('@/components/meta/SupportKoel.vue'))
const DropZone = defineAsyncComponent(() => import('@/components/ui/upload/DropZone.vue'))
2023-08-20 22:35:58 +00:00
const AcceptInvitation = defineAsyncComponent(() => import('@/components/invitation/AcceptInvitation.vue'))
2024-02-25 19:32:53 +00:00
const ResetPasswordForm = defineAsyncComponent(() => import('@/components/auth/ResetPasswordForm.vue'))
2022-04-15 14:24:30 +00:00
const overlay = ref<InstanceType<typeof Overlay>>()
const dialog = ref<InstanceType<typeof DialogBox>>()
const toaster = ref<InstanceType<typeof MessageToaster>>()
const currentSong = ref<Song>()
const showDropZone = ref(false)
2024-02-25 19:32:53 +00:00
const layout = ref<'main' | 'auth' | 'invitation' | 'reset-password'>()
2023-08-20 22:35:58 +00:00
2024-02-25 19:32:53 +00:00
const { isCurrentScreen, getCurrentScreen, resolveRoute } = useRouter()
2024-03-16 18:11:08 +00:00
const online = useOnline()
2022-04-15 14:24:30 +00:00
/**
* Request for notification permission if it's not provided and the user is OK with notifications.
*/
const requestNotificationPermission = async () => {
if (preferences.show_now_playing_notification && window.Notification && window.Notification.permission !== 'granted') {
preferences.show_now_playing_notification = await window.Notification.requestPermission() === 'denied'
2022-04-15 14:24:30 +00:00
}
}
const onUserLoggedIn = async () => {
2023-08-20 22:35:58 +00:00
layout.value = 'main'
await init()
2022-04-15 14:24:30 +00:00
}
onMounted(async () => {
// If the user is authenticated via a proxy, we have the token in the window object.
// Simply forward it to the authService and continue with the normal flow.
if (window.AUTH_TOKEN) {
authService.setTokensUsingCompositeToken(window.AUTH_TOKEN)
}
2022-04-15 14:24:30 +00:00
// The app has just been initialized, check if we can get the user data with an already existing token
2022-11-16 17:57:38 +00:00
if (authService.hasApiToken()) {
2022-04-15 14:24:30 +00:00
await init()
2023-08-20 22:35:58 +00:00
// call resolveRoute() after init() so that the onResolve hooks can use the stores
await resolveRoute()
layout.value = 'main'
} else {
await resolveRoute()
2024-02-25 19:32:53 +00:00
switch (getCurrentScreen()) {
case 'Invitation.Accept':
layout.value = 'invitation'
break
case 'Password.Reset':
layout.value = 'reset-password'
break
default:
layout.value = 'auth'
}
2022-04-15 14:24:30 +00:00
}
// Add an ugly mac/non-mac class for OS-targeting styles.
// I'm crying inside.
2022-07-25 12:57:58 +00:00
document.documentElement.classList.add(navigator.userAgent.includes('Mac') ? 'mac' : 'non-mac')
2022-04-15 14:24:30 +00:00
})
const initialized = ref(false)
2022-04-15 14:24:30 +00:00
const init = async () => {
2024-04-04 22:20:42 +00:00
useOverlay(overlay).showOverlay({ message: 'Just a little patience…' })
2022-04-15 14:24:30 +00:00
try {
2022-04-24 08:50:45 +00:00
await commonStore.init()
initialized.value = true
2022-04-15 14:24:30 +00:00
2022-07-25 08:35:15 +00:00
await requestNotificationPermission()
2022-04-15 14:24:30 +00:00
window.addEventListener('beforeunload', (e: BeforeUnloadEvent) => {
if (uploadService.shouldWarnUponWindowUnload() || preferences.confirm_before_closing) {
e.preventDefault()
e.returnValue = ''
2022-07-25 08:35:15 +00:00
}
})
await socketService.init() && socketListener.listen()
2022-04-15 14:24:30 +00:00
} catch (err) {
2023-08-20 22:35:58 +00:00
layout.value = 'auth'
2022-04-15 14:24:30 +00:00
throw err
2023-12-22 13:46:53 +00:00
} finally {
2024-04-04 22:20:42 +00:00
useOverlay(overlay).hideOverlay()
2022-04-15 14:24:30 +00:00
}
}
const onDragOver = (e: DragEvent) => {
2022-11-18 18:44:20 +00:00
showDropZone.value = Boolean(e.dataTransfer?.types.includes('Files')) && !isCurrentScreen('Upload')
}
watch(() => queueStore.current, song => (currentSong.value = song))
2022-10-13 15:18:47 +00:00
const onDragEnd = () => (showDropZone.value = false)
const onDrop = () => (showDropZone.value = false)
provide(OverlayKey, overlay)
provide(DialogBoxKey, dialog)
provide(MessageToasterKey, toaster)
2022-10-13 15:18:47 +00:00
provide(CurrentSongKey, currentSong)
2022-04-15 14:24:30 +00:00
</script>
2024-04-04 20:13:35 +00:00
<style lang="postcss">
2022-04-15 14:24:30 +00:00
#dragGhost {
2024-04-04 22:20:42 +00:00
@apply inline-block py-2 px-3 rounded-md text-base font-sans fixed top-0 left-0 z-[-1] bg-k-success
text-k-text-primary no-hover:hidden;
2022-04-15 14:24:30 +00:00
}
#copyArea {
2024-04-04 22:20:42 +00:00
@apply absolute -left-full bottom-px w-px h-px no-hover:hidden;
2022-10-13 15:18:47 +00:00
}
2022-04-15 14:24:30 +00:00
</style>