Merge remote-tracking branch 'origin/master'

This commit is contained in:
Erick Clark 2017-12-03 12:52:48 -06:00
commit ef0cbfaca7
10 changed files with 105 additions and 19 deletions

View file

@ -1,9 +1,7 @@
// @flow
import { memoize } from 'ramda';
import FilterRecord from '../records/FilterRecord';
const BASE_URL = 'aretherecookies.herokuapp.com';
// const BASE_URL = '192.168.1.6:3000';
import { BASE_URL } from '../constants/AppConstants';
export type FoodItemsFilter = {
radius?: number,

27
js/apis/QuantityApi.js Normal file
View file

@ -0,0 +1,27 @@
// @flow
import type { Quantity } from '../constants/QuantityConstants';
import { BASE_URL } from '../constants/AppConstants';
import type { QuantityResponse } from '../constants/QuantityConstants';
export const setQuantity = ({
foodItemId,
quantity,
}: {
foodItemId: string,
quantity: Quantity,
}): ?Promise<QuantityResponse> => {
try {
return fetch(`http://${BASE_URL}/quantity`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
foodItemId,
quantity,
}),
}).then(res => res.json());
} catch (error) {
console.log(error); //eslint-disable-line
}
};

View file

@ -0,0 +1,4 @@
// @flow
export const BASE_URL = 'aretherecookies.herokuapp.com';
// export const BASE_URL = '192.168.1.6:3000';

View file

@ -3,5 +3,15 @@ export const QUANTITY_NONE: 'none' = 'none';
export const QUANTITY_FEW: 'few' = 'few';
export const QUANTITY_MANY: 'many' = 'many';
export const QUANTITY_LOTS: 'lots' = 'lots';
export type Quantity = typeof QUANTITY_NONE | typeof QUANTITY_FEW | typeof QUANTITY_MANY | typeof QUANTITY_LOTS;
export const QUANTITIES = [QUANTITY_NONE, QUANTITY_FEW, QUANTITY_LOTS, QUANTITY_MANY];
export type Quantity = typeof QUANTITY_NONE | typeof QUANTITY_FEW | typeof QUANTITY_MANY | typeof QUANTITY_LOTS;
export type QuantityResponse = {
food_item_id: string,
quantity: Quantity,
date: number,
};
export type QuantityFragment = { quantity: Quantity, lastupdated: number };

View file

@ -0,0 +1,13 @@
//@flow
import { withProps } from 'recompose';
import { setQuantity } from '../apis/QuantityApi';
import { emit } from '../streams/QuantityStream';
import type { Quantity, QuantityResponse } from '../constants/QuantityConstants';
import { nth } from 'ramda';
export const withUpdateQuantity = withProps({
updateQuantity: async ({ foodItemId, quantity }: { foodItemId: string, quantity: Quantity }) => {
const newQuantity: QuantityResponse = nth(0, await setQuantity({ foodItemId, quantity }));
emit(newQuantity);
},
});

View file

