mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-15 17:12:59 -04:00
let everyone make ptz presets.. .. .. .. .. . . . .
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -78,8 +78,8 @@
|
||||
<script defer src="https://analytics.otter.land/script.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land"></script>
|
||||
<script defer src="https://analytics.otter.land/recorder.js" data-website-id="82dd56a5-db44-4279-bd1e-a4d9fee39af7" data-domains="rover.otter.land" data-sample-rate="0.15" data-mask-level="moderate" data-max-duration="300000"></script>
|
||||
<title>Roomba Rover</title>
|
||||
<script type="module" crossorigin src="/assets/index-pC9M0gPv.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-BN3kEVFL.css">
|
||||
<script type="module" crossorigin src="/assets/index-eqv2XD2p.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-B_3-jvHM.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
|
||||
@@ -1013,9 +1013,10 @@ function requirePtzUser(socket) {
|
||||
|
||||
function requirePresetAdmin(socket) {
|
||||
/*
|
||||
Preset creation and deletion changes shared camera state for everyone. Keep
|
||||
that narrower than normal PTZ operation so regular camera users can only
|
||||
choose from positions an admin has intentionally published.
|
||||
Preset removal is intentionally narrower than normal PTZ operation because
|
||||
deleting a shared camera position is destructive for every future operator.
|
||||
Creation now uses requirePtzUser instead so any authorized PTZ user can save
|
||||
a useful current position without also being allowed to remove presets.
|
||||
*/
|
||||
if (!enabled) throw new Error('PTZ camera disabled');
|
||||
if (!passesMode(socket)) throw new Error('Not authorized for PTZ camera');
|
||||
@@ -1117,7 +1118,7 @@ async function gotoPreset(socket, payload = {}) {
|
||||
}
|
||||
|
||||
async function createPreset(socket, payload = {}) {
|
||||
requirePresetAdmin(socket);
|
||||
requirePtzUser(socket);
|
||||
await initialize();
|
||||
const presetName = normalizePresetCreateName(payload.name || payload.presetName);
|
||||
const options = {
|
||||
|
||||
@@ -372,6 +372,7 @@ function PtzPresetPanel({ ptz }) {
|
||||
const [busy, setBusy] = useState('');
|
||||
const presets = Array.isArray(ptz?.presets) ? ptz.presets : [];
|
||||
const isPresetAdmin = role === 'admin' || role === 'lockdown';
|
||||
const canCreatePreset = Boolean(ptz?.canUse);
|
||||
const canMoveToPreset = Boolean(ptz?.isOperator);
|
||||
|
||||
const refreshPresets = async () => {
|
||||
@@ -410,13 +411,14 @@ function PtzPresetPanel({ ptz }) {
|
||||
const createPreset = async (event) => {
|
||||
event.preventDefault();
|
||||
const trimmed = name.trim();
|
||||
if (!isPresetAdmin || busy || !trimmed) return;
|
||||
if (!canCreatePreset || busy || !trimmed) return;
|
||||
setBusy('create');
|
||||
try {
|
||||
/*
|
||||
ONVIF setPreset stores the camera's current physical position. The UI
|
||||
only sends the admin's label; the server supplies the active profile
|
||||
token so browser code does not need to know camera profile internals.
|
||||
only sends the user's label; the server supplies the active profile
|
||||
token so browser code does not need camera profile internals, and the
|
||||
server still enforces the PTZ feature gate for raw socket callers.
|
||||
*/
|
||||
await ptzCreatePreset({ name: trimmed });
|
||||
setName('');
|
||||
@@ -461,15 +463,20 @@ function PtzPresetPanel({ ptz }) {
|
||||
{ptz.presetsError}
|
||||
</div>
|
||||
) : null}
|
||||
<div className="min-h-0 flex-1 space-y-0.5 overflow-y-auto">
|
||||
<div className="flex min-h-0 flex-1 flex-wrap content-start items-start gap-0.5 overflow-y-auto">
|
||||
{/*
|
||||
Presets should behave like a compact pile of actions, not a table.
|
||||
flex-wrap lets each preset keep its natural button width and only
|
||||
starts a new visual line when the current line runs out of room.
|
||||
*/}
|
||||
{presets.length ? presets.map((preset) => {
|
||||
const gotoBusy = busy === `goto:${preset.token}`;
|
||||
const removeBusy = busy === `remove:${preset.token}`;
|
||||
return (
|
||||
<div key={preset.token} className="surface grid grid-cols-[minmax(0,1fr)_auto] items-center gap-1">
|
||||
<div key={preset.token} className="surface inline-flex max-w-full items-center gap-1">
|
||||
<button
|
||||
type="button"
|
||||
className="button-dark min-w-0 truncate text-left text-xs disabled:opacity-50"
|
||||
className="button-dark min-w-0 max-w-40 truncate text-left text-xs disabled:opacity-50"
|
||||
disabled={!canMoveToPreset || Boolean(busy)}
|
||||
onClick={() => goToPreset(preset)}
|
||||
title={canMoveToPreset ? `Move to ${preset.name}` : 'Your PTZ turn must be active'}
|
||||
@@ -494,10 +501,10 @@ function PtzPresetPanel({ ptz }) {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{isPresetAdmin ? (
|
||||
{canCreatePreset ? (
|
||||
<form className="grid grid-cols-[minmax(0,1fr)_auto] gap-1" onSubmit={createPreset}>
|
||||
<input
|
||||
className="min-w-0 rounded border border-slate-700 bg-black px-2 py-1 text-xs text-slate-100 outline-none focus:border-cyan-300"
|
||||
className="field-input min-w-0 text-xs"
|
||||
value={name}
|
||||
maxLength={60}
|
||||
disabled={Boolean(busy)}
|
||||
|
||||
Reference in New Issue
Block a user