mirror of
https://github.com/blakeblackshear/frigate.git
synced 2025-07-26 13:47:03 +02:00
feat(web): activity indicator while loading
This commit is contained in:
parent
cc7929932b
commit
45526a7652
@ -1,4 +1,5 @@
|
|||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
|
import ActivityIndicator from './components/ActivityIndicator';
|
||||||
import Camera from './Camera';
|
import Camera from './Camera';
|
||||||
import CameraMap from './CameraMap';
|
import CameraMap from './CameraMap';
|
||||||
import Cameras from './Cameras';
|
import Cameras from './Cameras';
|
||||||
@ -7,12 +8,14 @@ import Event from './Event';
|
|||||||
import Events from './Events';
|
import Events from './Events';
|
||||||
import { Router } from 'preact-router';
|
import { Router } from 'preact-router';
|
||||||
import Sidebar from './Sidebar';
|
import Sidebar from './Sidebar';
|
||||||
import Api, { useConfig } from './api';
|
import Api, { FetchStatus, useConfig } from './api';
|
||||||
|
|
||||||
export default function App() {
|
export default function App() {
|
||||||
const { data, status } = useConfig();
|
const { data, status } = useConfig();
|
||||||
return !data ? (
|
return status !== FetchStatus.LOADED ? (
|
||||||
<div />
|
<div className="flex flex-grow-1 min-h-screen justify-center items-center">
|
||||||
|
<ActivityIndicator />
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="md:flex flex-col md:flex-row md:min-h-screen w-full bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white">
|
<div className="md:flex flex-col md:flex-row md:min-h-screen w-full bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white">
|
||||||
<Sidebar />
|
<Sidebar />
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
|
import ActivityIndicator from './components/ActivityIndicator';
|
||||||
import Box from './components/Box';
|
import Box from './components/Box';
|
||||||
import CameraImage from './components/CameraImage';
|
import CameraImage from './components/CameraImage';
|
||||||
import Events from './Events';
|
import Events from './Events';
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
|
import ActivityIndicator from './components/ActivityIndicator';
|
||||||
import Box from './components/Box';
|
import Box from './components/Box';
|
||||||
import Button from './components/Button';
|
import Button from './components/Button';
|
||||||
import Heading from './components/Heading';
|
import Heading from './components/Heading';
|
||||||
import Link from './components/Link';
|
import Link from './components/Link';
|
||||||
import { useConfig, useStats } from './api';
|
import { FetchStatus, useConfig, useStats } from './api';
|
||||||
import { Table, Tbody, Thead, Tr, Th, Td } from './components/Table';
|
import { Table, Tbody, Thead, Tr, Th, Td } from './components/Table';
|
||||||
import { useCallback, useEffect, useState } from 'preact/hooks';
|
import { useCallback, useEffect, useState } from 'preact/hooks';
|
||||||
|
|
||||||
@ -27,8 +28,8 @@ export default function Debug() {
|
|||||||
}, [timeoutId]);
|
}, [timeoutId]);
|
||||||
const { data: stats, status } = useStats(null, timeoutId);
|
const { data: stats, status } = useStats(null, timeoutId);
|
||||||
|
|
||||||
if (!stats) {
|
if (stats === null && (status === FetchStatus.LOADING || status === FetchStatus.NONE)) {
|
||||||
return 'loading…';
|
return <ActivityIndicator />;
|
||||||
}
|
}
|
||||||
|
|
||||||
const { detectors, detection_fps, service, ...cameras } = stats;
|
const { detectors, detection_fps, service, ...cameras } = stats;
|
||||||
|
@ -1,21 +1,17 @@
|
|||||||
import { h, Fragment } from 'preact';
|
import { h, Fragment } from 'preact';
|
||||||
|
import ActivityIndicator from './components/ActivityIndicator';
|
||||||
import Box from './components/Box';
|
import Box from './components/Box';
|
||||||
import Heading from './components/Heading';
|
import Heading from './components/Heading';
|
||||||
import Link from './components/Link';
|
import Link from './components/Link';
|
||||||
|
import { FetchStatus, useApiHost, useEvent } from './api';
|
||||||
import { Table, Thead, Tbody, Tfoot, Th, Tr, Td } from './components/Table';
|
import { Table, Thead, Tbody, Tfoot, Th, Tr, Td } from './components/Table';
|
||||||
import { useApiHost, useEvent } from './api';
|
|
||||||
|
|
||||||
export default function Event({ eventId }) {
|
export default function Event({ eventId }) {
|
||||||
const apiHost = useApiHost();
|
const apiHost = useApiHost();
|
||||||
const { data } = useEvent(eventId);
|
const { data, status } = useEvent(eventId);
|
||||||
|
|
||||||
if (!data) {
|
if (status !== FetchStatus.LOADED) {
|
||||||
return (
|
return <ActivityIndicator />;
|
||||||
<div>
|
|
||||||
<Heading>{eventId}</Heading>
|
|
||||||
<p>loading…</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const startime = new Date(data.start_time * 1000);
|
const startime = new Date(data.start_time * 1000);
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
|
import ActivityIndicator from './components/ActivityIndicator';
|
||||||
import Box from './components/Box';
|
import Box from './components/Box';
|
||||||
import Heading from './components/Heading';
|
import Heading from './components/Heading';
|
||||||
import Link from './components/Link';
|
import Link from './components/Link';
|
||||||
import produce from 'immer';
|
import produce from 'immer';
|
||||||
import { route } from 'preact-router';
|
import { route } from 'preact-router';
|
||||||
|
import { FetchStatus, useApiHost, useConfig, useEvents } from './api';
|
||||||
import { Table, Thead, Tbody, Tfoot, Th, Tr, Td } from './components/Table';
|
import { Table, Thead, Tbody, Tfoot, Th, Tr, Td } from './components/Table';
|
||||||
import { useApiHost, useConfig, useEvents } from './api';
|
|
||||||
import { useCallback, useContext, useEffect, useMemo, useRef, useReducer, useState } from 'preact/hooks';
|
import { useCallback, useContext, useEffect, useMemo, useRef, useReducer, useState } from 'preact/hooks';
|
||||||
|
|
||||||
const API_LIMIT = 25;
|
const API_LIMIT = 25;
|
||||||
@ -194,8 +195,8 @@ export default function Events({ path: pathname } = {}) {
|
|||||||
</Tbody>
|
</Tbody>
|
||||||
<Tfoot>
|
<Tfoot>
|
||||||
<Tr>
|
<Tr>
|
||||||
<Td className="text-center" colspan="8">
|
<Td className="text-center p-4" colspan="8">
|
||||||
{status === 'loading' ? 'Loading…' : reachedEnd ? 'No more events' : null}
|
{status === FetchStatus.LOADING ? <ActivityIndicator /> : reachedEnd ? 'No more events' : null}
|
||||||
</Td>
|
</Td>
|
||||||
</Tr>
|
</Tr>
|
||||||
</Tfoot>
|
</Tfoot>
|
||||||
|
15
web/src/components/ActivityIndicator.jsx
Normal file
15
web/src/components/ActivityIndicator.jsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { h } from 'preact';
|
||||||
|
|
||||||
|
const sizes = {
|
||||||
|
sm: 'h-4 w-4 border-2 border-t-2',
|
||||||
|
md: 'h-8 w-8 border-4 border-t-4',
|
||||||
|
lg: 'h-16 w-16 border-8 border-t-8',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ActivityIndicator({ size = 'md' }) {
|
||||||
|
return (
|
||||||
|
<div className="w-full flex items-center justify-center" aria-label="Loading…">
|
||||||
|
<div className={`activityindicator ease-in rounded-full border-gray-200 text-blue-500 ${sizes[size]}`} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
import { h } from 'preact';
|
import { h } from 'preact';
|
||||||
|
import ActivityIndicator from './ActivityIndicator';
|
||||||
import { useApiHost, useConfig } from '../api';
|
import { useApiHost, useConfig } from '../api';
|
||||||
import { useCallback, useEffect, useContext, useMemo, useRef, useState } from 'preact/hooks';
|
import { useCallback, useEffect, useContext, useMemo, useRef, useState } from 'preact/hooks';
|
||||||
|
|
||||||
@ -54,7 +55,11 @@ export default function CameraImage({ camera, onload, searchParams = '' }) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div ref={containerRef}>
|
<div ref={containerRef}>
|
||||||
{loadedSrc ? <img width={scaledHeight * aspectRatio} height={scaledHeight} src={loadedSrc} alt={name} /> : null}
|
{loadedSrc ? (
|
||||||
|
<img width={scaledHeight * aspectRatio} height={scaledHeight} src={loadedSrc} alt={name} />
|
||||||
|
) : (
|
||||||
|
<ActivityIndicator />
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,27 @@
|
|||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
|
.activityindicator {
|
||||||
|
border-top-color: currentColor;
|
||||||
|
-webkit-animation: spinner 0.75s linear infinite;
|
||||||
|
animation: spinner 0.75s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@-webkit-keyframes spinner {
|
||||||
|
0% {
|
||||||
|
-webkit-transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
-webkit-transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spinner {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user