mirror of
				https://github.com/blakeblackshear/frigate.git
				synced 2025-10-27 10:52:11 +01: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 ActivityIndicator from './components/ActivityIndicator';
 | 
			
		||||
import Camera from './Camera';
 | 
			
		||||
import CameraMap from './CameraMap';
 | 
			
		||||
import Cameras from './Cameras';
 | 
			
		||||
@ -7,12 +8,14 @@ import Event from './Event';
 | 
			
		||||
import Events from './Events';
 | 
			
		||||
import { Router } from 'preact-router';
 | 
			
		||||
import Sidebar from './Sidebar';
 | 
			
		||||
import Api, { useConfig } from './api';
 | 
			
		||||
import Api, { FetchStatus, useConfig } from './api';
 | 
			
		||||
 | 
			
		||||
export default function App() {
 | 
			
		||||
  const { data, status } = useConfig();
 | 
			
		||||
  return !data ? (
 | 
			
		||||
    <div />
 | 
			
		||||
  return status !== FetchStatus.LOADED ? (
 | 
			
		||||
    <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">
 | 
			
		||||
      <Sidebar />
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,5 @@
 | 
			
		||||
import { h } from 'preact';
 | 
			
		||||
import ActivityIndicator from './components/ActivityIndicator';
 | 
			
		||||
import Box from './components/Box';
 | 
			
		||||
import CameraImage from './components/CameraImage';
 | 
			
		||||
import Events from './Events';
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,10 @@
 | 
			
		||||
import { h } from 'preact';
 | 
			
		||||
import ActivityIndicator from './components/ActivityIndicator';
 | 
			
		||||
import Box from './components/Box';
 | 
			
		||||
import Button from './components/Button';
 | 
			
		||||
import Heading from './components/Heading';
 | 
			
		||||
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 { useCallback, useEffect, useState } from 'preact/hooks';
 | 
			
		||||
 | 
			
		||||
@ -27,8 +28,8 @@ export default function Debug() {
 | 
			
		||||
  }, [timeoutId]);
 | 
			
		||||
  const { data: stats, status } = useStats(null, timeoutId);
 | 
			
		||||
 | 
			
		||||
  if (!stats) {
 | 
			
		||||
    return 'loading…';
 | 
			
		||||
  if (stats === null && (status === FetchStatus.LOADING || status === FetchStatus.NONE)) {
 | 
			
		||||
    return <ActivityIndicator />;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const { detectors, detection_fps, service, ...cameras } = stats;
 | 
			
		||||
 | 
			
		||||
@ -1,21 +1,17 @@
 | 
			
		||||
import { h, Fragment } from 'preact';
 | 
			
		||||
import ActivityIndicator from './components/ActivityIndicator';
 | 
			
		||||
import Box from './components/Box';
 | 
			
		||||
import Heading from './components/Heading';
 | 
			
		||||
import Link from './components/Link';
 | 
			
		||||
import { FetchStatus, useApiHost, useEvent } from './api';
 | 
			
		||||
import { Table, Thead, Tbody, Tfoot, Th, Tr, Td } from './components/Table';
 | 
			
		||||
import { useApiHost, useEvent } from './api';
 | 
			
		||||
 | 
			
		||||
export default function Event({ eventId }) {
 | 
			
		||||
  const apiHost = useApiHost();
 | 
			
		||||
  const { data } = useEvent(eventId);
 | 
			
		||||
  const { data, status } = useEvent(eventId);
 | 
			
		||||
 | 
			
		||||
  if (!data) {
 | 
			
		||||
    return (
 | 
			
		||||
      <div>
 | 
			
		||||
        <Heading>{eventId}</Heading>
 | 
			
		||||
        <p>loading…</p>
 | 
			
		||||
      </div>
 | 
			
		||||
    );
 | 
			
		||||
  if (status !== FetchStatus.LOADED) {
 | 
			
		||||
    return <ActivityIndicator />;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const startime = new Date(data.start_time * 1000);
 | 
			
		||||
 | 
			
		||||
@ -1,11 +1,12 @@
 | 
			
		||||
import { h } from 'preact';
 | 
			
		||||
import ActivityIndicator from './components/ActivityIndicator';
 | 
			
		||||
import Box from './components/Box';
 | 
			
		||||
import Heading from './components/Heading';
 | 
			
		||||
import Link from './components/Link';
 | 
			
		||||
import produce from 'immer';
 | 
			
		||||
import { route } from 'preact-router';
 | 
			
		||||
import { FetchStatus, useApiHost, useConfig, useEvents } from './api';
 | 
			
		||||
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';
 | 
			
		||||
 | 
			
		||||
const API_LIMIT = 25;
 | 
			
		||||
@ -194,8 +195,8 @@ export default function Events({ path: pathname } = {}) {
 | 
			
		||||
          </Tbody>
 | 
			
		||||
          <Tfoot>
 | 
			
		||||
            <Tr>
 | 
			
		||||
              <Td className="text-center" colspan="8">
 | 
			
		||||
                {status === 'loading' ? 'Loading…' : reachedEnd ? 'No more events' : null}
 | 
			
		||||
              <Td className="text-center p-4" colspan="8">
 | 
			
		||||
                {status === FetchStatus.LOADING ? <ActivityIndicator /> : reachedEnd ? 'No more events' : null}
 | 
			
		||||
              </Td>
 | 
			
		||||
            </Tr>
 | 
			
		||||
          </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 ActivityIndicator from './ActivityIndicator';
 | 
			
		||||
import { useApiHost, useConfig } from '../api';
 | 
			
		||||
import { useCallback, useEffect, useContext, useMemo, useRef, useState } from 'preact/hooks';
 | 
			
		||||
 | 
			
		||||
@ -54,7 +55,11 @@ export default function CameraImage({ camera, onload, searchParams = '' }) {
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <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>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,3 +1,27 @@
 | 
			
		||||
@tailwind base;
 | 
			
		||||
@tailwind components;
 | 
			
		||||
@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