mirror of
https://gitlab.com/wheres-the-tp/ui-mobile.git
synced 2026-01-25 06:14:55 -06:00
places detail view and tile hours
This commit is contained in:
parent
e23f279b65
commit
33303d7339
14 changed files with 251 additions and 90 deletions
|
|
@ -1,8 +1,9 @@
|
||||||
// @flow
|
// @flow
|
||||||
const apiKey = 'AIzaSyBfMm1y6JayCbXrQmgAG1R3ka4ZOJno_5E';
|
const apiKey = 'AIzaSyBfMm1y6JayCbXrQmgAG1R3ka4ZOJno_5E';
|
||||||
const placesUrl = `https://maps.googleapis.com/maps/api/place/details/json?key=${apiKey}`;
|
const placesUrl = `https://maps.googleapis.com/maps/api/place/details/json?key=${apiKey}`;
|
||||||
|
const photosUrl = `https://maps.googleapis.com/maps/api/place/photo?key=${apiKey}`;
|
||||||
|
|
||||||
export const getPlaceDetails = async (placeid: string) => {
|
export const getPlaceDetails = async (placeid: ?string) => {
|
||||||
if (!placeid || typeof placeid !== 'string') {
|
if (!placeid || typeof placeid !== 'string') {
|
||||||
throw new Error('placeid looks wrong');
|
throw new Error('placeid looks wrong');
|
||||||
}
|
}
|
||||||
|
|
@ -16,3 +17,19 @@ export const getPlaceDetails = async (placeid: string) => {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getURLForPhotoReference = (opts?: { maxheight?: number, maxwidth?: number } = {}) => (
|
||||||
|
photoReference: string
|
||||||
|
) => {
|
||||||
|
if (!photoReference) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const { maxheight, maxwidth } = opts;
|
||||||
|
|
||||||
|
const maxHeight = `&maxheight=${maxheight || 600}`;
|
||||||
|
const maxWidth = maxwidth ? `&maxwidth=${maxwidth}` : '';
|
||||||
|
const photoref = `&photoreference=${photoReference}`;
|
||||||
|
|
||||||
|
return `${photosUrl}${photoref}${maxHeight}${maxWidth}`;
|
||||||
|
};
|
||||||
|
|
|
||||||
37
js/components/FoodItemTile.js
Normal file
37
js/components/FoodItemTile.js
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
// @flow
|
||||||
|
import React from 'react';
|
||||||
|
import { View } from 'react-native';
|
||||||
|
import { pure } from 'recompose';
|
||||||
|
import FoodItemRecord from '../records/FoodItemRecord';
|
||||||
|
import typeof PlaceRecord from '../records/PlaceRecord';
|
||||||
|
import theme from '../ui-theme';
|
||||||
|
import { Link } from 'react-router-native';
|
||||||
|
import { routeWithTitle } from '../helpers/RouteHelpers';
|
||||||
|
import { TileBox, StrongText, SubText, Thumbnail } from './ItemTile';
|
||||||
|
|
||||||
|
export default pure(({ foodItem, place = {} }: { foodItem: FoodItemRecord, place: PlaceRecord }) => {
|
||||||
|
if (!foodItem) {
|
||||||
|
return <View />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Link
|
||||||
|
to={routeWithTitle(`/foodItem/${foodItem.id || ''}`, foodItem.name)}
|
||||||
|
underlayColor={theme.itemTile.pressHighlightColor}
|
||||||
|
>
|
||||||
|
<View>
|
||||||
|
<TileBox>
|
||||||
|
<Thumbnail thumb={foodItem.thumbImage} />
|
||||||
|
<View style={{ paddingTop: 15 }}>
|
||||||
|
<StrongText>
|
||||||
|
{foodItem.name || ''}
|
||||||
|
</StrongText>
|
||||||
|
<SubText>
|
||||||
|
{`${place.name || ''} - ${foodItem.distance} mi`}
|
||||||
|
</SubText>
|
||||||
|
</View>
|
||||||
|
</TileBox>
|
||||||
|
</View>
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
@ -4,13 +4,15 @@ import { Text, View, TouchableOpacity } from 'react-native';
|
||||||
import { pure } from 'recompose';
|
import { pure } from 'recompose';
|
||||||
import { Icon } from 'react-native-material-ui';
|
import { Icon } from 'react-native-material-ui';
|
||||||
|
|
||||||
type Props = { glyph: string, text: string, route: string, onPress: Function, underlayColor?: string };
|
type Props = { glyph: string, text: string, route: string, onPress: Function, color?: string };
|
||||||
|
|
||||||
export default pure(({ glyph, text, underlayColor = 'grey', ...others }: Props) => (
|
export default pure(({ glyph, text, color = 'grey', ...others }: Props) =>
|
||||||
<TouchableOpacity {...others}>
|
<TouchableOpacity {...others}>
|
||||||
<View style={{ flexDirection: 'row', marginTop: 5 }}>
|
<View style={{ flexDirection: 'row', marginTop: 5 }}>
|
||||||
<Icon name={glyph} size={35} color={underlayColor} style={{ marginRight: 10 }} />
|
<Icon name={glyph} size={35} color={color} style={{ marginRight: 10 }} />
|
||||||
<Text style={{ lineHeight: 26 }}>{text}</Text>
|
<Text style={{ lineHeight: 26 }}>
|
||||||
|
{text}
|
||||||
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
));
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,7 @@
|
||||||
//@flow
|
//@flow
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { Text, View, Image } from 'react-native';
|
import { Text, View, Image } from 'react-native';
|
||||||
import { pure } from 'recompose';
|
|
||||||
import FoodItemRecord from '../records/FoodItemRecord';
|
|
||||||
import typeof PlaceRecord from '../records/PlaceRecord';
|
|
||||||
import theme from '../ui-theme';
|
import theme from '../ui-theme';
|
||||||
import { Link } from 'react-router-native';
|
|
||||||
import { routeWithTitle } from '../helpers/RouteHelpers';
|
|
||||||
|
|
||||||
export const Thumbnail = ({ thumb }: { thumb: ?string }) =>
|
export const Thumbnail = ({ thumb }: { thumb: ?string }) =>
|
||||||
<View
|
<View
|
||||||
|
|
@ -49,24 +44,3 @@ export const TileBox = ({ children, style = {} }: { children?: any, style?: Obje
|
||||||
<View style={{ height: 70, flexDirection: 'row', backgroundColor: 'white', ...style }}>
|
<View style={{ height: 70, flexDirection: 'row', backgroundColor: 'white', ...style }}>
|
||||||
{children}
|
{children}
|
||||||
</View>;
|
</View>;
|
||||||
|
|
||||||
export const FoodItemTile = pure(({ foodItem = {}, place = {} }: { foodItem: FoodItemRecord, place: PlaceRecord }) =>
|
|
||||||
<Link
|
|
||||||
to={routeWithTitle(`/foodItem/${foodItem.id || ''}`, foodItem.name)}
|
|
||||||
underlayColor={theme.itemTile.pressHighlightColor}
|
|
||||||
>
|
|
||||||
<View>
|
|
||||||
<TileBox>
|
|
||||||
<Thumbnail thumb={foodItem.thumbImage} />
|
|
||||||
<View style={{ paddingTop: 15 }}>
|
|
||||||
<StrongText>
|
|
||||||
{foodItem.name || ''}
|
|
||||||
</StrongText>
|
|
||||||
<SubText>
|
|
||||||
{`${place.name || ''} - ${foodItem.distance} mi`}
|
|
||||||
</SubText>
|
|
||||||
</View>
|
|
||||||
</TileBox>
|
|
||||||
</View>
|
|
||||||
</Link>
|
|
||||||
);
|
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,23 @@ import { Link } from 'react-router-native';
|
||||||
import { Thumbnail, StrongText, SubText } from './ItemTile';
|
import { Thumbnail, StrongText, SubText } from './ItemTile';
|
||||||
import { routeWithTitle } from '../helpers/RouteHelpers';
|
import { routeWithTitle } from '../helpers/RouteHelpers';
|
||||||
|
|
||||||
|
const getHoursText = (hours: List<string>) => {
|
||||||
|
if (!hours) {
|
||||||
|
return 'not listed';
|
||||||
|
}
|
||||||
|
// Note: js Date return 0-Sun/6-Sat where google return 0-Mon/6-Sun
|
||||||
|
// Immutable.List is smart enough to know that a negative index is counted from the last index
|
||||||
|
return hours.get(new Date().getDay() - 1);
|
||||||
|
};
|
||||||
|
|
||||||
import theme from '../ui-theme';
|
import theme from '../ui-theme';
|
||||||
|
|
||||||
type PlaceTileProps = { place: PlaceRecord, distance: number, categories: List<string> };
|
type PlaceTileProps = { place: PlaceRecord, distance: number, categories: List<string> };
|
||||||
export default pure(({ place = {}, distance = 999, categories = new List() }: PlaceTileProps) => {
|
export default pure(({ place, distance = 999, categories = new List() }: PlaceTileProps) => {
|
||||||
|
if (!place) {
|
||||||
|
return <View />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link
|
<Link
|
||||||
to={routeWithTitle(`/place/${place.id || ''}`, place.name)}
|
to={routeWithTitle(`/place/${place.id || ''}`, place.name)}
|
||||||
|
|
@ -19,7 +32,7 @@ export default pure(({ place = {}, distance = 999, categories = new List() }: Pl
|
||||||
>
|
>
|
||||||
<View style={{ height: 70, flexDirection: 'row', backgroundColor: 'white' }}>
|
<View style={{ height: 70, flexDirection: 'row', backgroundColor: 'white' }}>
|
||||||
<Thumbnail thumb={place.thumb} />
|
<Thumbnail thumb={place.thumb} />
|
||||||
<View style={{ paddingTop: 15 }}>
|
<View style={{ paddingTop: 5 }}>
|
||||||
<StrongText>
|
<StrongText>
|
||||||
{`${place.name} - ${distance} mi`}
|
{`${place.name} - ${distance} mi`}
|
||||||
</StrongText>
|
</StrongText>
|
||||||
|
|
@ -27,7 +40,7 @@ export default pure(({ place = {}, distance = 999, categories = new List() }: Pl
|
||||||
{`${categories.interpose(', ').reduce((str, token) => str + token, '')}`}
|
{`${categories.interpose(', ').reduce((str, token) => str + token, '')}`}
|
||||||
</SubText>
|
</SubText>
|
||||||
<SubText>
|
<SubText>
|
||||||
{'hours'}
|
{getHoursText(place.hours)}
|
||||||
</SubText>
|
</SubText>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import withProps from 'recompose/withProps';
|
||||||
import mapPropsStream from 'recompose/mapPropsStream';
|
import mapPropsStream from 'recompose/mapPropsStream';
|
||||||
import compose from 'recompose/compose';
|
import compose from 'recompose/compose';
|
||||||
import Places$ from '../streams/PlacesStream';
|
import Places$ from '../streams/PlacesStream';
|
||||||
|
import FoodItems$ from '../streams/FoodItemsStream';
|
||||||
import { path } from 'ramda';
|
import { path } from 'ramda';
|
||||||
|
|
||||||
export const withPlaces = mapPropsStream(props$ =>
|
export const withPlaces = mapPropsStream(props$ =>
|
||||||
|
|
@ -40,3 +41,13 @@ export const withPlaceForFoodItem = mapPropsStream(props$ =>
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const withFoodItemsForPlace = mapPropsStream(props$ =>
|
||||||
|
props$.combineLatest(FoodItems$, (props, foodItems) => {
|
||||||
|
const placeId = props.placeId;
|
||||||
|
return {
|
||||||
|
...props,
|
||||||
|
foodItems: foodItems.toList().filter(foodItem => placeId === foodItem.placeId),
|
||||||
|
};
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
import React, { Component, PropTypes } from 'react';
|
import React, { Component, PropTypes } from 'react';
|
||||||
import { View, ScrollView } from 'react-native';
|
import { View, ScrollView } from 'react-native';
|
||||||
import theme from '../ui-theme';
|
import theme from '../ui-theme';
|
||||||
import { FoodItemTile } from '../components/ItemTile';
|
import FoodItemTile from '../components/FoodItemTile';
|
||||||
import { ActionButton } from 'react-native-material-ui';
|
import { ActionButton } from 'react-native-material-ui';
|
||||||
import { routeWithTitle } from '../helpers/RouteHelpers';
|
import { routeWithTitle } from '../helpers/RouteHelpers';
|
||||||
import FoodItemList from '../components/FoodItemList';
|
import FoodItemList from '../components/FoodItemList';
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,11 @@ export class FoodItemDetail extends Component {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { foodItem, place } = this.props;
|
const { foodItem, place } = this.props;
|
||||||
|
|
||||||
|
if (!foodItem || !place) {
|
||||||
|
return <View />;
|
||||||
|
}
|
||||||
|
|
||||||
const { images, quantity } = foodItem;
|
const { images, quantity } = foodItem;
|
||||||
const viewableImages = images.filter(image => !!image);
|
const viewableImages = images.filter(image => !!image);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,112 @@
|
||||||
// @flow
|
// @flow
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { Text, View } from 'react-native';
|
import { View, Image, ScrollView } from 'react-native';
|
||||||
import theme from '../ui-theme';
|
import theme from '../ui-theme';
|
||||||
import { compose, pure } from 'recompose';
|
import { compose, pure } from 'recompose';
|
||||||
|
import typeof PlaceRecord from '../records/PlaceRecord';
|
||||||
|
import { withPlace, withPlaceIdFromRoute, withFoodItemsForPlace } from '../enhancers/placeEnhancers';
|
||||||
|
import Carousel from 'react-native-looped-carousel';
|
||||||
|
import CountBadge from '../components/CountBadge';
|
||||||
|
import { StrongText } from '../components/ItemTile';
|
||||||
|
import IconButton from '../components/IconButton';
|
||||||
|
import { type List } from 'immutable';
|
||||||
|
import typeof FoodItemRecord from '../records/FoodItemRecord';
|
||||||
|
import FoodItemTile from '../components/FoodItemTile';
|
||||||
|
|
||||||
|
const { placeDetails: style } = theme;
|
||||||
|
|
||||||
|
const stretchedStyle = { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 };
|
||||||
|
|
||||||
|
const contentTileStyle = {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
paddingLeft: 20,
|
||||||
|
paddingRight: 20,
|
||||||
|
};
|
||||||
|
|
||||||
export class PlaceDetail extends Component {
|
export class PlaceDetail extends Component {
|
||||||
static displayName = 'PlaceDetail';
|
static displayName = 'PlaceDetail';
|
||||||
|
|
||||||
props: {
|
props: {
|
||||||
match: {
|
place: ?PlaceRecord,
|
||||||
params: {
|
foodItems: ?List<FoodItemRecord>,
|
||||||
id: string,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
state: {
|
||||||
|
currentImage: number,
|
||||||
|
};
|
||||||
|
|
||||||
|
state = {
|
||||||
|
currentImage: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
changeCurrentImage = (currentImage: number) => this.setState({ currentImage });
|
||||||
|
|
||||||
|
// TODO placeholder implementation until we get a backend
|
||||||
|
addToFaves = () =>
|
||||||
|
this.setState(prevState => ({
|
||||||
|
isFavorite: !prevState.isFavorite,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// todo - build a map view and open it here
|
||||||
|
viewOnMap = () => {};
|
||||||
|
|
||||||
// todo: need to get the item from the stream by this id
|
// todo: need to get the item from the stream by this id
|
||||||
render() {
|
render() {
|
||||||
const { match: { params: { id } } } = this.props;
|
const { place, foodItems } = this.props;
|
||||||
|
|
||||||
|
if (!place) {
|
||||||
|
return <View />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { photos } = place;
|
||||||
|
const { currentImage } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={theme.page.container}>
|
<ScrollView style={theme.page.container}>
|
||||||
<Text>{id}</Text>
|
<View style={{ height: 200 }}>
|
||||||
|
{photos.size === 1 && <Image style={stretchedStyle} source={{ uri: photos.first() }} />}
|
||||||
|
{photos.size > 1 &&
|
||||||
|
<Carousel autoplay={false} onAnimateNextPage={this.changeCurrentImage} style={stretchedStyle}>
|
||||||
|
{photos.map(uri =>
|
||||||
|
<Image key={uri} style={{ flex: 1, resizeMode: 'stretch' }} source={{ uri }} />
|
||||||
|
)}
|
||||||
|
</Carousel>}
|
||||||
|
<CountBadge currentImage={currentImage + 1} totalCount={photos.size} />
|
||||||
</View>
|
</View>
|
||||||
|
<View style={{ height: 50, marginBottom: 10, ...contentTileStyle }}>
|
||||||
|
<View style={{ marginTop: 15 }}>
|
||||||
|
<StrongText>
|
||||||
|
{place.address}
|
||||||
|
</StrongText>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={{ flexBasis: 75, ...contentTileStyle, marginBottom: 10, paddingTop: 5, paddingBottom: 10 }}
|
||||||
|
>
|
||||||
|
<IconButton
|
||||||
|
glyph="stars"
|
||||||
|
text="Add to Faves"
|
||||||
|
onPress={this.addToFaves}
|
||||||
|
color={style.actionIconColor}
|
||||||
|
/>
|
||||||
|
<IconButton
|
||||||
|
glyph="map"
|
||||||
|
text="View on a Map"
|
||||||
|
onPress={this.viewOnMap}
|
||||||
|
color={style.actionIconColor}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<View style={{ flex: 2, ...contentTileStyle, paddingTop: 10 }}>
|
||||||
|
<StrongText>Products</StrongText>
|
||||||
|
{!!foodItems &&
|
||||||
|
foodItems.size &&
|
||||||
|
foodItems.map(foodItem => <FoodItemTile key={foodItem.id} foodItem={foodItem} place={place} />)}
|
||||||
|
</View>
|
||||||
|
</ScrollView>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const enhance = compose(pure);
|
const enhance = compose(pure, withPlaceIdFromRoute, withPlace, withFoodItemsForPlace);
|
||||||
|
|
||||||
export default enhance(PlaceDetail);
|
export default enhance(PlaceDetail);
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@ const PlacesList = withFoodItemsGroupedByPlace(
|
||||||
<View>
|
<View>
|
||||||
{foodItemsByPlace
|
{foodItemsByPlace
|
||||||
.map((foodItems: Map<string, FoodItemRecord>, placeId: string) => {
|
.map((foodItems: Map<string, FoodItemRecord>, placeId: string) => {
|
||||||
const Comp = withPlaceProps(PlaceTile);
|
const EnhancedPlaceTile = withPlaceProps(PlaceTile);
|
||||||
return <Comp key={placeId} placeId={placeId} foodItems={foodItems} />;
|
return <EnhancedPlaceTile key={placeId} placeId={placeId} foodItems={foodItems} />;
|
||||||
})
|
})
|
||||||
.toList()}
|
.toList()}
|
||||||
</View>
|
</View>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { List, Record, fromJS } from 'immutable';
|
import { List, Map, Record, fromJS } from 'immutable';
|
||||||
|
import { pipe, pathOr, map, head, memoizeWith } from 'ramda';
|
||||||
|
import { getURLForPhotoReference } from '../apis/PlaceDetailsApi';
|
||||||
|
|
||||||
export type GooglePlaceObj = {
|
export type GooglePlaceObj = {
|
||||||
place_id: string,
|
place_id: string,
|
||||||
|
|
@ -24,7 +26,6 @@ export type GooglePlaceObj = {
|
||||||
formatted_phone_number: string,
|
formatted_phone_number: string,
|
||||||
url: string,
|
url: string,
|
||||||
photos: Array<string>,
|
photos: Array<string>,
|
||||||
icon: string,
|
|
||||||
opening_hours: {
|
opening_hours: {
|
||||||
open_now: boolean,
|
open_now: boolean,
|
||||||
periods: Array<{
|
periods: Array<{
|
||||||
|
|
@ -41,6 +42,8 @@ export type GooglePlaceObj = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type Period = Map<string, any>;
|
||||||
|
|
||||||
export type Place = {
|
export type Place = {
|
||||||
id: string,
|
id: string,
|
||||||
name: string,
|
name: string,
|
||||||
|
|
@ -50,8 +53,9 @@ export type Place = {
|
||||||
phoneNumber: string,
|
phoneNumber: string,
|
||||||
googleMapsUrl: string,
|
googleMapsUrl: string,
|
||||||
photos: List<string>,
|
photos: List<string>,
|
||||||
icon: string,
|
thumb: string,
|
||||||
hours: List<string>,
|
hours: List<string>,
|
||||||
|
periods: List<Map<string, Period>>,
|
||||||
openNow: boolean,
|
openNow: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -64,14 +68,25 @@ const FoodRecordDefaults: Place = {
|
||||||
phoneNumber: '',
|
phoneNumber: '',
|
||||||
googleMapsUrl: '',
|
googleMapsUrl: '',
|
||||||
photos: new List(),
|
photos: new List(),
|
||||||
icon: '',
|
thumb: '',
|
||||||
hours: new List(),
|
hours: new List(),
|
||||||
|
periods: new List(),
|
||||||
openNow: false,
|
openNow: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const PlaceRecord = Record(FoodRecordDefaults, 'PlaceRecord');
|
const PlaceRecord = Record(FoodRecordDefaults, 'PlaceRecord');
|
||||||
|
|
||||||
export const buildPlaceRecord = (place: GooglePlaceObj) => {
|
const getPhotos = pathOr([], ['photos']);
|
||||||
|
|
||||||
|
const getPhotoRef = photo => photo.photo_reference || '';
|
||||||
|
|
||||||
|
const getThumb = pipe(getPhotos, head, getPhotoRef, getURLForPhotoReference({ maxheight: 200 }));
|
||||||
|
|
||||||
|
const getPhotoUrls = pipe(getPhotos, map(getPhotoRef), map(getURLForPhotoReference({ maxheight: 600 })));
|
||||||
|
|
||||||
|
export const buildPlaceRecord = memoizeWith(
|
||||||
|
(place: GooglePlaceObj) => place.place_id,
|
||||||
|
(place: GooglePlaceObj) => {
|
||||||
const {
|
const {
|
||||||
place_id,
|
place_id,
|
||||||
name,
|
name,
|
||||||
|
|
@ -79,11 +94,12 @@ export const buildPlaceRecord = (place: GooglePlaceObj) => {
|
||||||
geometry: { location = {} } = {},
|
geometry: { location = {} } = {},
|
||||||
formatted_phone_number,
|
formatted_phone_number,
|
||||||
url,
|
url,
|
||||||
photos,
|
|
||||||
icon,
|
|
||||||
opening_hours = {},
|
opening_hours = {},
|
||||||
} = place;
|
} = place;
|
||||||
|
|
||||||
|
const photos = fromJS(getPhotoUrls(place));
|
||||||
|
const thumb = getThumb(place);
|
||||||
|
|
||||||
return new PlaceRecord({
|
return new PlaceRecord({
|
||||||
id: place_id,
|
id: place_id,
|
||||||
name: name,
|
name: name,
|
||||||
|
|
@ -92,11 +108,14 @@ export const buildPlaceRecord = (place: GooglePlaceObj) => {
|
||||||
longitude: location.lng,
|
longitude: location.lng,
|
||||||
phoneNumber: formatted_phone_number,
|
phoneNumber: formatted_phone_number,
|
||||||
googleMapsUrl: url,
|
googleMapsUrl: url,
|
||||||
photos: fromJS(photos),
|
photos,
|
||||||
icon: icon,
|
thumb,
|
||||||
hours: fromJS(opening_hours.weekday_text),
|
hours: fromJS(opening_hours.weekday_text),
|
||||||
|
periods: fromJS(opening_hours.periods),
|
||||||
|
open: !!opening_hours.open_now,
|
||||||
openNow: opening_hours.open_now,
|
openNow: opening_hours.open_now,
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
);
|
||||||
|
|
||||||
export default PlaceRecord;
|
export default PlaceRecord;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
// @flow
|
// @flow
|
||||||
import { buildPlaceRecord } from '../records/PlaceRecord';
|
import { buildPlaceRecord } from '../records/PlaceRecord';
|
||||||
import { Map } from 'immutable';
|
import { Map } from 'immutable';
|
||||||
import { foodItemsRaw$ } from './FoodItemsStream';
|
import FoodItems$ from './FoodItemsStream';
|
||||||
import { getPlaceDetails } from '../apis/PlaceDetailsApi';
|
import { getPlaceDetails } from '../apis/PlaceDetailsApi';
|
||||||
import { memoize } from 'ramda';
|
import { memoize } from 'ramda';
|
||||||
|
|
||||||
|
|
@ -17,7 +17,9 @@ const safeGetPlaceDetails = memoize(placeId => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
const uniquePlaceIds$ = foodItemsRaw$.map(({ placeId }) => placeId).distinct();
|
const uniquePlaceIds$ = FoodItems$.mergeMap(foodItemMap => {
|
||||||
|
return foodItemMap.map(foodItem => foodItem.placeId).toArray();
|
||||||
|
}).distinct();
|
||||||
|
|
||||||
const placeRecords$ = uniquePlaceIds$.mergeMap(safeGetPlaceDetails).map(buildPlaceRecord);
|
const placeRecords$ = uniquePlaceIds$.mergeMap(safeGetPlaceDetails).map(buildPlaceRecord);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,9 @@ export default {
|
||||||
color: '#0E6E9E',
|
color: '#0E6E9E',
|
||||||
},
|
},
|
||||||
foodItemDetails: {
|
foodItemDetails: {
|
||||||
actionIconColor: COLOR.blue500,
|
actionIconColor: '#0E6E9E',
|
||||||
|
},
|
||||||
|
placeDetails: {
|
||||||
|
actionIconColor: '#0E6E9E',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,6 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"allowJs": true,
|
|
||||||
"allowSyntheticDefaultImports": true
|
"allowSyntheticDefaultImports": true
|
||||||
},
|
},
|
||||||
"exclude": [
|
"exclude": ["node_modules"]
|
||||||
"node_modules"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
Loading…
Add table
Reference in a new issue