mobile controls fixes

This commit is contained in:
legop3
2026-06-12 14:35:45 -04:00
parent 3efb31caae
commit 033bafeead
9 changed files with 192 additions and 138 deletions
+1
View File
@@ -1,3 +1,4 @@
# FIXED!
# Issue 007: Timers and Polling Cleanup
## Summary
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
@@ -6,13 +6,13 @@
<link rel="apple-touch-icon" href="/bitmap.png" />
<link rel="manifest" href="/manifest.json" />
<!-- Mobile driving uses dense press controls, so the viewport opts out of browser zoom gestures that can steal touches from the controls. -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#020617" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Roomba Rover" />
<title>Roomba Rover</title>
<script type="module" crossorigin src="/assets/index-CCuQOOMm.js"></script>
<script type="module" crossorigin src="/assets/index-Df0ksHL_.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-BVKa8Zph.css">
</head>
<body>
+2 -2
View File
@@ -1,10 +1,10 @@
1. # FIX ALL OF THE PERFORMANCE PROFILED ISSUES!!
2. fix up ALL discord admin commands
1. fix up ALL discord admin commands
1. make sure all permissions are correct
2. fuzzy search all the things
3. dont break on multi word nicknames
4. make all rs commands work form both the site chat and discord
1. make sure all the permissions are correct
2. make alert feed.jsx show more alerts at once
3. Overseer personality swapping.
4. maybe make a better joystick for mobile
5. make google tts the default everywhere but roverd
+1 -1
View File
@@ -6,7 +6,7 @@
<link rel="apple-touch-icon" href="/bitmap.png" />
<link rel="manifest" href="/manifest.json" />
<!-- Mobile driving uses dense press controls, so the viewport opts out of browser zoom gestures that can steal touches from the controls. -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<meta name="theme-color" content="#020617" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
+32 -3
View File
@@ -33,6 +33,7 @@ export default function HornControl({
);
const [waveform, setWaveform] = useState(hornSettings?.waveform || HORN_SETTINGS_DEFAULTS.waveform);
const settingsRootRef = useRef(null);
const activePointerIdRef = useRef(null);
const [freqs, setFreqs] = useState(() => {
const base = Array.isArray(hornSettings?.freqs) ? hornSettings.freqs : HORN_SETTINGS_DEFAULTS.freqs;
return [...base, 0, 0, 0, 0].slice(0, 4).map((f) => clampFreq(f));
@@ -129,16 +130,39 @@ export default function HornControl({
const handlePointerDown = (event) => {
if (disabled) return;
if (activePointerIdRef.current !== null) return;
const tag = event.target?.tagName?.toLowerCase();
if (tag === 'input' || tag === 'select' || tag === 'option' || tag === 'label' || tag === 'button') return;
/*
Horn is a hold control, so it should claim exactly the finger that started
it. Capturing that pointer prevents tiny mobile finger drift or nearby
controls from making the horn feel delayed or randomly interrupted.
*/
event.preventDefault();
activePointerIdRef.current = event.pointerId;
event.currentTarget.setPointerCapture?.(event.pointerId);
start();
};
const handlePointerUp = (event) => {
if (activePointerIdRef.current !== event.pointerId) return;
const tag = event.target?.tagName?.toLowerCase();
if (tag === 'input' || tag === 'select' || tag === 'option' || tag === 'label' || tag === 'button') return;
event.preventDefault();
activePointerIdRef.current = null;
event.currentTarget.releasePointerCapture?.(event.pointerId);
stop();
};
const handlePointerCancel = (event) => {
if (activePointerIdRef.current !== event.pointerId) return;
activePointerIdRef.current = null;
stop();
};
const handleBlur = () => {
activePointerIdRef.current = null;
stop();
};
@@ -164,10 +188,15 @@ export default function HornControl({
aria-pressed={pressed}
onPointerDown={handlePointerDown}
onPointerUp={handlePointerUp}
onPointerLeave={stop}
onPointerCancel={stop}
onPointerCancel={handlePointerCancel}
onLostPointerCapture={(event) => {
if (activePointerIdRef.current === event.pointerId) {
activePointerIdRef.current = null;
stop();
}
}}
onContextMenu={(event) => event.preventDefault()}
onBlur={stop}
onBlur={handleBlur}
className={buttonClasses}
>
<div
@@ -19,6 +19,7 @@ export default function NightVisionControl({
isBoolean(nightVisionOn) ? nightVisionOn : null,
);
const suppressClickRef = useRef(false);
const suppressClickTimerRef = useRef(null);
useEffect(() => {
if (isBoolean(nightVisionOn)) {
@@ -26,6 +27,15 @@ export default function NightVisionControl({
}
}, [nightVisionOn]);
useEffect(
() => () => {
if (suppressClickTimerRef.current) {
clearTimeout(suppressClickTimerRef.current);
}
},
[],
);
const hasState = isBoolean(optimistic);
const displayOn = hasState ? optimistic : false;
const statusLabel = hasState ? (displayOn ? 'On' : 'Off') : '—';
@@ -52,15 +62,29 @@ export default function NightVisionControl({
*/
event.preventDefault();
suppressClickRef.current = true;
handleToggle();
window.setTimeout(() => {
if (suppressClickTimerRef.current) {
clearTimeout(suppressClickTimerRef.current);
}
/*
The next click normally clears this flag, but Safari may suppress the click
completely after preventDefault(). The timer prevents a stale touch flag
from swallowing a later keyboard or desktop click.
*/
suppressClickTimerRef.current = window.setTimeout(() => {
suppressClickRef.current = false;
}, 0);
suppressClickTimerRef.current = null;
}, 800);
handleToggle();
};
const handleClick = (event) => {
if (suppressClickRef.current) {
event.preventDefault();
suppressClickRef.current = false;
if (suppressClickTimerRef.current) {
clearTimeout(suppressClickTimerRef.current);
suppressClickTimerRef.current = null;
}
return;
}
handleToggle();
+1 -1
View File
@@ -473,7 +473,7 @@ export function ControlSystemProvider({ children }) {
(nightVisionOn) => {
if (!pipeline.nightVision) return;
if (typeof nightVisionOn === 'boolean') {
const action = nightVisionOn ? 'off' : 'on';
const action = nightVisionOn ? 'on' : 'off';
pipeline.sendNightVision(action);
} else {
pipeline.sendNightVision('toggle');