diff --git a/js/App.js b/js/App.js
index 6dbcaee..c9d1ae8 100644
--- a/js/App.js
+++ b/js/App.js
@@ -8,9 +8,9 @@ import { StatusBar } from 'react-native';
import { ThemeContext, getTheme } from 'react-native-material-ui';
import { NativeRouter, Route, Redirect, AndroidBackButton, Switch } from 'react-router-native';
import Nav from './pages/Nav';
-import FoodItemDetail from './pages/FoodItemDetail';
+import ProductDetail from './pages/ProductDetail';
import PlaceDetail from './pages/PlaceDetail';
-import CreateFoodItem from './pages/CreateFoodItem';
+import CreateProduct from './pages/CreateProduct';
import LoginPage from './pages/LoginPage';
import LandingPage from './pages/LandingPage';
import ZipcodePage from './pages/ZipcodePage';
@@ -34,12 +34,12 @@ export default class App extends Component {
-
+
-
+
diff --git a/js/apis/FavesApi.js b/js/apis/FavesApi.js
index f3ec904..3ff5f49 100644
--- a/js/apis/FavesApi.js
+++ b/js/apis/FavesApi.js
@@ -21,24 +21,24 @@ export const fetchFaves = withLoggedInEmail(async email => {
});
});
-export const putFaves = withLoggedInEmail(async (email, foodItemIds) => {
+export const putFaves = withLoggedInEmail(async (email, productIds) => {
return fetchRequest({
endpoint: '/fave',
method: 'PUT',
body: {
email,
- foodItemIds,
+ productIds,
},
});
});
-export const deleteFaves = withLoggedInEmail(async (email, foodItemIds) => {
+export const deleteFaves = withLoggedInEmail(async (email, productIds) => {
return fetchRequest({
endpoint: '/fave',
method: 'DELETE',
body: {
email,
- foodItemIds,
+ productIds,
},
});
});
diff --git a/js/apis/ImagesApi.js b/js/apis/ImagesApi.js
index 78e1d39..ec35484 100644
--- a/js/apis/ImagesApi.js
+++ b/js/apis/ImagesApi.js
@@ -2,19 +2,19 @@
import type { ImageRaw } from '../records/ImageRecord';
import { fetchRequest, fetchRequestBinary } from './FetchApi';
-export const getImages = (foodItemId: string): Promise> => {
+export const getImages = (productId: string): Promise> => {
return fetchRequest({
- endpoint: `/images/${foodItemId}`,
+ endpoint: `/images/${productId}`,
method: 'GET',
});
};
export const addImage = async ({
- foodItemId,
+ productId,
username,
imageUri,
}: {
- foodItemId: string,
+ productId: string,
username: string,
imageUri: string,
}) => {
@@ -30,7 +30,7 @@ export const addImage = async ({
body.append('username', username);
const res = await fetchRequestBinary({
- endpoint: `/images/${foodItemId}`,
+ endpoint: `/images/${productId}`,
body,
});
diff --git a/js/apis/FoodItemsApi.js b/js/apis/ProductsApi.js
similarity index 56%
rename from js/apis/FoodItemsApi.js
rename to js/apis/ProductsApi.js
index 73e4aff..bbec943 100644
--- a/js/apis/FoodItemsApi.js
+++ b/js/apis/ProductsApi.js
@@ -1,16 +1,15 @@
-import { memoizeWith, pathOr, map, nth } from 'ramda';
-import FilterRecord from '../records/FilterRecord';
-import FoodItemRecord from '../records/FoodItemRecord';
+import { pathOr, map, nth } from 'ramda';
+import ProductRecord from '../records/ProductRecord';
import AuthManager from '../AuthManager';
import { addImage } from './ImagesApi';
import { fetchRequest } from './FetchApi';
import debounce from '../helpers/debounce';
-export type FoodItemsFilter = {
+export type ProductsFilter = {
radius?: number,
};
-export type RawFoodItem = {
+export type RawProduct = {
id: string,
name: string,
placeid: string,
@@ -23,30 +22,30 @@ export type RawFoodItem = {
lastupdated: number,
};
-export type FoodItemsForLocation = {
+export type ProductsForLocation = {
orderby: string,
- filter: FoodItemsFilter,
- fooditems: ?Array,
+ filter: ProductsFilter,
+ products: ?Array,
};
-export const getFoodItems = debounce(async ({ filter }) => {
+export const getProducts = debounce(async ({ filter }) => {
const { search } = filter;
if (!search) {
return {
- fooditems: [],
+ products: [],
loading: false,
error: null,
};
}
try {
- const { fooditems } = await fetchRequest({
- endpoint: `/fooditems/${encodeURIComponent(search)}`,
+ const { products } = await fetchRequest({
+ endpoint: `/products/${encodeURIComponent(search)}`,
});
return {
- fooditems,
+ products,
loading: false,
error: null,
};
@@ -55,14 +54,14 @@ export const getFoodItems = debounce(async ({ filter }) => {
return {
orderby: 'distance',
filter: {},
- fooditems: [],
+ products: [],
loading: false,
error: error,
};
}
}, 500);
-export const createFoodItem = async (foodItem: FoodItemRecord) => {
+export const createProduct = async (product: ProductRecord) => {
if (!AuthManager.user) {
throw new Error('You must be logged in to create food items');
}
@@ -70,14 +69,14 @@ export const createFoodItem = async (foodItem: FoodItemRecord) => {
const username = AuthManager.user.name;
const res = await fetchRequest({
- endpoint: '/addfooditem',
+ endpoint: '/addproduct',
method: 'POST',
- body: foodItem,
+ body: product,
});
- const addImageUri = (imageUri: string) => addImage({ foodItemId: res.id, imageUri, username });
+ const addImageUri = (imageUri: string) => addImage({ productId: res.id, imageUri, username });
- const images = await Promise.all(map(addImageUri, foodItem.images.toArray()));
+ const images = await Promise.all(map(addImageUri, product.images.toArray()));
const thumbimage = pathOr('', ['url'], nth(0, images));
diff --git a/js/apis/QuantityApi.js b/js/apis/QuantityApi.js
index 466dc41..b50c5ec 100644
--- a/js/apis/QuantityApi.js
+++ b/js/apis/QuantityApi.js
@@ -4,17 +4,17 @@ import type { QuantityResponse } from '../constants/QuantityConstants';
import { fetchRequest } from './FetchApi';
export const setQuantity = ({
- foodItemId,
+ productId,
quantity,
}: {
- foodItemId: string,
+ productId: string,
quantity: Quantity,
}): Promise => {
return fetchRequest({
method: 'POST',
endpoint: '/quantity',
body: {
- foodItemId,
+ productId,
quantity,
},
});
diff --git a/js/components/FoodItemTile.js b/js/components/FoodItemTile.js
deleted file mode 100644
index 6ed40ab..0000000
--- a/js/components/FoodItemTile.js
+++ /dev/null
@@ -1,56 +0,0 @@
-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, QuantityLine } from './ItemTile';
-import { withPlace } from '../enhancers/placeEnhancers';
-
-const PlaceNameAndDistance = withPlace(
- ({ place, distance = 999.9 }: { place: ?PlaceRecord, distance: number }) => {
- return (
- {`${(place && place.name) || 'Loading...'} - ${parseFloat(distance).toFixed(
- 1
- )} mi`}
- );
- }
-);
-
-const EmptyFoodItemTile = () => {
- return (
-
-
-
-
- Loading...
-
-
-
- );
-};
-
-export default pure(({ foodItem }: { foodItem: FoodItemRecord }) => {
- if (!foodItem) {
- return ;
- }
-
- return (
-
-
-
-
-
- {foodItem.name || ''}
-
-
-
-
-
-
- );
-});
diff --git a/js/components/PlaceTile.js b/js/components/PlaceTile.js
index 7dfb324..5cad92e 100644
--- a/js/components/PlaceTile.js
+++ b/js/components/PlaceTile.js
@@ -1,7 +1,7 @@
import { View } from 'react-native';
import React from 'react';
import { pure } from 'recompose';
-import FoodItemRecord from '../records/FoodItemRecord';
+import ProductRecord from '../records/ProductRecord';
import typeof PlaceRecord from '../records/PlaceRecord';
import { Link } from 'react-router-native';
import { TileBox, Thumbnail, StrongText, SubText } from './ItemTile';
@@ -13,8 +13,8 @@ import { type Map } from 'immutable';
const getHoursText = (place: PlaceRecord) => (place.openNow ? 'Open now' : 'Closed');
-// const getCategoriesText = (foodItems: Map) => {
-// const categories = getCategories(foodItems);
+// const getCategoriesText = (products: Map) => {
+// const categories = getCategories(products);
// if (categories.size < 1) {
// return 'Nothing listed yet';
// }
@@ -25,7 +25,7 @@ import theme from '../ui-theme';
type PlaceTileProps = {
place: PlaceRecord,
- foodItems: Map,
+ products: Map,
};
export default pure(({ place }: PlaceTileProps) => {
@@ -38,7 +38,7 @@ export default pure(({ place }: PlaceTileProps) => {
{place.name}
- {/* {getCategoriesText(foodItems)} */}
+ {/* {getCategoriesText(products)} */}
{getHoursText(place)} - {parseFloat(place.distance).toFixed(1)} mi
diff --git a/js/components/FoodItemSaveBtn.js b/js/components/ProductSaveBtn.js
similarity index 62%
rename from js/components/FoodItemSaveBtn.js
rename to js/components/ProductSaveBtn.js
index 68b6be9..f2efe76 100644
--- a/js/components/FoodItemSaveBtn.js
+++ b/js/components/ProductSaveBtn.js
@@ -1,20 +1,20 @@
// @flow
-import { withCreateFoodItemState } from '../enhancers/createFoodItemEnhancers';
+import { withCreateProductState } from '../enhancers/createProductEnhancers';
import { withReplaceRoute } from '../enhancers/routeEnhancers';
import { compose, onlyUpdateForKeys, withHandlers } from 'recompose';
import { routeWithTitle } from '../helpers/RouteHelpers';
import TopSaveButton from './TopSaveButton';
export default compose(
- withCreateFoodItemState,
+ withCreateProductState,
withReplaceRoute,
withHandlers({
- onSave: ({ saveFoodItem, setLoading, setError, replaceRoute }) => async () => {
+ onSave: ({ saveProduct, setLoading, setError, replaceRoute }) => async () => {
setError(null);
setLoading(true);
try {
- const { id, name } = await saveFoodItem();
- replaceRoute(routeWithTitle(`/foodItem/${id || ''}`, name));
+ const { id, name } = await saveProduct();
+ replaceRoute(routeWithTitle(`/product/${id || ''}`, name));
} catch (error) {
setError(error);
} finally {
diff --git a/js/components/ProductTile.js b/js/components/ProductTile.js
new file mode 100644
index 0000000..48bad24
--- /dev/null
+++ b/js/components/ProductTile.js
@@ -0,0 +1,54 @@
+import React from 'react';
+import { View } from 'react-native';
+import { pure } from 'recompose';
+import ProductRecord from '../records/ProductRecord';
+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, QuantityLine } from './ItemTile';
+import { withPlace } from '../enhancers/placeEnhancers';
+
+const PlaceNameAndDistance = withPlace(({ place }) => {
+ return (
+ {`${(place && place.name) || 'Loading...'} - ${parseFloat(place.distance).toFixed(
+ 1
+ )} mi`}
+ );
+});
+
+const EmptyProductTile = () => {
+ return (
+
+
+
+
+ Loading...
+
+
+
+ );
+};
+
+export default pure(({ product }: { product: ProductRecord }) => {
+ if (!product) {
+ return ;
+ }
+
+ return (
+
+
+
+
+
+ {product.name || ''}
+
+
+
+
+
+
+ );
+});
diff --git a/js/components/FoodItemList.js b/js/components/ProductsList.js
similarity index 53%
rename from js/components/FoodItemList.js
rename to js/components/ProductsList.js
index e8ffa32..86ed243 100644
--- a/js/components/FoodItemList.js
+++ b/js/components/ProductsList.js
@@ -2,7 +2,7 @@
import React, { Component } from 'react';
import { View } from 'react-native';
import { type SetSeq } from 'immutable';
-import FoodItemRecord from '../records/FoodItemRecord';
+import ProductRecord from '../records/ProductRecord';
import { compose, pure } from 'recompose';
import { pipe } from 'ramda';
import { withFilter } from '../enhancers/filterEnhancers';
@@ -14,20 +14,20 @@ type homomorph = T => T;
type Props = {
filter: FilterRecord,
- foodItemsSeq: SetSeq,
- children: (foodItem: FoodItemRecord) => Component<*, *, *>,
+ productsSeq: SetSeq,
+ children: (product: ProductRecord) => Component<*, *, *>,
};
-const sortByDistance = (foodItemsSeq: SetSeq): SetSeq => {
- return foodItemsSeq.sort((left, right) => left.distance - right.distance);
+const sortByDistance = (productsSeq: SetSeq): SetSeq => {
+ return productsSeq.sort((left, right) => left.distance - right.distance);
};
-const sortByLastUpdated = (foodItemsSeq: SetSeq): SetSeq => {
- return foodItemsSeq.sort((left, right) => right.lastupdated - left.lastupdated);
+const sortByLastUpdated = (productsSeq: SetSeq): SetSeq => {
+ return productsSeq.sort((left, right) => right.lastupdated - left.lastupdated);
};
-const sortByQuantity = (foodItemsSeq: SetSeq): SetSeq => {
- return foodItemsSeq.sort((left, right) => {
+const sortByQuantity = (productsSeq: SetSeq): SetSeq => {
+ return productsSeq.sort((left, right) => {
const quantityCompare = compareQuantity(left.quantity, right.quantity);
return quantityCompare === 0 ? left.distance - right.distance : quantityCompare;
});
@@ -44,20 +44,20 @@ const getSortBy = (filter: FilterRecord): homomorph<*> => {
}
};
-const intoArray = (foodItemsSeq: SetSeq): Array =>
- foodItemsSeq ? foodItemsSeq.toArray() : [];
+const intoArray = (productsSeq: SetSeq): Array =>
+ productsSeq ? productsSeq.toArray() : [];
-const FoodItemList = (props: Props) => {
- const { filter, foodItemsSeq, children } = props;
+const ProductList = (props: Props) => {
+ const { filter, productsSeq, children } = props;
- if (!foodItemsSeq) {
+ if (!productsSeq) {
return null;
}
const items = pipe(
getSortBy(filter),
intoArray
- )(foodItemsSeq);
+ )(productsSeq);
return {items.map(children)};
};
@@ -65,4 +65,4 @@ const FoodItemList = (props: Props) => {
export default compose(
pure,
withFilter
-)(FoodItemList);
+)(ProductList);
diff --git a/js/components/TopToolbar.js b/js/components/TopToolbar.js
index f6a5682..b9a8595 100644
--- a/js/components/TopToolbar.js
+++ b/js/components/TopToolbar.js
@@ -5,7 +5,7 @@ import { Toolbar, Icon } from 'react-native-material-ui';
import { path, cond, test } from 'ramda';
import queryString from 'query-string';
import { getSearch, getViewMode } from '../helpers/RouteHelpers';
-import FoodItemSaveBtn from '../components/FoodItemSaveBtn';
+import ProductSaveBtn from '../components/ProductSaveBtn';
import { withFilter } from '../enhancers/filterEnhancers';
import FilterRecord from '../records/FilterRecord';
import { palette } from '../ui-theme';
@@ -56,7 +56,7 @@ class TopToolbar extends Component {
setFilter(filter.set('search', ''));
};
- onChangeText = (search) => {
+ onChangeText = search => {
const { setFilter, filter } = this.props;
setFilter(filter.set('search', search));
};
@@ -64,8 +64,8 @@ class TopToolbar extends Component {
getRightElement = () => {
const route = path(['router', 'route', 'location', 'pathname'], this.props);
- if (/^\/createFoodItem/.test(route)) {
- return ;
+ if (/^\/createProduct/.test(route)) {
+ return ;
}
};
diff --git a/js/enhancers/createFoodItemEnhancers.js b/js/enhancers/createProductEnhancers.js
similarity index 59%
rename from js/enhancers/createFoodItemEnhancers.js
rename to js/enhancers/createProductEnhancers.js
index 020c271..152fba5 100644
--- a/js/enhancers/createFoodItemEnhancers.js
+++ b/js/enhancers/createProductEnhancers.js
@@ -1,34 +1,34 @@
import mapPropsStream from 'recompose/mapPropsStream';
-import CreateFoodItem$, { emitter as emitCreateItemState } from '../streams/CreateFoodItemStream';
-import { emitter as emitFoodItemsState } from '../streams/FoodItemsStream';
-import { createFoodItem } from '../apis/FoodItemsApi';
-import FoodItemRecord, { createFoodItem as buildFoodItem } from '../records/FoodItemRecord';
+import CreateProduct$, { emitter as emitCreateItemState } from '../streams/CreateProductStream';
+import { emitter as emitProductsState } from '../streams/ProductsStream';
+import { createProduct } from '../apis/ProductsApi';
+import ProductRecord, { createProduct as buildProduct } from '../records/ProductRecord';
import Snackbar from 'react-native-snackbar';
-export const withCreateFoodItemState = mapPropsStream((props$) => {
- return props$.combineLatest(CreateFoodItem$, (props, state) => {
- const { foodItem, loading, error } = state;
+export const withCreateProductState = mapPropsStream(props$ => {
+ return props$.combineLatest(CreateProduct$, (props, state) => {
+ const { product, loading, error } = state;
- const setFoodItem = (foodItem: FoodItemRecord) => emitCreateItemState({ ...state, foodItem });
+ const setProduct = (product: ProductRecord) => emitCreateItemState({ ...state, product });
const setLoading = (loading: boolean) => emitCreateItemState({ ...state, loading });
const setError = (error: Error) => emitCreateItemState({ ...state, error });
- const saveFoodItem = async () => {
+ const saveProduct = async () => {
try {
- // insert new item into db and cast it into FoodItemRecord
- const newItem = buildFoodItem(await createFoodItem(foodItem));
+ // insert new item into db and cast it into ProductRecord
+ const newItem = buildProduct(await createProduct(product));
Snackbar.show({
- title: foodItem.name + ' added',
+ title: product.name + ' added',
backgroundColor: 'black',
color: 'white',
});
// notify food items state of new item
- emitFoodItemsState(newItem);
+ emitProductsState(newItem);
// clear the create item form to default empty record
- setFoodItem(new FoodItemRecord());
+ setProduct(new ProductRecord());
// allow caller to see what was created
return newItem;
@@ -48,11 +48,11 @@ export const withCreateFoodItemState = mapPropsStream((props$) => {
return {
...props,
- foodItem,
+ product,
loading,
error,
- setFoodItem,
- saveFoodItem,
+ setProduct,
+ saveProduct,
setLoading,
setError,
};
diff --git a/js/enhancers/favesEnhancer.js b/js/enhancers/favesEnhancer.js
index a9c4f9e..a96e351 100644
--- a/js/enhancers/favesEnhancer.js
+++ b/js/enhancers/favesEnhancer.js
@@ -9,9 +9,9 @@ const fetchAndEmitFaves = async () => {
emitFaves(faves);
};
-const buildTempFave = foodItemId =>
+const buildTempFave = productId =>
fromJS({
- food_item_id: foodItemId,
+ food_item_id: productId,
date: Date.now(),
});
@@ -27,19 +27,19 @@ export const withFaves = mapPropsStream(props$ =>
console.log(err); // eslint-disable-line no-console
}
},
- addFave: async foodItemId => {
+ addFave: async productId => {
try {
- await putFaves([foodItemId]);
- emitFave(buildTempFave(foodItemId));
+ await putFaves([productId]);
+ emitFave(buildTempFave(productId));
} catch (err) {
console.log(err); // eslint-disable-line no-console
}
},
- deleteFave: async foodItemId => {
+ deleteFave: async productId => {
try {
- await deleteFaves([foodItemId]);
+ await deleteFaves([productId]);
- const idx = faves.findIndex(fave => get(fave, 'id') === foodItemId);
+ const idx = faves.findIndex(fave => get(fave, 'id') === productId);
if (idx >= 0) {
emitFaves(faves.delete(idx));
}
diff --git a/js/enhancers/foodItemEnhancers.js b/js/enhancers/foodItemEnhancers.js
deleted file mode 100644
index ab673a5..0000000
--- a/js/enhancers/foodItemEnhancers.js
+++ /dev/null
@@ -1,87 +0,0 @@
-// @flow
-import withProps from 'recompose/withProps';
-import compose from 'recompose/compose';
-import mapPropsStream from 'recompose/mapPropsStream';
-import FoodItems$ from '../streams/FoodItemsStream';
-import typeof FoodItemRecord from '../records/FoodItemRecord';
-import { Map, Set } from 'immutable';
-import { pipe, path, toLower, equals } from 'ramda';
-
-type FindPredicate = (left: A) => (right: A) => Boolean;
-
-const getName: (f: FoodItemRecord) => String = pipe(path(['name']), toLower);
-
-const matchesName: FindPredicate = left => right => {
- return equals(getName(left), getName(right));
-};
-
-const addIfNotExisting = (predicate: FindPredicate) => (
- set: Set,
- item: FoodItemRecord
-) => {
- return !set.find(predicate(item)) ? set.add(item) : set;
-};
-
-const intoSet = reducer => items => {
- return items ? items.reduce(reducer, new Set()) : new Set();
-};
-
-export const withFoodItems = mapPropsStream(props$ =>
- props$.combineLatest(FoodItems$, (props, foodItems) => {
- return {
- ...props,
- foodItemsMap: foodItems,
- };
- })
-);
-
-export const withFoodItemsAsSeq = mapPropsStream(props$ =>
- props$.combineLatest(FoodItems$, (props, foodItems) => {
- return {
- ...props,
- foodItemsSeq: foodItems && foodItems.valueSeq(),
- };
- })
-);
-
-export const withFoodItemIdFromRoute = withProps((props: { match: { params: { id: string } } }) => {
- const id: string = path(['match', 'params', 'id'], props) || '';
- return { foodItemId: id };
-});
-
-export const withFoodItem = compose(
- withFoodItems,
- withFoodItemIdFromRoute,
- withProps((props: { foodItemsMap: ?Map, foodItemId: number }) => {
- const { foodItemsMap, foodItemId } = props;
- return { foodItem: foodItemsMap && foodItemsMap.get(foodItemId) };
- })
-);
-
-export const withFoodItemPlaceId = withProps((props: { foodItem: FoodItemRecord }) => {
- const { foodItem } = props;
- return {
- placeId: foodItem && foodItem.placeId,
- };
-});
-
-export const withFoodItemsGroupedByPlace = compose(
- withFoodItems,
- withProps((props: { foodItemsMap: ?Map }) => {
- if (!props.foodItemsMap) {
- return {};
- }
- return {
- foodItemsByPlace: props.foodItemsMap.groupBy(foodItem => foodItem.placeId),
- };
- })
-);
-
-export const withUniqueFoodItems = compose(
- withFoodItemsAsSeq,
- withProps(({ foodItemsSeq }) => {
- return {
- foodItemsSeq: intoSet(addIfNotExisting(matchesName))(foodItemsSeq),
- };
- })
-);
diff --git a/js/enhancers/imagesEnhancers.js b/js/enhancers/imagesEnhancers.js
index ba17fe0..0a88a2c 100644
--- a/js/enhancers/imagesEnhancers.js
+++ b/js/enhancers/imagesEnhancers.js
@@ -1,39 +1,39 @@
// @flow
-import { withProps } from "recompose";
-import { emit } from "../streams/ImagesStream";
-import { addImage, getImages } from "../apis/ImagesApi";
-import AuthManager from "../AuthManager";
-import { map } from "ramda";
+import { withProps } from 'recompose';
+import { emit } from '../streams/ImagesStream';
+import { addImage, getImages } from '../apis/ImagesApi';
+import AuthManager from '../AuthManager';
+import { map } from 'ramda';
export const withImages = withProps({
- addImage: async ({ foodItemId, imageUri }) => {
+ addImage: async ({ productId, imageUri }) => {
try {
if (!AuthManager.user) {
- throw new Error("You need to be logged in to add images");
+ throw new Error('You need to be logged in to add images');
}
const username = AuthManager.user.name;
- const { url, date } = await addImage({ foodItemId, username, imageUri });
+ const { url, date } = await addImage({ productId, username, imageUri });
emit({
url,
username,
date,
- food_item_id: foodItemId
+ food_item_id: productId,
});
} catch (error) {
// TODO error handling in the UI
console.error(error); // eslint-disable-line
}
},
- getImages: async ({ foodItemId }) => {
+ getImages: async ({ productId }) => {
try {
- const images = await getImages(foodItemId);
+ const images = await getImages(productId);
map(emit, images);
} catch (error) {
// TODO error handling in the UI
console.error(error); // eslint-disable-line
}
- }
+ },
});
diff --git a/js/enhancers/placeEnhancers.js b/js/enhancers/placeEnhancers.js
index 158dbda..fa1a9ec 100644
--- a/js/enhancers/placeEnhancers.js
+++ b/js/enhancers/placeEnhancers.js
@@ -1,24 +1,16 @@
-// @flow
import withProps from 'recompose/withProps';
import mapPropsStream from 'recompose/mapPropsStream';
import compose from 'recompose/compose';
import Places$, { emitter as emitPlace } from '../streams/PlacesStream';
-import FoodItems$ from '../streams/FoodItemsStream';
+import Products$ from '../streams/ProductsStream';
import { path } from 'ramda';
-import { List } from 'immutable';
+import { List, getIn } from 'immutable';
import { buildPlaceRecord } from '../records/PlaceRecord';
import { getPlaceDetails } from '../apis/GooglePlacesApi';
import { getSearch } from '../helpers/RouteHelpers';
import { withRouterContext } from './routeEnhancers';
-import { find } from 'rxjs/operator/find';
-export const fetchPlaceDetails = async ({
- distance,
- placeId,
-}: {
- distance: number,
- placeId: ?string,
-}) => {
+export const fetchPlaceDetails = async ({ distance, placeId }) => {
const place = await getPlaceDetails({
distance,
placeId,
@@ -41,7 +33,6 @@ export const withPlaceIdFromRoute = withProps((props: { match: { params: { id: s
});
export const withPlaceId = compose(
- withPlaces,
withRouterContext,
withProps(props => {
const placeId = props.placeId || getSearch(props).placeId;
@@ -59,40 +50,34 @@ export const withPlaceActions = withProps(() => {
export const withPlace = compose(
withPlaceId,
withPlaces,
- withProps(({ placeId, places }) => {
- if (!placeId) {
- return {
- place: null,
- fetchPlaceDetails,
- };
- }
- return {
- place: places && places.get(placeId),
- fetchPlaceDetails,
- };
+ withProps(({ placeId, places, placeType }) => {
+ const place =
+ placeId && placeType && getIn(places, [placeType], []).find(place => place.id === placeId);
+
+ return { place, fetchPlaceDetails };
})
);
-export const withPlaceForFoodItem = compose(
+export const withPlaceForProduct = compose(
withPlaces,
- withProps(({ places, foodItem }) => {
- if (!foodItem || !foodItem.placeType) {
+ withProps(({ places, product }) => {
+ if (!product || !product.placeType) {
return { place: null };
}
- const place = places.find(place => place.placeType === foodItem.placeType);
+ const place = getIn(places, [product.placeType, 0]);
return { place };
})
);
-export const withFoodItemsForPlace = mapPropsStream(props$ =>
- props$.combineLatest(FoodItems$, (props, foodItemsMap) => {
+export const withProductsForPlace = mapPropsStream(props$ =>
+ props$.combineLatest(Products$, (props, productsMap) => {
const placeId = props.placeId;
- const foodItems = foodItemsMap
- ? foodItemsMap.toList().filter(foodItem => placeId === foodItem.placeId)
+ const products = productsMap
+ ? productsMap.toList().filter(product => placeId === product.placeId)
: List();
return {
...props,
- foodItems,
+ products,
};
})
);
diff --git a/js/enhancers/productsEnhancers.js b/js/enhancers/productsEnhancers.js
new file mode 100644
index 0000000..8446264
--- /dev/null
+++ b/js/enhancers/productsEnhancers.js
@@ -0,0 +1,90 @@
+// @flow
+import withProps from 'recompose/withProps';
+import compose from 'recompose/compose';
+import mapPropsStream from 'recompose/mapPropsStream';
+import Products$ from '../streams/ProductsStream';
+import typeof ProductRecord from '../records/ProductRecord';
+import { Map, Set } from 'immutable';
+import { pipe, path, toLower, equals } from 'ramda';
+
+type FindPredicate = (left: A) => (right: A) => Boolean;
+
+const getName: (f: ProductRecord) => String = pipe(
+ path(['name']),
+ toLower
+);
+
+const matchesName: FindPredicate = left => right => {
+ return equals(getName(left), getName(right));
+};
+
+const addIfNotExisting = (predicate: FindPredicate) => (
+ set: Set,
+ item: ProductRecord
+) => {
+ return !set.find(predicate(item)) ? set.add(item) : set;
+};
+
+const intoSet = reducer => items => {
+ return items ? items.reduce(reducer, new Set()) : new Set();
+};
+
+export const withProducts = mapPropsStream(props$ =>
+ props$.combineLatest(Products$, (props, products) => {
+ return {
+ ...props,
+ productsMap: products,
+ };
+ })
+);
+
+export const withProductsAsSeq = mapPropsStream(props$ =>
+ props$.combineLatest(Products$, (props, products) => {
+ return {
+ ...props,
+ productsSeq: products && products.valueSeq(),
+ };
+ })
+);
+
+export const withProductIdFromRoute = withProps((props: { match: { params: { id: string } } }) => {
+ const id: string = path(['match', 'params', 'id'], props) || '';
+ return { productId: id };
+});
+
+export const withProduct = compose(
+ withProducts,
+ withProductIdFromRoute,
+ withProps((props: { productsMap: ?Map, productId: number }) => {
+ const { productsMap, productId } = props;
+ return { product: productsMap && productsMap.get(productId) };
+ })
+);
+
+export const withProductPlaceId = withProps((props: { product: ProductRecord }) => {
+ const { product } = props;
+ return {
+ placeId: product && product.placeId,
+ };
+});
+
+export const withProductsGroupedByPlace = compose(
+ withProducts,
+ withProps((props: { productsMap: ?Map }) => {
+ if (!props.productsMap) {
+ return {};
+ }
+ return {
+ productsByPlace: props.productsMap.groupBy(product => product.placeId),
+ };
+ })
+);
+
+export const withUniqueProducts = compose(
+ withProductsAsSeq,
+ withProps(({ productsSeq }) => {
+ return {
+ productsSeq: intoSet(addIfNotExisting(matchesName))(productsSeq),
+ };
+ })
+);
diff --git a/js/enhancers/quantityEnhancers.js b/js/enhancers/quantityEnhancers.js
index 6d18807..ad7d03f 100644
--- a/js/enhancers/quantityEnhancers.js
+++ b/js/enhancers/quantityEnhancers.js
@@ -1,18 +1,18 @@
//@flow
-import { withProps } from "recompose";
-import { setQuantity } from "../apis/QuantityApi";
-import { emit as emitQuantity } from "../streams/QuantityStream";
-import type { Quantity, QuantityResponse } from "../constants/QuantityConstants";
-import { nth } from "ramda";
+import { withProps } from 'recompose';
+import { setQuantity } from '../apis/QuantityApi';
+import { emit as emitQuantity } 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 }) => {
+ updateQuantity: async ({ productId, quantity }: { productId: string, quantity: Quantity }) => {
try {
- const newQuantity: QuantityResponse = nth(0, await setQuantity({ foodItemId, quantity }));
+ const newQuantity: QuantityResponse = nth(0, await setQuantity({ productId, quantity }));
emitQuantity(newQuantity);
} catch (error) {
// todo - error states in food item detail page
console.error(error); // eslint-disable-line
}
- }
+ },
});
diff --git a/js/helpers/CategoryHelpers.js b/js/helpers/CategoryHelpers.js
index a1ad968..bb62bb9 100644
--- a/js/helpers/CategoryHelpers.js
+++ b/js/helpers/CategoryHelpers.js
@@ -1,5 +1,5 @@
// @flow
-import FoodItemRecord from '../records/FoodItemRecord';
+import ProductRecord from '../records/ProductRecord';
import {
type Category,
CATEGORY_BEVERAGES,
@@ -21,9 +21,9 @@ export const getCategoryText = (category: Category) => {
}
};
-export const getCategories = (foodItems: Map) => {
- return foodItems
- .map(foodItem => foodItem.get('category'))
+export const getCategories = (products: Map) => {
+ return products
+ .map(product => product.get('category'))
.toSet()
.map(getCategoryText)
.toList();
diff --git a/js/helpers/CoordinatesHelpers.js b/js/helpers/CoordinatesHelpers.js
index af35685..7447c32 100644
--- a/js/helpers/CoordinatesHelpers.js
+++ b/js/helpers/CoordinatesHelpers.js
@@ -1,28 +1,32 @@
-import { memoizeWith, identity } from "ramda";
+import { memoizeWith, identity } from 'ramda';
-export const getZoomBox = memoizeWith(identity, (foodItemsMap, coords) =>
- foodItemsMap.reduce(
- (prev, foodItem) => {
- const minLat = !prev.minLat || prev.minLat > foodItem.latitude ? foodItem.latitude : prev.minLat;
+export const getZoomBox = memoizeWith(identity, (productsMap, coords) =>
+ productsMap.reduce(
+ (prev, product) => {
+ const minLat =
+ !prev.minLat || prev.minLat > product.latitude ? product.latitude : prev.minLat;
- const maxLat = !prev.maxLat || prev.maxLat < foodItem.latitude ? foodItem.latitude : prev.maxLat;
+ const maxLat =
+ !prev.maxLat || prev.maxLat < product.latitude ? product.latitude : prev.maxLat;
- const minLng = !prev.minLng || prev.minLng > foodItem.longitude ? foodItem.longitude : prev.minLng;
+ const minLng =
+ !prev.minLng || prev.minLng > product.longitude ? product.longitude : prev.minLng;
- const maxLng = !prev.maxLng || prev.maxLng < foodItem.longitude ? foodItem.longitude : prev.maxLng;
+ const maxLng =
+ !prev.maxLng || prev.maxLng < product.longitude ? product.longitude : prev.maxLng;
return {
minLat,
maxLat,
minLng,
- maxLng
+ maxLng,
};
},
{
minLat: coords.latitude,
maxLat: coords.latitude,
minLng: coords.longitude,
- maxLng: coords.longitude
+ maxLng: coords.longitude,
}
)
);
diff --git a/js/modals/CategoryModal.js b/js/modals/CategoryModal.js
index eadafd0..677769a 100644
--- a/js/modals/CategoryModal.js
+++ b/js/modals/CategoryModal.js
@@ -1,6 +1,6 @@
// @flow
import React, { PureComponent } from 'react';
-import typeof FoodItemRecord from '../records/FoodItemRecord';
+import typeof ProductRecord from '../records/ProductRecord';
import { CATEGORIES } from '../constants/CategoryConstants';
import { getCategoryText } from '../helpers/CategoryHelpers';
import FullScreenModal from './FullScreenModal';
@@ -10,7 +10,7 @@ import PickerItemRow from '../components/PickerItemRow';
type Props = {
onClose: () => void,
onUpdate: (c: Category) => void,
- foodItem: FoodItemRecord,
+ product: ProductRecord,
};
class CategoryModal extends PureComponent {
@@ -24,13 +24,13 @@ class CategoryModal extends PureComponent {
};
render() {
- const { onClose, foodItem } = this.props;
+ const { onClose, product } = this.props;
return (
{CATEGORIES.map(category => (
this.updateAndClose(category)}
/>
diff --git a/js/modals/FullScreenModal.js b/js/modals/FullScreenModal.js
index 722c390..05aca81 100644
--- a/js/modals/FullScreenModal.js
+++ b/js/modals/FullScreenModal.js
@@ -50,7 +50,7 @@ export const wrapModalComponent = ({ component, onCloseProp, onUpdateProp }) =>
pure,
mapProps((props: Props) => {
return {
- foodItem: props.foodItem,
+ product: props.product,
onClose: props[onCloseProp],
onUpdate: props[onUpdateProp],
};
diff --git a/js/modals/FoodItemNameModal.js b/js/modals/ProductNameModal.js
similarity index 76%
rename from js/modals/FoodItemNameModal.js
rename to js/modals/ProductNameModal.js
index bd30f34..b8d628e 100644
--- a/js/modals/FoodItemNameModal.js
+++ b/js/modals/ProductNameModal.js
@@ -1,20 +1,19 @@
-// @flow
import React, { Component } from 'react';
import { TouchableOpacity, ScrollView, SafeAreaView } from 'react-native';
import { TextButton } from './Modal';
-import FoodItemList from '../components/FoodItemList';
-import FoodItemRecord from '../records/FoodItemRecord';
+import ProductList from '../components/ProductsList';
+import ProductRecord from '../records/ProductRecord';
import { StrongText } from '../components/ItemTile';
import { Toolbar } from 'react-native-material-ui';
import FilterRecord from '../records/FilterRecord';
import { compose, pure } from 'recompose';
-import { withUniqueFoodItems } from '../enhancers/foodItemEnhancers';
+import { withUniqueProducts } from '../enhancers/productsEnhancers';
import { Set } from 'immutable';
type Props = {
onClose: () => void,
onUpdate: (name: string) => void,
- foodItemsSeq: ?Set,
+ productsSeq: ?Set,
};
class NameModal extends Component {
@@ -45,7 +44,7 @@ class NameModal extends Component {
};
render() {
- const { onClose, foodItemsSeq } = this.props;
+ const { onClose, productsSeq } = this.props;
const { filter } = this.state;
return (
)}
-
- {(foodItem: FoodItemRecord) => (
- this.updateAndClose(foodItem.name)}>
+
+ {(product: ProductRecord) => (
+ this.updateAndClose(product.name)}>
- {foodItem.name}
+ {product.name}
)}
-
+
);
@@ -99,5 +96,5 @@ class NameModal extends Component {
export default compose(
pure,
- withUniqueFoodItems
+ withUniqueProducts
)(NameModal);
diff --git a/js/modals/QuantityModal.js b/js/modals/QuantityModal.js
index 45df363..7f0d480 100644
--- a/js/modals/QuantityModal.js
+++ b/js/modals/QuantityModal.js
@@ -3,17 +3,17 @@ import React from 'react';
import { getQuantityDropdownText } from '../helpers/QuantityHelpers';
import { type Quantity, QUANTITIES } from '../constants/QuantityConstants';
import FullScreenModal from './FullScreenModal';
-import { typeof FoodItem } from '../records/FoodItemRecord';
+import { typeof Product } from '../records/ProductRecord';
import PickerItemRow from '../components/PickerItemRow';
type Props = {
onClose: () => void,
- foodItem: FoodItem,
+ product: Product,
onUpdate: (q: Quantity) => void,
};
const QuantityModal = (props: Props) => {
- const { foodItem, onUpdate, onClose } = props;
+ const { product, onUpdate, onClose } = props;
const onPress = (q: Quantity) => () => {
onUpdate(q);
@@ -26,7 +26,7 @@ const QuantityModal = (props: Props) => {
))}
diff --git a/js/pages/CreateFoodItem.js b/js/pages/CreateProduct.js
similarity index 73%
rename from js/pages/CreateFoodItem.js
rename to js/pages/CreateProduct.js
index e56ad5d..5e31f0f 100644
--- a/js/pages/CreateFoodItem.js
+++ b/js/pages/CreateProduct.js
@@ -1,11 +1,10 @@
-// @flow
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import theme from '../ui-theme';
import { Divider } from 'react-native-material-ui';
-import FoodItemRecord from '../records/FoodItemRecord';
+import ProductRecord from '../records/ProductRecord';
import PlaceRecord from '../records/PlaceRecord';
-import NameModal from '../modals/FoodItemNameModal';
+import NameModal from '../modals/ProductNameModal';
import CategoryModal from '../modals/CategoryModal';
import ImagePreviewModal from '../modals/ImagePreviewModal';
import {
@@ -19,8 +18,8 @@ import {
} from 'recompose';
import RNGooglePlaces from 'react-native-google-places';
import { ImageThumb, ImagePicker } from '../components/ImagePicker';
-import { withCreateFoodItemState } from '../enhancers/createFoodItemEnhancers';
-import { withPlaceForFoodItem, withPlaceId, withPlaceActions } from '../enhancers/placeEnhancers';
+import { withCreateProductState } from '../enhancers/createProductEnhancers';
+import { withPlaceForProduct, withPlaceId, withPlaceActions } from '../enhancers/placeEnhancers';
import Spinner from 'react-native-loading-spinner-overlay';
import { openImagePicker } from '../helpers/ImagePickerHelpers';
import { IndexedSeq } from 'immutable';
@@ -32,10 +31,10 @@ import { wrapModalComponent } from '../modals/FullScreenModal';
import { withAuthRedirect } from '../enhancers/authEnhancers';
type Props = {
- foodItem: typeof FoodItemRecord,
- setPropOfFoodItem: Function,
+ product: typeof ProductRecord,
+ setPropOfProduct: Function,
toggleNameModal: Function,
- setFoodItem: Function,
+ setProduct: Function,
setModalsVisible: Function,
place: ?PlaceRecord,
updatePlace: (place: GooglePlaceObject) => void,
@@ -44,7 +43,7 @@ type Props = {
loading: boolean,
setLoading: (arg: boolean) => void,
emitPlace: (place: Object) => void,
- withFoodItemsAsSeq: IndexedSeq,
+ withProductsAsSeq: IndexedSeq,
toggleCategoryModal: () => void,
toggleQuantityModal: () => void,
};
@@ -89,9 +88,9 @@ const openPlaceModal = (onChoosePlace: (place: GooglePlaceObject) => void) => ()
RNGooglePlaces.openAutocompleteModal({ type: 'establishment' }).then(onChoosePlace);
};
-const CreateFoodItem = (props: Props) => {
+const CreateProduct = (props: Props) => {
const {
- foodItem,
+ product,
toggleNameModal,
updatePlace,
addImage,
@@ -105,20 +104,20 @@ const CreateFoodItem = (props: Props) => {
return (
-
+
- {foodItem.images.map((imageURI: string) => (
+ {product.images.map((imageURI: string) => (
setImagePreview(imageURI)} />
))}
@@ -128,31 +127,31 @@ const CreateFoodItem = (props: Props) => {
const ImagePreviewComp = compose(
renderComponent,
- mapProps(({ foodItem, setFoodItem, setImagePreview, imagePreview }) => {
+ mapProps(({ product, setProduct, setImagePreview, imagePreview }) => {
return {
onClose: () => setImagePreview(-1),
onDelete: () => {
- setFoodItem(foodItem.deleteIn(['images', imagePreview]));
+ setProduct(product.deleteIn(['images', imagePreview]));
setImagePreview(null);
},
- imageSrc: foodItem.images.get(imagePreview),
+ imageSrc: product.images.get(imagePreview),
};
})
)(ImagePreviewModal);
-const setPropOfFoodItem = ({ foodItem, setFoodItem }: Props) => (prop: string) => (value: any) => {
- setFoodItem(foodItem.set(prop, value));
+const setPropOfProduct = ({ product, setProduct }: Props) => (prop: string) => (value: any) => {
+ setProduct(product.set(prop, value));
};
-const addImage = ({ foodItem, setFoodItem }: Props) => async () => {
+const addImage = ({ product, setProduct }: Props) => async () => {
const { path } = await openImagePicker();
Snackbar.show({
title: 'Photo added.',
});
- setFoodItem(foodItem.update('images', images => images.add(path)));
+ setProduct(product.update('images', images => images.add(path)));
};
-const updatePlace = ({ foodItem, setFoodItem, emitPlace }: Props) => placeDetails => {
+const updatePlace = ({ product, setProduct, emitPlace }: Props) => placeDetails => {
const { placeID, latitude, longitude } = placeDetails;
emitPlace(
@@ -162,8 +161,8 @@ const updatePlace = ({ foodItem, setFoodItem, emitPlace }: Props) => placeDetail
})
);
- setFoodItem(
- foodItem.merge({
+ setProduct(
+ product.merge({
placeId: placeID,
latitude,
longitude,
@@ -173,16 +172,16 @@ const updatePlace = ({ foodItem, setFoodItem, emitPlace }: Props) => placeDetail
export default compose(
withAuthRedirect,
- withCreateFoodItemState,
+ withCreateProductState,
withPlaceId,
- withPlaceForFoodItem,
+ withPlaceForProduct,
withPlaceActions,
withState('nameModalOpen', 'setNameModalOpen', false),
withState('imagePreview', 'setImagePreview', null),
withState('categoryModalOpen', 'setCategoryModalOpen', false),
withState('quantityModalOpen', 'setQuantityModalOpen', false),
withHandlers({
- setPropOfFoodItem,
+ setPropOfProduct,
addImage,
updatePlace,
toggleNameModal: ({ nameModalOpen, setNameModalOpen }) => () => {
@@ -196,14 +195,14 @@ export default compose(
},
}),
withHandlers({
- setQuantity: ({ setPropOfFoodItem }) => setPropOfFoodItem('quantity'),
- setCategory: ({ setPropOfFoodItem }) => setPropOfFoodItem('category'),
- setName: ({ setPropOfFoodItem }) => setPropOfFoodItem('name'),
+ setQuantity: ({ setPropOfProduct }) => setPropOfProduct('quantity'),
+ setCategory: ({ setPropOfProduct }) => setPropOfProduct('category'),
+ setName: ({ setPropOfProduct }) => setPropOfProduct('name'),
}),
lifecycle({
componentDidMount() {
- const { placeId, setFoodItem } = this.props;
- setFoodItem(new FoodItemRecord({ placeId }));
+ const { placeId, setProduct } = this.props;
+ setProduct(new ProductRecord({ placeId }));
},
}),
branch(
@@ -231,4 +230,4 @@ export default compose(
onUpdateProp: 'setQuantity',
})
)
-)(CreateFoodItem);
+)(CreateProduct);
diff --git a/js/pages/FavesList.js b/js/pages/FavesList.js
index a3d5f0d..d872b88 100644
--- a/js/pages/FavesList.js
+++ b/js/pages/FavesList.js
@@ -2,12 +2,12 @@
import React from 'react';
import { View, ScrollView, RefreshControl } from 'react-native';
import { compose, pure, withState, withHandlers, lifecycle } from 'recompose';
-import { withFoodItems } from '../enhancers/foodItemEnhancers';
+import { withProducts } from '../enhancers/productsEnhancers';
import { Map, get } from 'immutable';
-import typeof FoodItemRecord from '../records/FoodItemRecord';
+import typeof ProductRecord from '../records/ProductRecord';
import { withFaves } from '../enhancers/favesEnhancer';
import type { Faves, Fave } from '../streams/FavesStream';
-import FoodItemTile from '../components/FoodItemTile';
+import ProductTile from '../components/ProductTile';
import { withAuthRedirect } from '../enhancers/authEnhancers';
type Props = {
@@ -26,7 +26,7 @@ const FavesList = ({ faves, isRefreshing, onPulldown }: Props) => {
refreshControl={}>
{faves &&
faves.map((fave: Faves, index: number) => {
- return ;
+ return ;
})}
diff --git a/js/pages/FavesMap.js b/js/pages/FavesMap.js
index eb8a772..4a2bb46 100644
--- a/js/pages/FavesMap.js
+++ b/js/pages/FavesMap.js
@@ -6,7 +6,7 @@ import { compose, renderComponent, withState, withProps } from 'recompose';
import { Map, get } from 'immutable';
import MapView from 'react-native-maps';
import { routeWithTitle } from '../helpers/RouteHelpers';
-import FoodItemTile from '../components/FoodItemTile';
+import ProductTile from '../components/ProductTile';
import { getZoomBox } from '../helpers/CoordinatesHelpers';
import { withFaves } from '../enhancers/favesEnhancer';
import { type Faves } from '../streams/FavesStream';
@@ -47,9 +47,9 @@ const FavesMap = ({ faves, region, onRegionChange, pushRoute, initialRegion }: P
}}>
{
- pushRoute(routeWithTitle(`/foodItem/${get(fave, 'id', '')}`, get(fave, 'name')));
+ pushRoute(routeWithTitle(`/product/${get(fave, 'id', '')}`, get(fave, 'name')));
}}>
-
+
))}
@@ -58,8 +58,8 @@ const FavesMap = ({ faves, region, onRegionChange, pushRoute, initialRegion }: P
);
};
-const withInitialRegionProp = withProps(({ foodItemsMap = Map(), location }) => {
- const zoomBox = getZoomBox(foodItemsMap, location.coords);
+const withInitialRegionProp = withProps(({ productsMap = Map(), location }) => {
+ const zoomBox = getZoomBox(productsMap, location.coords);
return {
initialRegion: {
diff --git a/js/pages/Nav.js b/js/pages/Nav.js
index f8c2830..aff6a82 100644
--- a/js/pages/Nav.js
+++ b/js/pages/Nav.js
@@ -1,7 +1,7 @@
// @flow
import React from 'react';
import { View, SafeAreaView } from 'react-native';
-import Food from './Food';
+import Food from './Products';
import Places from './Places';
import { BottomNavigation } from 'react-native-material-ui';
import { getLocation } from '../apis/PositionApi';
@@ -60,7 +60,7 @@ const Nav = (props: Props) => {
key="add"
icon="add-circle"
label="Add"
- onPress={pushRoute(`/createFoodItem?backto=${location.pathname}`)}
+ onPress={pushRoute(`/createProduct?backto=${location.pathname}`)}
style={{
container: {
minWidth: 40,
diff --git a/js/pages/PlaceDetail.js b/js/pages/PlaceDetail.js
index f87807b..c1e3cb6 100644
--- a/js/pages/PlaceDetail.js
+++ b/js/pages/PlaceDetail.js
@@ -4,18 +4,14 @@ import { View, Text, Image, ScrollView } from 'react-native';
import theme, { palette } from '../ui-theme';
import { compose, pure, withState, withHandlers, lifecycle } from 'recompose';
import typeof PlaceRecord from '../records/PlaceRecord';
-import {
- withPlace,
- withPlaceIdFromRoute,
- withFoodItemsForPlace,
-} from '../enhancers/placeEnhancers';
+import { withPlace, withPlaceIdFromRoute, withProductsForPlace } 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';
+import typeof ProductRecord from '../records/ProductRecord';
+import ProductTile from '../components/ProductTile';
import { openUrl } from '../helpers/linkHelpers';
import { routeWithQuery } from '../helpers/RouteHelpers';
import { Link } from 'react-router-native';
@@ -32,7 +28,7 @@ const contentTileStyle = {
type Props = {
place: ?PlaceRecord,
- foodItems: ?List,
+ products: ?List,
currentImage: number,
setCurrentImage: (idx: number) => void,
viewOnMap: () => void,
@@ -41,7 +37,7 @@ type Props = {
};
const PlaceDetail = (props: Props) => {
- const { place, foodItems, currentImage, setCurrentImage, viewOnMap, phoneCall } = props;
+ const { place, products, currentImage, setCurrentImage, viewOnMap, phoneCall } = props;
if (!place) {
return ;
@@ -56,7 +52,7 @@ const PlaceDetail = (props: Props) => {
{photos.size === 1 && }
{photos.size > 1 && (
- {photos.map((uri) => (
+ {photos.map(uri => (
))}
@@ -84,14 +80,14 @@ const PlaceDetail = (props: Props) => {
Products
- {!!foodItems &&
- foodItems.map((foodItem) => (
-
+ {!!products &&
+ products.map(product => (
+
))}
- {!foodItems ||
- (!foodItems.size && (
+ {!products ||
+ (!products.size && (
@@ -116,7 +112,7 @@ export default compose(
pure,
withPlaceIdFromRoute,
withPlace,
- withFoodItemsForPlace,
+ withProductsForPlace,
withState('currentImage', 'setCurrentImage', 0),
withHandlers({
viewOnMap: (props: Props) => () => {
diff --git a/js/pages/PlacesList.js b/js/pages/PlacesList.js
index f6fec01..c18b223 100644
--- a/js/pages/PlacesList.js
+++ b/js/pages/PlacesList.js
@@ -3,15 +3,15 @@ import React from 'react';
import { View, ScrollView, RefreshControl } from 'react-native';
import PlaceTile from '../components/PlaceTile';
import { compose, pure, withState, withHandlers, lifecycle } from 'recompose';
-// import { withFoodItemsGroupedByPlace } from '../enhancers/foodItemEnhancers';
+// import { withProductsGroupedByPlace } from '../enhancers/productEnhancers';
import { withPlaces } from '../enhancers/placeEnhancers';
import { Map, List } from 'immutable';
-import typeof FoodItemRecord from '../records/FoodItemRecord';
+import typeof ProductRecord from '../records/ProductRecord';
import typeof PlaceRecord from '../records/PlaceRecord';
import { withRouterContext } from '../enhancers/routeEnhancers';
type Props = {
- foodItemsByPlace: Map>,
+ productsByPlace: Map>,
places: ?Map,
onRefresh: () => Promise,
onPulldown: () => {},
@@ -21,7 +21,7 @@ type Props = {
const byDistance = (left: PlaceRecord, right: PlaceRecord) => left.distance - right.distance;
-const PlacesList = ({ foodItemsByPlace = Map(), places, isRefreshing, onPulldown }: Props) => {
+const PlacesList = ({ productsByPlace = Map(), places, isRefreshing, onPulldown }: Props) => {
const refreshing = isRefreshing || !places;
return (
@@ -31,8 +31,8 @@ const PlacesList = ({ foodItemsByPlace = Map(), places, isRefreshing, onPulldown
places
.sort(byDistance)
.map((place: PlaceRecord, placeId: string) => {
- // const foodItems = foodItemsByPlace.get(placeId, new List());
- return ;
+ // const products = productsByPlace.get(placeId, new List());
+ return ;
})
.toList()}
@@ -42,7 +42,7 @@ const PlacesList = ({ foodItemsByPlace = Map(), places, isRefreshing, onPulldown
export default compose(
pure,
- // withFoodItemsGroupedByPlace,
+ // withProductsGroupedByPlace,
withPlaces,
withRouterContext,
withState('isRefreshing', 'setRefreshing', false),
diff --git a/js/pages/PlacesMap.js b/js/pages/PlacesMap.js
index 5b5d6e9..a35360a 100644
--- a/js/pages/PlacesMap.js
+++ b/js/pages/PlacesMap.js
@@ -1,14 +1,14 @@
// @flow
import React from 'react';
import { View } from 'react-native';
-import typeof FoodItemRecord from '../records/FoodItemRecord';
+import typeof ProductRecord from '../records/ProductRecord';
import { compose, renderComponent } from 'recompose';
import { Map } from 'immutable';
import MapView from 'react-native-maps';
import { routeWithTitle } from '../helpers/RouteHelpers';
import { withPlaces } from '../enhancers/placeEnhancers';
import PlaceTile from '../components/PlaceTile';
-import { withFoodItemsGroupedByPlace } from '../enhancers/foodItemEnhancers';
+import { withProductsGroupedByPlace } from '../enhancers/productsEnhancers';
import { withRegionState } from '../enhancers/mapViewEnhancers';
import typeof PlaceRecord from '../records/PlaceRecord';
@@ -21,7 +21,7 @@ type Region = {
type Props = {
places: Map,
- foodItemsByPlace: Map>,
+ productsByPlace: Map>,
location: Location,
region: Region,
onRegionChange: Region => void,
@@ -30,7 +30,7 @@ type Props = {
const PlacesMap = ({
places = Map(),
- foodItemsByPlace = Map(),
+ productsByPlace = Map(),
region,
onRegionChange,
pushRoute,
@@ -44,10 +44,10 @@ const PlacesMap = ({
onRegionChange={onRegionChange}>
{places
.map((place: PlaceRecord, placeId: string) => {
- const foodItems = foodItemsByPlace.get(placeId, new Map());
- const firstFoodItem = foodItems.first();
+ const products = productsByPlace.get(placeId, new Map());
+ const firstProduct = products.first();
- if (!firstFoodItem) {
+ if (!firstProduct) {
return;
}
@@ -56,14 +56,14 @@ const PlacesMap = ({
key={placeId}
title={place.name}
coordinate={{
- latitude: firstFoodItem.latitude,
- longitude: firstFoodItem.longitude,
+ latitude: firstProduct.latitude,
+ longitude: firstProduct.longitude,
}}>
{
pushRoute(routeWithTitle(`/place/${placeId || ''}`, place.name));
}}>
-
+
);
@@ -76,7 +76,7 @@ const PlacesMap = ({
export default compose(
renderComponent,
- withFoodItemsGroupedByPlace,
+ withProductsGroupedByPlace,
withPlaces,
withRegionState
)(PlacesMap);
diff --git a/js/pages/FoodItemDetail.js b/js/pages/ProductDetail.js
similarity index 79%
rename from js/pages/FoodItemDetail.js
rename to js/pages/ProductDetail.js
index 27b3c64..696161e 100644
--- a/js/pages/FoodItemDetail.js
+++ b/js/pages/ProductDetail.js
@@ -3,7 +3,7 @@ import React from 'react';
import { Button, Image, Text, View } from 'react-native';
import theme from '../ui-theme';
import { StrongText, SubText } from '../components/ItemTile';
-import typeof FoodItemRecord from '../records/FoodItemRecord';
+import typeof ProductRecord from '../records/ProductRecord';
import { type Quantity } from '../constants/QuantityConstants';
import typeof PlaceRecord from '../records/PlaceRecord';
import {
@@ -15,8 +15,8 @@ import {
lifecycle,
} from 'recompose';
import IconButton from '../components/IconButton';
-import { withFoodItem } from '../enhancers/foodItemEnhancers';
-import { withPlaceForFoodItem } from '../enhancers/placeEnhancers';
+import { withProduct } from '../enhancers/productsEnhancers';
+import { withPlaceForProduct } from '../enhancers/placeEnhancers';
import Carousel from 'react-native-looped-carousel';
import CountBadge from '../components/CountBadge';
import { routeWithTitle, loginWithBackto } from '../helpers/RouteHelpers';
@@ -36,11 +36,11 @@ import { withFaves } from '../enhancers/favesEnhancer';
import debounce from '../helpers/debounce';
import { withCurrentPath, withReplaceRoute } from '../enhancers/routeEnhancers';
-const { foodItemDetails: style } = theme;
+const { productDetails: style } = theme;
const stretchedStyle = { position: 'absolute', top: 0, left: 0, right: 0, bottom: 0 };
-const FoodItemImages = ({
+const ProductImages = ({
currentImage,
visibleImages,
changeCurrentImage,
@@ -90,26 +90,26 @@ const contentTileStyle = {
};
type Props = {
- foodItem: ?FoodItemRecord,
- foodItemId: string,
+ product: ?ProductRecord,
+ productId: string,
place: PlaceRecord,
currentImage: number,
quantityModalOpen: boolean,
isAuthed: boolean,
changeCurrentImage: (index: number) => void,
updateAmount: (quantity: Quantity) => void,
- updateQuantity: (arg: { foodItemId: string, quantity: Quantity }) => void,
+ updateQuantity: (arg: { productId: string, quantity: Quantity }) => void,
toggleQuantityModal: () => void,
addPhoto: () => void,
- addImage: (arg: { foodItemId: string, imageUri: string }) => Promise,
+ addImage: (arg: { productId: string, imageUri: string }) => Promise,
addToFaves: () => void,
isFave: boolean,
deleteFromFaves: () => void,
};
-export const FoodItemDetail = (props: Props) => {
+export const ProductDetail = (props: Props) => {
const {
- foodItem,
+ product,
place,
currentImage,
changeCurrentImage,
@@ -123,15 +123,15 @@ export const FoodItemDetail = (props: Props) => {
deleteFromFaves,
} = props;
- if (!foodItem || !place) {
+ if (!product || !place) {
return ;
}
return (
-
@@ -153,10 +153,10 @@ export const FoodItemDetail = (props: Props) => {
justifyContent: 'space-between',
}}>
- {getQuantityLabelText(foodItem.quantity)}
+ {getQuantityLabelText(product.quantity)}
- Last updated at {moment(foodItem.lastupdated).format('h:mm A on MMM D, YYYY')}
+ Last updated at {moment(product.lastupdated).format('h:mm A on MMM D, YYYY')}
{isAuthed && (
{quantityModalOpen && (
-
+
)}
);
};
export default compose(
- withFoodItem,
- withPlaceForFoodItem,
+ withProduct,
+ withPlaceForProduct,
withUpdateQuantity,
withAuthed,
withFaves,
@@ -221,33 +221,33 @@ export default compose(
withState('currentImage', 'changeCurrentImage', 0),
withState('quantityModalOpen', 'setQuantityModalOpen', false),
withHandlers({
- updateAmount: ({ updateQuantity, foodItem }: Props) => async (quantity: Quantity) => {
- if (!foodItem) {
+ updateAmount: ({ updateQuantity, product }: Props) => async (quantity: Quantity) => {
+ if (!product) {
return;
}
- await updateQuantity({ foodItemId: foodItem.id, quantity });
- Snackbar.show({ title: 'Food updated.', backgroundColor: 'black', color: 'white' });
+ await updateQuantity({ productId: product.id, quantity });
+ Snackbar.show({ title: 'Product updated.', backgroundColor: 'black', color: 'white' });
},
toggleQuantityModal: ({ quantityModalOpen, setQuantityModalOpen }) =>
debounce(() => {
setQuantityModalOpen(!quantityModalOpen);
}, 500),
- addToFaves: ({ addFave, foodItemId, isAuthed, replaceRoute }) =>
+ addToFaves: ({ addFave, productId, isAuthed, replaceRoute }) =>
debounce(() => {
if (!isAuthed) {
- replaceRoute(loginWithBackto(`/foodItem/${foodItemId}?loginAction=add-to-faves`));
+ replaceRoute(loginWithBackto(`/product/${productId}?loginAction=add-to-faves`));
} else {
- addFave(foodItemId);
+ addFave(productId);
}
}, 500),
- deleteFromFaves: ({ deleteFave, foodItemId }) => () => deleteFave(foodItemId),
+ deleteFromFaves: ({ deleteFave, productId }) => () => deleteFave(productId),
}),
withProps(props => ({
- isFave: props.faves && !!props.faves.find(fave => get(fave, 'id') === props.foodItemId),
+ isFave: props.faves && !!props.faves.find(fave => get(fave, 'id') === props.productId),
loginAction: pathOr('', [1], /loginAction=(.+)(&?.*$)/.exec(props.currentPath)),
})),
onlyUpdateForKeys([
- 'foodItem',
+ 'product',
'place',
'quantityModalOpen',
// 'imagesLoading',
@@ -267,4 +267,4 @@ export default compose(
}
},
})
-)(FoodItemDetail);
+)(ProductDetail);
diff --git a/js/pages/FoodList.js b/js/pages/ProductList.js
similarity index 61%
rename from js/pages/FoodList.js
rename to js/pages/ProductList.js
index f3a570f..1e4e31c 100644
--- a/js/pages/FoodList.js
+++ b/js/pages/ProductList.js
@@ -1,30 +1,19 @@
-// @flow
import React from 'react';
import { View, Text, ScrollView, RefreshControl } from 'react-native';
-import FoodItemTile from '../components/FoodItemTile';
-import FoodItemList from '../components/FoodItemList';
-import typeof FoodItemRecord from '../records/FoodItemRecord';
-import { withFoodItemsAsSeq } from '../enhancers/foodItemEnhancers';
-import { type SetSeq } from 'immutable';
+import ProductTile from '../components/ProductTile';
+import ProductsList from '../components/ProductsList';
+import { withProductsAsSeq } from '../enhancers/productsEnhancers';
import { compose, pure, withState, withHandlers, lifecycle } from 'recompose';
import { withFilter } from '../enhancers/filterEnhancers';
import { withRouterContext } from '../enhancers/routeEnhancers';
import FullScreenMessage from '../components/FullScreenMessage';
import { withFaves } from '../enhancers/favesEnhancer';
-type Props = {
- foodItemsSeq: SetSeq,
- isRefreshing: boolean,
- onRefresh: () => Promise,
- onPulldown: () => {},
- isFilterDirty: boolean,
-};
+const ProductList = props => {
+ const { productsSeq, isRefreshing, onPulldown, isFilterDirty } = props;
-const FoodList = (props: Props) => {
- const { foodItemsSeq, isRefreshing, onPulldown, isFilterDirty } = props;
-
- const refreshing = isRefreshing || !foodItemsSeq;
- const showNoResults = !isRefreshing && isFilterDirty && foodItemsSeq && !foodItemsSeq.size;
+ const refreshing = isRefreshing || !productsSeq;
+ const showNoResults = !isRefreshing && isFilterDirty && productsSeq && !productsSeq.size;
return (
@@ -38,9 +27,9 @@ const FoodList = (props: Props) => {
}>
-
- {(foodItem: FoodItemRecord) => }
-
+
+ {product => }
+
)}
@@ -50,7 +39,7 @@ const FoodList = (props: Props) => {
export default compose(
pure,
withRouterContext,
- withFoodItemsAsSeq,
+ withProductsAsSeq,
withFilter,
withFaves,
withState('isRefreshing', 'setRefreshing', false),
@@ -72,4 +61,4 @@ export default compose(
this.props.onPulldown();
},
})
-)(FoodList);
+)(ProductList);
diff --git a/js/pages/Food.js b/js/pages/Products.js
similarity index 70%
rename from js/pages/Food.js
rename to js/pages/Products.js
index 64f97f9..bbfcdf6 100644
--- a/js/pages/Food.js
+++ b/js/pages/Products.js
@@ -1,7 +1,6 @@
-// @flow
import { compose, branch } from 'recompose';
-import FoodList from './FoodList';
-import FoodMap from './FoodMap';
+import ProductList from './ProductList';
+import ProductMap from './ProductsMap';
import { withRouterContext, withViewMode, withPushRoute } from '../enhancers/routeEnhancers';
export default compose(
@@ -10,5 +9,5 @@ export default compose(
withPushRoute,
branch(({ viewMode }) => {
return viewMode === 'map';
- }, FoodMap)
-)(FoodList);
+ }, ProductMap)
+)(ProductList);
diff --git a/js/pages/FoodMap.js b/js/pages/ProductsMap.js
similarity index 60%
rename from js/pages/FoodMap.js
rename to js/pages/ProductsMap.js
index 1060f09..f67eca6 100644
--- a/js/pages/FoodMap.js
+++ b/js/pages/ProductsMap.js
@@ -1,42 +1,24 @@
-// @flow
import React from 'react';
import { View, Image } from 'react-native';
-import typeof FoodItemRecord from '../records/FoodItemRecord';
-import { withFoodItems } from '../enhancers/foodItemEnhancers';
+import { withProducts } from '../enhancers/productsEnhancers';
import { withLocation } from '../enhancers/locationEnhancers';
import { compose, renderComponent, withState, withProps } from 'recompose';
import { Map } from 'immutable';
import MapView, { Marker } from 'react-native-maps';
import { routeWithTitle } from '../helpers/RouteHelpers';
-import FoodItemTile from '../components/FoodItemTile';
+import ProductTile from '../components/ProductTile';
import { getZoomBox } from '../helpers/CoordinatesHelpers';
import LOCATION_DOT from '../../static/location-dot.png';
-type Region = {
- latitude: number,
- longitude: number,
- latitudeDelta: number,
- longitudeDelta: number,
-};
-
-type Props = {
- foodItemsMap: Map,
- location: Location,
- initialRegion: Region,
- region: Region,
- onRegionChange: (Region) => void,
- pushRoute: () => {},
-};
-
-const FoodMap = ({
- foodItemsMap,
+const ProductsMap = ({
+ productsMap,
region,
onRegionChange,
pushRoute,
initialRegion,
location,
-}: Props) => {
- if (!foodItemsMap) {
+}) => {
+ if (!productsMap) {
return null;
}
@@ -46,21 +28,21 @@ const FoodMap = ({
region={region || initialRegion}
style={{ flex: 1 }}
onRegionChangeComplete={onRegionChange}>
- {foodItemsMap
- .map((foodItem, id) => {
+ {productsMap
+ .map((product, id) => {
return (
{
- pushRoute(routeWithTitle(`/foodItem/${foodItem.id || ''}`, foodItem.name));
+ pushRoute(routeWithTitle(`/product/${product.id || ''}`, product.name));
}}>
-
+
);
@@ -76,8 +58,8 @@ const FoodMap = ({
);
};
-const withInitialRegionProp = withProps(({ foodItemsMap = Map(), location }) => {
- const zoomBox = getZoomBox(foodItemsMap, location.coords);
+const withInitialRegionProp = withProps(({ productsMap = Map(), location }) => {
+ const zoomBox = getZoomBox(productsMap, location.coords);
return {
initialRegion: {
@@ -91,8 +73,8 @@ const withInitialRegionProp = withProps(({ foodItemsMap = Map(), location }) =>
export default compose(
renderComponent,
- withFoodItems,
+ withProducts,
withLocation,
withState('region', 'onRegionChange', null),
withInitialRegionProp
-)(FoodMap);
+)(ProductsMap);
diff --git a/js/records/ImageRecord.js b/js/records/ImageRecord.js
index f5a66ab..5049f0f 100644
--- a/js/records/ImageRecord.js
+++ b/js/records/ImageRecord.js
@@ -11,7 +11,7 @@ const ImageRecord = Record({
url: '',
username: '',
date: Date.now(),
- foodItemId: '',
+ productId: '',
});
export type ImageFragment = { id: string, images: OrderedSet };
@@ -19,7 +19,7 @@ export type ImageFragment = { id: string, images: OrderedSet
export const buildImageRecord = (imageRaw: ImageRaw) => {
return new ImageRecord({
...imageRaw,
- foodItemId: imageRaw.food_item_id,
+ productId: imageRaw.food_item_id,
});
};
diff --git a/js/records/FoodItemRecord.js b/js/records/ProductRecord.js
similarity index 51%
rename from js/records/FoodItemRecord.js
rename to js/records/ProductRecord.js
index 540517e..a25e10f 100644
--- a/js/records/FoodItemRecord.js
+++ b/js/records/ProductRecord.js
@@ -1,11 +1,10 @@
import { Set, Record } from 'immutable';
-import { type RawFoodItem } from '../apis/FoodItemsApi';
+import { type RawProduct } from '../apis/ProductsApi';
import { type Category } from '../constants/CategoryConstants';
import { type Quantity } from '../constants/QuantityConstants';
-import ImageRecord, { buildImageRecord } from '../records/ImageRecord';
-import { map, pathOr } from 'ramda';
+import ImageRecord, { buildImageRecord } from './ImageRecord';
-export type FoodItem = {
+export type Product = {
id: ?string,
name: string,
placeId: ?number,
@@ -20,7 +19,7 @@ export type FoodItem = {
lastupdated: number,
};
-const FoodRecordDefaults: FoodItem = {
+const FoodRecordDefaults: Product = {
id: '',
name: '',
placeId: null,
@@ -36,19 +35,19 @@ const FoodRecordDefaults: FoodItem = {
lastupdated: 0,
};
-const FoodItemRecord = Record(FoodRecordDefaults, 'FoodItemRecord');
+const ProductRecord = Record(FoodRecordDefaults, 'ProductRecord');
-export const createFoodItem = (foodItemRaw: ?RawFoodItem) => {
- if (!foodItemRaw) {
- return foodItemRaw;
+export const createProduct = (productRaw: ?RawProduct) => {
+ if (!productRaw) {
+ return productRaw;
}
- return new FoodItemRecord({
- ...foodItemRaw,
- placeType: foodItemRaw.placeType,
- thumbImage: foodItemRaw.thumbimage,
- // images: new Set(map(buildImageRecord, pathOr([], ['images'], foodItemRaw))),
- images: new Set([buildImageRecord({ url: foodItemRaw.thumbimage })]),
+ return new ProductRecord({
+ ...productRaw,
+ placeType: productRaw.placeType,
+ thumbImage: productRaw.thumbimage,
+ // images: new Set(map(buildImageRecord, pathOr([], ['images'], productRaw))),
+ images: new Set([buildImageRecord({ url: productRaw.thumbimage })]),
});
};
-export default FoodItemRecord;
+export default ProductRecord;
diff --git a/js/streams/CreateFoodItemStream.js b/js/streams/CreateFoodItemStream.js
deleted file mode 100644
index cdde05d..0000000
--- a/js/streams/CreateFoodItemStream.js
+++ /dev/null
@@ -1,19 +0,0 @@
-// @flow
-import { ReplaySubject } from 'rxjs';
-import FoodItemRecord from '../records/FoodItemRecord';
-
-type CreateFoodItemState = {
- foodItem: FoodItemRecord,
- loading: boolean,
- error?: ?Error,
-};
-
-const multicaster: ReplaySubject = new ReplaySubject();
-
-export function emitter(val: CreateFoodItemState) {
- multicaster.next(val);
-}
-
-emitter({ foodItem: new FoodItemRecord(), loading: false, error: null });
-
-export default multicaster;
diff --git a/js/streams/CreateProductStream.js b/js/streams/CreateProductStream.js
new file mode 100644
index 0000000..82c050c
--- /dev/null
+++ b/js/streams/CreateProductStream.js
@@ -0,0 +1,19 @@
+// @flow
+import { ReplaySubject } from 'rxjs';
+import ProductRecord from '../records/ProductRecord';
+
+type CreateProductState = {
+ product: ProductRecord,
+ loading: boolean,
+ error?: ?Error,
+};
+
+const multicaster: ReplaySubject = new ReplaySubject();
+
+export function emitter(val: CreateProductState) {
+ multicaster.next(val);
+}
+
+emitter({ product: new ProductRecord(), loading: false, error: null });
+
+export default multicaster;
diff --git a/js/streams/FavesStream.js b/js/streams/FavesStream.js
index cb5ff88..fcc4daa 100644
--- a/js/streams/FavesStream.js
+++ b/js/streams/FavesStream.js
@@ -2,7 +2,7 @@
import { BehaviorSubject } from 'rxjs';
import { Record, List, get } from 'immutable';
import filter$ from './FilterStream';
-import foodItems$ from './FoodItemsStream';
+import products$ from './ProductsStream';
import { test } from 'ramda';
export type Fave = {
@@ -22,20 +22,20 @@ export function emitOne(fave: ?Fave) {
}
export default observable
- .combineLatest(foodItems$)
- .map(([faves, foodItems]) => {
- return faves && faves.map(fave => get(foodItems, get(fave, 'food_item_id')));
+ .combineLatest(products$)
+ .map(([faves, products]) => {
+ return faves && faves.map(fave => get(products, get(fave, 'food_item_id')));
})
.combineLatest(filter$)
- .map(([faveFoodItems, filter]) => {
+ .map(([faveProducts, filter]) => {
const filterTest = filter.search && test(new RegExp(filter.search, 'i'));
return (
- faveFoodItems &&
- faveFoodItems.filter(faveFoodItem => {
- if (!faveFoodItem) {
+ faveProducts &&
+ faveProducts.filter(faveProduct => {
+ if (!faveProduct) {
return false;
}
- return !filterTest || filterTest(faveFoodItem.name);
+ return !filterTest || filterTest(faveProduct.name);
})
);
});
diff --git a/js/streams/FoodItemsStream.js b/js/streams/FoodItemsStream.js
deleted file mode 100644
index 8152591..0000000
--- a/js/streams/FoodItemsStream.js
+++ /dev/null
@@ -1,68 +0,0 @@
-//@flow
-import { BehaviorSubject, Observable } from 'rxjs';
-import FoodItemRecord, { createFoodItem, type FoodItem } from '../records/FoodItemRecord';
-import { setById } from '../helpers/ImmutableHelpers';
-import { Map, type Record } from 'immutable';
-import location$ from './LocationStream';
-import { getFoodItems, type FoodItemsForLocation } from '../apis/FoodItemsApi';
-import Filter$ from './FilterStream';
-import Quantity$ from './QuantityStream';
-import type { QuantityFragment } from '../constants/QuantityConstants';
-import { type ImageFragment } from '../records/ImageRecord';
-import Image$ from './ImagesStream';
-
-const foodItemSubject: BehaviorSubject = new BehaviorSubject();
-
-export function emitter(val?: ?FoodItemRecord) {
- foodItemSubject.next(val);
-}
-
-emitter(null);
-
-const manualUpdate$ = foodItemSubject.scan(
- (foodItemMap: Map, foodItem: FoodItemRecord) => {
- return foodItem ? foodItemMap.set(foodItem.id, foodItem) : foodItemMap;
- },
- Map()
-);
-
-const fetchedFoodItems$ = Filter$.combineLatest(location$)
- .debounceTime(200)
- .mergeMap(([filter, loc]) => {
- if (!loc) {
- return Promise.resolve({});
- }
- return getFoodItems({ filter, loc });
- })
- .map(({ fooditems }: FoodItemsForLocation) => {
- if (fooditems) {
- return fooditems.map(createFoodItem).reduce(setById, new Map());
- }
- return null;
- });
-
-export default fetchedFoodItems$
- .combineLatest(manualUpdate$, (foodItemMap: Map, manualUpdates) => {
- if (foodItemMap) {
- return foodItemMap.mergeDeep(manualUpdates);
- }
- })
- .combineLatest(
- Quantity$,
- (
- foodItems: ?Map,
- quantitiesFromStream: Map
- ) => {
- if (foodItems) {
- return foodItems.mergeDeep(quantitiesFromStream);
- }
- }
- )
- .combineLatest(
- Image$,
- (foodItems: ?Map, latestFromImages$: Map) => {
- if (foodItems) {
- return foodItems.mergeDeep(latestFromImages$);
- }
- }
- );
diff --git a/js/streams/ImagesStream.js b/js/streams/ImagesStream.js
index c5020c0..3ed0f29 100644
--- a/js/streams/ImagesStream.js
+++ b/js/streams/ImagesStream.js
@@ -13,18 +13,15 @@ export function emit(val: ?ImageRaw) {
// force our observable to emit an initial empty map so that food items will load
emit(null);
-export default observable.scan(
- (imagesByFoodItemId: Map, image: ImageRaw) => {
- if (!image || !image.food_item_id) {
- return imagesByFoodItemId;
- }
+export default observable.scan((imagesByProductId: Map, image: ImageRaw) => {
+ if (!image || !image.food_item_id) {
+ return imagesByProductId;
+ }
- return imagesByFoodItemId.update(image.food_item_id, ({ images = OrderedSet() } = {}) => {
- return {
- id: image.food_item_id,
- images: images.add(buildImageRecord(image)),
- };
- });
- },
- new Map()
-);
+ return imagesByProductId.update(image.food_item_id, ({ images = OrderedSet() } = {}) => {
+ return {
+ id: image.food_item_id,
+ images: images.add(buildImageRecord(image)),
+ };
+ });
+}, new Map());
diff --git a/js/streams/PlacesStream.js b/js/streams/PlacesStream.js
index 3b0d581..733eef1 100644
--- a/js/streams/PlacesStream.js
+++ b/js/streams/PlacesStream.js
@@ -1,14 +1,14 @@
-import { ReplaySubject, Observable } from 'rxjs';
+import { ReplaySubject } from 'rxjs';
import { buildPlaceRecord } from '../records/PlaceRecord';
import { Map } from 'immutable';
-import { findNearbyPlaces, getPlaceDetails } from '../apis/GooglePlacesApi';
+import { findNearbyPlaces } from '../apis/GooglePlacesApi';
import { path } from 'ramda';
import { type GooglePlaceObj } from '../records/PlaceRecord';
import { setById } from '../helpers/ImmutableHelpers';
import location$ from './LocationStream';
import filter$ from './FilterStream';
import FilterRecord from '../records/FilterRecord';
-import foodItems$ from './FoodItemsStream';
+// import products$ from './ProductsStream';
import PlaceRecord from '../records/PlaceRecord';
import geodist from 'geodist';
@@ -22,9 +22,9 @@ export function emitter(val?: ?PlaceRecord) {
filter$.subscribe(() => emitter(null));
-// foodItems$
-// .mergeMap((foodItems = Map()) => Observable.from(foodItems.toArray()))
-// .mergeMap(([foodItemId, foodItem]) => getPlaceDetails(foodItem))
+// products$
+// .mergeMap((products = Map()) => Observable.from(products.toArray()))
+// .mergeMap(([productId, product]) => getPlaceDetails(product))
// .map(buildPlaceRecord)
// .subscribe(emitter);
@@ -63,9 +63,22 @@ location$
})
.subscribe(places => places && places.map(emitter));
-export default placesSubject.scan((places, place) => {
- if (!place) {
- return null;
- }
- return setById(places || new Map(), place);
-});
+export default placesSubject.scan(
+ (typeToPlace, place) => {
+ if (!place) {
+ return typeToPlace;
+ }
+
+ const { placeType } = place;
+
+ if (Object.keys(typeToPlace).includes(placeType)) {
+ return {
+ ...typeToPlace,
+ [placeType]: [...typeToPlace[placeType], place],
+ };
+ }
+
+ return typeToPlace;
+ },
+ { HEB: [], WholeFoods: [] }
+);
diff --git a/js/streams/ProductsStream.js b/js/streams/ProductsStream.js
new file mode 100644
index 0000000..ac196df
--- /dev/null
+++ b/js/streams/ProductsStream.js
@@ -0,0 +1,81 @@
+//@flow
+import { BehaviorSubject } from 'rxjs';
+import ProductRecord, { createProduct } from '../records/ProductRecord';
+import { setById } from '../helpers/ImmutableHelpers';
+import { Map, set, getIn } from 'immutable';
+import location$ from './LocationStream';
+import { getProducts, type ProductsForLocation } from '../apis/ProductsApi';
+import Filter$ from './FilterStream';
+import Quantity$ from './QuantityStream';
+import type { QuantityFragment } from '../constants/QuantityConstants';
+// import { type ImageFragment } from '../records/ImageRecord';
+// import Image$ from './ImagesStream';
+import places$ from './PlacesStream';
+
+const productSubject: BehaviorSubject = new BehaviorSubject();
+
+export function emitter(val?: ?ProductRecord) {
+ productSubject.next(val);
+}
+
+emitter(null);
+
+const manualUpdate$ = productSubject.scan(
+ (productMap: Map, product: ProductRecord) => {
+ return product ? productMap.set(product.id, product) : productMap;
+ },
+ Map()
+);
+
+const fetchedProducts$ = Filter$.combineLatest(location$)
+ .debounceTime(200)
+ .mergeMap(([filter, loc]) => {
+ if (!loc) {
+ return Promise.resolve({});
+ }
+ return getProducts({ filter, loc });
+ })
+ .map(({ products }: ProductsForLocation) => {
+ if (products) {
+ return products.map(createProduct).reduce(setById, new Map());
+ }
+ return null;
+ });
+
+export default fetchedProducts$
+ .combineLatest(manualUpdate$, (productMap: Map, manualUpdates) => {
+ if (productMap) {
+ return productMap.mergeDeep(manualUpdates);
+ }
+ })
+ .combineLatest(
+ Quantity$,
+ (
+ products: ?Map,
+ quantitiesFromStream: Map
+ ) => {
+ if (products) {
+ return products.mergeDeep(quantitiesFromStream);
+ }
+ }
+ )
+ .combineLatest(places$, (products, places) => {
+ if (!places || !products) {
+ return products;
+ }
+
+ const getPlaceIdForNearest = placeType => getIn(places, [placeType, 0, 'id'], '');
+
+ return products.map(product =>
+ set(product, 'placeId', getPlaceIdForNearest(product.placeType))
+ );
+ });
+// )
+// .combineLatest(
+// Image$,
+// (products: ?Map, latestFromImages$: Map) => {
+// if (products) {
+// return products.mergeDeep(latestFromImages$);
+// }
+// }
+// );
diff --git a/js/streams/QuantityStream.js b/js/streams/QuantityStream.js
index 1657d09..0f3e512 100644
--- a/js/streams/QuantityStream.js
+++ b/js/streams/QuantityStream.js
@@ -12,13 +12,16 @@ export function emit(val: ?QuantityResponse) {
// force our observable to emit an initial empty map so that food items will load
emit(null);
-export default observable.scan((quantitiesByFoodItemId: Map, quantity?: QuantityResponse) => {
- if (!quantity) {
- return quantitiesByFoodItemId;
- }
+export default observable.scan(
+ (quantitiesByProductId: Map, quantity?: QuantityResponse) => {
+ if (!quantity) {
+ return quantitiesByProductId;
+ }
- return quantitiesByFoodItemId.set(quantity.food_item_id, {
- quantity: quantity.quantity,
- lastupdated: quantity.date,
- });
-}, new Map());
+ return quantitiesByProductId.set(quantity.food_item_id, {
+ quantity: quantity.quantity,
+ lastupdated: quantity.date,
+ });
+ },
+ new Map()
+);
diff --git a/js/ui-theme.js b/js/ui-theme.js
index d82dda2..cb65cfa 100644
--- a/js/ui-theme.js
+++ b/js/ui-theme.js
@@ -96,7 +96,7 @@ export default {
defaultColor: '#000000',
selectedColor: '#017C9A',
},
- foodItemDetails: {
+ productDetails: {
actionIconColor: '#017C9A',
},
placeDetails: {