diff --git a/resources/assets/js/App.vue b/resources/assets/js/App.vue
index 95e3e5b2..2ce8c073 100644
--- a/resources/assets/js/App.vue
+++ b/resources/assets/js/App.vue
@@ -3,6 +3,7 @@
+
@@ -29,10 +30,12 @@ import { eventBus, hideOverlay, requireInjection, showOverlay } from '@/utils'
import { commonStore, preferenceStore as preferences, queueStore } from '@/stores'
import { authService, playbackService, socketListener, socketService, uploadService } from '@/services'
import { CurrentSongKey, DialogBoxKey, MessageToasterKey, RouterKey } from '@/symbols'
+import { useNetworkStatus } from '@/composables'
import DialogBox from '@/components/ui/DialogBox.vue'
import MessageToaster from '@/components/ui/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
element
// that is necessary to properly initialize the playService and equalizer.
@@ -59,6 +62,8 @@ const currentSong = ref(null)
const authenticated = ref(false)
const showDropZone = ref(false)
+const { offline } = useNetworkStatus()
+
/**
* Request for notification permission if it's not provided and the user is OK with notifications.
*/
diff --git a/resources/assets/js/app.ts b/resources/assets/js/app.ts
index 2e31c90a..a4d5af12 100644
--- a/resources/assets/js/app.ts
+++ b/resources/assets/js/app.ts
@@ -1,6 +1,6 @@
import { createApp } from 'vue'
import { clickaway, focus, tooltip } from '@/directives'
-import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
+import { FontAwesomeIcon, FontAwesomeLayers } from '@fortawesome/vue-fontawesome'
import { RouterKey } from '@/symbols'
import { routes } from '@/config'
import Router from '@/router'
@@ -9,6 +9,7 @@ import App from './App.vue'
createApp(App)
.provide(RouterKey, new Router(routes))
.component('icon', FontAwesomeIcon)
+ .component('icon-layers', FontAwesomeLayers)
.directive('koel-focus', focus)
.directive('koel-clickaway', clickaway)
.directive('koel-tooltip', tooltip)
diff --git a/resources/assets/js/components/ui/OfflineNotification.vue b/resources/assets/js/components/ui/OfflineNotification.vue
new file mode 100644
index 00000000..e1d71774
--- /dev/null
+++ b/resources/assets/js/components/ui/OfflineNotification.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+ You’re offline.
+
+
+
+
+
+
diff --git a/resources/assets/js/composables/index.ts b/resources/assets/js/composables/index.ts
index 46b0703b..1cce2f97 100644
--- a/resources/assets/js/composables/index.ts
+++ b/resources/assets/js/composables/index.ts
@@ -9,3 +9,4 @@ export * from './useDragAndDrop'
export * from './useUpload'
export * from './useScreen'
export * from './usePlaylistManagement'
+export * from './useNetworkStatus'
diff --git a/resources/assets/js/composables/useNetworkStatus.ts b/resources/assets/js/composables/useNetworkStatus.ts
new file mode 100644
index 00000000..f9cfbcca
--- /dev/null
+++ b/resources/assets/js/composables/useNetworkStatus.ts
@@ -0,0 +1,21 @@
+import { computed, onUnmounted, ref } from 'vue'
+
+export const useNetworkStatus = () => {
+ const online = ref(navigator.onLine)
+ const offline = computed(() => !online.value)
+
+ const updateOnlineStatus = () => (online.value = navigator.onLine)
+
+ window.addEventListener('online', updateOnlineStatus)
+ window.addEventListener('offline', updateOnlineStatus)
+
+ onUnmounted(() => {
+ window.removeEventListener('online', updateOnlineStatus)
+ window.removeEventListener('offline', updateOnlineStatus)
+ })
+
+ return {
+ online,
+ offline
+ }
+}