nexle-tvguide-lib
Version:
TV guide library for Android TV
231 lines (211 loc) • 9.78 kB
JavaScript
import React, { PureComponent } from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
import { TV_GUIDE_CONSTANTS } from '../../constants';
import { formatHourMin } from '../../util';
import { debounce } from 'lodash';
import PropTypes from 'prop-types';
const ProgramTimeFrame = ({ program }) => (
`${formatHourMin(program?.startDate)} - ${formatHourMin(program?.endDate)}`
);
const checkShowProgramContent = (program) => {
const programDuration = program?.endDateAdjusted - program?.startDateAdjusted;
if (programDuration >= TV_GUIDE_CONSTANTS.PROGRAM_MIN_DURATION_SHOW_CONTENT) return true;
return false;
};
const getProgramTimeType = (program) => {
const now = new Date().getTime();
if (program.startDateAdjusted <= now && program.endDateAdjusted > now) {
return TV_GUIDE_CONSTANTS.PROGRAM_TIME_TYPE.CURRENT;
} else if (program.endDateAdjusted <= now) {
return TV_GUIDE_CONSTANTS.PROGRAM_TIME_TYPE.PAST
}
return TV_GUIDE_CONSTANTS.PROGRAM_TIME_TYPE.FUTURE;
};
class ProgramCell extends React.Component {
constructor(props) {
super(props);
this.state = {
isShowProgramContent: checkShowProgramContent(props.program),
active: props.isFocused,
programCellReference: React.createRef(),
programWidth: Number((props.program?.endDateAdjusted - props.program?.startDateAdjusted) / TV_GUIDE_CONSTANTS.HALF_HOUR_DURATION) * props.timelineCellWidth,
isCurrentProgram: getProgramTimeType(props.program),
programContainerStylesFlatten: StyleSheet.flatten([styles.programContainer, props.programContainerStyles]),
activeProgramStylesFlatten: StyleSheet.flatten([styles.activeProgram, { backgroundColor: props.programStylesColors.activeProgramBackgroundColor }]),
currentProgramStylesFlatten: StyleSheet.flatten([styles.nowProgram, { backgroundColor: props.programStylesColors.currentProgramBacgroundColor }]),
pastProgramStylesFlatten: StyleSheet.flatten([styles.defaultProgram, { backgroundColor: props.programStylesColors.pastProgramBackgroundColor }]),
futureProgramStylesFlatten: StyleSheet.flatten([styles.defaultProgram, { backgroundColor: props.programStylesColors.futureProgramBackgroundColor }]),
activeProgramTextStylesFlatten: StyleSheet.flatten([styles.titleActive, { color: props.programStylesColors.activeProgramTextColor }]),
currentProgramTextStylesFlatten: StyleSheet.flatten([styles.title, { color: props.programStylesColors.currrentProgramTextColor }]),
pastProgramTextStylesFlatten: StyleSheet.flatten([styles.titlePast, { color: props.programStylesColors.pastProgramTextColor }]),
futureProgramTextStylesFlatten: StyleSheet.flatten([styles.title, { color: props.programStylesColors.futureProgramTextColor }]),
}
this.handleCellFocus = this.handleCellFocus.bind(this);
this.handleCellBlur = this.handleCellBlur.bind(this);
this.onDebounceCellFocus = this.onDebounceCellFocus(this);
this.onDebounceCellBlur = this.onDebounceCellBlur(this);
}
shouldComponentUpdate(nextProps, nextState) {
if (nextProps.isFocused !== this.props.isFocused) {
return true;
}
if (nextState.active !== this.state.active) {
return true;
}
if (nextProps.program !== this.props.program) {
return true;
}
return false;
}
onDebounceCellFocus() {
return debounce(this.handleCellFocus, TV_GUIDE_CONSTANTS.DEBOUNCE_TIME, { leading: true, trailing: false });
}
onDebounceCellBlur() {
return debounce(this.handleCellBlur, TV_GUIDE_CONSTANTS.DEBOUNCE_TIME, { leading: true, trailing: false });
}
handleCellFocus() {
const { index, lineNumber, program } = this.props;
this.setState({ active: true });
this.state.programCellReference.current.setNativeProps({ hasTVPreferredFocus: true });
global.focusManager.setFocusForRoute('selectedAt', { lineNumber, index });
this.props.onFocus(index, lineNumber, program);
}
handleCellBlur() {
this.setState({ active: false });
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.isFocused === false && prevState.active === false && this.props.isFocused === true && this.state.active === false) {
this.handleCellFocus();
}
}
componentDidMount() {
if (this.props.isFocused) {
this.handleCellFocus();
}
}
render() {
const { program, programHeight } = this.props;
const { id } = program;
const { isShowProgramContent,
programCellReference,
active,
programWidth,
isCurrentProgram,
activeProgramStylesFlatten,
currentProgramStylesFlatten,
programContainerStylesFlatten,
pastProgramStylesFlatten,
futureProgramStylesFlatten,
activeProgramTextStylesFlatten,
currentProgramTextStylesFlatten,
pastProgramTextStylesFlatten,
futureProgramTextStylesFlatten
} = this.state;
let programStylesFlatten = styles.defaultProgram;
let programTextStylesFlattent = styles.title;
if (isCurrentProgram === TV_GUIDE_CONSTANTS.PROGRAM_TIME_TYPE.CURRENT) {
programStylesFlatten = currentProgramStylesFlatten;
programTextStylesFlattent = currentProgramTextStylesFlatten;
} else if (isCurrentProgram === TV_GUIDE_CONSTANTS.PROGRAM_TIME_TYPE.FUTURE) {
programStylesFlatten = futureProgramStylesFlatten;
programTextStylesFlattent = futureProgramTextStylesFlatten;
} else {
programStylesFlatten = pastProgramStylesFlatten;
programTextStylesFlattent = pastProgramTextStylesFlatten;
}
return (
<TouchableOpacity
ref={programCellReference}
activeOpacity={1}
onFocus={this.onDebounceCellFocus}
onBlur={this.onDebounceCellBlur}
style={[
programContainerStylesFlatten,
active ? activeProgramStylesFlatten : programStylesFlatten,
{ width: programWidth, height: programHeight },
]}>
{isShowProgramContent && (
<View>
<Text style={active ? activeProgramTextStylesFlatten : programTextStylesFlattent} numberOfLines={1}>
{id !== -1 ? program.name : 'No program info'}
</Text>
<Text style={active ? activeProgramTextStylesFlatten : programTextStylesFlattent}>
<ProgramTimeFrame program={program} />
</Text>
</View>
)}
</TouchableOpacity>
);
}
}
ProgramCell.propTypes = {
program: PropTypes.shape({ name: PropTypes.string, id: PropTypes.number }),
index: PropTypes.number.isRequired,
lineNumber: PropTypes.number.isRequired,
onFocus: PropTypes.func.isRequired,
programStylesColors: PropTypes.object,
programContainerStyles: PropTypes.object,
}
ProgramCell.defaultProps = {
program: { name: '', id: -1 },
index: 0,
lineNumber: 0,
onFocus: () => { },
programStylesColors: {
activeProgramBackgroundColor: TV_GUIDE_CONSTANTS.THEME_STYLES.ACTIVE_PROGRAM_BG_COLOR,
currentProgramBacgroundColor: TV_GUIDE_CONSTANTS.THEME_STYLES.CURRENT_PROGRAM_BG_COLOR,
pastProgramBackgroundColor: TV_GUIDE_CONSTANTS.THEME_STYLES.DEFAULT_PROGRAM_BG_COLOR,
futureProgramBackgroundColor: TV_GUIDE_CONSTANTS.THEME_STYLES.DEFAULT_PROGRAM_BG_COLOR,
activeProgramTextColor: TV_GUIDE_CONSTANTS.THEME_STYLES.FUTURE_PROGRAM_TEXT_COLOR,
currrentProgramTextColor: TV_GUIDE_CONSTANTS.THEME_STYLES.PROGRAM_TEXT_COLOR,
pastProgramTextColor: TV_GUIDE_CONSTANTS.THEME_STYLES.PAST_PROGRAM_TEXT_COLOR,
futureProgramTextColor: TV_GUIDE_CONSTANTS.THEME_STYLES.PROGRAM_TEXT_COLOR,
},
programContainerStyles: {
}
};
export default ProgramCell;
const styles = StyleSheet.create({
activeProgram: {
backgroundColor: TV_GUIDE_CONSTANTS.THEME_STYLES.ACTIVE_PROGRAM_BG_COLOR,
borderColor: '#ffffff',
borderWidth: 5,
borderTopWidth: 5,
borderStartWidth: 5,
},
defaultProgram: {
backgroundColor: TV_GUIDE_CONSTANTS.THEME_STYLES.DEFAULT_PROGRAM_BG_COLOR,
},
nowProgram: {
backgroundColor: TV_GUIDE_CONSTANTS.THEME_STYLES.CURRENT_PROGRAM_BG_COLOR,
},
programContainer: {
color: '#ffff',
justifyContent: 'center',
borderWidth: 2,
borderColor: TV_GUIDE_CONSTANTS.THEME_STYLES.PROGRAM_TEXT_COLOR,
},
innerView: {
flex: 1,
flexDirection: 'column',
justifyContent: 'center',
},
title: {
color: TV_GUIDE_CONSTANTS.THEME_STYLES.PROGRAM_TEXT_COLOR,
fontSize: TV_GUIDE_CONSTANTS.THEME_STYLES.TIME_LINE_TITLE_FONT_SIZE,
fontWeight: 'bold',
paddingLeft: 5
},
titleActive: {
color: TV_GUIDE_CONSTANTS.THEME_STYLES.FUTURE_PROGRAM_TEXT_COLOR,
fontSize: TV_GUIDE_CONSTANTS.THEME_STYLES.TIME_LINE_TITLE_FONT_SIZE,
fontWeight: 'bold',
paddingLeft: 5
},
titlePast: {
color: TV_GUIDE_CONSTANTS.THEME_STYLES.PAST_PROGRAM_TEXT_COLOR,
fontSize: TV_GUIDE_CONSTANTS.THEME_STYLES.TIME_LINE_TITLE_FONT_SIZE,
fontWeight: 'bold',
paddingLeft: 5
},
});