@livetv-app/tvguide
Version:
An Android TV Live Channels-like Electronic Programme Guide for React DOM and React Native applications.
332 lines (317 loc) • 15.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.css = exports.CLASS_PREFIX = exports.EmptyProgrammeCell = exports.ProgrammeCell = exports.showProgrammeActions = exports.ProgrammeRow = exports.ChannelIcon = void 0;
const React = require("react");
const react_1 = require("react");
const constants_1 = require("../constants");
const context_1 = require("./context");
const util_1 = require("../util");
const MIN_PROGRAMME_DURATION = 60000;
function ChannelRow(props) {
var _a, _b;
const name = util_1.useLocalised(props.channel.name);
const programmes = react_1.useMemo(() => props.programmes.filter(p => p.channel === props.channel.id), [props.programmes, props.channel]);
const { setSelectedChannel: sC, setSelectedProgramme: sP } = react_1.useContext(context_1.SelectionContext);
const setSelectedProgramme = react_1.useCallback((programme) => (sC === null || sC === void 0 ? void 0 : sC(props.channel), sP === null || sP === void 0 ? void 0 : sP(programme)), [sC, sP, props.channel.id]);
const leftPosition = react_1.useContext(context_1.PositionContext);
return React.createElement("div", { className: [
exports.CLASS_PREFIX + '-row',
props.isFirstRow ? null : 'isFirstRow' in props ? exports.CLASS_PREFIX + '-rowNotFirst' : null,
props.isLastRow ? exports.CLASS_PREFIX + '-rowLast' : null,
].filter(c => c).join(' ') },
React.createElement("div", { className: [
exports.CLASS_PREFIX + '-cell',
exports.CLASS_PREFIX + '-main',
props.active ? exports.CLASS_PREFIX + '-mainActive' : null,
props.selected ? exports.CLASS_PREFIX + '-mainSelected' : null,
].filter(c => c).join(' ') },
React.createElement("span", { className: exports.CLASS_PREFIX + '-number' }, props.channel.number ?
(props.channel.number_group ? props.channel.number_group + '-' : '') +
props.channel.number : (_a = props.index) !== null && _a !== void 0 ? _a : '0'),
React.createElement(ChannelIcon, { name: name, icon: ((_b = props.showChannelLogos) !== null && _b !== void 0 ? _b : true) ? props.channel.icon : undefined })),
React.createElement(ProgrammeRow, { position: leftPosition, programmes: programmes, selected: props.selected && props.selectedProgramme || null, setSelected: setSelectedProgramme, isFirstRow: props.isFirstRow, isPreviousRowSelected: props.isPreviousRowSelected, width: props.programmesRowWidth }));
}
exports.default = React.memo(ChannelRow);
function ChannelIcon(props) {
const [loaded, setLoaded] = react_1.useState(false);
const [error, setError] = react_1.useState(null);
return React.createElement("div", { className: [
exports.CLASS_PREFIX + '-icon',
loaded && !error ? exports.CLASS_PREFIX + '-iconHasLoaded' : null,
].filter(c => c).join(' ') },
props.icon && !error ? React.createElement("img", { src: props.icon, style: { height: 34, maxWidth: loaded && !error ? 110 : 0 }, onLoad: () => setLoaded(true), onError: err => (setLoaded(true), setError(err)) }) : null,
!props.icon || (!loaded || error) ? React.createElement("span", { className: exports.CLASS_PREFIX + '-name' }, props.name) : null);
}
exports.ChannelIcon = ChannelIcon;
function ProgrammeRow(props) {
var _a, _b, _c;
// Current time, to one minute
const now = util_1.useCurrentTime(60000);
const language = react_1.useContext(util_1.LanguageContext).toString();
const leftPosition = props.position;
const rightPosition = react_1.useMemo(() => new Date(leftPosition.getTime() + ((props.width / constants_1.CELL_WIDTH) * constants_1.CELL_LENGTH)), [leftPosition, props.width]);
const programmes = react_1.useMemo(() => {
const programmes = {};
const sorted = props.programmes.sort((p1, p2) => {
if (p1.start === p2.start)
return 0;
if (p1.start < p2.start)
return -1;
return 1;
});
let last = Math.floor(leftPosition.getTime() / MIN_PROGRAMME_DURATION) * MIN_PROGRAMME_DURATION;
for (const programme of sorted) {
const start = Math.floor(programme.start.getTime() / MIN_PROGRAMME_DURATION) * MIN_PROGRAMME_DURATION;
const end = Math.floor(programme.end.getTime() / MIN_PROGRAMME_DURATION) * MIN_PROGRAMME_DURATION;
if (end - start < MIN_PROGRAMME_DURATION)
continue;
if (leftPosition.getTime() >= end)
continue;
if (rightPosition.getTime() < start)
continue;
if (start in programmes)
continue;
if (last && start > last)
programmes[last] = null;
programmes[start] = programme;
last = Math.floor(programme.end.getTime() / MIN_PROGRAMME_DURATION) * MIN_PROGRAMME_DURATION;
}
programmes[last] = null;
return programmes;
}, [props.programmes, leftPosition, rightPosition]);
const entries = Object.entries(programmes);
const list = [];
for (const i in entries) {
const [_start, programme] = entries[i];
const start = parseInt(_start);
const index = parseInt(i);
const end = parseInt((_a = entries[index + 1]) === null || _a === void 0 ? void 0 : _a[0]);
const next = (_b = entries[index + 1]) === null || _b === void 0 ? void 0 : _b[1];
if (programme) {
const offset = Math.max(0, leftPosition.getTime() - start);
const length = end - start - offset;
const cells = Math.min(length, rightPosition.getTime() - start) / constants_1.CELL_LENGTH;
const width = cells * constants_1.CELL_WIDTH;
list.push(React.createElement(ProgrammeCell, { key: start, programme: programme, next: next, selected: ((_c = props.selected) === null || _c === void 0 ? void 0 : _c.id) === programme.id && props.selected.start === programme.start, width: width, onPress: e => { var _a, _b; return ((_a = props.setSelected) === null || _a === void 0 ? void 0 : _a.call(null, programme), (_b = programme.onPress) === null || _b === void 0 ? void 0 : _b.call(null, e)); }, onLongPress: e => { var _a; return ((_a = programme.actions) === null || _a === void 0 ? void 0 : _a.length) ? showProgrammeActions(programme, language) : null; } }));
}
else {
const length = isNaN(end) ? rightPosition.getTime() - start : end - start;
const cells = length / constants_1.CELL_LENGTH;
const width = cells * constants_1.CELL_WIDTH;
list.push(React.createElement(EmptyProgrammeCell, { key: start, width: width }));
}
}
const nowindicator_position = now.getTime() - leftPosition.getTime();
const nowindicator_cells = nowindicator_position / constants_1.CELL_LENGTH;
const nowindicator = nowindicator_cells * constants_1.CELL_WIDTH;
return React.createElement("div", { className: exports.CLASS_PREFIX + '-programmeRow' },
React.createElement("div", { className: exports.CLASS_PREFIX + '-scrollContainer' },
React.createElement("div", { className: exports.CLASS_PREFIX + '-scroller' }, list)),
nowindicator >= 0 && props.width >= nowindicator ? React.createElement(React.Fragment, null,
React.createElement("div", { className: [
exports.CLASS_PREFIX + '-nowIndicator',
].filter(c => c).join(' '), style: { left: nowindicator } }),
props.isFirstRow || props.isPreviousRowSelected ? null : React.createElement("div", { className: [
exports.CLASS_PREFIX + '-nowIndicator',
exports.CLASS_PREFIX + '-nowIndicatorNotFirstRow',
].join(' '), style: { left: nowindicator } })) : null);
}
exports.ProgrammeRow = ProgrammeRow;
function showProgrammeActions(programme, language) {
// TODO
}
exports.showProgrammeActions = showProgrammeActions;
function ProgrammeCell(props) {
var _a;
const name = util_1.useLocalised(props.programme.name);
const desc = util_1.useLocalised(props.programme.description);
const et = typeof props.programme.episode === 'number' || props.programme.episode_name ?
typeof props.programme.series === 'number' && typeof props.programme.episode === 'number' &&
props.programme.episode_name ?
`S${props.programme.series + 1}, E${props.programme.episode + 1}: ${props.programme.episode_name}` :
typeof props.programme.series === 'number' && typeof props.programme.episode === 'number' ?
`Series ${props.programme.series + 1}, Episode ${props.programme.episode + 1}` +
(props.programme.episode_name ? ': ' + props.programme.episode_name : '') :
typeof props.programme.series === 'number' && props.programme.episode_name ?
`S${props.programme.series + 1}: ${props.programme.episode_name}` :
typeof props.programme.episode === 'number' && props.programme.episode_name ?
`E${props.programme.episode + 1}: ${props.programme.episode_name}` :
typeof props.programme.episode === 'number' ?
`Episode ${props.programme.episode + 1}` :
props.programme.episode_name ?
props.programme.episode_name :
null : null;
const large = props.width >= constants_1.MIN_LARGE_CELL_WIDTH;
return React.createElement("div", { onClick: (_a = props.onPress) !== null && _a !== void 0 ? _a : props.programme.onPress, onContextMenu: props.onLongPress, className: [
exports.CLASS_PREFIX + '-cell',
exports.CLASS_PREFIX + '-programme',
props.selected ? exports.CLASS_PREFIX + '-programmeSelected' : null,
props.next ? exports.CLASS_PREFIX + '-programmeHasSeparator' : null,
props.onPress || props.programme.onPress ? exports.CLASS_PREFIX + '-clickable' : null,
].filter(c => c).join(' '), style: { width: props.width } },
React.createElement("div", { className: exports.CLASS_PREFIX + '-programmeDetail' },
React.createElement("div", { className: [
exports.CLASS_PREFIX + '-programmeName',
props.selected ? exports.CLASS_PREFIX + '-programmeSelectedName' : null,
exports.CLASS_PREFIX + '-ellipsis',
].filter(c => c).join(' ') }, name),
large && (et || (desc && !props.selected)) ? React.createElement("div", { className: exports.CLASS_PREFIX + '-programmeDescription' },
et ? React.createElement("div", { className: [
exports.CLASS_PREFIX + '-programmeDescriptionText',
exports.CLASS_PREFIX + '-programmeEpisodeText',
props.selected ? exports.CLASS_PREFIX + '-programmeSelectedDescriptionText' : null,
exports.CLASS_PREFIX + '-ellipsis',
].filter(c => c).join(' ') }, et) : null,
!et && desc && !props.selected ? React.createElement("div", { className: [
exports.CLASS_PREFIX + '-programmeDescriptionText',
exports.CLASS_PREFIX + '-ellipsis',
].join(' ') }, desc) : null) : null));
}
exports.ProgrammeCell = ProgrammeCell;
function EmptyProgrammeCell(props) {
return React.createElement("div", { className: [
exports.CLASS_PREFIX + '-cell',
exports.CLASS_PREFIX + '-programme',
exports.CLASS_PREFIX + '-emptyProgramme',
].filter(c => c).join(' '), style: { width: props.width } },
React.createElement("div", { className: exports.CLASS_PREFIX + '-programmeDetail' },
React.createElement("div", { className: [
exports.CLASS_PREFIX + '-emptyProgrammeText',
exports.CLASS_PREFIX + '-emptyProgrammeTextEllipsis',
].filter(c => c).join(' ') }, "No information")));
}
exports.EmptyProgrammeCell = EmptyProgrammeCell;
exports.CLASS_PREFIX = 'tvguide-cr';
exports.css = `
.${exports.CLASS_PREFIX}-row {
background-color: #37474fdd;
display: flex;
flex-direction: row;
}
.${exports.CLASS_PREFIX}-rowNotFirst {
border-top-color: #263238dd;
border-top-width: 1px;
border-top-style: solid;
}
.${exports.CLASS_PREFIX}-rowLast {
border-bottom-left-radius: 2px;
}
.${exports.CLASS_PREFIX}-header {
}
.${exports.CLASS_PREFIX}-cell {
padding: 10px 20px;
box-sizing: border-box;
flex-shrink: 0;
}
.${exports.CLASS_PREFIX}-main {
width: ${70 + 20 + 110}px;
border-right-color: #263238aa;
border-right-width: 1px;
border-right-style: solid;
display: flex;
flex-direction: row;
}
.${exports.CLASS_PREFIX}-mainActive {
background-color: #607d8baa;
}
.${exports.CLASS_PREFIX}-mainSelected {
}
.${exports.CLASS_PREFIX}-number {
font-size: 26px;
color: #eeeeee;
width: 70px;
padding-right: 20px;
}
.${exports.CLASS_PREFIX}-icon {
justify-content: center;
display: flex;
flex-direction: column;
width: 110px;
}
.${exports.CLASS_PREFIX}-iconHasLoaded {
flex-direction: row;
}
.${exports.CLASS_PREFIX}-name {
color: #eeeeee;
width: 110px;
}
.${exports.CLASS_PREFIX}-programmeRow {
flex: 1;
position: relative;
}
.${exports.CLASS_PREFIX}-scrollContainer {
flex: 1;
overflow: hidden;
}
.${exports.CLASS_PREFIX}-scroller {
flex: 1;
display: flex;
flex-direction: row;
}
.${exports.CLASS_PREFIX}-programme {
padding-left: 10px;
padding-right: 10px;
display: flex;
flex-direction: row;
overflow: hidden;
height: ${34 + 20}px;
}
.${exports.CLASS_PREFIX}-programmeSelected {
background-color: #90a4aeaa;
}
.${exports.CLASS_PREFIX}-programmeHasSeparator {
border-right-color: #263238aa;
border-right-width: 1px;
border-right-style: solid;
}
.${exports.CLASS_PREFIX}-programmeDetail {
flex: 1;
justify-content: center;
min-width: 50px;
display: flex;
flex-direction: column;
}
.${exports.CLASS_PREFIX}-programmeName {
color: #eeeeee;
}
.${exports.CLASS_PREFIX}-programmeSelectedName {
color: #ffffff;
}
.${exports.CLASS_PREFIX}-programmeDescription {
}
.${exports.CLASS_PREFIX}-programmeDescriptionText {
color: #90a4ae;
font-size: 13px;
}
.${exports.CLASS_PREFIX}-programmeEpisodeText {
font-weight: 700;
}
.${exports.CLASS_PREFIX}-programmeSelectedDescriptionText {
color: #eceff1;
}
.${exports.CLASS_PREFIX}-emptyProgramme {
background-color: #455a64aa;
}
.${exports.CLASS_PREFIX}-emptyProgrammeText {
font-style: italic;
color: #aaaaaa;
}
.${exports.CLASS_PREFIX}-nowIndicator {
position: absolute;
height: 100%;
width: 2px;
background-color: #00b0ff;
top: 0;
}
.${exports.CLASS_PREFIX}-nowIndicatorNotFirstRow {
margin-top: -1px;
}
.${exports.CLASS_PREFIX}-clickable {
cursor: pointer;
}
.${exports.CLASS_PREFIX}-ellipsis {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
`;