diff --git a/app/components/Player/Fullscreen.vue b/app/components/Player/Fullscreen.vue
index 7404194..6cfbae9 100644
--- a/app/components/Player/Fullscreen.vue
+++ b/app/components/Player/Fullscreen.vue
@@ -1,5 +1,5 @@
@@ -51,7 +51,7 @@ nuxtApp.hook("page:finish", () => {
name="lucide:x"
class="close-btn"
:class="{ show: mouseMovedRecently }"
- @click="fullscreen = false"
+ @click="closeFullscreen()"
/>
diff --git a/app/components/Player/Fullscreen/LyricsDisplay.vue b/app/components/Player/Fullscreen/LyricsDisplay.vue
index 09c49ef..819b104 100644
--- a/app/components/Player/Fullscreen/LyricsDisplay.vue
+++ b/app/components/Player/Fullscreen/LyricsDisplay.vue
@@ -6,7 +6,7 @@ const { animationDuration, offset, showScrollBar } = defineProps<{
}>()
const song = useSong()
-const fullscreen = useFullscreen()
+const { fullscreen } = useFullscreen()
const { secondsPlayed } = usePlayer()
const parsedLyrics = computed(() => {
diff --git a/app/components/Player/index.vue b/app/components/Player/index.vue
index 1b08c68..9b9206b 100644
--- a/app/components/Player/index.vue
+++ b/app/components/Player/index.vue
@@ -39,7 +39,7 @@ const durationFormatted = computed(() =>
)
const toast = useToast()
-const fullscreen = useFullscreen()
+const { open: openFullscreen } = useFullscreen()
@@ -107,7 +107,7 @@ const fullscreen = useFullscreen()
name="lucide:list"
@click="toast.show('Not yet implemented')"
/>
-
+
diff --git a/app/composables/useFullscreen.ts b/app/composables/useFullscreen.ts
index cf3c848..0a4436d 100644
--- a/app/composables/useFullscreen.ts
+++ b/app/composables/useFullscreen.ts
@@ -1 +1,66 @@
-export const useFullscreen = () => useState("fullscreen", () => false)
+type FullscreenState = ReturnType>
+
+type FullscreenControls = {
+ fullscreen: FullscreenState
+ open: () => Promise
+ close: () => Promise
+ toggle: () => Promise
+}
+
+const syncFullscreenState = (fullscreen: FullscreenState) => {
+ fullscreen.value = Boolean(document.fullscreenElement)
+}
+
+let fullscreenListenerInstalled = false
+
+export const useFullscreen = () => {
+ const fullscreen = useState("fullscreen", () => false)
+
+ const open = async () => {
+ fullscreen.value = true
+
+ if (!import.meta.client) return true
+ if (!document.documentElement.requestFullscreen) return true
+
+ try {
+ await document.documentElement.requestFullscreen()
+ return true
+ } catch {
+ fullscreen.value = false
+ return false
+ }
+ }
+
+ const close = async () => {
+ if (import.meta.client && document.fullscreenElement) {
+ try {
+ await document.exitFullscreen()
+ } catch {
+ // Fall through to keep the app state consistent.
+ }
+ }
+
+ fullscreen.value = false
+ return true
+ }
+
+ const toggle = async () => {
+ if (fullscreen.value) return close()
+ return open()
+ }
+
+ if (import.meta.client && !fullscreenListenerInstalled) {
+ fullscreenListenerInstalled = true
+ document.addEventListener("fullscreenchange", () => {
+ syncFullscreenState(fullscreen)
+ })
+ syncFullscreenState(fullscreen)
+ }
+
+ return {
+ fullscreen,
+ open,
+ close,
+ toggle,
+ } as FullscreenControls
+}