mirror of
https://gitlab.com/wheres-the-tp/ui-mobile.git
synced 2026-01-25 07:24:56 -06:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
a98fcc247c
23 changed files with 259 additions and 105 deletions
|
|
@ -91,7 +91,7 @@ android {
|
|||
applicationId "com.aretherecookies"
|
||||
minSdkVersion 16
|
||||
targetSdkVersion 22
|
||||
versionCode 1
|
||||
versionCode 6
|
||||
versionName "1.0"
|
||||
ndk {
|
||||
abiFilters "armeabi-v7a", "x86"
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@
|
|||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="com.aretherecookies"
|
||||
android:versionCode="1"
|
||||
android:versionName="1.0">
|
||||
android:versionName="1.0.1">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
<uses-permission android:name="android.permission.CAMERA" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<!-- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> -->
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="16"
|
||||
|
|
|
|||
BIN
captures/com.aretherecookies_2018.04.23_16.10.li
Normal file
BIN
captures/com.aretherecookies_2018.04.23_16.10.li
Normal file
Binary file not shown.
Binary file not shown.
11
js/App.js
11
js/App.js
|
|
@ -1,13 +1,13 @@
|
|||
//@flow
|
||||
import React, { Component } from 'react';
|
||||
import { View } from 'react-native';
|
||||
import { View, StatusBar } from 'react-native';
|
||||
import { ThemeProvider } from 'react-native-material-ui';
|
||||
import theme from './ui-theme';
|
||||
import DrawerMenu from './pages/DrawerMenu';
|
||||
import rxjsconfig from 'recompose/rxjsObservableConfig';
|
||||
import setObservableConfig from 'recompose/setObservableConfig';
|
||||
import TopToolbar from './components/TopToolbar';
|
||||
import { NativeRouter, Route, Redirect, AndroidBackButton } from 'react-router-native';
|
||||
import { NativeRouter, Route, Redirect, AndroidBackButton, Switch } from 'react-router-native';
|
||||
import List from './pages/List';
|
||||
import FoodItemDetail from './pages/FoodItemDetail';
|
||||
import PlaceDetail from './pages/PlaceDetail';
|
||||
|
|
@ -15,6 +15,7 @@ import Drawer from 'react-native-drawer';
|
|||
import CreateFoodItem from './pages/CreateFoodItem';
|
||||
import AuthManager from './AuthManager';
|
||||
import LoginPage from './pages/LoginPage';
|
||||
import LandingPage from './pages/LandingPage';
|
||||
|
||||
setObservableConfig(rxjsconfig);
|
||||
|
||||
|
|
@ -42,9 +43,12 @@ export default class App extends Component {
|
|||
openDrawerOffset={100}
|
||||
content={<DrawerMenu onCloseDrawer={this.closeDrawer} />}
|
||||
tweenDuration={150}>
|
||||
<StatusBar backgroundColor={theme.statusBarColor} />
|
||||
<Redirect from="/" to="/landing" />
|
||||
<Switch>
|
||||
<Route path="/landing" component={LandingPage} />
|
||||
<View style={theme.page.container}>
|
||||
<TopToolbar toggleSideMenu={this.openDrawer} />
|
||||
<Redirect from="/" to="/list/food" />
|
||||
<Route path="/list/:type" component={List} />
|
||||
<Route path="/foodItem/:id" component={FoodItemDetail} />
|
||||
<Route path="/place/:id" component={PlaceDetail} />
|
||||
|
|
@ -59,6 +63,7 @@ export default class App extends Component {
|
|||
}}
|
||||
/>
|
||||
</View>
|
||||
</Switch>
|
||||
</Drawer>
|
||||
</ThemeProvider>
|
||||
</AndroidBackButton>
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ export type RawFoodItem = {
|
|||
export type FoodItemsForLocation = {
|
||||
orderby: string,
|
||||
filter: FoodItemsFilter,
|
||||
fooditems: Array<RawFoodItem>,
|
||||
fooditems: ?Array<RawFoodItem>,
|
||||
};
|
||||
|
||||
export const getFoodItems = memoize(
|
||||
|
|
@ -38,7 +38,9 @@ export const getFoodItems = memoize(
|
|||
loc: Position,
|
||||
filter: FilterRecord,
|
||||
}): Promise<FoodItemsForLocation> => {
|
||||
const { coords: { latitude: lat, longitude: lng } } = loc;
|
||||
const {
|
||||
coords: { latitude: lat, longitude: lng },
|
||||
} = loc;
|
||||
const { orderby, categories, radius } = filter;
|
||||
|
||||
try {
|
||||
|
|
|
|||
27
js/apis/PositionApi.js
Normal file
27
js/apis/PositionApi.js
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// @flow
|
||||
import { emitter } from '../streams/LocationStream';
|
||||
|
||||
export const getCurrentPosition = () => {
|
||||
// $FlowFixMe: maximumAge not found on object literal
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(pos: Position) => emitter(pos),
|
||||
err => {
|
||||
throw err;
|
||||
},
|
||||
{
|
||||
enableHighAccuracy: false,
|
||||
timeout: 2000,
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// TODO actually implement geolocation for zipcode into lat/lng
|
||||
export const getPositionFromZip = () => {
|
||||
const dummyPos: any = {
|
||||
coords: {
|
||||
latitude: 30.267,
|
||||
longitude: -97.7485,
|
||||
},
|
||||
};
|
||||
emitter(dummyPos);
|
||||
};
|
||||
|
|
@ -3,11 +3,12 @@ import React, { Component } from 'react';
|
|||
import { View } from 'react-native';
|
||||
import { type SetSeq } from 'immutable';
|
||||
import FoodItemRecord from '../records/FoodItemRecord';
|
||||
import { pure, compose } from 'recompose';
|
||||
import { withFoodItemsAsSeq } from '../enhancers/foodItemEnhancers';
|
||||
import { pure } from 'recompose';
|
||||
import R from 'ramda';
|
||||
|
||||
const matchString = R.memoize((match = '', str = '') => str.toLowerCase().includes(match.toLowerCase()));
|
||||
const matchString = R.memoize((match = '', str = '') =>
|
||||
str.toLowerCase().includes(match.toLowerCase())
|
||||
);
|
||||
|
||||
const filterBy = (filter?: string = '') => (foodItemsSeq: SetSeq<FoodItemRecord>) => {
|
||||
if (!filter) {
|
||||
|
|
@ -27,7 +28,8 @@ const sortByDistance = (foodItemsSeq: SetSeq<FoodItemRecord>) => {
|
|||
return foodItemsSeq.sort((left, right) => left.distance - right.distance);
|
||||
};
|
||||
|
||||
const intoArray = (foodItemsSeq: SetSeq<FoodItemRecord>) => foodItemsSeq.toArray();
|
||||
const intoArray = (foodItemsSeq: SetSeq<FoodItemRecord>) =>
|
||||
foodItemsSeq ? foodItemsSeq.toArray() : [];
|
||||
|
||||
class FoodItemList extends Component {
|
||||
static displayName = 'FoodItemList';
|
||||
|
|
@ -37,16 +39,21 @@ class FoodItemList extends Component {
|
|||
limit?: number,
|
||||
foodItemsSeq: SetSeq<FoodItemRecord>,
|
||||
renderFoodItem: (foodItem: typeof FoodItemRecord) => Component<*, *, *>,
|
||||
foodItemsLoading: boolean,
|
||||
};
|
||||
|
||||
render() {
|
||||
const { filter, foodItemsSeq, renderFoodItem, limit } = this.props;
|
||||
|
||||
if (!foodItemsSeq) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const getItems = R.compose(intoArray, limitBy(limit), sortByDistance, filterBy(filter));
|
||||
const items = getItems(foodItemsSeq);
|
||||
|
||||
return <View style={{ flexShrink: 2 }}>{items.map(renderFoodItem)}</View>;
|
||||
}
|
||||
}
|
||||
|
||||
const enhance = compose(pure, withFoodItemsAsSeq);
|
||||
|
||||
export default enhance(FoodItemList);
|
||||
export default pure(FoodItemList);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ export const SubText = ({ children, style = {} }: { children?: string, style?: O
|
|||
};
|
||||
|
||||
export const TileBox = ({ children, style = {} }: { children?: any, style?: Object }) => (
|
||||
<View style={{ height: 70, flexDirection: 'row', backgroundColor: 'white', alignItems: 'center', ...style }}>
|
||||
<View style={{ flexDirection: 'row', backgroundColor: 'white', alignItems: 'center', paddingTop: 15, paddingBottom: 15, ...style }}>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { List } from 'immutable';
|
|||
import FoodItemRecord from '../records/FoodItemRecord';
|
||||
import typeof PlaceRecord from '../records/PlaceRecord';
|
||||
import { Link } from 'react-router-native';
|
||||
import { Thumbnail, StrongText, SubText } from './ItemTile';
|
||||
import { TileBox, Thumbnail, StrongText, SubText } from './ItemTile';
|
||||
import { routeWithTitle } from '../helpers/RouteHelpers';
|
||||
import { getCategories } from '../helpers/CategoryHelpers';
|
||||
import { type Map } from 'immutable';
|
||||
|
|
@ -47,13 +47,15 @@ export default pure(({ place, foodItems }: PlaceTileProps) => {
|
|||
to={routeWithTitle(`/place/${place.id || ''}`, place.name)}
|
||||
underlayColor={theme.itemTile.pressHighlightColor}
|
||||
>
|
||||
<View style={{ height: 70, flexDirection: 'row', backgroundColor: 'white' }}>
|
||||
<View>
|
||||
<TileBox>
|
||||
<Thumbnail thumb={place.thumb} />
|
||||
<View style={{ paddingTop: 5 }}>
|
||||
<View>
|
||||
<StrongText>{`${place.name} - ${distance} mi`}</StrongText>
|
||||
<SubText>{getCategoriesText(foodItems)}</SubText>
|
||||
<SubText>{getHoursText(place.hours)}</SubText>
|
||||
</View>
|
||||
</TileBox>
|
||||
</View>
|
||||
</Link>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ const renderItem = (selected: number) => (item: number) => (
|
|||
type radiusPickerProps = { selected: number, onValueChange: Function, style: Object };
|
||||
const radiusPicker = pure(({ selected, onValueChange, style }: radiusPickerProps) => (
|
||||
<View style={style}>
|
||||
<Picker selectedValue={selected} onValueChange={debounce(onValueChange)} style={{ flex: 1 }}>
|
||||
<Picker selectedValue={selected} onValueChange={debounce(onValueChange)} style={{ flex: 1, }}>
|
||||
{CHOICES.map(renderItem(selected))}
|
||||
</Picker>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ export const withFoodItems = mapPropsStream(props$ =>
|
|||
props$.combineLatest(FoodItems$, (props, foodItems) => {
|
||||
return {
|
||||
...props,
|
||||
foodItemsMap: foodItems,
|
||||
foodItemsMap: foodItems && foodItems,
|
||||
};
|
||||
})
|
||||
);
|
||||
|
|
@ -20,7 +20,7 @@ export const withFoodItemsAsSeq = mapPropsStream(props$ =>
|
|||
props$.combineLatest(FoodItems$, (props, foodItems) => {
|
||||
return {
|
||||
...props,
|
||||
foodItemsSeq: foodItems.valueSeq(),
|
||||
foodItemsSeq: foodItems && foodItems.valueSeq(),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
|
@ -33,10 +33,9 @@ export const withFoodItemIdFromRoute = withProps((props: { match: { params: { id
|
|||
export const withFoodItem = compose(
|
||||
withFoodItems,
|
||||
withFoodItemIdFromRoute,
|
||||
withProps((props: { foodItemsMap: Map<number, FoodItemRecord>, foodItemId: number }) => {
|
||||
withProps((props: { foodItemsMap: ?Map<number, FoodItemRecord>, foodItemId: number }) => {
|
||||
const { foodItemsMap, foodItemId } = props;
|
||||
const foodItem = foodItemsMap.get(foodItemId);
|
||||
return { foodItem };
|
||||
return { foodItem: foodItemsMap && foodItemsMap.get(foodItemId) };
|
||||
})
|
||||
);
|
||||
|
||||
|
|
@ -49,10 +48,12 @@ export const withFoodItemPlaceId = withProps((props: { foodItem: FoodItemRecord
|
|||
|
||||
export const withFoodItemsGroupedByPlace = compose(
|
||||
withFoodItems,
|
||||
withProps((props: { foodItemsMap: Map<number, FoodItemRecord> }) => {
|
||||
const foodItemsByPlace = props.foodItemsMap.groupBy(foodItem => foodItem.placeId);
|
||||
withProps((props: { foodItemsMap: ?Map<number, FoodItemRecord> }) => {
|
||||
if (!props.foodItemsMap) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
foodItemsByPlace,
|
||||
foodItemsByPlace: props.foodItemsMap.groupBy(foodItem => foodItem.placeId),
|
||||
};
|
||||
})
|
||||
);
|
||||
|
|
|
|||
|
|
@ -69,29 +69,29 @@ const FilterModal = (props: Props) => {
|
|||
})}
|
||||
<View
|
||||
style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Text style={{ fontSize: 15 }}>Sort By</Text>
|
||||
<Text>Sort By</Text>
|
||||
<OrderbyPicker
|
||||
selected={orderby}
|
||||
onValueChange={updateOrderby}
|
||||
style={{ height: 40, width: 150 }}
|
||||
style={theme.modalDropDown}
|
||||
/>
|
||||
</View>
|
||||
<View
|
||||
style={{ flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Text style={{ fontSize: 15 }}>Search Radius</Text>
|
||||
<Text>Search Radius</Text>
|
||||
<RadiusPicker
|
||||
selected={radius}
|
||||
onValueChange={updateRadius}
|
||||
style={{ height: 40, width: 150 }}
|
||||
style={theme.modalDropDown}
|
||||
/>
|
||||
</View>
|
||||
<View style={{ flexDirection: 'row', alignItems: 'center', justifyContent: 'flex-end' }}>
|
||||
<View style={{ width: 100, flexDirection: 'row', justifyContent: 'space-between' }}>
|
||||
<View style={{ width: 150, flexDirection: 'row', justifyContent: 'flex-end' }}>
|
||||
<TouchableOpacity onPress={onClose}>
|
||||
<Text style={{ color: palette.accentColor }}>Cancel</Text>
|
||||
<Text style={theme.modalButton}>CANCEL</Text>
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity onPress={applyChanges}>
|
||||
<Text style={{ color: palette.accentColor }}>Apply</Text>
|
||||
<Text style={theme.modalButton}>APPLY</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ const contentTileStyle = {
|
|||
};
|
||||
|
||||
type Props = {
|
||||
foodItem: FoodItemRecord,
|
||||
foodItem: ?FoodItemRecord,
|
||||
place: PlaceRecord,
|
||||
currentImage: number,
|
||||
quantityModalOpen: boolean,
|
||||
|
|
@ -196,12 +196,18 @@ export default compose(
|
|||
withState('imagesLoading', 'setImagesLoading', true),
|
||||
withHandlers({
|
||||
addPhoto: ({ addImage, foodItem, setImagesLoading }: Props) => async () => {
|
||||
if (!foodItem) {
|
||||
return;
|
||||
}
|
||||
const imageUri = await openImagePicker();
|
||||
setImagesLoading(true);
|
||||
await addImage({ foodItemId: foodItem.id, imageUri });
|
||||
setImagesLoading(false);
|
||||
},
|
||||
updateAmount: ({ updateQuantity, foodItem }: Props) => (quantity: Quantity) => {
|
||||
if (!foodItem) {
|
||||
return;
|
||||
}
|
||||
updateQuantity({ foodItemId: foodItem.id, quantity });
|
||||
},
|
||||
toggleQuantityModal: ({ quantityModalOpen, setQuantityModalOpen }) => () => {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,9 @@ import { routeWithTitle } from '../helpers/RouteHelpers';
|
|||
import FoodItemList from '../components/FoodItemList';
|
||||
import typeof FoodItemRecord from '../records/FoodItemRecord';
|
||||
import FilterModal from '../modals/FilterModal';
|
||||
import { withFoodItemsAsSeq } from '../enhancers/foodItemEnhancers';
|
||||
import { type SetSeq } from 'immutable';
|
||||
import Spinner from 'react-native-loading-spinner-overlay';
|
||||
|
||||
import theme from '../ui-theme';
|
||||
|
||||
|
|
@ -14,13 +17,14 @@ const renderFoodItem = (foodItem: FoodItemRecord) => (
|
|||
<FoodItemTile key={foodItem.id} foodItem={foodItem} />
|
||||
);
|
||||
|
||||
export default class FoodList extends Component {
|
||||
class FoodList extends Component {
|
||||
static displayName = 'FoodList';
|
||||
|
||||
props: {
|
||||
pushRoute: Object => void,
|
||||
isFilterModalOpen: boolean,
|
||||
toggleFilterModal: (val: boolean) => void,
|
||||
foodItemsSeq: SetSeq<FoodItemRecord>,
|
||||
};
|
||||
|
||||
addFoodItem = () => {
|
||||
|
|
@ -45,12 +49,13 @@ export default class FoodList extends Component {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { isFilterModalOpen } = this.props;
|
||||
const { isFilterModalOpen, foodItemsSeq } = this.props;
|
||||
|
||||
return (
|
||||
<View style={theme.page.container}>
|
||||
<Spinner visible={!foodItemsSeq} />
|
||||
<ScrollView>
|
||||
<FoodItemList renderFoodItem={renderFoodItem} />
|
||||
<FoodItemList foodItemsSeq={foodItemsSeq} renderFoodItem={renderFoodItem} />
|
||||
</ScrollView>
|
||||
<FilterModal isVisible={isFilterModalOpen} onClose={this.toggleFilterModal} />
|
||||
<ActionButton
|
||||
|
|
@ -66,3 +71,5 @@ export default class FoodList extends Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withFoodItemsAsSeq(FoodList);
|
||||
|
|
|
|||
40
js/pages/LandingPage.js
Normal file
40
js/pages/LandingPage.js
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
// @flow
|
||||
import React from 'react';
|
||||
import { View, Text, Image } from 'react-native';
|
||||
import atcCookieImage from '../../static/atc-cookie-logo.png';
|
||||
import { Link } from 'react-router-native';
|
||||
import RouterButton from 'react-router-native-button';
|
||||
|
||||
import theme from '../ui-theme';
|
||||
|
||||
const LandingPage = () => {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
padding: 20,
|
||||
justifyContent: 'space-between',
|
||||
flex: 1,
|
||||
height: '100%',
|
||||
}}>
|
||||
<Image source={atcCookieImage} style={{ height: 275 }} resizeMode="contain" />
|
||||
<Text style={{ fontSize: 24, textAlign: 'center' }}>
|
||||
We need to use your location to bring you the best experience possible.
|
||||
</Text>
|
||||
<View style={{ width: 275 }}>
|
||||
<RouterButton
|
||||
to="/list/food?positionBy=location"
|
||||
replace
|
||||
title="OK, fine"
|
||||
color={theme.palette.primaryColor}
|
||||
/>
|
||||
</View>
|
||||
<Link to="/list/food?positionBy=zip" replace>
|
||||
<Text style={{ color: theme.palette.primaryColor, padding: 20 }}>I'd rather not.</Text>
|
||||
</Link>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default LandingPage;
|
||||
|
|
@ -5,10 +5,16 @@ import Places from './Places';
|
|||
import ScrollableTabView from 'react-native-scrollable-tab-view';
|
||||
import theme from '../ui-theme.js';
|
||||
import { type RoutingContextFlowTypes, routingContextPropTypes } from '../routes';
|
||||
import { getSearch } from '../helpers/RouteHelpers';
|
||||
import { getCurrentPosition, getPositionFromZip } from '../apis/PositionApi';
|
||||
|
||||
const tabs = ['food', 'places'];
|
||||
|
||||
const getTabIndex = ({ match: { params: { type = '' } } }: RoutingContextFlowTypes): number => {
|
||||
const getTabIndex = ({
|
||||
match: {
|
||||
params: { type = '' },
|
||||
},
|
||||
}: RoutingContextFlowTypes): number => {
|
||||
return tabs.indexOf(type) || 0;
|
||||
};
|
||||
|
||||
|
|
@ -28,6 +34,17 @@ class List extends Component {
|
|||
|
||||
tabView: TabView;
|
||||
|
||||
// TODO convert this component to SFC using recompose
|
||||
// also, figure out a less lifecyle-y way to do this request
|
||||
componentDidMount() {
|
||||
const { positionBy = 'zip' } = getSearch(this.context);
|
||||
if (positionBy === 'location') {
|
||||
getCurrentPosition();
|
||||
} else {
|
||||
getPositionFromZip();
|
||||
}
|
||||
}
|
||||
|
||||
updateTabRoute = ({ i }: { i: number }) => {
|
||||
const currentTab = getTabIndex(this.props);
|
||||
|
||||
|
|
@ -68,8 +85,7 @@ class List extends Component {
|
|||
tabBarInactiveTextColor={theme.topTabs.selectedTextColor}
|
||||
prerenderingSiblingsNumber={Infinity}
|
||||
onChangeTab={this.updateTabRoute}
|
||||
initialPage={getTabIndex(this.props)}
|
||||
>
|
||||
initialPage={getTabIndex(this.props)}>
|
||||
<Food tabLabel="FOOD" />
|
||||
<Places tabLabel="PLACES" />
|
||||
</ScrollableTabView>
|
||||
|
|
|
|||
|
|
@ -27,34 +27,51 @@ const manualUpdate$ = foodItemSubject.scan(
|
|||
Map()
|
||||
);
|
||||
|
||||
const fetchedFoodItems$ = location$
|
||||
.combineLatest(Filter$)
|
||||
.mergeMap(([loc, filter]: [Position, FilterRecord]) => getFoodItems({ loc, filter }))
|
||||
.map(({ fooditems = [] }: FoodItemsForLocation) => {
|
||||
const fetchedFoodItems$ = Filter$.combineLatest(location$)
|
||||
.mergeMap(([filter, loc]: [Position, FilterRecord]) => {
|
||||
if (loc) {
|
||||
return getFoodItems({ loc, filter });
|
||||
}
|
||||
return Promise.resolve({});
|
||||
})
|
||||
.map(({ fooditems }: FoodItemsForLocation) => {
|
||||
if (fooditems) {
|
||||
return fooditems.map(createFoodItem).reduce(setById, new Map());
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
export default fetchedFoodItems$
|
||||
.combineLatest(manualUpdate$, (foodItemMap: Map<string, FoodItemRecord>, manualUpdates) => {
|
||||
if (foodItemMap) {
|
||||
return foodItemMap.mergeDeep(manualUpdates);
|
||||
}
|
||||
})
|
||||
.combineLatest(
|
||||
Quantity$,
|
||||
(
|
||||
foodItems: Map<string, FoodItemRecord>,
|
||||
foodItems: ?Map<string, FoodItemRecord>,
|
||||
quantitiesFromStream: Map<string, QuantityFragment>
|
||||
) => {
|
||||
if (foodItems) {
|
||||
return foodItems.mergeDeepWith(
|
||||
(foodItem, foodItemQuantities) => foodItem.merge(foodItemQuantities),
|
||||
quantitiesFromStream
|
||||
);
|
||||
}
|
||||
}
|
||||
)
|
||||
.combineLatest(
|
||||
Image$,
|
||||
(foodItems: Map<string, FoodItemRecord>, latestFromImages$: Map<string, ImageFragment>) =>
|
||||
// $FlowFixMe this type is incompatible with the expected param type of object type
|
||||
foodItems.mergeDeepWith((foodItem: FoodItemRecord, imageFragment: ImageFragment) => {
|
||||
(foodItems: ?Map<string, FoodItemRecord>, latestFromImages$: Map<string, ImageFragment>) => {
|
||||
if (foodItems) {
|
||||
return foodItems.mergeDeepWith(
|
||||
// $FlowFixMe
|
||||
(foodItem: FoodItemRecord, imageFragment: ImageFragment) => {
|
||||
return foodItem.set('images', imageFragment.images);
|
||||
}, latestFromImages$)
|
||||
},
|
||||
latestFromImages$
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,16 +1,12 @@
|
|||
// @flow
|
||||
import { Observable, type Observer } from 'rxjs';
|
||||
import { ReplaySubject } from 'rxjs';
|
||||
|
||||
export default Observable.create((obs: Observer<Position>): Observable<Position> => {
|
||||
// $FlowFixMe: property maximumAge not found on object literal
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(pos: Position) => obs.next(pos),
|
||||
err => {
|
||||
throw err;
|
||||
},
|
||||
{
|
||||
enableHighAccuracy: false,
|
||||
timeout: 2000,
|
||||
const multicaster: ReplaySubject<Position> = new ReplaySubject();
|
||||
|
||||
export function emitter(val: ?Position) {
|
||||
multicaster.next(val);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
emitter(null);
|
||||
|
||||
export default multicaster;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ const safeGetPlaceDetails = memoize((placeId: string): Promise<?GooglePlaceObj>
|
|||
});
|
||||
|
||||
const Places$ = foodItems$
|
||||
.mergeMap((foodItems: Map<string, FoodItemRecord>): Observable<string> => {
|
||||
.mergeMap((foodItems: Map<string, FoodItemRecord> = Map()): Observable<string> => {
|
||||
return Observable.from(foodItems.toArray().map(foodItem => foodItem.placeId));
|
||||
})
|
||||
.distinct()
|
||||
|
|
|
|||
|
|
@ -7,15 +7,18 @@ import { COLOR } from 'react-native-material-ui';
|
|||
|
||||
export const primaryColor = '#6d5354';
|
||||
|
||||
export default {
|
||||
palette: {
|
||||
export const palette = {
|
||||
primaryColor,
|
||||
accentColor: '#0E6E9E',
|
||||
disabledColor: COLOR.grey500,
|
||||
facebook: '#3B5998',
|
||||
google: '#DB4437',
|
||||
errorColor: '#B92D00',
|
||||
},
|
||||
};
|
||||
|
||||
export default {
|
||||
statusBarColor: '#412A2B',
|
||||
palette: palette,
|
||||
toolbar: {
|
||||
titleText: { color: COLOR.white },
|
||||
leftElement: { color: COLOR.white },
|
||||
|
|
@ -26,6 +29,11 @@ export default {
|
|||
elevation: 0,
|
||||
},
|
||||
},
|
||||
actionButton: {
|
||||
speedDialActionIcon: {
|
||||
backgroundColor: '#48A0CC',
|
||||
},
|
||||
},
|
||||
checkbox: {
|
||||
icon: {
|
||||
color: '#0E6E9E',
|
||||
|
|
@ -45,14 +53,29 @@ export default {
|
|||
selectedTextColor: 'rgba(255, 255, 255, 0.7)',
|
||||
backgroundColor: primaryColor,
|
||||
},
|
||||
modalButton: {
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
color: palette.accentColor,
|
||||
paddingLeft: 30,
|
||||
},
|
||||
modalDropDown: {
|
||||
/* fontSize: 16,
|
||||
fontWeight: 'bold',
|
||||
color: 'black', */
|
||||
height: 40,
|
||||
width: 150,
|
||||
},
|
||||
itemTile: {
|
||||
thumbnailSize: 50,
|
||||
thumbnailColor: COLOR.grey500,
|
||||
thumbnailColor: COLOR.grey400,
|
||||
itemNameStyle: {
|
||||
fontSize: 16,
|
||||
color: COLOR.black,
|
||||
},
|
||||
itemPlaceStyle: {
|
||||
color: COLOR.grey500,
|
||||
color: COLOR.grey700,
|
||||
paddingTop: 3,
|
||||
},
|
||||
availableCountStyle: {
|
||||
fontSize: 25,
|
||||
|
|
|
|||
|
|
@ -1,12 +1,17 @@
|
|||
{
|
||||
"name": "aretherecookies",
|
||||
<<<<<<< HEAD
|
||||
"version": "1.6.0",
|
||||
=======
|
||||
"version": "1.0.1",
|
||||
>>>>>>> master
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"start": "node node_modules/react-native/local-cli/cli.js start",
|
||||
"test": "jest",
|
||||
"lint": "flow && eslint js",
|
||||
"android": "react-native run-android"
|
||||
"android:dev": "react-native run-android && react-native start",
|
||||
"android:release": "cd android && ./gradlew assembleRelease"
|
||||
},
|
||||
"dependencies": {
|
||||
"babel-preset-es2015": "^6.24.0",
|
||||
|
|
|
|||
BIN
static/atc-cookie-logo.png
Normal file
BIN
static/atc-cookie-logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 216 KiB |
Loading…
Add table
Reference in a new issue