mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
fullscreen prompt cookie saving, and more...
This commit is contained in:
+4
-10
@@ -1,14 +1,8 @@
|
||||
1. add faster way for admins to login, like an invisible button in top left or something
|
||||
2. make camera card not wait for ack before sending next command. speed is inconsistent and this is bad.
|
||||
3. add tool call embeds or something for the llm bot in discord, probably not in web ui
|
||||
1. fix overseer prompt so it doesnt talk every run
|
||||
2. rename neato locate to neato playsound for overseer
|
||||
3. you can't control the rovers
|
||||
4. add discord bot typing thing for when someone requests a replay
|
||||
5. change replay title for ones requested from discord, something other than "requester driving rover"
|
||||
6. fix rover request spam queue cheat
|
||||
7. make replays more instant, probably make segments shorter
|
||||
1. fix replay UI so it doesnt save anything in cookie
|
||||
2. add discord bot typing thing for when someone requests a replay
|
||||
3. change replay title for ones requested from discord, something other than "requester driving rover"
|
||||
4. fix rover request spam queue cheat
|
||||
|
||||
|
||||
# relative pipe dreams:
|
||||
1. VPS video forwarding
|
||||
|
||||
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
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-title" content="Roomba Rover" />
|
||||
<title>Roomba Rover</title>
|
||||
<script type="module" crossorigin src="/assets/index-BWZIF1Sp.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-jwQkh99Y.css">
|
||||
<script type="module" crossorigin src="/assets/index-DHhBFQ9m.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-Dd7xX8GI.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -114,7 +114,7 @@ function buildVoteStatus() {
|
||||
else noCount += 1;
|
||||
});
|
||||
const onlineCount = yesCount + noCount;
|
||||
const gatePassed = yesCount > noCount;
|
||||
const gatePassed = eligibleCount === 0 ? true : yesCount > noCount;
|
||||
return {
|
||||
yesCount,
|
||||
noCount,
|
||||
|
||||
+15
-1
@@ -22,6 +22,9 @@ import ModeGateOverlay from './components/ModeGateOverlay/index.jsx';
|
||||
import HomeAssistantControls from './components/HomeAssistantControls/index.jsx';
|
||||
import TurnAlertListener from './components/TurnAlertListener/index.jsx';
|
||||
import RawUserPilePanel from './components/RawUserPilePanel/index.jsx';
|
||||
import OverseerPreferencePanel from './components/OverseerPreferencePanel/index.jsx';
|
||||
import NicknameForm from './components/NicknameForm/index.jsx';
|
||||
import SocialButtonsGrid from './components/SocialButtonsGrid/index.jsx';
|
||||
import ChatPanel from './components/ChatPanel/index.jsx';
|
||||
import FullscreenPrompt from './components/FullscreenPrompt/index.jsx';
|
||||
import FloatingFullscreenButton from './components/FloatingFullscreenButton/index.jsx';
|
||||
@@ -142,7 +145,18 @@ function MobileFeatureTabs({
|
||||
<TabPanel id="chat">
|
||||
<div className="space-y-0.5">
|
||||
<ChatPanel />
|
||||
<RawUserPilePanel />
|
||||
<div className="space-y-0.5">
|
||||
<div className="grid gap-0.5 md:grid-cols-[minmax(0,1.4fr)_minmax(0,1fr)]">
|
||||
<div className="surface flex w-full items-center px-0 py-0">
|
||||
<NicknameForm />
|
||||
</div>
|
||||
<SocialButtonsGrid />
|
||||
</div>
|
||||
<div className="grid gap-0.5 grid-cols-[minmax(0,1fr)_minmax(0,1fr)]">
|
||||
<OverseerPreferencePanel />
|
||||
<RawUserPilePanel hideNicknameForm />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TabPanel>
|
||||
<TabPanel id="vip" keepMounted>
|
||||
|
||||
@@ -22,12 +22,14 @@ export default function OverseerPreferencePanel() {
|
||||
|
||||
return (
|
||||
<section className="surface p-1 text-xs text-slate-200 text-center">
|
||||
<label className="flex items-center justify-center gap-1.5">
|
||||
<label className="flex items-center justify-center gap-1.5 bg-gray-700 surface">
|
||||
<input type="checkbox" checked={enabled} onChange={onToggle} />
|
||||
<span>Enable Overseer LLM</span>
|
||||
</label>
|
||||
<span className='text-xs'>Cast your to enable Overseer in chat.</span>
|
||||
|
||||
<div className="mt-0.5 text-slate-400">
|
||||
Yes?: {Number(vote?.yesCount || 0)}, No!: {Number(vote?.noCount || 0)}
|
||||
Yes!: {Number(vote?.yesCount || 0)}, No...: {Number(vote?.noCount || 0)}
|
||||
</div>
|
||||
<div className="mt-0.5 flex justify-center">
|
||||
<span
|
||||
|
||||
@@ -1,39 +1,10 @@
|
||||
// Hook: useFullscreenPrompt
|
||||
// Purpose: Manages fullscreen prompt visibility and dismissal logic across screen sizes/devices. Scope: Provides reusable fullscreen UX state and action handlers.
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import { useSettingsNamespace } from '../settings/index.js';
|
||||
|
||||
const SESSION_KEY = 'fullscreenPromptDismissed';
|
||||
const MOBILE_LAYOUTS = new Set(['mobile-portrait', 'mobile-landscape']);
|
||||
|
||||
const getStorage = () => {
|
||||
if (typeof window === 'undefined') return null;
|
||||
try {
|
||||
return window.sessionStorage;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const isDismissed = () => {
|
||||
const storage = getStorage();
|
||||
if (!storage) return false;
|
||||
try {
|
||||
return storage.getItem(SESSION_KEY) === '1';
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const markDismissed = () => {
|
||||
const storage = getStorage();
|
||||
if (!storage) return;
|
||||
try {
|
||||
storage.setItem(SESSION_KEY, '1');
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
};
|
||||
|
||||
const detectIOS = () => {
|
||||
if (typeof navigator === 'undefined') return false;
|
||||
const ua = navigator.userAgent || navigator.platform || '';
|
||||
@@ -52,20 +23,35 @@ const supportsFullscreen = () => {
|
||||
};
|
||||
|
||||
export function useFullscreenPrompt(layout) {
|
||||
const { value: pageSettings, save: savePageSettings, status: settingsStatus } = useSettingsNamespace('page', {
|
||||
fullscreenPromptShown: false,
|
||||
});
|
||||
const isMobileLayout = MOBILE_LAYOUTS.has(layout);
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [mode, setMode] = useState('native');
|
||||
const [forceVisible, setForceVisible] = useState(false);
|
||||
const hasShownPrompt = Boolean(pageSettings?.fullscreenPromptShown);
|
||||
const isIOS = useMemo(() => detectIOS(), []);
|
||||
const nativeSupported = useMemo(() => supportsFullscreen(), []);
|
||||
const markShown = useCallback(() => {
|
||||
if (settingsStatus !== 'ready') return;
|
||||
savePageSettings((current) => {
|
||||
if (current?.fullscreenPromptShown) return current;
|
||||
return { ...(current || {}), fullscreenPromptShown: true };
|
||||
});
|
||||
}, [savePageSettings, settingsStatus]);
|
||||
|
||||
const reevaluate = useCallback(() => {
|
||||
if (settingsStatus !== 'ready') {
|
||||
setVisible(false);
|
||||
return;
|
||||
}
|
||||
if (!isMobileLayout) {
|
||||
setVisible(false);
|
||||
setForceVisible(false);
|
||||
return;
|
||||
}
|
||||
if (!forceVisible && isDismissed()) {
|
||||
if (!forceVisible && hasShownPrompt) {
|
||||
setVisible(false);
|
||||
return;
|
||||
}
|
||||
@@ -84,7 +70,7 @@ export function useFullscreenPrompt(layout) {
|
||||
}
|
||||
setMode('native');
|
||||
setVisible(true);
|
||||
}, [forceVisible, isIOS, isMobileLayout]);
|
||||
}, [forceVisible, hasShownPrompt, isIOS, isMobileLayout, settingsStatus]);
|
||||
|
||||
useEffect(() => {
|
||||
reevaluate();
|
||||
@@ -109,10 +95,10 @@ export function useFullscreenPrompt(layout) {
|
||||
}, [reevaluate]);
|
||||
|
||||
const dismiss = useCallback(() => {
|
||||
markDismissed();
|
||||
markShown();
|
||||
setForceVisible(false);
|
||||
setVisible(false);
|
||||
}, []);
|
||||
}, [markShown]);
|
||||
|
||||
const enterFullscreen = useCallback(async () => {
|
||||
if (!isMobileLayout || isIOS) return false;
|
||||
@@ -131,7 +117,7 @@ export function useFullscreenPrompt(layout) {
|
||||
if (result && typeof result.then === 'function') {
|
||||
await result;
|
||||
}
|
||||
markDismissed();
|
||||
markShown();
|
||||
setForceVisible(false);
|
||||
setVisible(false);
|
||||
return true;
|
||||
@@ -139,7 +125,7 @@ export function useFullscreenPrompt(layout) {
|
||||
console.warn('Failed to enter fullscreen', error);
|
||||
return false;
|
||||
}
|
||||
}, [isIOS, isMobileLayout]);
|
||||
}, [isIOS, isMobileLayout, markShown]);
|
||||
|
||||
const showPrompt = useCallback(() => {
|
||||
if (!isMobileLayout) return;
|
||||
|
||||
@@ -30,9 +30,10 @@ export function SettingsProvider({ children }) {
|
||||
|
||||
const setNamespace = useCallback((namespace, updater) => {
|
||||
setState((prev) => {
|
||||
const current = prev.data[namespace] || {};
|
||||
const baseData = prev.status === 'ready' ? prev.data : loadSettings() ?? {};
|
||||
const current = baseData[namespace] || {};
|
||||
const nextValue = typeof updater === 'function' ? updater(current) : { ...current, ...(updater ?? {}) };
|
||||
const nextData = { ...prev.data, [namespace]: nextValue };
|
||||
const nextData = { ...baseData, [namespace]: nextValue };
|
||||
const success = saveSettings(nextData);
|
||||
if (!success) {
|
||||
return { ...prev, status: 'error' };
|
||||
|
||||
Reference in New Issue
Block a user