mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-12-06 21:44:13 +03:00
always show camera group buttons on mobile so users don't get stuck
This commit is contained in:
parent
bb31dd18a4
commit
7a7ab98888
@ -177,6 +177,10 @@
|
||||
"noCameras": {
|
||||
"title": "No Cameras Configured",
|
||||
"description": "Get started by connecting a camera to Frigate.",
|
||||
"buttonText": "Add Camera"
|
||||
"buttonText": "Add Camera",
|
||||
"restricted": {
|
||||
"title": "No Cameras Available",
|
||||
"description": "You don't have permission to view any cameras in this group."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,7 +20,14 @@ import {
|
||||
FrigateConfig,
|
||||
} from "@/types/frigateConfig";
|
||||
import { ReviewSegment } from "@/types/review";
|
||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||
import {
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
} from "react";
|
||||
import {
|
||||
isDesktop,
|
||||
isMobile,
|
||||
@ -46,6 +53,8 @@ import { useStreamingSettings } from "@/context/streaming-settings-provider";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { EmptyCard } from "@/components/card/EmptyCard";
|
||||
import { BsFillCameraVideoOffFill } from "react-icons/bs";
|
||||
import { AuthContext } from "@/context/auth-context";
|
||||
import { useIsCustomRole } from "@/hooks/use-is-custom-role";
|
||||
|
||||
type LiveDashboardViewProps = {
|
||||
cameras: CameraConfig[];
|
||||
@ -374,10 +383,6 @@ export default function LiveDashboardView({
|
||||
onSaveMuting(true);
|
||||
};
|
||||
|
||||
if (cameras.length == 0 && !includeBirdseye) {
|
||||
return <NoCameraView />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className="scrollbar-container size-full select-none overflow-y-auto px-1 pt-2 md:p-2"
|
||||
@ -439,6 +444,10 @@ export default function LiveDashboardView({
|
||||
</div>
|
||||
)}
|
||||
|
||||
{cameras.length == 0 && !includeBirdseye ? (
|
||||
<NoCameraView />
|
||||
) : (
|
||||
<>
|
||||
{!fullscreen && events && events.length > 0 && (
|
||||
<ScrollArea>
|
||||
<TooltipProvider>
|
||||
@ -494,7 +503,8 @@ export default function LiveDashboardView({
|
||||
)}
|
||||
{cameras.map((camera) => {
|
||||
let grow;
|
||||
const aspectRatio = camera.detect.width / camera.detect.height;
|
||||
const aspectRatio =
|
||||
camera.detect.width / camera.detect.height;
|
||||
if (aspectRatio > 2) {
|
||||
grow = `${mobileLayout == "grid" && "col-span-2"} aspect-wide`;
|
||||
} else if (aspectRatio < 1) {
|
||||
@ -503,10 +513,12 @@ export default function LiveDashboardView({
|
||||
grow = "aspect-video";
|
||||
}
|
||||
const availableStreams = camera.live.streams || {};
|
||||
const firstStreamEntry = Object.values(availableStreams)[0] || "";
|
||||
const firstStreamEntry =
|
||||
Object.values(availableStreams)[0] || "";
|
||||
|
||||
const streamNameFromSettings =
|
||||
currentGroupStreamingSettings?.[camera.name]?.streamName || "";
|
||||
currentGroupStreamingSettings?.[camera.name]?.streamName ||
|
||||
"";
|
||||
const streamExists =
|
||||
streamNameFromSettings &&
|
||||
Object.values(availableStreams).includes(
|
||||
@ -535,7 +547,9 @@ export default function LiveDashboardView({
|
||||
camera={camera.name}
|
||||
cameraGroup={cameraGroup}
|
||||
streamName={streamName}
|
||||
preferredLiveMode={preferredLiveModes[camera.name] ?? "mse"}
|
||||
preferredLiveMode={
|
||||
preferredLiveModes[camera.name] ?? "mse"
|
||||
}
|
||||
isRestreamed={isRestreamedStates[camera.name]}
|
||||
supportsAudio={
|
||||
supportsAudioOutputStates[streamName]?.supportsAudio ??
|
||||
@ -566,9 +580,13 @@ export default function LiveDashboardView({
|
||||
windowVisible && visibleCameras.includes(camera.name)
|
||||
}
|
||||
cameraConfig={camera}
|
||||
preferredLiveMode={preferredLiveModes[camera.name] ?? "mse"}
|
||||
preferredLiveMode={
|
||||
preferredLiveModes[camera.name] ?? "mse"
|
||||
}
|
||||
autoLive={autoLive ?? globalAutoLive}
|
||||
showStillWithoutActivity={showStillWithoutActivity ?? true}
|
||||
showStillWithoutActivity={
|
||||
showStillWithoutActivity ?? true
|
||||
}
|
||||
alwaysShowCameraName={displayCameraNames}
|
||||
useWebGL={useWebGL}
|
||||
playInBackground={false}
|
||||
@ -576,7 +594,9 @@ export default function LiveDashboardView({
|
||||
streamName={streamName}
|
||||
onClick={() => onSelectCamera(camera.name)}
|
||||
onError={(e) => handleError(camera.name, e)}
|
||||
onResetLiveMode={() => resetPreferredLiveMode(camera.name)}
|
||||
onResetLiveMode={() =>
|
||||
resetPreferredLiveMode(camera.name)
|
||||
}
|
||||
playAudio={audioStates[camera.name] ?? false}
|
||||
volume={volumeStates[camera.name]}
|
||||
/>
|
||||
@ -632,21 +652,34 @@ export default function LiveDashboardView({
|
||||
toggleFullscreen={toggleFullscreen}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function NoCameraView() {
|
||||
const { t } = useTranslation(["views/live"]);
|
||||
const { auth } = useContext(AuthContext);
|
||||
const isCustomRole = useIsCustomRole();
|
||||
|
||||
// Check if this is a restricted user with no cameras in this group
|
||||
const isRestricted = isCustomRole && auth.isAuthenticated;
|
||||
|
||||
return (
|
||||
<div className="flex size-full items-center justify-center">
|
||||
<EmptyCard
|
||||
icon={<BsFillCameraVideoOffFill className="size-8" />}
|
||||
title={t("noCameras.title")}
|
||||
description={t("noCameras.description")}
|
||||
buttonText={t("noCameras.buttonText")}
|
||||
link="/settings?page=cameraManagement"
|
||||
title={
|
||||
isRestricted ? t("noCameras.restricted.title") : t("noCameras.title")
|
||||
}
|
||||
description={
|
||||
isRestricted
|
||||
? t("noCameras.restricted.description")
|
||||
: t("noCameras.description")
|
||||
}
|
||||
buttonText={!isRestricted ? t("noCameras.buttonText") : undefined}
|
||||
link={!isRestricted ? "/settings?page=cameraManagement" : undefined}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user