unified-video-framework
Version:
Cross-platform video player framework supporting iOS, Android, Web, Smart TVs (Samsung/LG), Roku, and more
443 lines (435 loc) • 20 kB
JavaScript
import React, { useState, useEffect } from 'react';
import { DEFAULT_EPG_THEME } from '../types/EPGTypes.js';
import { formatDateTime, getProgramDuration, isProgramLive, getProgramProgress } from '../utils/EPGUtils.js';
export const EPGProgramDetails = ({ program, channel, onClose, onAction, isModal = false, currentTime = Date.now(), className = '', style = {}, config, theme: themeProp, }) => {
const theme = { ...DEFAULT_EPG_THEME, ...themeProp };
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const showFavoriteButton = config?.showFavoriteButton ?? false;
const showRecordButton = config?.showRecordButton ?? false;
const showReminderButton = config?.showReminderButton ?? false;
const showCatchupButton = config?.showCatchupButton ?? false;
useEffect(() => {
setError(null);
}, [program]);
if (!program)
return null;
const duration = getProgramDuration(program);
const isLive = isProgramLive(program, currentTime);
const progress = isLive ? getProgramProgress(program, currentTime) : 0;
const hasEnded = new Date(program.till).getTime() < currentTime;
const handleAction = async (actionType) => {
if (!channel || isLoading)
return;
setIsLoading(true);
setError(null);
try {
await onAction({
type: actionType,
program,
channel,
});
}
catch (err) {
setError(err instanceof Error ? err.message : 'Action failed. Please try again.');
}
finally {
setIsLoading(false);
}
};
const handleOverlayClick = (e) => {
if (e.target === e.currentTarget && isModal) {
onClose();
}
};
const detailsContent = (React.createElement("div", { className: `epg-program-details ${isModal ? 'modal' : 'panel'} ${className}`, style: {
backgroundColor: '#1a1a1a',
border: '1px solid #333',
borderRadius: isModal ? '12px' : '8px',
overflow: 'hidden',
boxShadow: '0 8px 24px rgba(0,0,0,0.4)',
maxWidth: isModal ? '600px' : '100%',
maxHeight: isModal ? '85vh' : '70vh',
display: 'flex',
flexDirection: 'column',
...style,
} },
React.createElement("div", { className: "epg-details-header", style: {
display: 'flex',
alignItems: 'flex-start',
padding: '20px',
borderBottom: '1px solid #333',
position: 'relative',
flexShrink: 0,
} },
program.image && (React.createElement("div", { style: {
width: '120px',
height: '68px',
borderRadius: '8px',
overflow: 'hidden',
marginRight: '16px',
flexShrink: 0,
position: 'relative',
} },
React.createElement("img", { src: program.image, alt: program.title, style: {
width: '100%',
height: '100%',
objectFit: 'cover',
}, onError: (e) => {
e.currentTarget.style.display = 'none';
} }),
isLive && (React.createElement("div", { style: {
position: 'absolute',
top: '4px',
right: '4px',
backgroundColor: theme.primaryColor,
color: '#fff',
fontSize: '10px',
fontWeight: '700',
padding: '2px 6px',
borderRadius: '4px',
} }, "LIVE")))),
React.createElement("div", { style: { flex: 1, minWidth: 0 } },
React.createElement("h2", { style: {
color: '#fff',
fontSize: '20px',
fontWeight: '700',
lineHeight: '1.3',
margin: '0 0 8px 0',
} }, program.title),
channel && (React.createElement("div", { style: {
color: '#888',
fontSize: '14px',
fontWeight: '500',
marginBottom: '8px',
} }, channel.programTitle)),
React.createElement("div", { style: {
display: 'flex',
alignItems: 'center',
gap: '16px',
marginBottom: '8px',
flexWrap: 'wrap',
} },
React.createElement("div", { style: {
color: '#fff',
fontSize: '14px',
fontWeight: '600',
} }, formatDateTime(new Date(program.since).getTime())),
React.createElement("div", { style: {
color: '#888',
fontSize: '13px',
} },
duration,
" min",
duration !== 1 ? 's' : ''),
program.category && (React.createElement("div", { style: {
backgroundColor: '#333',
color: '#fff',
fontSize: '11px',
fontWeight: '600',
padding: '2px 8px',
borderRadius: '12px',
} }, program.category)),
program.rating && (React.createElement("div", { style: {
backgroundColor: 'theme.primaryColor',
color: '#fff',
fontSize: '11px',
fontWeight: '700',
padding: '2px 8px',
borderRadius: '4px',
} }, program.rating))),
isLive && (React.createElement("div", { style: {
marginTop: '8px',
marginBottom: '8px',
} },
React.createElement("div", { style: {
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: '4px',
} },
React.createElement("span", { style: { color: theme.primaryColor, fontSize: '12px', fontWeight: '600' } }, "LIVE NOW"),
React.createElement("span", { style: { color: '#888', fontSize: '12px' } },
Math.round(progress),
"% complete")),
React.createElement("div", { style: {
height: '4px',
backgroundColor: '#333',
borderRadius: '2px',
overflow: 'hidden',
} },
React.createElement("div", { style: {
height: '100%',
width: `${progress}%`,
backgroundColor: theme.primaryColor,
transition: 'width 0.3s ease',
} }))))),
React.createElement("button", { onClick: onClose, style: {
position: 'absolute',
top: '16px',
right: '16px',
width: '32px',
height: '32px',
borderRadius: '50%',
border: 'none',
backgroundColor: '#333',
color: '#fff',
cursor: 'pointer',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
fontSize: '16px',
transition: 'background-color 0.2s ease',
}, onMouseEnter: (e) => {
e.currentTarget.style.backgroundColor = '#444';
}, onMouseLeave: (e) => {
e.currentTarget.style.backgroundColor = '#333';
} }, "\u00D7")),
React.createElement("div", { className: "epg-details-content", style: {
padding: '20px',
paddingRight: '16px',
flex: 1,
overflow: 'auto',
minHeight: 0,
scrollbarWidth: 'thin',
scrollbarColor: '#444 #2a2a2a',
} },
program.description && (React.createElement("div", { style: { marginBottom: '20px' } },
React.createElement("h3", { style: {
color: '#fff',
fontSize: '16px',
fontWeight: '600',
marginBottom: '8px',
} }, "Description"),
React.createElement("p", { style: {
color: '#ccc',
fontSize: '14px',
lineHeight: '1.5',
margin: 0,
} }, program.description))),
(program.isFavorite || program.isRecording || program.hasReminder || program.hasCatchup) && (React.createElement("div", { style: { marginBottom: '20px' } },
React.createElement("h3", { style: {
color: '#fff',
fontSize: '16px',
fontWeight: '600',
marginBottom: '8px',
} }, "Status"),
React.createElement("div", { style: {
display: 'flex',
flexWrap: 'wrap',
gap: '8px',
} },
program.isFavorite && (React.createElement("span", { style: {
backgroundColor: 'theme.primaryColor',
color: '#fff',
fontSize: '12px',
fontWeight: '600',
padding: '4px 8px',
borderRadius: '4px',
} }, "\u2605 Favorite")),
program.isRecording && (React.createElement("span", { style: {
backgroundColor: '#e74c3c',
color: '#fff',
fontSize: '12px',
fontWeight: '600',
padding: '4px 8px',
borderRadius: '4px',
} }, "\u25CF Recording")),
program.hasReminder && (React.createElement("span", { style: {
backgroundColor: '#3498db',
color: '#fff',
fontSize: '12px',
fontWeight: '600',
padding: '4px 8px',
borderRadius: '4px',
} }, "\uD83D\uDD14 Reminder")),
program.hasCatchup && (React.createElement("span", { style: {
backgroundColor: '#2ecc71',
color: '#fff',
fontSize: '12px',
fontWeight: '600',
padding: '4px 8px',
borderRadius: '4px',
} }, "\u21BB Catchup"))))),
error && (React.createElement("div", { style: {
backgroundColor: '#e74c3c',
color: '#fff',
padding: '12px',
borderRadius: '4px',
marginBottom: '16px',
fontSize: '14px',
} }, error))),
React.createElement("div", { className: "epg-details-actions", style: {
padding: '20px',
borderTop: '1px solid #333',
display: 'flex',
gap: '12px',
flexWrap: 'wrap',
flexShrink: 0,
backgroundColor: '#1a1a1a',
} },
showFavoriteButton && (React.createElement("button", { onClick: () => handleAction('favorite'), disabled: isLoading, style: {
flex: '1 1 120px',
height: '40px',
borderRadius: '6px',
border: 'none',
backgroundColor: program.isFavorite ? 'theme.primaryColor' : '#333',
color: '#fff',
fontSize: '13px',
fontWeight: '600',
cursor: isLoading ? 'not-allowed' : 'pointer',
transition: 'background-color 0.2s ease',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '6px',
}, onMouseEnter: (e) => {
if (!isLoading) {
e.currentTarget.style.backgroundColor = program.isFavorite ? '#ff8555' : '#444';
}
}, onMouseLeave: (e) => {
if (!isLoading) {
e.currentTarget.style.backgroundColor = program.isFavorite ? 'theme.primaryColor' : '#333';
}
} },
"\u2605 ",
program.isFavorite ? 'Favorited' : 'Favorite')),
showRecordButton && !hasEnded && (React.createElement("button", { onClick: () => handleAction('record'), disabled: isLoading, style: {
flex: '1 1 120px',
height: '40px',
borderRadius: '6px',
border: 'none',
backgroundColor: program.isRecording ? '#e74c3c' : '#333',
color: '#fff',
fontSize: '13px',
fontWeight: '600',
cursor: isLoading ? 'not-allowed' : 'pointer',
transition: 'background-color 0.2s ease',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '6px',
}, onMouseEnter: (e) => {
if (!isLoading) {
e.currentTarget.style.backgroundColor = program.isRecording ? '#c0392b' : '#444';
}
}, onMouseLeave: (e) => {
if (!isLoading) {
e.currentTarget.style.backgroundColor = program.isRecording ? '#e74c3c' : '#333';
}
} },
"\u25CF ",
program.isRecording ? 'Recording' : 'Record')),
showReminderButton && !isLive && !hasEnded && (React.createElement("button", { onClick: () => handleAction('reminder'), disabled: isLoading, style: {
flex: '1 1 120px',
height: '40px',
borderRadius: '6px',
border: 'none',
backgroundColor: program.hasReminder ? '#3498db' : '#333',
color: '#fff',
fontSize: '13px',
fontWeight: '600',
cursor: isLoading ? 'not-allowed' : 'pointer',
transition: 'background-color 0.2s ease',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '6px',
}, onMouseEnter: (e) => {
if (!isLoading) {
e.currentTarget.style.backgroundColor = program.hasReminder ? '#2980b9' : '#444';
}
}, onMouseLeave: (e) => {
if (!isLoading) {
e.currentTarget.style.backgroundColor = program.hasReminder ? '#3498db' : '#333';
}
} },
"\uD83D\uDD14 ",
program.hasReminder ? 'Reminder Set' : 'Remind Me')),
showCatchupButton && hasEnded && (React.createElement("button", { onClick: () => handleAction('catchup'), disabled: isLoading, style: {
flex: '1 1 120px',
height: '40px',
borderRadius: '6px',
border: 'none',
backgroundColor: '#2ecc71',
color: '#fff',
fontSize: '13px',
fontWeight: '600',
cursor: isLoading ? 'not-allowed' : 'pointer',
transition: 'background-color 0.2s ease',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
gap: '6px',
}, onMouseEnter: (e) => {
if (!isLoading) {
e.currentTarget.style.backgroundColor = '#27ae60';
}
}, onMouseLeave: (e) => {
if (!isLoading) {
e.currentTarget.style.backgroundColor = '#2ecc71';
}
} }, "\u21BB Watch Catchup")))));
if (isModal) {
return (React.createElement(React.Fragment, null,
React.createElement("div", { className: "epg-modal-overlay", style: {
position: 'fixed',
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: 'rgba(0, 0, 0, 0.7)',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
zIndex: 1000,
padding: '20px',
}, onClick: handleOverlayClick }, detailsContent),
React.createElement("style", null, `
.epg-details-content::-webkit-scrollbar {
width: 6px;
}
.epg-details-content::-webkit-scrollbar-track {
background: #2a2a2a;
border-radius: 3px;
}
.epg-details-content::-webkit-scrollbar-thumb {
background: #444;
border-radius: 3px;
transition: background 0.2s ease;
}
.epg-details-content::-webkit-scrollbar-thumb:hover {
background: #555;
}
.epg-program-details {
scrollbar-width: thin;
scrollbar-color: #444 #2a2a2a;
}
`)));
}
return (React.createElement(React.Fragment, null,
detailsContent,
React.createElement("style", null, `
.epg-details-content::-webkit-scrollbar {
width: 6px;
}
.epg-details-content::-webkit-scrollbar-track {
background: #2a2a2a;
border-radius: 3px;
}
.epg-details-content::-webkit-scrollbar-thumb {
background: #444;
border-radius: 3px;
transition: background 0.2s ease;
}
.epg-details-content::-webkit-scrollbar-thumb:hover {
background: #555;
}
.epg-program-details {
scrollbar-width: thin;
scrollbar-color: #444 #2a2a2a;
}
`)));
};
export default EPGProgramDetails;
//# sourceMappingURL=EPGProgramDetails.js.map