diff --git a/js/components/ImagePicker.js b/js/components/ImagePicker.js index 9bbfe1e..fc5f3af 100644 --- a/js/components/ImagePicker.js +++ b/js/components/ImagePicker.js @@ -4,6 +4,7 @@ import { Text, View, TouchableOpacity, Image } from 'react-native'; import { Icon } from 'react-native-material-ui'; import uiTheme from '../ui-theme.js'; import ReactNativeImagePicker from 'react-native-image-picker'; +import { type List } from 'immutable'; const { palette: { accentColor } } = uiTheme; @@ -19,12 +20,12 @@ export const ImageThumb = ({ uri, onPress }: { uri?: string, onPress: () => void ; -export const ImagePicker = ({ children, onCreateNew }: { children?: any, onCreateNew: Function }) => +export const ImagePicker = ({ children, onCreateNew }: { children?: List, onCreateNew: Function }) => - {(!children || !children.length) && + {(!children || !children.size) && Add Images } diff --git a/js/modals/ImagePreviewModal.js b/js/modals/ImagePreviewModal.js new file mode 100644 index 0000000..2e234db --- /dev/null +++ b/js/modals/ImagePreviewModal.js @@ -0,0 +1,38 @@ +// @flow +import React from 'react'; +import { TouchableOpacity, Image } from 'react-native'; +import { ActionButton } from 'react-native-material-ui'; + +const backdropStyle = { + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + backgroundColor: 'black', + flexDirection: 'column', + justifyContent: 'center', +}; + +const imageStyle = { + flex: 2, + resizeMode: 'contain', +}; + +type Props = { + onClose: () => void, + onDelete: () => void, + imageSrc: string, +}; + +const ImagePreviewModal = ({ onClose, onDelete, imageSrc }: Props) => { + return ( + + + + + ); +}; +ImagePreviewModal.displayName = 'ImagePreviewModal'; + +export default ImagePreviewModal; diff --git a/js/pages/CreateFoodItem.js b/js/pages/CreateFoodItem.js index ccbfe45..5bc269b 100644 --- a/js/pages/CreateFoodItem.js +++ b/js/pages/CreateFoodItem.js @@ -6,6 +6,7 @@ import { Divider } from 'react-native-material-ui'; import FoodItemRecord from '../records/FoodItemRecord'; import PlaceRecord from '../records/PlaceRecord'; import NameModal from '../modals/FoodItemNameModal'; +import ImagePreviewModal from '../modals/ImagePreviewModal'; import QuantityPicker from '../components/QuantityPicker'; import { compose, branch, withState, withHandlers, renderComponent, mapProps } from 'recompose'; import RNGooglePlaces from 'react-native-google-places'; @@ -52,12 +53,12 @@ type Props = { setModalsVisible: Function, place: typeof PlaceRecord, setPlace: (place: typeof PlaceRecord) => void, - removeImageAtIndex: (i: number) => () => void, updatePlace: (place: GooglePlaceObject) => void, addImage: (uri: string) => void, + setImagePreview: (index?: number) => void, }; const CreateFoodItem = (props: Props) => { - const { foodItem, toggleNameModal, setPropOfFoodItem, place, updatePlace, removeImageAtIndex, addImage } = props; + const { foodItem, toggleNameModal, setPropOfFoodItem, place, updatePlace, addImage, setImagePreview } = props; return ( @@ -77,14 +78,14 @@ const CreateFoodItem = (props: Props) => { {foodItem.images.map((image, index) => - + setImagePreview(index)} /> )} ); }; -const enhanceNameModal = compose( +const NameModalComp = compose( renderComponent, mapProps(({ toggleNameModal, setPropOfFoodItem }: Props) => { return { @@ -92,57 +93,73 @@ const enhanceNameModal = compose( onUpdate: setPropOfFoodItem('name'), }; }) -); +)(NameModal); + +const ImagePreviewComp = compose( + renderComponent, + mapProps(({ foodItem, setFoodItem, setImagePreview, imagePreview }) => { + return { + onClose: () => setImagePreview(-1), + onDelete: () => { + setFoodItem(foodItem.deleteIn(['images', imagePreview])); + setImagePreview(-1); + }, + imageSrc: foodItem.images.get(imagePreview), + }; + }) +)(ImagePreviewModal); + +const setPropOfFoodItem = ({ foodItem, setFoodItem }: Props) => (prop: string) => (value: any) => { + setFoodItem(foodItem.set(prop, value)); +}; + +const addImage = ({ foodItem, setFoodItem }: Props) => (uri: string) => { + setFoodItem(foodItem.update('images', images => images.push(uri))); +}; + +const updatePlace = ({ setPlace, foodItem, setFoodItem }: Props) => ({ + placeID, + latitude, + longitude, + name, + address, + phoneNumber, + website, +}) => { + setPlace( + new PlaceRecord({ + id: placeID, + name, + address, + latitude, + longitude, + phoneNumber, + website, + }) + ); + + setFoodItem( + foodItem.merge({ + placeId: placeID, + latitude, + longitude, + }) + ); +}; + +const toggleNameModal = ({ nameModalOpen, setNameModalOpen }) => () => setNameModalOpen(!nameModalOpen); export default compose( withState('foodItem', 'setFoodItem', new FoodItemRecord()), withState('place', 'setPlace', new PlaceRecord()), withState('nameModalOpen', 'setNameModalOpen', false), + withState('imagePreview', 'setImagePreview', -1), withHandlers({ - setPropOfFoodItem: ({ foodItem, setFoodItem }: Props) => (prop: string) => (value: any) => { - setFoodItem(foodItem.set(prop, value)); - }, - addImage: ({ foodItem, setFoodItem }: Props) => (uri: string) => { - setFoodItem(foodItem.update('images', images => [uri].concat(images))); - }, - removeImageAtIndex: ({ foodItem, setFoodItem }: Props) => (index: number) => () => { - setFoodItem( - foodItem.update('images', (images: Array) => { - images.splice(index, 1); - return images; - }) - ); - }, - updatePlace: ({ setPlace, foodItem, setFoodItem }: Props) => ({ - placeID, - latitude, - longitude, - name, - address, - phoneNumber, - website, - }) => { - setPlace( - new PlaceRecord({ - id: placeID, - name, - address, - latitude, - longitude, - phoneNumber, - website, - }) - ); - - setFoodItem( - foodItem.merge({ - placeId: placeID, - latitude, - longitude, - }) - ); - }, - toggleNameModal: ({ nameModalOpen, setNameModalOpen }) => () => setNameModalOpen(!nameModalOpen), + setPropOfFoodItem, + addImage, + updatePlace, + toggleNameModal, }), - branch(({ nameModalOpen }) => !!nameModalOpen, enhanceNameModal(NameModal)) + branch(({ nameModalOpen }) => !!nameModalOpen, NameModalComp), + branch(({ imagePreview }) => imagePreview > -1, ImagePreviewComp) )(CreateFoodItem); diff --git a/js/pages/FoodItemDetail.js b/js/pages/FoodItemDetail.js index c2f0c46..95fbd77 100644 --- a/js/pages/FoodItemDetail.js +++ b/js/pages/FoodItemDetail.js @@ -68,15 +68,15 @@ export class FoodItemDetail extends Component { return ( - {viewableImages.length === 1 && - } - {viewableImages.length > 1 && + {viewableImages.size === 1 && + } + {viewableImages.size > 1 && {viewableImages.map(uri => )} } - + diff --git a/js/records/FoodItemRecord.js b/js/records/FoodItemRecord.js index 039d5c4..11b6d5c 100644 --- a/js/records/FoodItemRecord.js +++ b/js/records/FoodItemRecord.js @@ -1,5 +1,5 @@ //@flow -import { Record } from 'immutable'; +import { fromJS, List, Record } from 'immutable'; export const QUANTITY_NONE: 'none' = 'none'; export const QUANTITY_FEW: 'few' = 'few'; @@ -28,7 +28,7 @@ export type FoodItem = { distance: number, quantity: Quantity, category: Category, - images: Array, + images: List, thumbImage: ?string, titleImage: ?string, }; @@ -42,9 +42,18 @@ const FoodRecordDefaults: FoodItem = { distance: 999, quantity: QUANTITY_MANY, category: CATEGORY_DESSERTS, - images: [], + images: new List(), thumbImage: '', titleImage: '', }; -export default Record(FoodRecordDefaults, 'FoodItemRecord'); +const FoodItemRecord = Record(FoodRecordDefaults, 'FoodItemRecord'); + +export const createFoodItem = (foodItemRaw: Object) => { + return new FoodItemRecord({ + ...foodItemRaw, + images: fromJS(foodItemRaw.images), + }); +}; + +export default FoodItemRecord; diff --git a/js/streams/FoodItemsStream.js b/js/streams/FoodItemsStream.js index 691a75c..4f927df 100644 --- a/js/streams/FoodItemsStream.js +++ b/js/streams/FoodItemsStream.js @@ -1,5 +1,5 @@ //@flow -import FoodItemRecord from '../records/FoodItemRecord'; +import { createFoodItem } from '../records/FoodItemRecord'; import { Observable } from 'rxjs'; import { setById } from '../helpers/ImmutableHelpers'; import { Map } from 'immutable'; @@ -110,4 +110,4 @@ const DUMMY_DATA = [ export const foodItemsRaw$ = Observable.from(DUMMY_DATA); -export default foodItemsRaw$.map(FoodItemRecord).scan(setById, Map()); +export default foodItemsRaw$.map(createFoodItem).scan(setById, Map());