// This file is part of Moonfire NVR, a security camera network video recorder. // Copyright (C) 2021 The Moonfire NVR Authors; see AUTHORS and LICENSE.txt. // SPDX-License-Identifier: GPL-v3.0-or-later WITH GPL-3.0-linking-exception import Container from "@material-ui/core/Container"; import React, { useEffect, useReducer, useState } from "react"; 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 "@material-ui/core/AppBar"; import LiveActivity, { MultiviewChooser } from "./Live"; import Drawer from "@material-ui/core/Drawer"; import List from "@material-ui/core/List"; import ListItem from "@material-ui/core/ListItem"; import ListItemText from "@material-ui/core/ListItemText"; import ListIcon from "@material-ui/icons/List"; import Videocam from "@material-ui/icons/Videocam"; import ListItemIcon from "@material-ui/core/ListItemIcon"; import FilterList from "@material-ui/icons/FilterList"; import IconButton from "@material-ui/core/IconButton"; export type LoginState = | "unknown" | "logged-in" | "not-logged-in" | "server-requires-login" | "user-requested-login"; type Activity = "list" | "live"; function App() { const [showMenu, toggleShowMenu] = useReducer((m: boolean) => !m, false); const [showListSelectors, toggleShowListSelectors] = useReducer( (m: boolean) => !m, true ); const [activity, setActivity] = useState("list"); const [multiviewLayoutIndex, setMultiviewLayoutIndex] = useState(0); const [session, setSession] = useState(null); const [cameras, setCameras] = useState(null); const [timeZoneName, setTimeZoneName] = useState(null); const [fetchSeq, setFetchSeq] = useState(0); const [loginState, setLoginState] = useState("unknown"); const [error, setError] = useState(null); const needNewFetch = () => setFetchSeq((seq) => seq + 1); const snackbars = useSnackbars(); const clickActivity = (activity: Activity) => { toggleShowMenu(); setActivity(activity); }; const onLoginSuccess = () => { setLoginState("logged-in"); needNewFetch(); }; const logout = async () => { const resp = await api.logout( { csrf: session!.csrf, }, {} ); switch (resp.status) { case "aborted": break; case "error": snackbars.enqueue({ message: "Logout failed: " + resp.message, }); break; case "success": setSession(null); needNewFetch(); break; } }; useEffect(() => { const abort = new AbortController(); const doFetch = async (signal: AbortSignal) => { const resp = await api.toplevel({ signal }); switch (resp.status) { case "aborted": break; case "error": if (resp.httpStatus === 401) { setLoginState("server-requires-login"); return; } setError(resp); break; case "success": setError(null); setLoginState( resp.response.user?.session === undefined ? "not-logged-in" : "logged-in" ); setSession(resp.response.user?.session || null); setCameras(resp.response.cameras); setTimeZoneName(resp.response.timeZoneName); } }; doFetch(abort.signal); return () => { abort.abort(); }; }, [fetchSeq]); let activityMenu = null; let activityMain = null; if (error === null && cameras !== null && cameras.length > 0) { switch (activity) { case "list": activityMenu = ( ); activityMain = ( ); break; case "live": activityMenu = ( ); activityMain = ( ); break; } } return ( <> { setLoginState("user-requested-login"); }} logout={logout} menuClick={toggleShowMenu} activityMenuPart={activityMenu} /> clickActivity("list")}> clickActivity("live")}> { setLoginState((s) => s === "user-requested-login" ? "not-logged-in" : s ); }} /> {error !== null && (

Error querying server

{error.message}

You may find more information in the Javascript console. Try reloading the page once you believe the problem is resolved.

)} {activityMain} ); } export default App;