- Left filter sidebar: All Photos / Favorites / Date by Year / Tags / Collections with counts, See more/less toggles, and blue active-row highlight - SSE ingest badge in header: pulsing dot + filename + indexed count during scan; auto-refreshes the library when a scan finishes - Starred/Favorites: starred column on photos, star button in lightbox toolbar, Favorites row in sidebar, ?starred=1 filter on /api/photos - GET /api/library/stats endpoint (totals, years, tags, collections) - GET /api/ingest/stream SSE endpoint (polls ingest_status table every 1.5s) - Worker writes ingest_status (active, current file, scanned count) to SQLite Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
54 lines
1.4 KiB
React
54 lines
1.4 KiB
React
import { useState, useEffect, useRef } from 'react';
|
|
|
|
export default function IngestBadge({ onScanComplete }) {
|
|
const [status, setStatus] = useState(null);
|
|
const wasActive = useRef(false);
|
|
|
|
useEffect(() => {
|
|
const es = new EventSource('/api/ingest/stream');
|
|
|
|
es.onmessage = (e) => {
|
|
const data = JSON.parse(e.data);
|
|
setStatus(data);
|
|
if (wasActive.current && !data.active) {
|
|
onScanComplete?.();
|
|
}
|
|
wasActive.current = !!data.active;
|
|
};
|
|
|
|
es.onerror = () => {
|
|
// Reconnect is handled automatically by EventSource
|
|
};
|
|
|
|
return () => es.close();
|
|
}, []);
|
|
|
|
if (!status?.active) return null;
|
|
|
|
return (
|
|
<div style={{
|
|
display: 'flex', alignItems: 'center', gap: 7,
|
|
fontSize: 12, color: '#888',
|
|
overflow: 'hidden', maxWidth: 340,
|
|
}}>
|
|
<span style={{
|
|
width: 7, height: 7, borderRadius: '50%',
|
|
background: '#4d9eff', flexShrink: 0,
|
|
animation: 'pulse-dot 1.4s ease-in-out infinite',
|
|
}} />
|
|
<span style={{ whiteSpace: 'nowrap', color: '#aaa' }}>
|
|
Scanning · {status.scanned.toLocaleString()} indexed
|
|
</span>
|
|
{status.current && (
|
|
<span style={{
|
|
color: '#444', overflow: 'hidden',
|
|
textOverflow: 'ellipsis', whiteSpace: 'nowrap',
|
|
fontSize: 11,
|
|
}}>
|
|
{status.current}
|
|
</span>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|