mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-17 10:00:46 -04:00
always there fullscreen button
This commit is contained in:
+3
-3
@@ -1,5 +1,5 @@
|
|||||||
1. fix controls remapping [X]
|
1. fix controls remapping [x]
|
||||||
2. trusted user system [X]
|
2. trusted user system [x]
|
||||||
3. private rovers
|
3. private rovers
|
||||||
4. custom webhook profile pictures for chat bridge in discord
|
4. custom webhook profile pictures for chat bridge in discord
|
||||||
5. home assistant switch that tells the server to force the lights on
|
5. home assistant switch that tells the server to force the lights on
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
- streaming from server to rovers [x]
|
- streaming from server to rovers [x]
|
||||||
- audio files first [x]
|
- audio files first [x]
|
||||||
- then voice chat [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
|
9. fix fullscreen on mobile so that you can re-enter it
|
||||||
10. home assistant rover mute switch
|
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
@@ -11,8 +11,8 @@
|
|||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
|
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
|
||||||
<title>Multi Roomba Rover</title>
|
<title>Multi Roomba Rover</title>
|
||||||
<script type="module" crossorigin src="/assets/index-Cf-6drTv.js"></script>
|
<script type="module" crossorigin src="/assets/index-Dnr0s1G9.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-B-TLXecO.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-r3OnudWV.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import TurnAlertListener from './components/TurnAlertListener.jsx';
|
|||||||
import RawUserPilePanel from './components/RawUserPilePanel.jsx';
|
import RawUserPilePanel from './components/RawUserPilePanel.jsx';
|
||||||
import ChatPanel from './components/ChatPanel.jsx';
|
import ChatPanel from './components/ChatPanel.jsx';
|
||||||
import FullscreenPrompt from './components/FullscreenPrompt.jsx';
|
import FullscreenPrompt from './components/FullscreenPrompt.jsx';
|
||||||
|
import FloatingFullscreenButton from './components/FloatingFullscreenButton.jsx';
|
||||||
import { useFullscreenPrompt } from './hooks/useFullscreenPrompt.js';
|
import { useFullscreenPrompt } from './hooks/useFullscreenPrompt.js';
|
||||||
import { useSettingsNamespace } from './settings/index.js';
|
import { useSettingsNamespace } from './settings/index.js';
|
||||||
import HelpOverlay from './components/HelpOverlay.jsx';
|
import HelpOverlay from './components/HelpOverlay.jsx';
|
||||||
@@ -231,8 +232,11 @@ function AppWithProviders({ layout, isDesktop, fullscreen }) {
|
|||||||
const {
|
const {
|
||||||
visible: fullscreenVisible,
|
visible: fullscreenVisible,
|
||||||
mode: fullscreenMode,
|
mode: fullscreenMode,
|
||||||
|
isIOS: fullscreenIsIOS,
|
||||||
|
nativeSupported: fullscreenNativeSupported,
|
||||||
enterFullscreen,
|
enterFullscreen,
|
||||||
dismiss,
|
dismiss,
|
||||||
|
showPrompt,
|
||||||
} = fullscreen;
|
} = fullscreen;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -244,6 +248,8 @@ function AppWithProviders({ layout, isDesktop, fullscreen }) {
|
|||||||
swapMobileControlColumns: false,
|
swapMobileControlColumns: false,
|
||||||
});
|
});
|
||||||
const swapMobileControlColumns = Boolean(pageSettings?.swapMobileControlColumns);
|
const swapMobileControlColumns = Boolean(pageSettings?.swapMobileControlColumns);
|
||||||
|
const fullscreenButtonSide = swapMobileControlColumns ? 'left' : 'right';
|
||||||
|
const showFloatingFullscreenButton = !isDesktop && (fullscreenIsIOS || fullscreenNativeSupported);
|
||||||
const [helpVisible, setHelpVisible] = useState(false);
|
const [helpVisible, setHelpVisible] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -254,6 +260,16 @@ function AppWithProviders({ layout, isDesktop, fullscreen }) {
|
|||||||
|
|
||||||
const openHelp = useCallback(() => setHelpVisible(true), []);
|
const openHelp = useCallback(() => setHelpVisible(true), []);
|
||||||
const closeHelp = useCallback(() => setHelpVisible(false), []);
|
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(
|
const setShowOnLoad = useCallback(
|
||||||
(enabled) => {
|
(enabled) => {
|
||||||
const next = Boolean(enabled);
|
const next = Boolean(enabled);
|
||||||
@@ -299,6 +315,12 @@ function AppWithProviders({ layout, isDesktop, fullscreen }) {
|
|||||||
onEnterFullscreen={enterFullscreen}
|
onEnterFullscreen={enterFullscreen}
|
||||||
onDismiss={dismiss}
|
onDismiss={dismiss}
|
||||||
/>
|
/>
|
||||||
|
{showFloatingFullscreenButton ? (
|
||||||
|
<FloatingFullscreenButton
|
||||||
|
side={fullscreenButtonSide}
|
||||||
|
onClick={handleFloatingFullscreen}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
</ControlSystemProvider>
|
</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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -53,14 +53,17 @@ export function useFullscreenPrompt(layout) {
|
|||||||
const isMobileLayout = MOBILE_LAYOUTS.has(layout);
|
const isMobileLayout = MOBILE_LAYOUTS.has(layout);
|
||||||
const [visible, setVisible] = useState(false);
|
const [visible, setVisible] = useState(false);
|
||||||
const [mode, setMode] = useState('native');
|
const [mode, setMode] = useState('native');
|
||||||
|
const [forceVisible, setForceVisible] = useState(false);
|
||||||
const isIOS = useMemo(() => detectIOS(), []);
|
const isIOS = useMemo(() => detectIOS(), []);
|
||||||
|
const nativeSupported = useMemo(() => supportsFullscreen(), []);
|
||||||
|
|
||||||
const reevaluate = useCallback(() => {
|
const reevaluate = useCallback(() => {
|
||||||
if (!isMobileLayout) {
|
if (!isMobileLayout) {
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
|
setForceVisible(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isDismissed()) {
|
if (!forceVisible && isDismissed()) {
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -79,7 +82,7 @@ export function useFullscreenPrompt(layout) {
|
|||||||
}
|
}
|
||||||
setMode('native');
|
setMode('native');
|
||||||
setVisible(true);
|
setVisible(true);
|
||||||
}, [isIOS, isMobileLayout]);
|
}, [forceVisible, isIOS, isMobileLayout]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
reevaluate();
|
reevaluate();
|
||||||
@@ -89,6 +92,7 @@ export function useFullscreenPrompt(layout) {
|
|||||||
if (typeof document === 'undefined') return undefined;
|
if (typeof document === 'undefined') return undefined;
|
||||||
const handler = () => {
|
const handler = () => {
|
||||||
if (document.fullscreenElement) {
|
if (document.fullscreenElement) {
|
||||||
|
setForceVisible(false);
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -104,12 +108,14 @@ export function useFullscreenPrompt(layout) {
|
|||||||
|
|
||||||
const dismiss = useCallback(() => {
|
const dismiss = useCallback(() => {
|
||||||
markDismissed();
|
markDismissed();
|
||||||
|
setForceVisible(false);
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const enterFullscreen = useCallback(async () => {
|
const enterFullscreen = useCallback(async () => {
|
||||||
if (!isMobileLayout || isIOS) return false;
|
if (!isMobileLayout || isIOS) return false;
|
||||||
if (typeof document === 'undefined') return false;
|
if (typeof document === 'undefined') return false;
|
||||||
|
if (document.fullscreenElement) return true;
|
||||||
const element = document.documentElement;
|
const element = document.documentElement;
|
||||||
if (!element) return false;
|
if (!element) return false;
|
||||||
const request =
|
const request =
|
||||||
@@ -124,6 +130,7 @@ export function useFullscreenPrompt(layout) {
|
|||||||
await result;
|
await result;
|
||||||
}
|
}
|
||||||
markDismissed();
|
markDismissed();
|
||||||
|
setForceVisible(false);
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
return true;
|
return true;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -132,8 +139,16 @@ export function useFullscreenPrompt(layout) {
|
|||||||
}
|
}
|
||||||
}, [isIOS, isMobileLayout]);
|
}, [isIOS, isMobileLayout]);
|
||||||
|
|
||||||
|
const showPrompt = useCallback(() => {
|
||||||
|
if (!isMobileLayout) return;
|
||||||
|
setForceVisible(true);
|
||||||
|
setMode(isIOS ? 'pwa-hint' : 'native');
|
||||||
|
setVisible(true);
|
||||||
|
}, [isIOS, isMobileLayout]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isMobileLayout) {
|
if (!isMobileLayout) {
|
||||||
|
setForceVisible(false);
|
||||||
setVisible(false);
|
setVisible(false);
|
||||||
}
|
}
|
||||||
}, [isMobileLayout]);
|
}, [isMobileLayout]);
|
||||||
@@ -141,8 +156,11 @@ export function useFullscreenPrompt(layout) {
|
|||||||
return {
|
return {
|
||||||
visible,
|
visible,
|
||||||
mode,
|
mode,
|
||||||
|
isIOS,
|
||||||
|
nativeSupported,
|
||||||
enterFullscreen,
|
enterFullscreen,
|
||||||
dismiss,
|
dismiss,
|
||||||
|
showPrompt,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user