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 && (