@ -3,6 +3,10 @@ import { type List, type Map } from 'immutable';
export const pushInto = <T>(list: List<T>, item: T): List<T> => list.push(item);
export function merge(a: Map<any, Object>, b: Map<any, Object>) {
return a.merge(b);
}
export const setById = (map: Map<string, any>, item: ?{ id?: string }): Map<string, any> => {
if (!item || !item.id) {
return map;

View file

@ -3,8 +3,8 @@ import React, { Component } from 'react';
import { Image, View } from 'react-native';
import theme from '../ui-theme';
import { StrongText, SubText } from '../components/ItemTile';
import { type FoodItem } from '../records/FoodItemRecord';
import { type Quantity, QUANTITY_MANY } from '../constants/QuantityConstants';
import typeof FoodItemRecord from '../records/FoodItemRecord';
import { type Quantity } from '../constants/QuantityConstants';
import typeof PlaceRecord from '../records/PlaceRecord';
import { compose, pure } from 'recompose';
import IconButton from '../components/IconButton';
@ -16,6 +16,7 @@ import QuantityPicker from '../components/QuantityPicker';
import { routeWithTitle } from '../helpers/RouteHelpers';
import { Link } from 'react-router-native';
import moment from 'moment';
import { withUpdateQuantity } from '../enhancers/quantityEnhancers';
const { foodItemDetails: style } = theme;
@ -31,19 +32,18 @@ export class FoodItemDetail extends Component {
static displayName = 'FoodItemDetail';
props: {
foodItem: FoodItem,
foodItem: FoodItemRecord,
place: PlaceRecord,
updateQuantity: ({ foodItemId: string, quantity: Quantity }) => void,
};
state: {
isFavorite: boolean,
quantity: Quantity,
currentImage: number,
};
state = {
currentImage: 0,
quantity: QUANTITY_MANY,
};
// TODO placeholder implementation until we get a backend
@ -52,7 +52,10 @@ export class FoodItemDetail extends Component {
isFavorite: !prevState.isFavorite,
}));
updateAmount = (quantity: Quantity) => this.setState({ quantity });
updateAmount = (quantity: Quantity) => {
const { updateQuantity, foodItem: { id: foodItemId } } = this.props;
updateQuantity({ foodItemId, quantity });
};
// TODO
addPhoto = () => {};
@ -102,11 +105,11 @@ export class FoodItemDetail extends Component {
}}
>
<QuantityPicker
quantity={this.state.quantity || quantity}
quantity={quantity}
onValueChange={this.updateAmount}
style={{ flex: 1, marginBottom: 10 }}
/>
<SubText>Last updated at {moment(foodItem.lastupdated).format('H:mm A on MMM D, YYYY')}</SubText>
<SubText>Last updated at {moment(foodItem.lastupdated).format('h:mm A on MMM D, YYYY')}</SubText>
</View>
<View style={{ flex: 2, ...contentTileStyle }}>
<IconButton
@ -127,6 +130,6 @@ export class FoodItemDetail extends Component {
}
}
const enhance = compose(pure, withFoodItem, withPlaceForFoodItem);
const enhance = compose(pure, withFoodItem, withPlaceForFoodItem, withUpdateQuantity);
export default enhance(FoodItemDetail);

View file

@ -1,15 +1,20 @@
//@flow
import { createFoodItem } from '../records/FoodItemRecord';
import FoodItemRecord, { createFoodItem } from '../records/FoodItemRecord';
import { setById } from '../helpers/ImmutableHelpers';
import { Map } from 'immutable';
import location$ from './LocationStream';
import { getFoodItems, type FoodItemsForLocation } from '../apis/FoodItemsApi';
import FilterSubject from './FilterStream';
import Filter$ from './FilterStream';
import FilterRecord from '../records/FilterRecord';
import Quantity$ from './QuantityStream';
import type { QuantityFragment } from '../constants/QuantityConstants';
export default location$
.combineLatest(FilterSubject)
.combineLatest(Filter$)
.mergeMap(([loc, filter]: [Position, FilterRecord]) => getFoodItems({ loc, filter }))
.map(({ fooditems = [] }: FoodItemsForLocation) => {
return fooditems.map(createFoodItem).reduce(setById, new Map());
})
.combineLatest(Quantity$, (foodItems: Map<string, FoodItemRecord>, quantities: Map<string, QuantityFragment>) => {
return foodItems.mergeDeepWith((foodItem, quantity) => foodItem.merge(quantity), quantities);
});

View file

@ -0,0 +1,24 @@
//@flow
import { ReplaySubject } from 'rxjs';
import type { QuantityResponse, QuantityFragment } from '../constants/QuantityConstants';
import { Map } from 'immutable';
const observable: ReplaySubject<QuantityResponse> = new ReplaySubject();
export function emit(val: ?QuantityResponse) {
observable.next(val);
}
// force our observable to emit an initial empty map so that food items will load
emit(null);
export default observable.scan((quantitiesByFoodItemId: Map<string, QuantityFragment>, quantity?: QuantityResponse) => {
if (!quantity) {
return quantitiesByFoodItemId;
}
return quantitiesByFoodItemId.set(quantity.food_item_id, {
quantity: quantity.quantity,
lastupdated: quantity.date,
});
}, new Map());

View file

@ -10,9 +10,7 @@ SELECT
q.quantity AS quantity,
q.date AS lastUpdated
FROM food_items f
LEFT OUTER JOIN (
SELECT food_item_id, quantity, MAX(date) AS date FROM quantities GROUP BY food_item_id, quantity
) q
LEFT OUTER JOIN latest_quantities q
ON f.id = q.food_item_id
WHERE ST_DWithin(loc, ST_SetSRID(ST_Point(-97.7286718, 30.3033267),4326)::geography, 20 * 1609)
AND f.category IN ('desserts', 'beverages', 'entrees', 'other')