mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 01:21:20 -04:00
block lift ui during movement
This commit is contained in:
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-Djed0CKH.js"></script>
|
<script type="module" crossorigin src="/assets/index-8SerLZt1.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-BrrBcPP-.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-Bn91Wr2B.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const liftConfig = haConfig.lift || {};
|
|||||||
const upSwitchId = String(liftConfig.upSwitch || '').trim();
|
const upSwitchId = String(liftConfig.upSwitch || '').trim();
|
||||||
const downSwitchId = String(liftConfig.downSwitch || '').trim();
|
const downSwitchId = String(liftConfig.downSwitch || '').trim();
|
||||||
const interlockMs = Math.max(250, Number(liftConfig.interlockMs) || 9000);
|
const interlockMs = Math.max(250, Number(liftConfig.interlockMs) || 9000);
|
||||||
const commandCooldownMs = Math.max(interlockMs, Number(liftConfig.commandCooldownMs) || 15000);
|
const commandCooldownMs = Math.max(interlockMs, Number(liftConfig.commandCooldownMs) || 25000);
|
||||||
|
|
||||||
const state = {
|
const state = {
|
||||||
busy: false,
|
busy: false,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
// Vip Lift Card
|
// Vip Lift Card
|
||||||
// Purpose: Renders a shared lift controller panel synced from server session state.
|
// Purpose: Renders a shared lift controller panel synced from server session state.
|
||||||
// Scope: Presents verified-user controls while reflecting global busy/position/cooldown state.
|
// Scope: Presents verified-user controls while reflecting global busy/position/cooldown state.
|
||||||
import { useState } from 'react';
|
import { useEffect, useMemo, useState } from 'react';
|
||||||
|
|
||||||
function badgeClass(tone) {
|
function badgeClass(tone) {
|
||||||
if (tone === 'good') return 'bg-emerald-600 text-white';
|
if (tone === 'good') return 'bg-emerald-600 text-white';
|
||||||
@@ -20,6 +20,7 @@ function positionLabel(value) {
|
|||||||
|
|
||||||
export default function VipLiftCard({ lift, onUp, onDown, fullWidth = false }) {
|
export default function VipLiftCard({ lift, onUp, onDown, fullWidth = false }) {
|
||||||
const [working, setWorking] = useState('');
|
const [working, setWorking] = useState('');
|
||||||
|
const [nowMs, setNowMs] = useState(() => Date.now());
|
||||||
const wrapClass = fullWidth ? 'w-full' : 'w-full max-w-xl';
|
const wrapClass = fullWidth ? 'w-full' : 'w-full max-w-xl';
|
||||||
|
|
||||||
const configured = Boolean(lift?.configured);
|
const configured = Boolean(lift?.configured);
|
||||||
@@ -29,11 +30,29 @@ export default function VipLiftCard({ lift, onUp, onDown, fullWidth = false }) {
|
|||||||
const position = String(lift?.position || '').toLowerCase();
|
const position = String(lift?.position || '').toLowerCase();
|
||||||
const upAvailable = Boolean(lift?.availability?.upSwitch);
|
const upAvailable = Boolean(lift?.availability?.upSwitch);
|
||||||
const downAvailable = Boolean(lift?.availability?.downSwitch);
|
const downAvailable = Boolean(lift?.availability?.downSwitch);
|
||||||
|
const lastActionAt = Number(lift?.lastActionAt || 0);
|
||||||
|
const commandCooldownMs = Math.max(0, Number(lift?.commandCooldownMs || 0));
|
||||||
|
const cooldownRemainingMs = Math.max(0, lastActionAt + commandCooldownMs - nowMs);
|
||||||
|
const cooldownActive = !busy && cooldownRemainingMs > 0;
|
||||||
|
const blocked = busy || cooldownActive;
|
||||||
|
|
||||||
const status = !configured ? 'Not configured' : !connected ? 'Offline' : busy ? 'Busy' : 'Ready';
|
useEffect(() => {
|
||||||
const statusTone = !configured ? 'warn' : !connected ? 'danger' : busy ? 'warn' : 'good';
|
if (!blocked) return undefined;
|
||||||
|
const t = setInterval(() => {
|
||||||
|
setNowMs(Date.now());
|
||||||
|
}, 100);
|
||||||
|
return () => clearInterval(t);
|
||||||
|
}, [blocked]);
|
||||||
|
|
||||||
const canRun = configured && connected && !busy && !working && upAvailable && downAvailable;
|
useEffect(() => {
|
||||||
|
setNowMs(Date.now());
|
||||||
|
}, [lastActionAt, commandCooldownMs, busy]);
|
||||||
|
|
||||||
|
const status = !configured ? 'Not configured' : !connected ? 'Offline' : blocked ? 'Busy' : 'Ready';
|
||||||
|
const statusTone = !configured ? 'warn' : !connected ? 'danger' : blocked ? 'warn' : 'good';
|
||||||
|
|
||||||
|
const canRun = configured && connected && !blocked && !working && upAvailable && downAvailable;
|
||||||
|
const cooldownSeconds = useMemo(() => Math.ceil(cooldownRemainingMs / 1000), [cooldownRemainingMs]);
|
||||||
|
|
||||||
const run = async (dir, fn) => {
|
const run = async (dir, fn) => {
|
||||||
if (!fn) return;
|
if (!fn) return;
|
||||||
@@ -48,7 +67,22 @@ export default function VipLiftCard({ lift, onUp, onDown, fullWidth = false }) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={`surface text-sm text-slate-200 ${wrapClass}`}>
|
<section className={`surface relative text-sm text-slate-200 ${wrapClass}`}>
|
||||||
|
{blocked ? (
|
||||||
|
<div className="absolute inset-0 z-20 flex items-center justify-center rounded-md bg-slate-950/80 px-1.5 text-center">
|
||||||
|
<div className="space-y-0.25">
|
||||||
|
<p className="text-sm font-semibold text-slate-100">
|
||||||
|
{busy ? 'Motion in progress' : 'Motion cooldown active'}
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-slate-300">
|
||||||
|
Controls are disabled while the lift is moving, otherwise it's tiny brain would get confused.
|
||||||
|
</p>
|
||||||
|
{!busy && cooldownActive ? (
|
||||||
|
<p className="text-xs text-slate-400">Try again in {cooldownSeconds}s.</p>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
<div className="grid gap-0.5">
|
<div className="grid gap-0.5">
|
||||||
<div className="relative flex items-center justify-center min-h-[1.5rem]">
|
<div className="relative flex items-center justify-center min-h-[1.5rem]">
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
|
|||||||
Reference in New Issue
Block a user