mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-17 18:10:47 -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/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>
|
<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>
|
<title>Roomba Rover</title>
|
||||||
<script type="module" crossorigin src="/assets/index-pC9M0gPv.js"></script>
|
<script type="module" crossorigin src="/assets/index-eqv2XD2p.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-BN3kEVFL.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-B_3-jvHM.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="root"></div>
|
<div id="root"></div>
|
||||||
|
|||||||
@@ -1013,9 +1013,10 @@ function requirePtzUser(socket) {
|
|||||||
|
|
||||||
function requirePresetAdmin(socket) {
|
function requirePresetAdmin(socket) {
|
||||||
/*
|
/*
|
||||||
Preset creation and deletion changes shared camera state for everyone. Keep
|
Preset removal is intentionally narrower than normal PTZ operation because
|
||||||
that narrower than normal PTZ operation so regular camera users can only
|
deleting a shared camera position is destructive for every future operator.
|
||||||
choose from positions an admin has intentionally published.
|
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 (!enabled) throw new Error('PTZ camera disabled');
|
||||||
if (!passesMode(socket)) throw new Error('Not authorized for PTZ camera');
|
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 = {}) {
|
async function createPreset(socket, payload = {}) {
|
||||||
requirePresetAdmin(socket);
|
requirePtzUser(socket);
|
||||||
await initialize();
|
await initialize();
|
||||||
const presetName = normalizePresetCreateName(payload.name || payload.presetName);
|
const presetName = normalizePresetCreateName(payload.name || payload.presetName);
|
||||||
const options = {
|
const options = {
|
||||||
|
|||||||
@@ -372,6 +372,7 @@ function PtzPresetPanel({ ptz }) {
|
|||||||
const [busy, setBusy] = useState('');
|
const [busy, setBusy] = useState('');
|
||||||
const presets = Array.isArray(ptz?.presets) ? ptz.presets : [];
|
const presets = Array.isArray(ptz?.presets) ? ptz.presets : [];
|
||||||
const isPresetAdmin = role === 'admin' || role === 'lockdown';
|
const isPresetAdmin = role === 'admin' || role === 'lockdown';
|
||||||
|
const canCreatePreset = Boolean(ptz?.canUse);
|
||||||
const canMoveToPreset = Boolean(ptz?.isOperator);
|
const canMoveToPreset = Boolean(ptz?.isOperator);
|
||||||
|
|
||||||
const refreshPresets = async () => {
|
const refreshPresets = async () => {
|
||||||
@@ -410,13 +411,14 @@ function PtzPresetPanel({ ptz }) {
|
|||||||
const createPreset = async (event) => {
|
const createPreset = async (event) => {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
const trimmed = name.trim();
|
const trimmed = name.trim();
|
||||||
if (!isPresetAdmin || busy || !trimmed) return;
|
if (!canCreatePreset || busy || !trimmed) return;
|
||||||
setBusy('create');
|
setBusy('create');
|
||||||
try {
|
try {
|
||||||
/*
|
/*
|
||||||
ONVIF setPreset stores the camera's current physical position. The UI
|
ONVIF setPreset stores the camera's current physical position. The UI
|
||||||
only sends the admin's label; the server supplies the active profile
|
only sends the user's label; the server supplies the active profile
|
||||||
token so browser code does not need to know camera profile internals.
|
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 });
|
await ptzCreatePreset({ name: trimmed });
|
||||||
setName('');
|
setName('');
|
||||||
@@ -461,15 +463,20 @@ function PtzPresetPanel({ ptz }) {
|
|||||||
{ptz.presetsError}
|
{ptz.presetsError}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : 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) => {
|
{presets.length ? presets.map((preset) => {
|
||||||
const gotoBusy = busy === `goto:${preset.token}`;
|
const gotoBusy = busy === `goto:${preset.token}`;
|
||||||
const removeBusy = busy === `remove:${preset.token}`;
|
const removeBusy = busy === `remove:${preset.token}`;
|
||||||
return (
|
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
|
<button
|
||||||
type="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)}
|
disabled={!canMoveToPreset || Boolean(busy)}
|
||||||
onClick={() => goToPreset(preset)}
|
onClick={() => goToPreset(preset)}
|
||||||
title={canMoveToPreset ? `Move to ${preset.name}` : 'Your PTZ turn must be active'}
|
title={canMoveToPreset ? `Move to ${preset.name}` : 'Your PTZ turn must be active'}
|
||||||
@@ -494,10 +501,10 @@ function PtzPresetPanel({ ptz }) {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
{isPresetAdmin ? (
|
{canCreatePreset ? (
|
||||||
<form className="grid grid-cols-[minmax(0,1fr)_auto] gap-1" onSubmit={createPreset}>
|
<form className="grid grid-cols-[minmax(0,1fr)_auto] gap-1" onSubmit={createPreset}>
|
||||||
<input
|
<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}
|
value={name}
|
||||||
maxLength={60}
|
maxLength={60}
|
||||||
disabled={Boolean(busy)}
|
disabled={Boolean(busy)}
|
||||||
|
|||||||
Reference in New Issue
Block a user