From bcc59e91093655839666ede31665bd7feb1d8470 Mon Sep 17 00:00:00 2001
From: Scott Lamb
Date: Fri, 4 Mar 2022 11:21:56 -0800
Subject: [PATCH] plumb more api response through to list view
This keeps a coarser-grained `toplevel` property rather than `user`
and `session`. It also synthesizes a `streams` field within it with ids.
This makes it easier to put the streams in the URL by id.
---
ui/src/App.tsx | 23 +++++++++--------------
ui/src/AppMenu.tsx | 2 --
ui/src/List/StreamMultiSelector.tsx | 9 +++++----
ui/src/List/index.tsx | 8 ++++----
ui/src/api.ts | 5 ++++-
ui/src/types.ts | 1 +
6 files changed, 23 insertions(+), 25 deletions(-)
diff --git a/ui/src/App.tsx b/ui/src/App.tsx
index db85979..13bed3c 100644
--- a/ui/src/App.tsx
+++ b/ui/src/App.tsx
@@ -8,7 +8,6 @@ import * as api from "./api";
import MoonfireMenu from "./AppMenu";
import Login from "./Login";
import { useSnackbars } from "./snackbars";
-import { Camera, Session } from "./types";
import ListActivity from "./List";
import AppBar from "@mui/material/AppBar";
import {
@@ -53,8 +52,7 @@ function App() {
const [multiviewLayoutIndex, setMultiviewLayoutIndex] = useState(
Number.parseInt(searchParams.get("layout") || "0", 10)
);
- const [session, setSession] = useState(null);
- const [cameras, setCameras] = useState(null);
+ const [toplevel, setToplevel] = useState(null);
const [timeZoneName, setTimeZoneName] = useState(null);
const [fetchSeq, setFetchSeq] = useState(0);
const [loginState, setLoginState] = useState("unknown");
@@ -75,7 +73,7 @@ function App() {
const logout = async () => {
const resp = await api.logout(
{
- csrf: session!.csrf,
+ csrf: toplevel!.user!.session!.csrf,
},
{}
);
@@ -88,21 +86,20 @@ function App() {
});
break;
case "success":
- setSession(null);
needNewFetch();
break;
}
};
- function fetchedCameras(cameras: Camera[] | null) {
- if (cameras !== null && cameras.length > 0) {
+ function fetchedToplevel(toplevel: api.ToplevelResponse | null) {
+ if (toplevel !== null && toplevel.cameras.length > 0) {
return (
<>
@@ -112,7 +109,7 @@ function App() {
path="live"
element={
}
@@ -143,8 +140,7 @@ function App() {
? "not-logged-in"
: "logged-in"
);
- setSession(resp.response.user?.session || null);
- setCameras(resp.response.cameras);
+ setToplevel(resp.response);
setTimeZoneName(resp.response.timeZoneName);
}
};
@@ -154,7 +150,7 @@ function App() {
};
}, [fetchSeq]);
let activityMenu = null;
- if (error === null && cameras !== null && cameras.length > 0) {
+ if (error === null && toplevel !== null && toplevel.cameras.length > 0) {
switch (activity) {
case "list":
activityMenu = (
@@ -186,7 +182,6 @@ function App() {
{
setLoginState("user-requested-login");
}}
@@ -252,7 +247,7 @@ function App() {
)}
- {fetchedCameras(cameras)}
+ {fetchedToplevel(toplevel)}
>
);
}
diff --git a/ui/src/AppMenu.tsx b/ui/src/AppMenu.tsx
index 4a82e46..a94143f 100644
--- a/ui/src/AppMenu.tsx
+++ b/ui/src/AppMenu.tsx
@@ -14,7 +14,6 @@ import AccountCircle from "@mui/icons-material/AccountCircle";
import MenuIcon from "@mui/icons-material/Menu";
import React from "react";
import { LoginState } from "./App";
-import { Session } from "./types";
const useStyles = makeStyles((theme: Theme) =>
createStyles({
@@ -29,7 +28,6 @@ const useStyles = makeStyles((theme: Theme) =>
interface Props {
loginState: LoginState;
- setSession: (session: Session | null) => void;
requestLogin: () => void;
logout: () => void;
menuClick?: () => void;
diff --git a/ui/src/List/StreamMultiSelector.tsx b/ui/src/List/StreamMultiSelector.tsx
index 9da2427..27d95ef 100644
--- a/ui/src/List/StreamMultiSelector.tsx
+++ b/ui/src/List/StreamMultiSelector.tsx
@@ -7,9 +7,10 @@ import { Camera, Stream, StreamType } from "../types";
import Checkbox from "@mui/material/Checkbox";
import { useTheme } from "@mui/material/styles";
import { makeStyles } from "@mui/styles";
+import { ToplevelResponse } from "../api";
interface Props {
- cameras: Camera[];
+ toplevel: ToplevelResponse;
selected: Set;
setSelected: (selected: Set) => void;
}
@@ -35,7 +36,7 @@ const useStyles = makeStyles({
});
/** Returns a table which allows selecting zero or more streams. */
-const StreamMultiSelector = ({ cameras, selected, setSelected }: Props) => {
+const StreamMultiSelector = ({ toplevel, selected, setSelected }: Props) => {
const theme = useTheme();
const classes = useStyles();
const setStream = (s: Stream, checked: boolean) => {
@@ -57,7 +58,7 @@ const StreamMultiSelector = ({ cameras, selected, setSelected }: Props) => {
}
}
if (!foundAny) {
- for (const c of cameras) {
+ for (const c of toplevel.cameras) {
if (c.streams[st] !== undefined) {
updated.add(c.streams[st as StreamType]!);
}
@@ -83,7 +84,7 @@ const StreamMultiSelector = ({ cameras, selected, setSelected }: Props) => {
setSelected(updated);
};
- const cameraRows = cameras.map((c) => {
+ const cameraRows = toplevel.cameras.map((c) => {
function checkbox(st: StreamType) {
const s = c.streams[st];
if (s === undefined) {
diff --git a/ui/src/List/index.tsx b/ui/src/List/index.tsx
index fedc4a9..d54163a 100644
--- a/ui/src/List/index.tsx
+++ b/ui/src/List/index.tsx
@@ -13,7 +13,7 @@ import utcToZonedTime from "date-fns-tz/utcToZonedTime";
import format from "date-fns/format";
import React, { useMemo, useState } from "react";
import * as api from "../api";
-import { Camera, Stream } from "../types";
+import { Stream } from "../types";
import DisplaySelector, { DEFAULT_DURATION } from "./DisplaySelector";
import StreamMultiSelector from "./StreamMultiSelector";
import TimerangeSelector from "./TimerangeSelector";
@@ -96,11 +96,11 @@ const FullScreenVideo = ({ src, aspect }: FullScreenVideoProps) => {
interface Props {
timeZoneName: string;
- cameras: Camera[];
+ toplevel: api.ToplevelResponse;
showSelectors: boolean;
}
-const Main = ({ cameras, timeZoneName, showSelectors }: Props) => {
+const Main = ({ toplevel, timeZoneName, showSelectors }: Props) => {
const classes = useStyles();
/**
@@ -161,7 +161,7 @@ const Main = ({ cameras, timeZoneName, showSelectors }: Props) => {
sx={{ display: showSelectors ? "block" : "none" }}
>
diff --git a/ui/src/api.ts b/ui/src/api.ts
index 25e4457..8c57b56 100644
--- a/ui/src/api.ts
+++ b/ui/src/api.ts
@@ -13,7 +13,7 @@
* This seems convenient for ensuring the caller handles all possibilities.
*/
-import { Camera, Session } from "./types";
+import { Camera, Session, Stream } from "./types";
export type StreamType = "main" | "sub";
@@ -157,6 +157,7 @@ async function json(
export interface ToplevelResponse {
timeZoneName: string;
cameras: Camera[];
+ streams: Map;
user: ToplevelUser | undefined;
}
@@ -170,11 +171,13 @@ export interface ToplevelUser {
export async function toplevel(init: RequestInit) {
const resp = await json("/api/?days=true", init);
if (resp.status === "success") {
+ resp.response.streams = new Map();
resp.response.cameras.forEach((c) => {
for (const key in c.streams) {
const s = c.streams[key as StreamType]!;
s.camera = c;
s.streamType = key as StreamType;
+ resp.response.streams.set(s.id, s);
}
});
}
diff --git a/ui/src/types.ts b/ui/src/types.ts
index 464e909..c6272ed 100644
--- a/ui/src/types.ts
+++ b/ui/src/types.ts
@@ -22,6 +22,7 @@ export interface Camera {
export interface Stream {
camera: Camera; // back-reference added within api.ts.
+ id: number;
streamType: StreamType; // likewise.
retainBytes: number;
minStartTime90k: number;