mirror of
https://gitlab.com/wheres-the-tp/ui-mobile.git
synced 2026-01-25 06:14:55 -06:00
use google places api for all places components
This commit is contained in:
parent
b6b5497a72
commit
e23f279b65
11 changed files with 152 additions and 61 deletions
18
js/apis/PlaceDetailsApi.js
Normal file
18
js/apis/PlaceDetailsApi.js
Normal file
|
|
@ -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;
|
||||
};
|
||||
|
|
@ -60,10 +60,10 @@ export const FoodItemTile = pure(({ foodItem = {}, place = {} }: { foodItem: Foo
|
|||
<Thumbnail thumb={foodItem.thumbImage} />
|
||||
<View style={{ paddingTop: 15 }}>
|
||||
<StrongText>
|
||||
{foodItem.name}
|
||||
{foodItem.name || ''}
|
||||
</StrongText>
|
||||
<SubText>
|
||||
{`${place.name} - ${foodItem.distance} mi`}
|
||||
{`${place.name || ''} - ${foodItem.distance} mi`}
|
||||
</SubText>
|
||||
</View>
|
||||
</TileBox>
|
||||
|
|
|
|||
|
|
@ -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<string> };
|
||||
export default pure(({ place, distance, categories }: PlaceTileProps) => {
|
||||
export default pure(({ place = {}, distance = 999, categories = new List() }: PlaceTileProps) => {
|
||||
return (
|
||||
<Link
|
||||
to={routeWithTitle(`/place/${place.id || ''}`, place.name)}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { path } from 'ramda';
|
|||
import mapPropsStream from 'recompose/mapPropsStream';
|
||||
import FoodItems$ from '../streams/FoodItemsStream';
|
||||
import typeof FoodItemRecord from '../records/FoodItemRecord';
|
||||
import { Set, Map } from 'immutable';
|
||||
import { Map } from 'immutable';
|
||||
import { getCategoryText } from '../helpers/CategoryHelpers';
|
||||
|
||||
export const withFoodItems = mapPropsStream(props$ =>
|
||||
|
|
|
|||
|
|
@ -3,4 +3,9 @@ import { type List, type Map } from 'immutable';
|
|||
|
||||
export const pushInto = <T>(list: List<T>, item: T): List<T> => list.push(item);
|
||||
|
||||
export const setById = (map: Map<string, any>, item: { id: string }): Map<string, any> => map.set(item.id, item);
|
||||
export const setById = (map: Map<string, any>, item: { id: string }): Map<string, any> => {
|
||||
if (!item.id) {
|
||||
return map;
|
||||
}
|
||||
return map.set(item.id, item);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
})
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<string>,
|
||||
icon: string,
|
||||
opening_hours: {
|
||||
open_now: boolean,
|
||||
periods: Array<{
|
||||
close: {
|
||||
day: number,
|
||||
time: string,
|
||||
},
|
||||
open: {
|
||||
day: number,
|
||||
time: string,
|
||||
},
|
||||
}>,
|
||||
weekday_text: Array<string>,
|
||||
},
|
||||
};
|
||||
|
||||
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<string>,
|
||||
icon: string,
|
||||
hours: List<string>,
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue