UNPKG

@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
"use strict"; 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; } `;