Skip to content

Instantly share code, notes, and snippets.

@jennmueng
Created May 24, 2020 08:17
Show Gist options
  • Save jennmueng/9d8fed636c8204c30724804cabba62a9 to your computer and use it in GitHub Desktop.
Save jennmueng/9d8fed636c8204c30724804cabba62a9 to your computer and use it in GitHub Desktop.
// @flow
import React, { PureComponent } from 'react';
import styled from 'styled-components';
import { connect } from 'react-redux';
import memoize from 'memoize-one';
import * as Animatable from 'react-native-animatable';
import { type PlannerTravel, type TravelType, type PlannerEntity } from '../../../../../../flow/planner';
import { type Flight } from '../../../../../../flow/flights';
import TravelIcon from '../travelIcon';
import DurationSection from '../durationSection';
import { Header6, Subtitle2 } from '../../../../../components/shared/type';
import TimeSeparator from '../timeSeparator';
import TimezoneDifferenceInsert from '../timezoneDifferenceInsert';
import CityIntroInsert from '../cityIntroInsert';
import DroppableHandler from '../draggable/droppableHandler';
import selectEntity from '../../../selectors/selectEntity';
import doesSeparatorHavePopup from '../../../functions/helpers/doesSeparatorHavePopup';
import TourStyles from '../../../../../config/TourStyles';
import MEASUREMENTS from '../../../constants/measurements';
import ENTITY_TYPES from '../../../constants/ENTITY_TYPES';
import ENTITY_ATTRIBUTES from '../../../constants/ENTITY_ATTRIBUTES';
// eslint-disable-next-line react/no-unused-prop-types
class Travel extends PureComponent<
{
id: string,
travel: PlannerTravel,
dayIndex: number,
dayEntityIndex: number,
isSliderAdjustable: boolean,
isFixed: boolean,
previousEntity: PlannerEntity,
nextEntity: PlannerEntity
},
{
cardHeight: number
}
> {
state = {
cardHeight: 0
};
isHidden = false;
componentDidMount() {
this.setCardHeight();
}
componentDidUpdate(prevProps) {
if (prevProps.id !== this.props.id || prevProps.travel.travelType !== this.props.travel.travelType) {
this.setCardHeight();
}
}
getTitleString = (travel: PlannerTravel) => {
if (travel.travelType === 'flight' && travel.travelData) {
// eslint-disable-next-line prefer-destructuring
const flight: Flight = travel.travelData.flight;
return `${flight.originCity.name} — ${flight.destinationCity.name}`;
}
return 'Travel';
};
getSubtitleString = memoize((travelType: TravelType) => {
if (travelType === 'flight') {
return 'Flight';
}
if (travelType === 'public-transport') {
return 'Public Transport';
}
if (travelType === 'car') {
return 'By Car';
}
if (travelType === 'bike') {
return 'By Bike';
}
if (travelType === 'walk') {
return 'By Walking';
}
return 'Other Method';
});
setCardHeight = () => {
// if no timeout, then the height comes out as 0.
setTimeout(() => {
try {
this.rowContainer.measure((x, y, width, height) => {
this.setState({
cardHeight: height
});
});
// eslint-disable-next-line no-empty
} catch (e) {}
}, 10);
};
hideCard = () => {
this.animatableShrinkContainer.transition(
{
height: this.state.cardHeight,
opacity: 1
},
{
height: MEASUREMENTS.ACTIVITY_CARD_HEIGHT_PER_HOUR,
opacity: 0
},
100
);
};
showCard = () => {
// hideCard needs to be called first to get the height.
this.animatableShrinkContainer.transition(
{
height: MEASUREMENTS.ACTIVITY_CARD_HEIGHT_PER_HOUR,
opacity: 0
},
{
height: this.state.cardHeight,
opacity: 1
},
100
);
};
rowContainer: any;
animatableShrinkContainer: Animatable.AnimatableComponent;
render() {
const {
dayIndex,
dayEntityIndex,
travel,
previousEntity,
nextEntity,
isSliderAdjustable,
isFixed
} = this.props;
const showFromSeparator = !previousEntity;
const showToSeparator = true;
// highlights text if the travel has details associated.
const hasDetails = travel.travelType === 'flight';
const titleString = this.getTitleString(travel);
const subtitleString = this.getSubtitleString(travel.travelType);
const hasTzDiff = travel.fromTimezone !== travel.toTimezone;
const hasDroppable = travel.travelType !== 'flight';
let toSliderIsAdjustable = false;
if (isSliderAdjustable && !isFixed && nextEntity) {
if (
nextEntity.attributes.includes(ENTITY_ATTRIBUTES.editingProperties.isSliderAdjustable)
&& !nextEntity.attributes.includes(ENTITY_ATTRIBUTES.editingProperties.isFixed)
) {
if (
!previousEntity
|| (!previousEntity.attributes.includes(ENTITY_ATTRIBUTES.editingProperties.isFixed)
&& previousEntity.attributes.includes(ENTITY_ATTRIBUTES.editingProperties.isSliderAdjustable))
) {
toSliderIsAdjustable = true;
}
}
}
return (
<Container>
{showFromSeparator && (
<TimeSeparator
time={travel.from}
adjustable={false}
separatorHasPopup={doesSeparatorHavePopup(previousEntity, {
entityType: ENTITY_TYPES.TRAVEL,
entity: { travelType: travel.travelType }
})}
dayIndex={dayIndex}
dayEntityIndex={dayEntityIndex}
loc="from"
/>
)}
<AnimatableShrinkContainer ref={c => (this.animatableShrinkContainer = c)}>
{hasDroppable && (
<DroppableHandler
dayIndex={dayIndex}
dayEntityIndex={dayEntityIndex}
onAttach={this.hideCard}
onDetach={this.showCard}
expandOnAttach={false}
attachBoundHeight={this.state.cardHeight}
/>
)}
<RowContainer innerRef={c => (this.rowContainer = c)}>
<DurationSection from={travel.from} to={travel.to} isFixed={isFixed} />
<CardContainer>
<InfoContainer>
<Title hasDetails={hasDetails}>{titleString}</Title>
<Subtitle hasDetails={hasDetails}>{subtitleString}</Subtitle>
</InfoContainer>
<TravelIcon
travelType={travel.travelType}
fill={hasDetails ? TourStyles.colors.highlight1 : TourStyles.colors.element1}
/>
</CardContainer>
</RowContainer>
</AnimatableShrinkContainer>
{hasTzDiff
&& (travel.travelType === 'flight' ? (
<CityIntroInsert
cityName={travel.destinationCity.name}
firstTime={travel.from}
secondTime={travel.to}
/>
) : (
<TimezoneDifferenceInsert firstTime={travel.from} secondTime={travel.to} />
))}
{showToSeparator && (
<TimeSeparator
time={travel.to}
adjustable={toSliderIsAdjustable}
separatorHasPopup={doesSeparatorHavePopup(
{ entityType: ENTITY_TYPES.TRAVEL, entity: { travelType: travel.travelType } },
nextEntity
)}
dayIndex={dayIndex}
dayEntityIndex={dayEntityIndex}
loc="to"
/>
)}
</Container>
);
}
}
const mapStateToProps = (state, props) => ({
previousEntity: selectEntity(state, { dayIndex: props.dayIndex, dayEntityIndex: props.dayEntityIndex - 1 }),
nextEntity: selectEntity(state, { dayIndex: props.dayIndex, dayEntityIndex: props.dayEntityIndex + 1 })
});
export default connect(mapStateToProps)(Travel);
const AnimatableShrinkContainer = Animatable.createAnimatableComponent(styled.View`
width: 100%;
height: auto;
overflow: hidden;
`);
const Container = styled.View`
width: 100%;
height: auto;
padding-right: 20px;
`;
const RowContainer = styled.View`
width: 100%;
height: auto;
flex-direction: row;
`;
const CardContainer = styled.View`
flex: 1;
height: auto;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding-horizontal: 14px;
padding-vertical: 16px;
`;
const InfoContainer = styled.View`
flex: 1;
height: auto;
`;
const Title = styled(Header6)`
color: ${({ hasDetails }) => (hasDetails ? TourStyles.colors.highlight1 : TourStyles.colors.text1)};
`;
const Subtitle = styled(Subtitle2)`
color: ${({ hasDetails }) => (hasDetails ? TourStyles.colors.highlight2 : TourStyles.colors.text2)};
`;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment