address review feedback

- Discard the export row when an export fails before or during ffmpeg, so it doesn't stay in progress until a restart
- Skip the empty thumbnail path when discarding a failed export
- Measure preview clip duration from the clamped start
- Default live player volume to 1 when the group has no saved setting
- Clear the unsaved registration message when notification registration fails
- Keep a highlight timer per trigger
- Remove the severity order_by in /review, which the start_time order_by replaced
This commit is contained in:
Josh Hawkins
2026-11-01 07:29:16 -06:00
parent f68f360386
commit 9fca3f276d
7 changed files with 27 additions and 16 deletions
@@ -415,6 +415,7 @@ export default function NotificationsSettingsExtras({
pushSubscription.unsubscribe();
workerRegistration.unregister();
setRegistration(null);
removeMessage("notification_settings", "registration");
});
})
.catch(() => {
@@ -423,9 +424,10 @@ export default function NotificationsSettingsExtras({
});
workerRegistration.unregister();
setRegistration(null);
removeMessage("notification_settings", "registration");
});
},
[addMessage, publicKey, t],
[addMessage, removeMessage, publicKey, t],
);
useEffect(() => {
+2 -2
View File
@@ -794,7 +794,7 @@ export default function DraggableGridLayout({
toggleAudio={() => toggleAudio(camera.name)}
statsState={statsStates[camera.name]}
toggleStats={() => toggleStats(camera.name)}
volumeState={volumeStates[camera.name]}
volumeState={volumeStates[camera.name] ?? 1}
setVolumeState={(value) =>
setVolumeStates((prev) => ({
...prev,
@@ -839,7 +839,7 @@ export default function DraggableGridLayout({
onError={(e) => handleError(camera.name, e)}
onResetLiveMode={() => resetPreferredLiveMode(camera.name)}
playAudio={audioStates[camera.name]}
volume={volumeStates[camera.name]}
volume={volumeStates[camera.name] ?? 1}
onLiveAspectChange={liveAspectHandlers[camera.name]}
/>
</GridLiveContextMenu>
+1 -1
View File
@@ -629,7 +629,7 @@ export default function LiveDashboardView({
resetPreferredLiveMode(camera.name)
}
playAudio={audioStates[camera.name] ?? false}
volume={volumeStates[camera.name]}
volume={volumeStates[camera.name] ?? 1}
/>
</LiveContextMenu>
);
+8 -5
View File
@@ -1,4 +1,4 @@
import { useCallback, useEffect, useMemo, useState } from "react";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { toast } from "sonner";
import useSWR from "swr";
@@ -133,6 +133,9 @@ export default function TriggerView({
// watch websocket for updates
const { payload: triggers_status_ws } = useTriggers();
const highlightTimers = useRef<Record<string, ReturnType<typeof setTimeout>>>(
{},
);
useEffect(() => {
if (!triggers_status_ws) return;
@@ -148,11 +151,11 @@ export default function TriggerView({
return current;
});
// not cleared on the next payload, or a second trigger firing within
// 3s would leave this one highlighted
setTimeout(() => {
const name = triggers_status_ws.name;
clearTimeout(highlightTimers.current[name]);
highlightTimers.current[name] = setTimeout(() => {
setTriggeredTrigger((prev) =>
(prev || []).filter((name) => name !== triggers_status_ws.name),
(prev || []).filter((triggered) => triggered !== name),
);
}, 3000);
}, [triggers_status_ws, selectedCamera, mutate]);