71 lines
2.7 KiB
React
71 lines
2.7 KiB
React
|
|
import React, { useState, useEffect } from 'react';
|
||
|
|
import { useRemoteControl } from '../hooks/useRemoteControl';
|
||
|
|
import { fetchChannels } from '../api';
|
||
|
|
|
||
|
|
export default function Guide({ onClose, onSelectChannel }) {
|
||
|
|
const [channels, setChannels] = useState([]);
|
||
|
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
||
|
|
|
||
|
|
useRemoteControl({
|
||
|
|
onUp: () => setSelectedIndex(prev => (prev - 1 + channels.length) % channels.length),
|
||
|
|
onDown: () => setSelectedIndex(prev => (prev + 1) % channels.length),
|
||
|
|
onSelect: () => onSelectChannel(channels[selectedIndex].id),
|
||
|
|
onBack: onClose
|
||
|
|
});
|
||
|
|
|
||
|
|
const [currentTime, setCurrentTime] = useState(new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }));
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
fetchChannels().then(data => {
|
||
|
|
// Map channels securely, providing a fallback block if properties are missing
|
||
|
|
const mapped = data.map(ch => ({
|
||
|
|
...ch,
|
||
|
|
currentlyPlaying: ch.currentlyPlaying || { title: 'Live Broadcast', time: 'Now Playing' }
|
||
|
|
}));
|
||
|
|
if (mapped.length > 0) {
|
||
|
|
setChannels(mapped);
|
||
|
|
} else {
|
||
|
|
setChannels([{id: 99, channel_number: '99', name: 'No Channels Found', currentlyPlaying: {title: 'Empty Database', time: '--'}}]);
|
||
|
|
}
|
||
|
|
}).catch(err => {
|
||
|
|
console.error(err);
|
||
|
|
setChannels([{id: 99, channel_number: '99', name: 'Network Error', currentlyPlaying: {title: 'Could not reach PyTV server', time: '--'}}]);
|
||
|
|
});
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const timer = setInterval(() => {
|
||
|
|
setCurrentTime(new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }));
|
||
|
|
}, 60000);
|
||
|
|
return () => clearInterval(timer);
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<div className="guide-container open">
|
||
|
|
<div className="guide-header">
|
||
|
|
<h1>PYTV Guide</h1>
|
||
|
|
<div className="guide-clock">{currentTime}</div>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div className="guide-grid">
|
||
|
|
{channels.length === 0 ? <p style={{color: 'white'}}>Loading TV Guide...</p> :
|
||
|
|
channels.map((chan, idx) => (
|
||
|
|
<div key={chan.id} className={`guide-row ${idx === selectedIndex ? 'active' : ''}`}>
|
||
|
|
<div className="guide-ch-col">
|
||
|
|
{chan.channel_number}
|
||
|
|
</div>
|
||
|
|
<div className="guide-prog-col">
|
||
|
|
<div className="guide-prog-title">{chan.name} - {chan.currentlyPlaying.title}</div>
|
||
|
|
<div className="guide-prog-time">{chan.currentlyPlaying.time}</div>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
))}
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div style={{ marginTop: '2rem', color: 'var(--pytv-text-dim)', textAlign: 'center' }}>
|
||
|
|
Press <span style={{color: '#fff'}}>Enter</span> to tune to the selected channel. Press <span style={{color: '#fff'}}>Escape</span> to exit guide.
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
);
|
||
|
|
}
|