always there fullscreen button

This commit is contained in:
legop3
2026-03-23 02:31:59 -04:00
parent 626b48f777
commit ad1b6b48f8
8 changed files with 84 additions and 15 deletions
+3 -3
View File
@@ -1,5 +1,5 @@
1. fix controls remapping [X]
2. trusted user system [X]
1. fix controls remapping [x]
2. trusted user system [x]
3. private rovers
4. custom webhook profile pictures for chat bridge in discord
5. home assistant switch that tells the server to force the lights on
@@ -8,7 +8,7 @@
- streaming from server to rovers [x]
- audio files first [x]
- then voice chat [x]
8. mobile controls column swapping (optional joystick on left)
8. mobile controls column swapping (optional joystick on left) [x]
9. fix fullscreen on mobile so that you can re-enter it
10. home assistant rover mute switch
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -11,8 +11,8 @@
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
<title>Multi Roomba Rover</title>
<script type="module" crossorigin src="/assets/index-Cf-6drTv.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-B-TLXecO.css">
<script type="module" crossorigin src="/assets/index-Dnr0s1G9.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-r3OnudWV.css">
</head>
<body>
<div id="root"></div>
+22
View File
@@ -22,6 +22,7 @@ import TurnAlertListener from './components/TurnAlertListener.jsx';
import RawUserPilePanel from './components/RawUserPilePanel.jsx';
import ChatPanel from './components/ChatPanel.jsx';
import FullscreenPrompt from './components/FullscreenPrompt.jsx';
import FloatingFullscreenButton from './components/FloatingFullscreenButton.jsx';
import { useFullscreenPrompt } from './hooks/useFullscreenPrompt.js';
import { useSettingsNamespace } from './settings/index.js';
import HelpOverlay from './components/HelpOverlay.jsx';
@@ -231,8 +232,11 @@ function AppWithProviders({ layout, isDesktop, fullscreen }) {
const {
visible: fullscreenVisible,
mode: fullscreenMode,
isIOS: fullscreenIsIOS,
nativeSupported: fullscreenNativeSupported,
enterFullscreen,
dismiss,
showPrompt,
} = fullscreen;
const {
@@ -244,6 +248,8 @@ function AppWithProviders({ layout, isDesktop, fullscreen }) {
swapMobileControlColumns: false,
});
const swapMobileControlColumns = Boolean(pageSettings?.swapMobileControlColumns);
const fullscreenButtonSide = swapMobileControlColumns ? 'left' : 'right';
const showFloatingFullscreenButton = !isDesktop && (fullscreenIsIOS || fullscreenNativeSupported);
const [helpVisible, setHelpVisible] = useState(false);
useEffect(() => {
@@ -254,6 +260,16 @@ function AppWithProviders({ layout, isDesktop, fullscreen }) {
const openHelp = useCallback(() => setHelpVisible(true), []);
const closeHelp = useCallback(() => setHelpVisible(false), []);
const handleFloatingFullscreen = useCallback(async () => {
if (fullscreenIsIOS) {
showPrompt();
return;
}
const entered = await enterFullscreen();
if (!entered) {
showPrompt();
}
}, [enterFullscreen, fullscreenIsIOS, showPrompt]);
const setShowOnLoad = useCallback(
(enabled) => {
const next = Boolean(enabled);
@@ -299,6 +315,12 @@ function AppWithProviders({ layout, isDesktop, fullscreen }) {
onEnterFullscreen={enterFullscreen}
onDismiss={dismiss}
/>
{showFloatingFullscreenButton ? (
<FloatingFullscreenButton
side={fullscreenButtonSide}
onClick={handleFloatingFullscreen}
/>
) : null}
</ControlSystemProvider>
);
}
@@ -0,0 +1,29 @@
export default function FloatingFullscreenButton({ side = 'right', onClick }) {
const sideClass = side === 'left' ? 'left-2' : 'right-2';
return (
<button
type="button"
onClick={onClick}
className={`fixed bottom-2 ${sideClass} z-30 flex h-10 w-10 items-center justify-center rounded-full border border-cyan-200/50 bg-slate-900/70 text-cyan-100 shadow-lg backdrop-blur-sm transition hover:bg-slate-800/80 active:scale-95`}
aria-label="Enter fullscreen"
title="Enter fullscreen"
>
<svg
viewBox="0 0 24 24"
width="18"
height="18"
aria-hidden="true"
className="pointer-events-none"
>
<path
d="M4 9V4h5M15 4h5v5M20 15v5h-5M9 20H4v-5"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</button>
);
}
+20 -2
View File
@@ -53,14 +53,17 @@ export function useFullscreenPrompt(layout) {
const isMobileLayout = MOBILE_LAYOUTS.has(layout);
const [visible, setVisible] = useState(false);
const [mode, setMode] = useState('native');
const [forceVisible, setForceVisible] = useState(false);
const isIOS = useMemo(() => detectIOS(), []);
const nativeSupported = useMemo(() => supportsFullscreen(), []);
const reevaluate = useCallback(() => {
if (!isMobileLayout) {
setVisible(false);
setForceVisible(false);
return;
}
if (isDismissed()) {
if (!forceVisible && isDismissed()) {
setVisible(false);
return;
}
@@ -79,7 +82,7 @@ export function useFullscreenPrompt(layout) {
}
setMode('native');
setVisible(true);
}, [isIOS, isMobileLayout]);
}, [forceVisible, isIOS, isMobileLayout]);
useEffect(() => {
reevaluate();
@@ -89,6 +92,7 @@ export function useFullscreenPrompt(layout) {
if (typeof document === 'undefined') return undefined;
const handler = () => {
if (document.fullscreenElement) {
setForceVisible(false);
setVisible(false);
return;
}
@@ -104,12 +108,14 @@ export function useFullscreenPrompt(layout) {
const dismiss = useCallback(() => {
markDismissed();
setForceVisible(false);
setVisible(false);
}, []);
const enterFullscreen = useCallback(async () => {
if (!isMobileLayout || isIOS) return false;
if (typeof document === 'undefined') return false;
if (document.fullscreenElement) return true;
const element = document.documentElement;
if (!element) return false;
const request =
@@ -124,6 +130,7 @@ export function useFullscreenPrompt(layout) {
await result;
}
markDismissed();
setForceVisible(false);
setVisible(false);
return true;
} catch (error) {
@@ -132,8 +139,16 @@ export function useFullscreenPrompt(layout) {
}
}, [isIOS, isMobileLayout]);
const showPrompt = useCallback(() => {
if (!isMobileLayout) return;
setForceVisible(true);
setMode(isIOS ? 'pwa-hint' : 'native');
setVisible(true);
}, [isIOS, isMobileLayout]);
useEffect(() => {
if (!isMobileLayout) {
setForceVisible(false);
setVisible(false);
}
}, [isMobileLayout]);
@@ -141,8 +156,11 @@ export function useFullscreenPrompt(layout) {
return {
visible,
mode,
isIOS,
nativeSupported,
enterFullscreen,
dismiss,
showPrompt,
};
}