|
1 | 1 | <template> |
2 | | - <div class="h-[100dvh] bg-gray-900 flex flex-col" data-meeting-component> |
| 2 | + <div |
| 3 | + ref="meetingContainer" |
| 4 | + class="h-[100dvh] bg-gray-900 flex flex-col" |
| 5 | + data-meeting-component |
| 6 | + id="meetingContainer" |
| 7 | + > |
3 | 8 | <!-- Loading state --> |
4 | 9 | <div v-if="isConnecting" class="flex-1 flex items-center justify-center"> |
5 | 10 | <div class="flex items-center justify-center text-white space-x-4"> |
|
65 | 70 | :isMicOn="meetingState.isMicOn.value" |
66 | 71 | :isCameraOn="meetingState.isCameraOn.value" |
67 | 72 | :isScreenSharing="meetingState.isScreenSharing.value" |
| 73 | + :isFullscreen="isFullscreen" |
68 | 74 | :isHandRaised="isHandRaised" |
69 | 75 | :isReactionPickerOpen="isReactionPickerOpen" |
70 | 76 | @update:isReactionPickerOpen="isReactionPickerOpen = $event" |
|
79 | 85 | @toggle-microphone="toggleMicrophone" |
80 | 86 | @toggle-camera="toggleCamera" |
81 | 87 | @toggle-screen-share="toggleScreenShare" |
| 88 | + @toggle-fullscreen="toggleFullscreen" |
82 | 89 | @toggle-raise-hand="toggleRaiseHand" |
83 | 90 | @end-call="endCall" |
84 | 91 | @device-changed="handleDeviceChanged" |
@@ -356,6 +363,8 @@ const lobbyUsersForNotifications = computed(() => { |
356 | 363 | // Refs |
357 | 364 | const chatNotificationQueue = ref(null); |
358 | 365 | const isReactionPickerOpen = ref(false); |
| 366 | +const meetingContainer = ref(null); |
| 367 | +const isFullscreen = ref(false); |
359 | 368 |
|
360 | 369 | // Methods |
361 | 370 | const resetToPreview = () => { |
@@ -574,6 +583,31 @@ const handleNotificationClick = () => { |
574 | 583 | } |
575 | 584 | }; |
576 | 585 |
|
| 586 | +const syncFullscreenState = () => { |
| 587 | + isFullscreen.value = !!document.fullscreenElement; |
| 588 | +}; |
| 589 | +
|
| 590 | +const toggleFullscreen = async () => { |
| 591 | + try { |
| 592 | + if (!document.fullscreenElement) { |
| 593 | + const targetElement = meetingContainer.value; |
| 594 | +
|
| 595 | + if (targetElement?.requestFullscreen) { |
| 596 | + await targetElement.requestFullscreen(); |
| 597 | + } |
| 598 | + return; |
| 599 | + } |
| 600 | +
|
| 601 | + if (document.exitFullscreen) { |
| 602 | + await document.exitFullscreen(); |
| 603 | + } |
| 604 | + } catch (error) { |
| 605 | + console.error("Failed to toggle fullscreen:", error); |
| 606 | + } finally { |
| 607 | + syncFullscreenState(); |
| 608 | + } |
| 609 | +}; |
| 610 | +
|
577 | 611 | const setSinkIdOnVideoElements = async (sinkId) => { |
578 | 612 | // Set speaker output on all video elements |
579 | 613 | const videoElements = document.querySelectorAll("video"); |
@@ -663,6 +697,8 @@ const handleDeviceChanged = async (event) => { |
663 | 697 | onMounted(async () => { |
664 | 698 | window.addEventListener("keydown", handleKeyDown); |
665 | 699 | window.addEventListener("keyup", handleKeyUp); |
| 700 | + document.addEventListener("fullscreenchange", syncFullscreenState); |
| 701 | + syncFullscreenState(); |
666 | 702 |
|
667 | 703 | // Clear any stale error/connection state from previous navigations |
668 | 704 | if (typeof meetingState.resetConnectionState === "function") { |
@@ -743,6 +779,7 @@ onMounted(async () => { |
743 | 779 | onUnmounted(() => { |
744 | 780 | window.removeEventListener("keydown", handleKeyDown); |
745 | 781 | window.removeEventListener("keyup", handleKeyUp); |
| 782 | + document.removeEventListener("fullscreenchange", syncFullscreenState); |
746 | 783 |
|
747 | 784 | // Cleanup will be handled by the meeting logic composable |
748 | 785 | }); |
|
0 commit comments