refactor(frontend): extract useEpgPreview to its own hook file

This commit is contained in:
Five Boroughs 2026-05-22 17:55:54 +02:00
parent 2cfea1df0f
commit 9903f0b75d
No known key found for this signature in database
4 changed files with 74 additions and 74 deletions

View file

@ -4,7 +4,6 @@ import ProgramPreview from '../ProgramPreview';
// Mock Mantine components as lightweight stubs
vi.mock('@mantine/core', () => {
const React = require('react');
return {
ActionIcon: ({ children, onClick, ...props }) => (
<button onClick={onClick} {...props}>

View file

@ -8,6 +8,7 @@ import useStreamProfilesStore from '../../store/streamProfiles';
import ChannelGroupForm from './ChannelGroup';
import logo from '../../images/logo.png';
import { useChannelLogoSelection } from '../../hooks/useSmartLogos';
import { useEpgPreview } from '../../hooks/useEpgPreview';
import useLogosStore from '../../store/logos';
import LazyLogo from '../LazyLogo';
import LogoForm from './Logo';
@ -92,78 +93,6 @@ const ProviderHintRow = ({ channel, field, formValue, hintText, onReset }) => {
);
};
export const useEpgPreview = (epgDataId) => {
const [currentProgram, setCurrentProgram] = useState(null);
const [isLoadingProgram, setIsLoadingProgram] = useState(false);
const [hasFetchedProgram, setHasFetchedProgram] = useState(false);
useEffect(() => {
if (!epgDataId || epgDataId === '0' || epgDataId === '') {
setCurrentProgram(null);
setIsLoadingProgram(false);
setHasFetchedProgram(false);
return;
}
let cancelled = false;
setIsLoadingProgram(true);
setHasFetchedProgram(false);
const fetchWithRetry = async () => {
const maxRetries = 5;
const deadlineMs = 3 * 60 * 1000; // 3 minutes
const startTime = Date.now();
let delay = 3000; // 3s initial
for (let attempt = 0; attempt <= maxRetries; attempt++) {
if (cancelled || Date.now() - startTime > deadlineMs) break;
try {
const program = await API.getCurrentProgramForEpg(epgDataId);
if (cancelled) return;
if (program && program.parsing && attempt < maxRetries) {
// Index still building, retry with backoff
await new Promise((resolve) => setTimeout(resolve, delay));
delay = Math.min(delay * 1.5, 15000); // 1.5x multiplier, 15s cap
continue;
}
if (!cancelled) {
setCurrentProgram(program && !program.parsing ? program : null);
setIsLoadingProgram(false);
setHasFetchedProgram(true);
}
return;
} catch (error) {
if (!cancelled) {
console.error('Failed to fetch current program:', error);
}
if (attempt < maxRetries) {
await new Promise((resolve) => setTimeout(resolve, delay));
delay = Math.min(delay * 1.5, 15000);
}
}
}
// Exhausted retries or deadline
if (!cancelled) {
setCurrentProgram(null);
setIsLoadingProgram(false);
setHasFetchedProgram(true);
}
};
fetchWithRetry();
return () => {
cancelled = true;
};
}, [epgDataId]);
return { currentProgram, isLoadingProgram, hasFetchedProgram };
};
const ChannelForm = ({ channel: channelProp = null, isOpen, onClose }) => {
const theme = useMantineTheme();

View file

@ -11,7 +11,7 @@ vi.mock('../../../api', () => ({
}));
import API from '../../../api';
import { useEpgPreview } from '../Channel';
import { useEpgPreview } from '../../../hooks/useEpgPreview';
describe('Channel EPG preview hook', () => {
beforeEach(() => {

View file

@ -0,0 +1,72 @@
import { useEffect, useState } from 'react';
import API from '../api';
export const useEpgPreview = (epgDataId) => {
const [currentProgram, setCurrentProgram] = useState(null);
const [isLoadingProgram, setIsLoadingProgram] = useState(false);
const [hasFetchedProgram, setHasFetchedProgram] = useState(false);
useEffect(() => {
if (!epgDataId || epgDataId === '0' || epgDataId === '') {
setCurrentProgram(null);
setIsLoadingProgram(false);
setHasFetchedProgram(false);
return;
}
let cancelled = false;
setIsLoadingProgram(true);
setHasFetchedProgram(false);
const fetchWithRetry = async () => {
const maxRetries = 5;
const deadlineMs = 3 * 60 * 1000;
const startTime = Date.now();
let delay = 3000;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
if (cancelled || Date.now() - startTime > deadlineMs) break;
try {
const program = await API.getCurrentProgramForEpg(epgDataId);
if (cancelled) return;
if (program && program.parsing && attempt < maxRetries) {
await new Promise((resolve) => setTimeout(resolve, delay));
delay = Math.min(delay * 1.5, 15000);
continue;
}
if (!cancelled) {
setCurrentProgram(program && !program.parsing ? program : null);
setIsLoadingProgram(false);
setHasFetchedProgram(true);
}
return;
} catch (error) {
if (!cancelled) {
console.error('Failed to fetch current program:', error);
}
if (attempt < maxRetries) {
await new Promise((resolve) => setTimeout(resolve, delay));
delay = Math.min(delay * 1.5, 15000);
}
}
}
if (!cancelled) {
setCurrentProgram(null);
setIsLoadingProgram(false);
setHasFetchedProgram(true);
}
};
fetchWithRetry();
return () => {
cancelled = true;
};
}, [epgDataId]);
return { currentProgram, isLoadingProgram, hasFetchedProgram };
};