From 45bd7ad45a9034d82f7a7eff4567eee2fd6b25ba Mon Sep 17 00:00:00 2001
From: Maksim Sukharev <antreesy.web@gmail.com>
Date: Sun, 15 Sep 2024 16:38:00 +0200
Subject: [PATCH 1/2] fix: migrate fullscreen store and related
 functions/listeners to composable

Signed-off-by: Maksim Sukharev <antreesy.web@gmail.com>
---
 src/components/TopBar/TopBar.vue         | 15 ------
 src/components/TopBar/TopBarMenu.vue     | 42 +++------------
 src/composables/useDocumentFullscreen.ts | 67 ++++++++++++++++++++++++
 src/composables/useViewer.js             |  7 +--
 src/env.d.ts                             | 11 ++++
 src/store/storeConfig.js                 |  2 -
 src/store/uiModeStore.js                 |  5 +-
 src/store/windowVisibilityStore.js       | 41 ---------------
 8 files changed, 94 insertions(+), 96 deletions(-)
 create mode 100644 src/composables/useDocumentFullscreen.ts
 delete mode 100644 src/store/windowVisibilityStore.js

diff --git a/src/components/TopBar/TopBar.vue b/src/components/TopBar/TopBar.vue
index b958c1e40e9..ae4cfc9bf30 100644
--- a/src/components/TopBar/TopBar.vue
+++ b/src/components/TopBar/TopBar.vue
@@ -327,17 +327,9 @@ export default {
 
 	mounted() {
 		document.body.classList.add('has-topbar')
-		document.addEventListener('fullscreenchange', this.fullScreenChanged, false)
-		document.addEventListener('mozfullscreenchange', this.fullScreenChanged, false)
-		document.addEventListener('MSFullscreenChange', this.fullScreenChanged, false)
-		document.addEventListener('webkitfullscreenchange', this.fullScreenChanged, false)
 	},
 
 	beforeDestroy() {
-		document.removeEventListener('fullscreenchange', this.fullScreenChanged, false)
-		document.removeEventListener('mozfullscreenchange', this.fullScreenChanged, false)
-		document.removeEventListener('MSFullscreenChange', this.fullScreenChanged, false)
-		document.removeEventListener('webkitfullscreenchange', this.fullScreenChanged, false)
 		document.body.classList.remove('has-topbar')
 	},
 
@@ -348,13 +340,6 @@ export default {
 			this.sidebarStore.showSidebar({ activeTab })
 		},
 
-		fullScreenChanged() {
-			this.$store.dispatch(
-				'setIsFullscreen',
-				document.webkitIsFullScreen || document.mozFullScreen || document.msFullscreenElement
-			)
-		},
-
 		openConversationSettings() {
 			emit('show-conversation-settings', { token: this.token })
 		},
diff --git a/src/components/TopBar/TopBarMenu.vue b/src/components/TopBar/TopBarMenu.vue
index 20d542a5340..b330099d595 100644
--- a/src/components/TopBar/TopBarMenu.vue
+++ b/src/components/TopBar/TopBarMenu.vue
@@ -174,6 +174,11 @@ import Tooltip from '@nextcloud/vue/dist/Directives/Tooltip.js'
 
 import TransitionExpand from '../MediaSettings/TransitionExpand.vue'
 
+import {
+	useDocumentFullscreen,
+	enableFullscreen,
+	disableFullscreen,
+} from '../../composables/useDocumentFullscreen.ts'
 import { useIsInCall } from '../../composables/useIsInCall.js'
 import { CALL, CONVERSATION, PARTICIPANT } from '../../constants.js'
 import { getTalkConfig } from '../../services/CapabilitiesManager.ts'
@@ -251,6 +256,7 @@ export default {
 	setup() {
 		return {
 			isInCall: useIsInCall(),
+			isFullscreen: useDocumentFullscreen(),
 			breakoutRoomsStore: useBreakoutRoomsStore(),
 		}
 	},
@@ -269,10 +275,6 @@ export default {
 			return this.$store.getters.conversation(this.token) || this.$store.getters.dummyConversation
 		},
 
-		isFullscreen() {
-			return this.$store.getters.isFullscreen()
-		},
-
 		labelFullscreen() {
 			return this.isFullscreen
 				? t('spreed', 'Exit full screen (F)')
@@ -435,38 +437,10 @@ export default {
 			}
 
 			if (this.isFullscreen) {
-				this.disableFullscreen()
-				this.$store.dispatch('setIsFullscreen', false)
+				disableFullscreen()
 			} else {
-				this.enableFullscreen()
 				emit('toggle-navigation', { open: false })
-				this.$store.dispatch('setIsFullscreen', true)
-			}
-		},
-
-		enableFullscreen() {
-			const fullscreenElem = document.getElementById('content-vue')
-
-			if (fullscreenElem.requestFullscreen) {
-				fullscreenElem.requestFullscreen()
-			} else if (fullscreenElem.webkitRequestFullscreen) {
-				fullscreenElem.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT)
-			} else if (fullscreenElem.mozRequestFullScreen) {
-				fullscreenElem.mozRequestFullScreen()
-			} else if (fullscreenElem.msRequestFullscreen) {
-				fullscreenElem.msRequestFullscreen()
-			}
-		},
-
-		disableFullscreen() {
-			if (document.exitFullscreen) {
-				document.exitFullscreen()
-			} else if (document.webkitExitFullscreen) {
-				document.webkitExitFullscreen()
-			} else if (document.mozCancelFullScreen) {
-				document.mozCancelFullScreen()
-			} else if (document.msExitFullscreen) {
-				document.msExitFullscreen()
+				enableFullscreen()
 			}
 		},
 
diff --git a/src/composables/useDocumentFullscreen.ts b/src/composables/useDocumentFullscreen.ts
new file mode 100644
index 00000000000..9cafd85410f
--- /dev/null
+++ b/src/composables/useDocumentFullscreen.ts
@@ -0,0 +1,67 @@
+/*
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+import { createSharedComposable } from '@vueuse/core'
+import { readonly, ref, onBeforeMount, onBeforeUnmount } from 'vue'
+import type { Ref, DeepReadonly } from 'vue'
+
+interface WebkitElement extends Element {
+	ALLOW_KEYBOARD_INPUT: FullscreenOptions;
+}
+
+/**
+ * Composable to check whether the page is displayed at fullscreen
+ * @return {DeepReadonly<Ref<boolean>>} - computed boolean whether the page is displayed at fullscreen
+ */
+function useDocumentFullscreenComposable() {
+	const isFullscreen = ref<boolean>(document.fullscreenElement !== null)
+
+	const changeIsFullscreen = () => {
+		isFullscreen.value = document.fullscreenElement !== null
+	}
+
+	document.addEventListener('fullscreenchange', changeIsFullscreen)
+	document.addEventListener('webkitfullscreenchange', changeIsFullscreen)
+
+	onBeforeUnmount(() => {
+		document.removeEventListener('fullscreenchange', changeIsFullscreen)
+		document.removeEventListener('webkitfullscreenchange', changeIsFullscreen)
+	})
+
+	return readonly(isFullscreen)
+}
+
+/**
+ * Enable a fullscreen with Fullscreen API
+ */
+export async function enableFullscreen() {
+	const element = document.getElementById('content-vue')
+	if (!element) {
+		return
+	}
+
+	if (element.requestFullscreen) {
+		await element.requestFullscreen()
+	} else if (element.webkitRequestFullscreen) {
+		await element.webkitRequestFullscreen((Element as unknown as WebkitElement).ALLOW_KEYBOARD_INPUT)
+	}
+}
+
+/**
+ * Disable a fullscreen
+ */
+export async function disableFullscreen() {
+	if (document.exitFullscreen) {
+		await document.exitFullscreen()
+	} else if (document.webkitExitFullscreen) {
+		await document.webkitExitFullscreen()
+	}
+}
+
+/**
+ * Shared composable to check whether the page is displayed at fullscreen
+ * @return {DeepReadonly<Ref<boolean>>} - computed boolean whether the page is displayed at fullscreen
+ */
+export const useDocumentFullscreen = createSharedComposable(useDocumentFullscreenComposable)
diff --git a/src/composables/useViewer.js b/src/composables/useViewer.js
index 92d018cdffb..6099c28c510 100644
--- a/src/composables/useViewer.js
+++ b/src/composables/useViewer.js
@@ -3,8 +3,9 @@
  * SPDX-License-Identifier: AGPL-3.0-or-later
  */
 
-import { computed, nextTick, ref, watch } from 'vue'
+import { nextTick, ref, watch } from 'vue'
 
+import { useDocumentFullscreen } from './useDocumentFullscreen.ts'
 import { useIsInCall } from './useIsInCall.js'
 import { useStore } from './useStore.js'
 import { useSidebarStore } from '../stores/sidebar.js'
@@ -74,7 +75,7 @@ function reparentViewer(isFullscreen) {
 
 	if (isFullscreen) {
 		// When changed to the fullscreen mode, Viewer should be moved to the talk app
-		document.getElementById('content-vue').appendChild(viewerElement)
+		document.getElementById('content-vue')?.appendChild(viewerElement)
 	} else {
 		// In normal mode if it was in fullscreen before, move back to body
 		// Otherwise it will be overlapped by web-page's header
@@ -98,7 +99,7 @@ const isViewerOpen = ref(false)
 export function useViewer(fileAPI) {
 	const store = useStore()
 	const isInCall = useIsInCall()
-	const isFullscreen = computed(() => store.getters.isFullscreen())
+	const isFullscreen = useDocumentFullscreen()
 	const sidebarStore = useSidebarStore()
 
 	watch(isFullscreen, () => {
diff --git a/src/env.d.ts b/src/env.d.ts
index 4d9841cd308..3acd12645ce 100644
--- a/src/env.d.ts
+++ b/src/env.d.ts
@@ -3,7 +3,18 @@
  * SPDX-License-Identifier: AGPL-3.0-or-later
  */
 
+type ExitFullscreen = typeof document.exitFullscreen
+type RequestFullscreen = typeof document.documentElement.requestFullscreen
+
 declare global {
+	interface Document {
+		webkitExitFullscreen: ExitFullscreen;
+	}
+
+	interface HTMLElement {
+		webkitRequestFullscreen: RequestFullscreen;
+	}
+
 	// @nextcloud/webpack-vue-config build globals
 	const appName: string
 	const appVersion: string
diff --git a/src/store/storeConfig.js b/src/store/storeConfig.js
index ba77b609aff..8c35643e669 100644
--- a/src/store/storeConfig.js
+++ b/src/store/storeConfig.js
@@ -13,7 +13,6 @@ import participantsStore from './participantsStore.js'
 import soundsStore from './soundsStore.js'
 import tokenStore from './tokenStore.js'
 import uiModeStore from './uiModeStore.js'
-import windowVisibilityStore from './windowVisibilityStore.js'
 
 export default {
 	modules: {
@@ -27,7 +26,6 @@ export default {
 		soundsStore,
 		tokenStore,
 		uiModeStore,
-		windowVisibilityStore,
 	},
 
 	mutations: {},
diff --git a/src/store/uiModeStore.js b/src/store/uiModeStore.js
index 75f308d7648..f79faf88ca4 100644
--- a/src/store/uiModeStore.js
+++ b/src/store/uiModeStore.js
@@ -3,6 +3,8 @@
  * SPDX-License-Identifier: AGPL-3.0-or-later
  */
 
+import { useDocumentFullscreen } from '../composables/useDocumentFullscreen.ts'
+
 /**
  * This store handles the values that need to be customized depending on the
  * current UI mode of Talk (main UI, embedded in Files sidebar, video
@@ -15,7 +17,8 @@ const state = {
 
 const getters = {
 	getMainContainerSelector: (state, getters, rootState, rootGetters) => () => {
-		return rootGetters.isFullscreen() ? state.mainContainerSelector : 'body'
+		const isFullscreen = useDocumentFullscreen()
+		return isFullscreen.value ? state.mainContainerSelector : 'body'
 	},
 }
 
diff --git a/src/store/windowVisibilityStore.js b/src/store/windowVisibilityStore.js
deleted file mode 100644
index f8af1239da5..00000000000
--- a/src/store/windowVisibilityStore.js
+++ /dev/null
@@ -1,41 +0,0 @@
-/**
- * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
- * SPDX-License-Identifier: AGPL-3.0-or-later
- */
-
-const state = {
-	fullscreen: false,
-}
-
-const getters = {
-	isFullscreen: (state) => () => {
-		return state.fullscreen
-	},
-}
-
-const mutations = {
-	/**
-	 * Sets the fullscreen state
-	 *
-	 * @param {object} state current store state;
-	 * @param {boolean} value the value;
-	 */
-	setIsFullscreen(state, value) {
-		state.fullscreen = value
-	},
-}
-
-const actions = {
-	/**
-	 * Sets the fullscreen state
-	 *
-	 * @param {object} context the context object;
-	 * @param {boolean} value the value;
-	 */
-	setIsFullscreen(context, value) {
-		context.commit('setIsFullscreen', value)
-	},
-
-}
-
-export default { state, mutations, getters, actions }

From 60299cade14771bec551999973d2338253ca5dde Mon Sep 17 00:00:00 2001
From: Maksim Sukharev <antreesy.web@gmail.com>
Date: Mon, 16 Sep 2024 17:16:53 +0200
Subject: [PATCH 2/2] fix: remove unsupported argument (remove since Chrome
 70+)

Signed-off-by: Maksim Sukharev <antreesy.web@gmail.com>
---
 src/composables/useDocumentFullscreen.ts | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/src/composables/useDocumentFullscreen.ts b/src/composables/useDocumentFullscreen.ts
index 9cafd85410f..0abd203c9ed 100644
--- a/src/composables/useDocumentFullscreen.ts
+++ b/src/composables/useDocumentFullscreen.ts
@@ -7,10 +7,6 @@ import { createSharedComposable } from '@vueuse/core'
 import { readonly, ref, onBeforeMount, onBeforeUnmount } from 'vue'
 import type { Ref, DeepReadonly } from 'vue'
 
-interface WebkitElement extends Element {
-	ALLOW_KEYBOARD_INPUT: FullscreenOptions;
-}
-
 /**
  * Composable to check whether the page is displayed at fullscreen
  * @return {DeepReadonly<Ref<boolean>>} - computed boolean whether the page is displayed at fullscreen
@@ -45,7 +41,7 @@ export async function enableFullscreen() {
 	if (element.requestFullscreen) {
 		await element.requestFullscreen()
 	} else if (element.webkitRequestFullscreen) {
-		await element.webkitRequestFullscreen((Element as unknown as WebkitElement).ALLOW_KEYBOARD_INPUT)
+		await element.webkitRequestFullscreen()
 	}
 }