location stream

This commit is contained in:
Bart Akeley 2017-08-27 14:29:37 -05:00
parent 56cc8a715e
commit 3e1249b6e8
8 changed files with 158 additions and 125 deletions

View file

@ -12,6 +12,10 @@
"eslint:recommended",
"plugin:react/recommended"
],
"globals": {
"Position": false,
"navigator": false
},
"rules": {
// overrides
"react/display-name": 1

111
js/apis/FoodItemsApi.js Normal file
View file

@ -0,0 +1,111 @@
// @flow
// TODO open a websoket and create observable from it
const DUMMY_DATA = [
{
id: 1,
name: 'Big John Cookies',
placeId: 'ChIJf5QJFBK1RIYRjjfxZz9Z0O0',
latitude: 30.270667599999996,
longitude: -97.7532464,
distance: 0.5,
quantity: 'alot',
category: 'desserts',
images: [
'https://s-media-cache-ak0.pinimg.com/564x/e7/5f/08/e75f08b00c0bc7f2b01b3d1a636389f6.jpg',
'http://images.media-allrecipes.com/userphotos/560x315/1107530.jpg',
],
thumbImage: 'http://images.media-allrecipes.com/userphotos/560x315/1107530.jpg',
},
{
id: 2,
name: 'Jelly Filled Donuts',
placeId: 'ChIJ72if-Qe1RIYRCzMucGEEdBA',
latitude: 30.263963300000004,
longitude: -97.742308,
distance: 1.0,
quantity: 'few',
category: 'desserts',
images: ['https://timenewsfeed.files.wordpress.com/2013/06/jellydonut.jpg'],
thumbImage: 'https://timenewsfeed.files.wordpress.com/2013/06/jellydonut.jpg',
},
{
id: 3,
name: 'Spelt Brownies',
placeId: 'ChIJG44vBQi1RIYRvWGHdYUolZY',
latitude: 30.265019100000004,
longitude: -97.7419085,
distance: 1.7,
quantity: 'few',
category: 'desserts',
images: ['http://www.momshealthyeats.com/wp-content/uploads/2011/11/spelt-fudge-brownie.jpg'],
thumbImage: 'http://www.momshealthyeats.com/wp-content/uploads/2011/11/spelt-fudge-brownie.jpg',
},
{
id: 4,
name: 'Oatmeal Raisin Cookies',
placeId: 'ChIJf5QJFBK1RIYRjjfxZz9Z0O0',
latitude: 30.270667599999996,
longitude: -97.7532464,
distance: 0.5,
quantity: 'few',
category: 'desserts',
images: ['http://cookingontheside.com/wp-content/uploads/2009/04/oatmeal_raisin_cookies_stack-400.jpg'],
thumbImage: 'http://cookingontheside.com/wp-content/uploads/2009/04/oatmeal_raisin_cookies_stack-400.jpg',
},
{
id: 5,
name: 'Donuts with Sprinkles',
placeId: 'ChIJ72if-Qe1RIYRCzMucGEEdBA',
latitude: 30.263963300000004,
longitude: -97.742308,
distance: 1.0,
quantity: 'few',
category: 'desserts',
images: ['https://dannivee.files.wordpress.com/2013/10/img_1950.jpg'],
thumbImage: 'https://dannivee.files.wordpress.com/2013/10/img_1950.jpg',
},
{
id: 6,
name: 'Powdered Donuts',
placeId: 'ChIJ72if-Qe1RIYRCzMucGEEdBA',
latitude: 30.263963300000004,
longitude: -97.742308,
distance: 1.0,
quantity: 'few',
category: 'desserts',
images: [
'http://3.bp.blogspot.com/-NUKSXr1qLHs/UQmsaEFgbTI/AAAAAAAAA_Y/l4psfVl4a5A/s1600/white-powdered-sugar-doughnuts-tracie-kaska.jpg',
],
thumbImage:
'http://3.bp.blogspot.com/-NUKSXr1qLHs/UQmsaEFgbTI/AAAAAAAAA_Y/l4psfVl4a5A/s1600/white-powdered-sugar-doughnuts-tracie-kaska.jpg',
},
{
id: 7,
name: 'Snickerdoodles',
placeId: 'ChIJr3szW6a1RIYRkM7LRpnBIO0',
latitude: 30.266898599999998,
longitude: -97.73798459999999,
quantity: 'few',
category: 'desserts',
images: ['http://josephcphillips.com/wp-content/uploads/2015/02/snickerdoodles2.jpg'],
thumbImage: 'http://josephcphillips.com/wp-content/uploads/2015/02/snickerdoodles2.jpg',
},
{
id: 8,
name: 'Pizza',
placeId: 'ChIJr3szW6a1RIYRkM7LRpnBIO0',
latitude: 30.266898599999998,
longitude: -97.73798459999999,
quantity: 'few',
category: 'entrees',
images: ['http://www.foodandhealth.co.uk/wp-content/uploads/2016/05/Hot-stone-Vegan-Pizza.jpg'],
thumbImage: 'http://www.foodandhealth.co.uk/wp-content/uploads/2016/05/Hot-stone-Vegan-Pizza.jpg',
},
];
/* eslint-disable no-unused-vars */
export const getFoodItemsForLocation = (location: Location) => {
return Promise.resolve(DUMMY_DATA);
};
/* eslint-enable no-unused-vars */

View file

@ -1,16 +1,20 @@
// @flow
import { type GooglePlaceObj } from '../records/PlaceRecord';
const apiKey = 'AIzaSyBfMm1y6JayCbXrQmgAG1R3ka4ZOJno_5E';
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) => {
type GooglePlaceDetailsResponse = { error_message: ?string, result: GooglePlaceObj };
export const getPlaceDetails = async (placeid: ?string): Promise<GooglePlaceObj> => {
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();
const { error_message, result }: GooglePlaceDetailsResponse = await res.json();
if (error_message) {
throw new Error(error_message);

View file

@ -3,8 +3,8 @@ 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> => {
if (!item.id) {
export const setById = (map: Map<string, any>, item: ?{ id?: string }): Map<string, any> => {
if (!item || !item.id) {
return map;
}
return map.set(item.id, item);

View file

@ -76,7 +76,7 @@ const FoodRecordDefaults: Place = {
const PlaceRecord = Record(FoodRecordDefaults, 'PlaceRecord');
const getPhotos = pathOr([], ['photos']);
const getPhotos = pathOr([{}], ['photos']);
const getPhotoRef = photo => photo.photo_reference || '';
@ -85,8 +85,12 @@ const getThumb = pipe(getPhotos, head, getPhotoRef, getURLForPhotoReference({ ma
const getPhotoUrls = pipe(getPhotos, map(getPhotoRef), map(getURLForPhotoReference({ maxheight: 600 })));
export const buildPlaceRecord = memoizeWith(
(place: GooglePlaceObj) => place.place_id,
(place: GooglePlaceObj) => {
(place: ?GooglePlaceObj) => place && place.place_id,
(place: ?GooglePlaceObj) => {
if (!place) {
return;
}
const {
place_id,
name,

View file

@ -1,113 +1,10 @@
//@flow
import { createFoodItem } from '../records/FoodItemRecord';
import { Observable } from 'rxjs';
import { setById } from '../helpers/ImmutableHelpers';
import { Map } from 'immutable';
import location$ from './LocationStream';
import { getFoodItemsForLocation } from '../apis/FoodItemsApi';
// TODO open a websoket and create observable from it
const DUMMY_DATA = [
{
id: 1,
name: 'Big John Cookies',
placeId: 'ChIJf5QJFBK1RIYRjjfxZz9Z0O0',
latitude: 30.270667599999996,
longitude: -97.7532464,
distance: 0.5,
quantity: 'alot',
category: 'desserts',
images: [
'https://s-media-cache-ak0.pinimg.com/564x/e7/5f/08/e75f08b00c0bc7f2b01b3d1a636389f6.jpg',
'http://images.media-allrecipes.com/userphotos/560x315/1107530.jpg',
],
thumbImage: 'http://images.media-allrecipes.com/userphotos/560x315/1107530.jpg',
},
{
id: 2,
name: 'Jelly Filled Donuts',
placeId: 'ChIJ72if-Qe1RIYRCzMucGEEdBA',
latitude: 30.263963300000004,
longitude: -97.742308,
distance: 1.0,
quantity: 'few',
category: 'desserts',
images: ['https://timenewsfeed.files.wordpress.com/2013/06/jellydonut.jpg'],
thumbImage: 'https://timenewsfeed.files.wordpress.com/2013/06/jellydonut.jpg',
},
{
id: 3,
name: 'Spelt Brownies',
placeId: 'ChIJG44vBQi1RIYRvWGHdYUolZY',
latitude: 30.265019100000004,
longitude: -97.7419085,
distance: 1.7,
quantity: 'few',
category: 'desserts',
images: ['http://www.momshealthyeats.com/wp-content/uploads/2011/11/spelt-fudge-brownie.jpg'],
thumbImage: 'http://www.momshealthyeats.com/wp-content/uploads/2011/11/spelt-fudge-brownie.jpg',
},
{
id: 4,
name: 'Oatmeal Raisin Cookies',
placeId: 'ChIJf5QJFBK1RIYRjjfxZz9Z0O0',
latitude: 30.270667599999996,
longitude: -97.7532464,
distance: 0.5,
quantity: 'few',
category: 'desserts',
images: ['http://cookingontheside.com/wp-content/uploads/2009/04/oatmeal_raisin_cookies_stack-400.jpg'],
thumbImage: 'http://cookingontheside.com/wp-content/uploads/2009/04/oatmeal_raisin_cookies_stack-400.jpg',
},
{
id: 5,
name: 'Donuts with Sprinkles',
placeId: 'ChIJ72if-Qe1RIYRCzMucGEEdBA',
latitude: 30.263963300000004,
longitude: -97.742308,
distance: 1.0,
quantity: 'few',
category: 'desserts',
images: ['https://dannivee.files.wordpress.com/2013/10/img_1950.jpg'],
thumbImage: 'https://dannivee.files.wordpress.com/2013/10/img_1950.jpg',
},
{
id: 6,
name: 'Powdered Donuts',
placeId: 'ChIJ72if-Qe1RIYRCzMucGEEdBA',
latitude: 30.263963300000004,
longitude: -97.742308,
distance: 1.0,
quantity: 'few',
category: 'desserts',
images: [
'http://3.bp.blogspot.com/-NUKSXr1qLHs/UQmsaEFgbTI/AAAAAAAAA_Y/l4psfVl4a5A/s1600/white-powdered-sugar-doughnuts-tracie-kaska.jpg',
],
thumbImage:
'http://3.bp.blogspot.com/-NUKSXr1qLHs/UQmsaEFgbTI/AAAAAAAAA_Y/l4psfVl4a5A/s1600/white-powdered-sugar-doughnuts-tracie-kaska.jpg',
},
{
id: 7,
name: 'Snickerdoodles',
placeId: 'ChIJr3szW6a1RIYRkM7LRpnBIO0',
latitude: 30.266898599999998,
longitude: -97.73798459999999,
quantity: 'few',
category: 'desserts',
images: ['http://josephcphillips.com/wp-content/uploads/2015/02/snickerdoodles2.jpg'],
thumbImage: 'http://josephcphillips.com/wp-content/uploads/2015/02/snickerdoodles2.jpg',
},
{
id: 8,
name: 'Pizza',
placeId: 'ChIJr3szW6a1RIYRkM7LRpnBIO0',
latitude: 30.266898599999998,
longitude: -97.73798459999999,
quantity: 'few',
category: 'entrees',
images: ['http://www.foodandhealth.co.uk/wp-content/uploads/2016/05/Hot-stone-Vegan-Pizza.jpg'],
thumbImage: 'http://www.foodandhealth.co.uk/wp-content/uploads/2016/05/Hot-stone-Vegan-Pizza.jpg',
},
];
export const foodItemsRaw$ = Observable.from(DUMMY_DATA);
export default foodItemsRaw$.map(createFoodItem).scan(setById, Map());
export default location$.mergeMap(getFoodItemsForLocation).map(foodItems => {
return foodItems.map(createFoodItem).reduce(setById, new Map());
});

View file

@ -0,0 +1,6 @@
// @flow
import { Observable, type Observer } from 'rxjs';
export default Observable.create((obs: Observer<Position>): Observable<Position> => {
navigator.geolocation.getCurrentPosition((pos: Position) => obs.next(pos));
});

View file

@ -1,26 +1,33 @@
// @flow
import { buildPlaceRecord } from '../records/PlaceRecord';
import { Map } from 'immutable';
import { foodItemsRaw$ } from './FoodItemsStream';
import foodItems$ from './FoodItemsStream';
import { getPlaceDetails } from '../apis/PlaceDetailsApi';
import { memoize } from 'ramda';
import { Observable } from 'rxjs';
import typeof FoodItemRecord from '../records/FoodItemRecord';
import PlaceRecord, { type GooglePlaceObj } from '../records/PlaceRecord';
import { setById } from '../helpers/ImmutableHelpers';
/**
* 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 => {
const safeGetPlaceDetails = memoize((placeId: string): Promise<?GooglePlaceObj> => {
return getPlaceDetails(placeId).catch(error => {
console.log(error); // eslint-disable-line no-console
return {};
return null;
});
});
const uniquePlaceIds$ = foodItemsRaw$.map(foodItem => foodItem.placeId).distinct();
const placeRecords$ = uniquePlaceIds$.mergeMap(safeGetPlaceDetails).map(buildPlaceRecord).distinct();
export default placeRecords$.scan((accMap, place) => {
return accMap.set(place.id, place);
}, new Map());
export default foodItems$
.map((foodItems: Map<string, FoodItemRecord>): Array<string> => {
return foodItems.map(foodItem => foodItem.placeId).toArray();
})
.mergeMap((placeIds: Array<string>): Observable<Array<Promise<?GooglePlaceObj>>> => {
return Observable.forkJoin(placeIds.map(safeGetPlaceDetails));
})
.map((places: Array<GooglePlaceObj>): Map<string, PlaceRecord> => {
return places.map(buildPlaceRecord).reduce(setById, new Map());
});