diff --git a/js/apis/PlaceDetailsApi.js b/js/apis/PlaceDetailsApi.js
new file mode 100644
index 0000000..3bce083
--- /dev/null
+++ b/js/apis/PlaceDetailsApi.js
@@ -0,0 +1,18 @@
+// @flow
+const apiKey = 'AIzaSyBfMm1y6JayCbXrQmgAG1R3ka4ZOJno_5E';
+const placesUrl = `https://maps.googleapis.com/maps/api/place/details/json?key=${apiKey}`;
+
+export const getPlaceDetails = async (placeid: string) => {
+ if (!placeid || typeof placeid !== 'string') {
+ throw new Error('placeid looks wrong');
+ }
+
+ const res = await fetch(`${placesUrl}&placeid=${placeid}`);
+
+ const { error_message, result } = await res.json();
+
+ if (error_message) {
+ throw new Error(error_message);
+ }
+ return result;
+};
diff --git a/js/components/ItemTile.js b/js/components/ItemTile.js
index 191b8c2..d9d1d7d 100644
--- a/js/components/ItemTile.js
+++ b/js/components/ItemTile.js
@@ -60,10 +60,10 @@ export const FoodItemTile = pure(({ foodItem = {}, place = {} }: { foodItem: Foo
- {foodItem.name}
+ {foodItem.name || ''}
- {`${place.name} - ${foodItem.distance} mi`}
+ {`${place.name || ''} - ${foodItem.distance} mi`}
diff --git a/js/components/PlaceTile.js b/js/components/PlaceTile.js
index 4e6bc9b..cb0a347 100644
--- a/js/components/PlaceTile.js
+++ b/js/components/PlaceTile.js
@@ -2,7 +2,7 @@
import { View } from 'react-native';
import React from 'react';
import { pure } from 'recompose';
-import { type List } from 'immutable';
+import { List } from 'immutable';
import typeof PlaceRecord from '../records/PlaceRecord';
import { Link } from 'react-router-native';
import { Thumbnail, StrongText, SubText } from './ItemTile';
@@ -11,7 +11,7 @@ import { routeWithTitle } from '../helpers/RouteHelpers';
import theme from '../ui-theme';
type PlaceTileProps = { place: PlaceRecord, distance: number, categories: List };
-export default pure(({ place, distance, categories }: PlaceTileProps) => {
+export default pure(({ place = {}, distance = 999, categories = new List() }: PlaceTileProps) => {
return (
diff --git a/js/helpers/ImmutableHelpers.js b/js/helpers/ImmutableHelpers.js
index e9107e3..a0bc4dd 100644
--- a/js/helpers/ImmutableHelpers.js
+++ b/js/helpers/ImmutableHelpers.js
@@ -3,4 +3,9 @@ import { type List, type Map } from 'immutable';
export const pushInto = (list: List, item: T): List => list.push(item);
-export const setById = (map: Map, item: { id: string }): Map => map.set(item.id, item);
+export const setById = (map: Map, item: { id: string }): Map => {
+ if (!item.id) {
+ return map;
+ }
+ return map.set(item.id, item);
+};
diff --git a/js/pages/CreateFoodItem.js b/js/pages/CreateFoodItem.js
index 3953b50..ccbfe45 100644
--- a/js/pages/CreateFoodItem.js
+++ b/js/pages/CreateFoodItem.js
@@ -119,6 +119,8 @@ export default compose(
longitude,
name,
address,
+ phoneNumber,
+ website,
}) => {
setPlace(
new PlaceRecord({
@@ -127,6 +129,8 @@ export default compose(
address,
latitude,
longitude,
+ phoneNumber,
+ website,
})
);
diff --git a/js/records/PlaceRecord.js b/js/records/PlaceRecord.js
index cc554da..db8034f 100644
--- a/js/records/PlaceRecord.js
+++ b/js/records/PlaceRecord.js
@@ -1,20 +1,102 @@
// @flow
-import { Record } from 'immutable';
+import { List, Record, fromJS } from 'immutable';
+
+export type GooglePlaceObj = {
+ place_id: string,
+ name: string,
+ formatted_address: string,
+ geometry: {
+ location: {
+ lat: number,
+ lng: number,
+ },
+ viewport: {
+ northeast: {
+ lat: number,
+ lng: number,
+ },
+ southwest: {
+ lat: number,
+ lng: number,
+ },
+ },
+ },
+ formatted_phone_number: string,
+ url: string,
+ photos: Array,
+ icon: string,
+ opening_hours: {
+ open_now: boolean,
+ periods: Array<{
+ close: {
+ day: number,
+ time: string,
+ },
+ open: {
+ day: number,
+ time: string,
+ },
+ }>,
+ weekday_text: Array,
+ },
+};
export type Place = {
- id: ?string,
+ id: string,
name: string,
address: string,
- latitude: ?number,
- longitude: ?number,
+ latitude: number,
+ longitude: number,
+ phoneNumber: string,
+ googleMapsUrl: string,
+ photos: List,
+ icon: string,
+ hours: List,
+ openNow: boolean,
};
const FoodRecordDefaults: Place = {
- id: null,
+ id: '',
name: '',
address: '',
- latitude: null,
- longitude: null,
+ latitude: 0,
+ longitude: 0,
+ phoneNumber: '',
+ googleMapsUrl: '',
+ photos: new List(),
+ icon: '',
+ hours: new List(),
+ openNow: false,
};
-export default Record(FoodRecordDefaults, 'PlaceRecord');
+const PlaceRecord = Record(FoodRecordDefaults, 'PlaceRecord');
+
+export const buildPlaceRecord = (place: GooglePlaceObj) => {
+ const {
+ place_id,
+ name,
+ formatted_address,
+ geometry: { location = {} } = {},
+ formatted_phone_number,
+ url,
+ photos,
+ icon,
+ opening_hours = {},
+ } = place;
+
+ return new PlaceRecord({
+ id: place_id,
+ name: name,
+ address: formatted_address,
+ latitude: location.lat,
+ longitude: location.lng,
+ phoneNumber: formatted_phone_number,
+ googleMapsUrl: url,
+ photos: fromJS(photos),
+ icon: icon,
+ hours: fromJS(opening_hours.weekday_text),
+ openNow: opening_hours.open_now,
+ });
+};
+
+export default PlaceRecord;
diff --git a/js/streams/FoodItemsStream.js b/js/streams/FoodItemsStream.js
index 6459fc1..691a75c 100644
--- a/js/streams/FoodItemsStream.js
+++ b/js/streams/FoodItemsStream.js
@@ -108,4 +108,6 @@ const DUMMY_DATA = [
},
];
-export default Observable.from(DUMMY_DATA).map(FoodItemRecord).scan(setById, Map());
+export const foodItemsRaw$ = Observable.from(DUMMY_DATA);
+
+export default foodItemsRaw$.map(FoodItemRecord).scan(setById, Map());
diff --git a/js/streams/PlacesStream.js b/js/streams/PlacesStream.js
index feb51a3..cf19efb 100644
--- a/js/streams/PlacesStream.js
+++ b/js/streams/PlacesStream.js
@@ -1,46 +1,26 @@
// @flow
-import PlaceRecord from '../records/PlaceRecord';
-import { Observable } from 'rxjs';
+import { buildPlaceRecord } from '../records/PlaceRecord';
import { Map } from 'immutable';
-import { setById } from '../helpers/ImmutableHelpers';
+import { foodItemsRaw$ } from './FoodItemsStream';
+import { getPlaceDetails } from '../apis/PlaceDetailsApi';
+import { memoize } from 'ramda';
-// TODO open a websoket and create observable from it
-const DUMMY_DATA = [
- {
- id: 'ChIJf5QJFBK1RIYRjjfxZz9Z0O0',
- name: 'Whole Foods',
- address: '525 N. Lamar Blvd, Austin',
- latitude: 30.270667599999996,
- longitude: -97.7532464,
- },
- {
- id: 'ChIJm4_R2BG1RIYRQcnsPEmzGQY',
- name: "Trader Joe's",
- address: '211 Walter Seaholm Dr, Ste 100, Austin',
- latitude: 30.267681999999994,
- longitude: -97.7527494,
- },
- {
- id: 'ChIJG44vBQi1RIYRvWGHdYUolZY',
- name: 'Royal Blue Grocery',
- address: '301 Brazos St Suite 110, Austin, TX 78701, USA',
- latitude: 30.265019100000004,
- longitude: -97.7419085,
- },
- {
- id: 'ChIJ72if-Qe1RIYRCzMucGEEdBA',
- name: 'Second Street Market',
- address: '200 San Jacinto Blvd, Austin',
- latitude: 30.263963300000004,
- longitude: -97.742308,
- },
- {
- id: 'ChIJr3szW6a1RIYRkM7LRpnBIO0',
- name: 'Lonestar Souvenir and Food',
- address: '502 E. 6th St, Austin',
- latitude: 30.266898599999998,
- longitude: -97.73798459999999,
- },
-];
+/**
+ * return a promise of a place details object
+ * if already requested return existing promise
+ * swallow exceptions so as not to break the stream
+ */
+const safeGetPlaceDetails = memoize(placeId => {
+ return getPlaceDetails(placeId).catch(error => {
+ console.log(error); // eslint-disable-line no-console
+ return {};
+ });
+});
-export default Observable.from(DUMMY_DATA).map(PlaceRecord).scan(setById, new Map());
+const uniquePlaceIds$ = foodItemsRaw$.map(({ placeId }) => placeId).distinct();
+
+const placeRecords$ = uniquePlaceIds$.mergeMap(safeGetPlaceDetails).map(buildPlaceRecord);
+
+export default placeRecords$.scan((accMap, place) => {
+ return accMap.set(place.id, place);
+}, new Map());
diff --git a/package.json b/package.json
index b63987a..8dd7f60 100644
--- a/package.json
+++ b/package.json
@@ -24,7 +24,7 @@
"react-native-vector-icons": "^4.0.0",
"react-router-native": "^4.0.0",
"recompose": "^0.23.4",
- "rxjs": "^5.2.0"
+ "rxjs": "^5.4.2"
},
"devDependencies": {
"babel-eslint": "^7.1.1",
diff --git a/yarn.lock b/yarn.lock
index 435b1a0..a9719cc 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -3973,9 +3973,9 @@ rx-lite@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102"
-rxjs@^5.2.0:
- version "5.4.0"
- resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.4.0.tgz#a7db14ab157f9d7aac6a56e655e7a3860d39bf26"
+rxjs@^5.4.2:
+ version "5.4.2"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-5.4.2.tgz#2a3236fcbf03df57bae06fd6972fd99e5c08fcf7"
dependencies:
symbol-observable "^1.0.1"