From 9d511d7f581a9ddefcd1f5bfc6ee1e75a0b71caf Mon Sep 17 00:00:00 2001 From: Adam Shiervani Date: Mon, 24 Mar 2025 23:32:13 +0100 Subject: [PATCH] Autoplay permission handling (#285) * feat(WebRTC): enhance connection management with connection failures after X attempts or a certain time * refactor(WebRTC): simplify WebRTCVideo component and enhance connection error handling * fix(WebRTC): extend connection timeout from 1 second to 60 seconds for improved error handling * feat(VideoOverlay): add NoAutoplayPermissionsOverlay component and improve HDMIErrorOverlay content * feat(VideoOverlay): update NoAutoplayPermissionsOverlay styling and improve user instructions * Remove unused PlayIcon import to clean up code --- ui/src/components/VideoOverlay.tsx | 74 ++++++++++++++++++++++++++---- ui/src/components/WebRTCVideo.tsx | 19 +++++++- 2 files changed, 81 insertions(+), 12 deletions(-) diff --git a/ui/src/components/VideoOverlay.tsx b/ui/src/components/VideoOverlay.tsx index 97d097b..a8560cb 100644 --- a/ui/src/components/VideoOverlay.tsx +++ b/ui/src/components/VideoOverlay.tsx @@ -1,10 +1,11 @@ import React from "react"; import { ExclamationTriangleIcon } from "@heroicons/react/24/solid"; import { ArrowRightIcon } from "@heroicons/react/16/solid"; -import { LinkButton } from "@components/Button"; +import { Button, LinkButton } from "@components/Button"; import LoadingSpinner from "@components/LoadingSpinner"; import { GridCard } from "@components/Card"; import { motion, AnimatePresence } from "motion/react"; +import { LuPlay } from "react-icons/lu"; interface OverlayContentProps { children: React.ReactNode; @@ -34,7 +35,7 @@ export function LoadingOverlay({ show }: LoadingOverlayProps) { exit={{ opacity: 0 }} transition={{ duration: show ? 0.3 : 0.1, - ease: "easeInOut" + ease: "easeInOut", }} > @@ -68,7 +69,7 @@ export function ConnectionErrorOverlay({ show }: ConnectionErrorOverlayProps) { exit={{ opacity: 0 }} transition={{ duration: 0.3, - ease: "easeInOut" + ease: "easeInOut", }} > @@ -118,25 +119,27 @@ export function HDMIErrorOverlay({ show, hdmiState }: HDMIErrorOverlayProps) { {show && isNoSignal && (
- -
+ +

No HDMI signal detected.

  • Ensure the HDMI cable securely connected at both ends
  • -
  • Ensure source device is powered on and outputting a signal
  • +
  • + Ensure source device is powered on and outputting a signal +
  • If using an adapter, it's compatible and functioning correctly @@ -169,7 +172,7 @@ export function HDMIErrorOverlay({ show, hdmiState }: HDMIErrorOverlayProps) { exit={{ opacity: 0 }} transition={{ duration: 0.3, - ease: "easeInOut" + ease: "easeInOut", }} > @@ -187,7 +190,7 @@ export function HDMIErrorOverlay({ show, hdmiState }: HDMIErrorOverlayProps) {
); } + +interface NoAutoplayPermissionsOverlayProps { + show: boolean; + onPlayClick: () => void; +} + +export function NoAutoplayPermissionsOverlay({ + show, + onPlayClick, +}: NoAutoplayPermissionsOverlayProps) { + return ( + + {show && ( + + +
+

+ Autoplay permissions required +

+ +
+
+
+ +
+ Please adjust browser settings to enable autoplay +
+
+
+
+
+ )} +
+ ); +} diff --git a/ui/src/components/WebRTCVideo.tsx b/ui/src/components/WebRTCVideo.tsx index 505ce73..4cd56f6 100644 --- a/ui/src/components/WebRTCVideo.tsx +++ b/ui/src/components/WebRTCVideo.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useDeviceSettingsStore, useHidStore, @@ -15,7 +15,7 @@ import Actionbar from "@components/ActionBar"; import InfoBar from "@components/InfoBar"; import useKeyboard from "@/hooks/useKeyboard"; import { useJsonRpc } from "@/hooks/useJsonRpc"; -import { HDMIErrorOverlay } from "./VideoOverlay"; +import { HDMIErrorOverlay, NoAutoplayPermissionsOverlay } from "./VideoOverlay"; import { ConnectionErrorOverlay } from "./VideoOverlay"; import { LoadingOverlay } from "./VideoOverlay"; @@ -418,6 +418,7 @@ export default function WebRTCVideo() { [keyDownHandler, keyUpHandler, resetKeyboardState, sendKeyboardEvent], ); + // Setup Video Event Listeners useEffect( function setupVideoEventListeners() { const videoElmRefValue = videoElm.current; @@ -510,6 +511,14 @@ export default function WebRTCVideo() { [settings.mouseMode, relMouseMoveHandler, mouseWheelHandler], ); + const hasNoAutoPlayPermissions = useMemo(() => { + if (peerConnectionState !== "connected") return false; + if (isPlaying) return false; + if (hdmiError) return false; + if (videoHeight === 0 || videoWidth === 0) return false; + return true; + }, [peerConnectionState, isPlaying, hdmiError, videoHeight, videoWidth]); + return (
@@ -575,6 +584,12 @@ export default function WebRTCVideo() { + { + videoElm.current?.play(); + }} + />