Footer enhancements and performance optimizations #9

Merged
deco merged 10 commits from footer-enhancements into main 2025-07-22 23:01:23 +03:00
3 changed files with 20 additions and 14 deletions
Showing only changes of commit f9363eac20 - Show all commits

View file

@ -1,13 +1,20 @@
import { NextResponse } from 'next/server'; import { NextResponse } from 'next/server';
import { getDatabase } from '../../../lib/database'; import { getDatabase } from '../../../lib/database';
import { Stream } from '@/types'; import { StreamWithTeam } from '@/types';
import { TABLE_NAMES } from '../../../lib/constants'; import { TABLE_NAMES } from '../../../lib/constants';
import { createSuccessResponse, createDatabaseError, withErrorHandling } from '../../../lib/apiHelpers'; import { createSuccessResponse, createDatabaseError, withErrorHandling } from '../../../lib/apiHelpers';
async function getStreamsHandler() { async function getStreamsHandler() {
try { try {
const db = await getDatabase(); const db = await getDatabase();
const streams: Stream[] = await db.all(`SELECT * FROM ${TABLE_NAMES.STREAMS}`); const streams: StreamWithTeam[] = await db.all(`
SELECT
s.*,
t.team_name,
t.group_name
FROM ${TABLE_NAMES.STREAMS} s
LEFT JOIN ${TABLE_NAMES.TEAMS} t ON s.team_id = t.team_id
`);
return createSuccessResponse(streams); return createSuccessResponse(streams);
} catch (error) { } catch (error) {
return createDatabaseError('fetch streams', error); return createDatabaseError('fetch streams', error);

View file

@ -7,17 +7,12 @@ import { useToast } from '@/lib/useToast';
import { ToastContainer } from '@/components/Toast'; import { ToastContainer } from '@/components/Toast';
import { useActiveSourceLookup, useDebounce, PerformanceMonitor } from '@/lib/performance'; import { useActiveSourceLookup, useDebounce, PerformanceMonitor } from '@/lib/performance';
type Stream = { import { StreamWithTeam } from '@/types';
id: number;
name: string;
obs_source_name: string;
url: string;
};
type ScreenType = 'large' | 'left' | 'right' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'; type ScreenType = 'large' | 'left' | 'right' | 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight';
export default function Home() { export default function Home() {
const [streams, setStreams] = useState<Stream[]>([]); const [streams, setStreams] = useState<StreamWithTeam[]>([]);
const [activeSources, setActiveSources] = useState<Record<ScreenType, string | null>>({ const [activeSources, setActiveSources] = useState<Record<ScreenType, string | null>>({
large: null, large: null,
left: null, left: null,

View file

@ -38,13 +38,17 @@ export function useThrottle<T extends (...args: unknown[]) => unknown>(
} }
// Memoized stream lookup utilities // Memoized stream lookup utilities
export function createStreamLookupMaps(streams: Array<{ id: number; obs_source_name: string; name: string }>) { export function createStreamLookupMaps(streams: Array<{ id: number; obs_source_name: string; name: string; team_name?: string; group_name?: string | null }>) {
const sourceToIdMap = new Map<string, number>(); const sourceToIdMap = new Map<string, number>();
const idToStreamMap = new Map<number, { id: number; obs_source_name: string; name: string }>(); const idToStreamMap = new Map<number, { id: number; obs_source_name: string; name: string; team_name?: string; group_name?: string | null }>();
streams.forEach(stream => { streams.forEach(stream => {
// Generate stream group name to match what's written to files // Generate stream group name to match what's written to files
const streamGroupName = `${stream.name.toLowerCase().replace(/\s+/g, '_')}_stream`; // Format: {team_name}_{stream_name}_stream (matching obsClient.js logic)
const cleanTeamName = stream.team_name ? stream.team_name.toLowerCase().replace(/\s+/g, '_') : 'unknown';
const cleanStreamName = stream.name.toLowerCase().replace(/\s+/g, '_');
const streamGroupName = `${cleanTeamName}_${cleanStreamName}_stream`;
sourceToIdMap.set(streamGroupName, stream.id); sourceToIdMap.set(streamGroupName, stream.id);
idToStreamMap.set(stream.id, stream); idToStreamMap.set(stream.id, stream);
}); });
@ -53,7 +57,7 @@ export function createStreamLookupMaps(streams: Array<{ id: number; obs_source_n
} }
// Hook version for React components // Hook version for React components
export function useStreamLookupMaps(streams: Array<{ id: number; obs_source_name: string; name: string }>) { export function useStreamLookupMaps(streams: Array<{ id: number; obs_source_name: string; name: string; team_name?: string; group_name?: string | null }>) {
return useMemo(() => { return useMemo(() => {
return createStreamLookupMaps(streams); return createStreamLookupMaps(streams);
}, [streams]); }, [streams]);
@ -61,7 +65,7 @@ export function useStreamLookupMaps(streams: Array<{ id: number; obs_source_name
// Efficient active source lookup // Efficient active source lookup
export function useActiveSourceLookup( export function useActiveSourceLookup(
streams: Array<{ id: number; obs_source_name: string; name: string }>, streams: Array<{ id: number; obs_source_name: string; name: string; team_name?: string; group_name?: string | null }>,
activeSources: Record<string, string | null> activeSources: Record<string, string | null>
) { ) {
const { sourceToIdMap } = useStreamLookupMaps(streams); const { sourceToIdMap } = useStreamLookupMaps(streams);