text-background-color-source #10

Merged
deco merged 7 commits from text-background-color-source into main 2025-07-22 23:36:53 +03:00
2 changed files with 83 additions and 7 deletions
Showing only changes of commit 02cad6a319 - Show all commits

25
app/api/counts/route.ts Normal file
View file

@ -0,0 +1,25 @@
import { NextResponse } from 'next/server';
import { getDatabase } from '../../../lib/database';
import { TABLE_NAMES } from '../../../lib/constants';
import { createSuccessResponse, createDatabaseError, withErrorHandling } from '../../../lib/apiHelpers';
async function getCountsHandler() {
try {
const db = await getDatabase();
// Get counts in parallel
const [streamsResult, teamsResult] = await Promise.all([
db.get(`SELECT COUNT(*) as count FROM ${TABLE_NAMES.STREAMS}`),
db.get(`SELECT COUNT(*) as count FROM ${TABLE_NAMES.TEAMS}`)
]);
return createSuccessResponse({
streams: streamsResult.count,
teams: teamsResult.count
});
} catch (error) {
return createDatabaseError('fetch counts', error);
}
}
export const GET = withErrorHandling(getCountsHandler);

View file

@ -19,8 +19,14 @@ type OBSStatus = {
error?: string; error?: string;
}; };
type Counts = {
streams: number;
teams: number;
};
export default function Footer() { export default function Footer() {
const [obsStatus, setObsStatus] = useState<OBSStatus | null>(null); const [obsStatus, setObsStatus] = useState<OBSStatus | null>(null);
const [counts, setCounts] = useState<Counts | null>(null);
const [isLoading, setIsLoading] = useState(true); const [isLoading, setIsLoading] = useState(true);
// Smart polling with performance monitoring and visibility detection // Smart polling with performance monitoring and visibility detection
@ -40,9 +46,24 @@ export default function Footer() {
} }
}; };
const fetchCounts = async () => {
try {
const response = await fetch('/api/counts');
const data = await response.json();
// Handle both old and new API response formats
const countsData = data.success ? data.data : data;
setCounts(countsData);
} catch (error) {
console.error('Failed to fetch counts:', error);
}
};
// Use smart polling that respects page visibility and adapts interval based on connection status // Use smart polling that respects page visibility and adapts interval based on connection status
const pollingInterval = obsStatus?.connected ? 15000 : 30000; // Poll faster when connected const pollingInterval = obsStatus?.connected ? 15000 : 30000; // Poll faster when connected
useSmartPolling(fetchOBSStatus, pollingInterval, [obsStatus?.connected]); useSmartPolling(fetchOBSStatus, pollingInterval, [obsStatus?.connected]);
// Poll counts less frequently (every 60 seconds) since they don't change as often
useSmartPolling(fetchCounts, 60000, []);
if (isLoading) { if (isLoading) {
return ( return (
@ -78,11 +99,27 @@ export default function Footer() {
)} )}
</div> </div>
{/* Live Status */} {/* Live Status or Database Stats */}
{obsStatus?.connected && ( <div>
<div> <h3 className="font-semibold mb-4">
<h3 className="font-semibold mb-4">Live Status</h3> {obsStatus?.connected ? 'Live Status' : 'Database Stats'}
</h3>
{/* Show counts when OBS is disconnected */}
{!obsStatus?.connected && counts && (
<div className="space-y-2 text-sm">
<div className="flex justify-between">
<span>Teams:</span>
<span className="font-medium">{counts.teams}</span>
</div>
<div className="flex justify-between">
<span>Streams:</span>
<span className="font-medium">{counts.streams}</span>
</div>
</div>
)}
{obsStatus?.connected && (
<div className="space-y-2 text-sm"> <div className="space-y-2 text-sm">
{obsStatus.currentScene && ( {obsStatus.currentScene && (
<div className="flex justify-between"> <div className="flex justify-between">
@ -98,6 +135,20 @@ export default function Footer() {
</div> </div>
)} )}
{/* Database counts */}
{counts && (
<>
<div className="flex justify-between">
<span>Teams:</span>
<span className="font-medium">{counts.teams}</span>
</div>
<div className="flex justify-between">
<span>Streams:</span>
<span className="font-medium">{counts.streams}</span>
</div>
</>
)}
<div className="flex gap-4 mt-4"> <div className="flex gap-4 mt-4">
<div className={`flex items-center gap-2 ${obsStatus.streaming ? 'text-red-400' : 'opacity-60'}`}> <div className={`flex items-center gap-2 ${obsStatus.streaming ? 'text-red-400' : 'opacity-60'}`}>
<div className={`w-2 h-2 rounded-full ${obsStatus.streaming ? 'bg-red-500' : 'bg-gray-500'}`}></div> <div className={`w-2 h-2 rounded-full ${obsStatus.streaming ? 'bg-red-500' : 'bg-gray-500'}`}></div>
@ -110,8 +161,8 @@ export default function Footer() {
</div> </div>
</div> </div>
</div> </div>
</div> )}
)} </div>
</div> </div>
{/* Error Message */} {/* Error Message */}