diff --git a/js/components/FoodItemSaveBtn.js b/js/components/FoodItemSaveBtn.js index 092d089..dc92e42 100644 --- a/js/components/FoodItemSaveBtn.js +++ b/js/components/FoodItemSaveBtn.js @@ -6,13 +6,12 @@ import { withReplaceRoute } from '../enhancers/routeEnhancers'; import { compose, onlyUpdateForKeys, withHandlers } from 'recompose'; import { routeWithTitle } from '../helpers/RouteHelpers'; -const FoodItemSaveBtn = ({ - saveFoodItem, - loading, -}: { - saveFoodItem?: Function, +type Props = { loading: boolean, -}) => { + doSave: Function, +}; + +const FoodItemSaveBtn = ({ loading, doSave }: Props) => { const textStyle = { color: 'white', marginRight: 20, @@ -21,7 +20,7 @@ const FoodItemSaveBtn = ({ }; return ( - + SAVE ); @@ -31,7 +30,7 @@ export default compose( withCreateFoodItemState, withReplaceRoute, withHandlers({ - saveFoodItem: ({ saveFoodItem, setLoading, setError, replaceRoute }) => async () => { + doSave: ({ saveFoodItem, setLoading, setError, replaceRoute }) => async () => { try { setLoading(true); const { id, name } = await saveFoodItem(); diff --git a/js/enhancers/foodItemEnhancers.js b/js/enhancers/foodItemEnhancers.js index 7cc7033..eec438d 100644 --- a/js/enhancers/foodItemEnhancers.js +++ b/js/enhancers/foodItemEnhancers.js @@ -1,11 +1,33 @@ // @flow import withProps from 'recompose/withProps'; import compose from 'recompose/compose'; -import { path } from 'ramda'; import mapPropsStream from 'recompose/mapPropsStream'; import FoodItems$ from '../streams/FoodItemsStream'; import typeof FoodItemRecord from '../records/FoodItemRecord'; -import { Map } from 'immutable'; +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) => { @@ -57,3 +79,12 @@ export const withFoodItemsGroupedByPlace = compose( }; }) ); + +export const withUniqueFoodItems = compose( + withFoodItemsAsSeq, + withProps(({ foodItemsSeq }) => { + return { + foodItemsSeq: intoSet(addIfNotExisting(matchesName))(foodItemsSeq), + }; + }) +); diff --git a/js/modals/FoodItemNameModal.js b/js/modals/FoodItemNameModal.js index 78be8f3..c1ec944 100644 --- a/js/modals/FoodItemNameModal.js +++ b/js/modals/FoodItemNameModal.js @@ -6,30 +6,37 @@ import FoodItemList from '../components/FoodItemList'; import FoodItemRecord from '../records/FoodItemRecord'; 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 { Set } from 'immutable'; + +type Props = { + onClose: () => void, + onUpdate: (name: string) => void, + foodItemsSeq: ?Set, +}; class NameModal extends Component { static displayName = 'NameModal'; - props: { - onClose: () => void, - onUpdate: (name: string) => void, - }; + props: Props; state: { - text: string, + filter: FilterRecord, }; state = { - text: '', + filter: new FilterRecord(), }; save = () => { - this.props.onUpdate(this.state.text); + this.props.onUpdate(this.state.filter.search); this.props.onClose(); }; setText = (text: string) => { - this.setState({ text }); + this.setState(({ filter }) => ({ filter: filter.set('search', text) })); }; updateAndClose = (text: string) => { @@ -37,15 +44,9 @@ class NameModal extends Component { this.props.onClose(); }; - renderFoodItem = (foodItem: typeof FoodItemRecord) => ( - this.updateAndClose(foodItem.name)}> - {foodItem.name} - - ); - render() { - const { onClose } = this.props; - const { text } = this.state; + const { onClose, foodItemsSeq } = this.props; + const { filter } = this.state; return ( - {!!text && ( + {!!filter.search && ( )} - + + {(foodItem: FoodItemRecord) => ( + this.updateAndClose(foodItem.name)}> + + {foodItem.name} + + + )} + ); } } -export default NameModal; +export default compose( + pure, + withUniqueFoodItems +)(NameModal); diff --git a/js/pages/CreateFoodItem.js b/js/pages/CreateFoodItem.js index 8e8792c..801fbd3 100644 --- a/js/pages/CreateFoodItem.js +++ b/js/pages/CreateFoodItem.js @@ -24,6 +24,23 @@ import { withCreateFoodItemState } from '../enhancers/createFoodItemEnhancers'; import { withPlaceForFoodItem, withPlaceId, withPlaceActions } from '../enhancers/placeEnhancers'; import Spinner from 'react-native-loading-spinner-overlay'; import { openImagePicker } from '../helpers/ImagePickerHelpers'; +import { IndexedSeq } from 'immutable'; + +type Props = { + foodItem: typeof FoodItemRecord, + setPropOfFoodItem: Function, + toggleNameModal: Function, + setFoodItem: Function, + setModalsVisible: Function, + place: ?PlaceRecord, + updatePlace: (place: GooglePlaceObject) => void, + addImage: (uri: string) => void, + setImagePreview: (index?: number) => void, + loading: boolean, + setLoading: (arg: boolean) => void, + emitPlace: (place: Object) => void, + withFoodItemsAsSeq: IndexedSeq, +}; type GooglePlaceObject = { placeID: string, @@ -54,20 +71,6 @@ const openPlaceModal = (onChoosePlace: (place: GooglePlaceObject) => void) => () RNGooglePlaces.openAutocompleteModal({ type: 'establishment' }).then(onChoosePlace); }; -type Props = { - foodItem: typeof FoodItemRecord, - setPropOfFoodItem: Function, - toggleNameModal: Function, - setFoodItem: Function, - setModalsVisible: Function, - place: ?PlaceRecord, - updatePlace: (place: GooglePlaceObject) => void, - addImage: (uri: string) => void, - setImagePreview: (index?: number) => void, - loading: boolean, - setLoading: (arg: boolean) => void, - emitPlace: (place: Object) => void, -}; const CreateFoodItem = (props: Props) => { const { foodItem,