Commit 44dff6e4 authored by Milan DiGiuseppe's avatar Milan DiGiuseppe

GeoMarker, cache modal, dev modal, theme

parent 5b3095a4
...@@ -36,11 +36,14 @@ ...@@ -36,11 +36,14 @@
"react/jsx-props-no-spreading": "off", "react/jsx-props-no-spreading": "off",
"react/destructuring-assignment": "off", "react/destructuring-assignment": "off",
"react/prop-types": "off", "react/prop-types": "off",
"react/jsx-curly-brace-presence": ["error", { "props": "always" }],
// "react-hooks/exhaustive-deps": "error", // "react-hooks/exhaustive-deps": "error",
"@typescript-eslint/indent": [2, 2], "@typescript-eslint/indent": [2, 2],
"@typescript-eslint/explicit-function-return-type": "off", "@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/no-non-null-assertion": "off",
"@typescript-eslint/no-unused-vars": "error", "@typescript-eslint/no-unused-vars": "error",
"import/extensions": [0, {}], "import/extensions": [0, {}],
"import/prefer-default-export": "off",
"import/no-extraneous-dependencies": [ "import/no-extraneous-dependencies": [
2, 2,
{ "devDependencies": ["**/test.tsx", "**/test.ts"] } { "devDependencies": ["**/test.tsx", "**/test.ts"] }
......
import React from 'react'; import React from 'react';
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
import { Icon } from 'react-native-elements' import { Icon } from 'react-native-elements';
import { createAppContainer } from 'react-navigation'; import { createAppContainer } from 'react-navigation';
import { createBottomTabNavigator } from 'react-navigation-tabs'; import { createBottomTabNavigator } from 'react-navigation-tabs';
import { configureStore } from "@reduxjs/toolkit"; import { configureStore } from '@reduxjs/toolkit';
import DiscoverScreen from './src/screens/DiscoverScreen'; import DiscoverScreen from './src/screens/DiscoverScreen';
import MessageCreateScreen from './src/screens/MessageCreateScreen'; import MessageCreateScreen from './src/screens/MessageCreateScreen';
import SavedMessagesScreen from './src/screens/SavedMessagesScreen'; import SavedMessagesScreen from './src/screens/SavedMessagesScreen';
...@@ -13,8 +13,8 @@ import RootReducer from './src/store/RootReducer'; ...@@ -13,8 +13,8 @@ import RootReducer from './src/store/RootReducer';
const TabNavigator = createBottomTabNavigator( const TabNavigator = createBottomTabNavigator(
{ {
Saved: SavedMessagesScreen,
Discover: DiscoverScreen, Discover: DiscoverScreen,
Saved: SavedMessagesScreen,
Create: MessageCreateScreen, Create: MessageCreateScreen,
}, },
{ {
...@@ -25,15 +25,15 @@ const TabNavigator = createBottomTabNavigator( ...@@ -25,15 +25,15 @@ const TabNavigator = createBottomTabNavigator(
if (routeName === 'Saved') { if (routeName === 'Saved') {
iconName = 'bookmark'; iconName = 'bookmark';
} else if (routeName === 'Discover') { } else if (routeName === 'Discover') {
iconName = `compass`; iconName = 'compass';
} else if (routeName === 'Create') { } else if (routeName === 'Create') {
iconName = `pencil`; iconName = 'pencil';
} }
return ( return (
<Icon <Icon
name={iconName} name={iconName}
type='font-awesome' type="font-awesome"
color={theme.colors.green} color={theme.colors.green}
/> />
); );
......
import React from 'react'; import React from 'react';
import { StyleSheet, Text, View } from 'react-native'; import { StyleSheet, View } from 'react-native';
import { Overlay, Button } from 'react-native-elements'; import { Overlay, Button } from 'react-native-elements';
import theme from '../theme'; import theme from '../theme';
import SimpleHeader from './SimpleHeader';
import GeoText from './GeoText'; import GeoText from './GeoText';
import { Cache } from '../store/caches/CachesState';
const { colors } = theme;
interface Props {
show: boolean;
user: string;
content: string;
onSave: () => void;
onReport: () => void;
}
const styles = StyleSheet.create({ const styles = StyleSheet.create({
overlay: {
borderRadius: 8,
borderColor: theme.colors.blue,
borderWidth: 1,
borderStyle: 'solid',
paddingVertical: theme.spacing.small,
paddingHorizontal: theme.spacing.medium,
width: '75%',
height: '40%',
},
buttonContainer: { buttonContainer: {
flex: 1, flex: 1,
}, },
...@@ -24,40 +25,46 @@ const styles = StyleSheet.create({ ...@@ -24,40 +25,46 @@ const styles = StyleSheet.create({
}, },
}); });
const MessageFoundModal: React.FC<Props> = ({ show, user, content, onSave, onReport }) => ( interface Props {
show: boolean;
cache: Cache;
onClose: () => void;
onSave: () => void;
onReport: () => void;
}
const MessageFoundModal: React.FC<Props> = ({
show, cache, onClose, onSave, onReport,
}) => (
<Overlay <Overlay
isVisible={show} isVisible={show}
overlayStyle={{ padding: 0 }} overlayStyle={styles.overlay}
onBackdropPress={onClose}
> >
<> {show ? (
<SimpleHeader title="Found Cache!" />
<View style={{ flex: 1, justifyContent: 'space-between' }}> <View style={{ flex: 1, justifyContent: 'space-between' }}>
<View style={{ padding: 10 }}> <View>
<GeoText text={user} variant={'textBold'} /> <GeoText text={cache.name} variant={'formHeader'} />
<GeoText text={content} /> <GeoText text={cache.message} style={{ marginTop: theme.spacing.medium }} />
</View> </View>
<View style={{ flexDirection: 'row' }}> <View style={{ flexDirection: 'row' }}>
<Button <Button
title="REPORT" title={'Report'}
containerStyle={styles.buttonContainer} type={'outline'}
buttonStyle={[ containerStyle={[styles.buttonContainer, { marginRight: theme.spacing.small }]}
styles.button, buttonStyle={styles.button}
{ backgroundColor: colors.red }
]}
onPress={onReport} onPress={onReport}
/> />
<Button <Button
title="SAVE" title={'Save'}
type={'solid'}
containerStyle={styles.buttonContainer} containerStyle={styles.buttonContainer}
buttonStyle={[ buttonStyle={styles.button}
styles.button,
{ backgroundColor: colors.green }
]}
onPress={onSave} onPress={onSave}
/> />
</View> </View>
</View> </View>
</> ) : <View />}
</Overlay> </Overlay>
); );
......
import React, { useCallback } from 'react';
import { StyleSheet, View } from 'react-native';
import { Overlay, Button } from 'react-native-elements';
import { useDispatch, useSelector } from 'react-redux';
import { geoSearch } from '../store/caches/CachesActions';
import { getUserLatLng } from '../store/location/LocationSelectors';
const styles = StyleSheet.create({
overlay: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
button: {
width: '100%',
},
});
interface Props {
isOpen: boolean;
onClose: () => void;
}
const DevModal: React.FC<Props> = ({ isOpen, onClose }) => {
const dispatch = useDispatch();
const userLocation = useSelector(getUserLatLng);
const geosearch = useCallback(() => {
dispatch(geoSearch(userLocation, '1'));
onClose();
}, [onClose, dispatch, userLocation]);
return (
<Overlay isVisible={isOpen} fullScreen>
<View style={styles.overlay}>
<Button
title={'geosearch'}
onPress={geosearch}
type={'solid'}
containerStyle={styles.button}
/>
<Button
title={'Close'}
onPress={onClose}
type={'outline'}
containerStyle={styles.button}
/>
</View>
</Overlay>
);
};
export default DevModal;
import React, { useCallback } from 'react';
import { Callout, Marker } from 'react-native-maps';
import { Cache } from '../store/caches/CachesState';
import GeoText from './GeoText';
import theme from '../theme';
interface Props {
cache: Cache;
onPress: (cache: Cache) => void;
}
const GeoMarker: React.FC<Props> = ({ cache, onPress }) => {
const handlePress = useCallback(() => onPress(cache), [onPress, cache]);
return (
<Marker
key={cache.id}
coordinate={{ latitude: cache.lat, longitude: cache.lng }}
>
<Callout
onPress={handlePress}
tooltip={false}
style={{ width: 200, padding: theme.spacing.small }}
>
<GeoText
text={cache.message}
variant={'text'}
style={{ flex: 1, flexWrap: 'wrap' }}
numberOfLines={3}
/>
</Callout>
</Marker>
);
};
export default GeoMarker;
import React from 'react'; import React from 'react';
import { Text, TextStyle, StyleSheet } from 'react-native'; import {
Text, TextStyle, StyleSheet, TextProps,
} from 'react-native';
import theme, { Color } from '../theme';
type Variant = 'header' | 'formHeader' | 'textBold' | 'text' | 'textWeak'; type Variant = 'header' | 'formHeader' | 'textBold' | 'text' | 'textWeak';
...@@ -13,7 +16,7 @@ const fontWeights = { ...@@ -13,7 +16,7 @@ const fontWeights = {
bold: '700', bold: '700',
normal: '500', normal: '500',
weak: '200', weak: '200',
}; } as const;
const styles = StyleSheet.create({ const styles = StyleSheet.create({
header: { header: {
...@@ -21,7 +24,7 @@ const styles = StyleSheet.create({ ...@@ -21,7 +24,7 @@ const styles = StyleSheet.create({
}, },
formHeader: { formHeader: {
fontSize: fontSizes.h3, fontSize: fontSizes.h3,
// fontWeight: fontWeights.bold, fontWeight: fontWeights.bold,
}, },
textBold: { textBold: {
fontSize: fontSizes.text, fontSize: fontSizes.text,
...@@ -34,17 +37,25 @@ const styles = StyleSheet.create({ ...@@ -34,17 +37,25 @@ const styles = StyleSheet.create({
textWeak: { textWeak: {
fontSize: fontSizes.text, fontSize: fontSizes.text,
fontWeight: fontWeights.weak, fontWeight: fontWeights.weak,
} },
}); });
interface Props { interface Props {
text: string; text: string;
style?: TextStyle;
variant?: Variant; variant?: Variant;
color?: Color;
style?: TextStyle;
} }
const GeoText: React.FC<Props> = ({ text, style, variant = 'text' }) => ( const GeoText: React.FC<Props & TextProps> = ({
<Text style={[styles[variant], style]}>{text}</Text> text, variant = 'text', color = theme.colors.black, style, ...textProps
}) => (
<Text
style={[styles[variant], style, { color }]}
{...textProps}
>
{text}
</Text>
); );
export default GeoText; export default GeoText;
...@@ -31,9 +31,9 @@ const SavedMessage: React.FC<Props> = ({ ...@@ -31,9 +31,9 @@ const SavedMessage: React.FC<Props> = ({
}) => ( }) => (
<View style={styles.container}> <View style={styles.container}>
<Text> <Text>
<GeoText variant="textBold" text={`${name} `} /> <GeoText variant={'textBold'} text={`${name} `} />
<GeoText variant="text" text={`${message} `} /> <GeoText variant={'text'} text={`${message} `} />
<GeoText variant="textWeak" text={moment(found).fromNow()} /> <GeoText variant={'textWeak'} text={moment(found).fromNow()} />
</Text> </Text>
</View> </View>
); );
......
import React, { useState } from 'react';
import { Header } from 'react-native-elements';
import theme from '../theme';
import DevModal from './DevModal';
const { colors, spacing } = theme;
interface Props {
title: string;
}
const ScreenHeader: React.FC<Props> = ({ title }) => {
const [devModalOpen, setDevModalOpen] = useState(false);
return (
<>
<Header
leftContainerStyle={{ display: 'none' }}
centerComponent={{
text: title,
style: {
fontSize: 32,
color: colors.white,
width: '100%',
},
}}
rightComponent={{
text: 'dev',
onPress: () => setDevModalOpen(true),
style: {
color: colors.white,
},
}}
containerStyle={{
paddingHorizontal: spacing.screenPadding,
backgroundColor: colors.green,
}}
/>
<DevModal isOpen={devModalOpen} onClose={() => setDevModalOpen(false)} />
</>
);
};
export default ScreenHeader;
import React from 'react';
import { Header } from 'react-native-elements';
import theme from '../theme';
const { colors, spacing } = theme;
interface Props {
title: string;
}
const SimpleHeader: React.FC<Props> = ({ title }) => (
<Header
leftContainerStyle={{ display: 'none' }}
centerComponent={{
text: title,
style: {
fontSize: 32,
color: colors.white,
width: '100%',
},
}}
containerStyle={{
paddingHorizontal: spacing.screenPadding,
backgroundColor: colors.green,
}}
/>
);
export default SimpleHeader;
import React, { ReactElement, useCallback } from 'react'; import React, { ReactElement, useCallback } from 'react';
import { import {
StyleSheet, StyleSheet,
Text,
TouchableOpacity, TouchableOpacity,
View, View,
ListRenderItemInfo, ListRenderItemInfo,
...@@ -9,6 +8,7 @@ import { ...@@ -9,6 +8,7 @@ import {
} from 'react-native'; } from 'react-native';
import { SwipeListView } from 'react-native-swipe-list-view'; import { SwipeListView } from 'react-native-swipe-list-view';
import theme from '../theme'; import theme from '../theme';
import GeoText from './GeoText';
const DELETE_BTN_WIDTH = 75; const DELETE_BTN_WIDTH = 75;
...@@ -37,7 +37,7 @@ const RowItemHidden = <T extends unknown>({ item, onDelete }: RowItemHiddenProps ...@@ -37,7 +37,7 @@ const RowItemHidden = <T extends unknown>({ item, onDelete }: RowItemHiddenProps
style={styles.backRightBtn} style={styles.backRightBtn}
onPress={() => onDelete(item)} onPress={() => onDelete(item)}
> >
<Text style={{ color: theme.colors.white }}>Delete</Text> <GeoText text={'Delete'} color={'white'} variant={'text'} />
</TouchableOpacity> </TouchableOpacity>
</View> </View>
); );
......
import React, { useState, useCallback, useEffect } from 'react'; import React, { useState, useCallback, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux'; import { useSelector, useDispatch } from 'react-redux';
import { StyleSheet, View } from 'react-native'; import { StyleSheet, View } from 'react-native';
import theme from '../theme'; import MapView from 'react-native-maps';
import MessageFoundModal from '../components/MessageFoundModal'; import CacheFoundModal from '../components/CacheFoundModal';
import { Cache, ReportRequest } from '../store/caches/CachesState'; import { Cache } from '../store/caches/CachesState';
import { getDiscoveredCaches } from '../store/caches/CachesSelectors'; import { getDiscoveredCaches } from '../store/caches/CachesSelectors';
import { getUserLatLng } from '../store/location/LocationSelectors'; import { getUserLatLng } from '../store/location/LocationSelectors';
import { saveCache, reportCache, geoSearch } from '../store/caches/CachesActions'; import { saveCache, reportCache, geoSearch } from '../store/caches/CachesActions';
import MapView, { Marker, LatLng, EventUserLocation } from 'react-native-maps'; import ScreenHeader from '../components/ScreenHeader';
import SimpleHeader from '../components/SimpleHeader'; import GeoMarker from '../components/GeoMarker';
import { sampleCaches } from '../SampleCaches';
const styles = StyleSheet.create({ const styles = StyleSheet.create({
background: { background: {
...@@ -23,59 +24,65 @@ const styles = StyleSheet.create({ ...@@ -23,59 +24,65 @@ const styles = StyleSheet.create({
const DiscoverScreen: React.FC = () => { const DiscoverScreen: React.FC = () => {
const dispatch = useDispatch(); const dispatch = useDispatch();
const discoveredCaches: Cache[] = useSelector(getDiscoveredCaches); const discoveredCaches: Cache[] = useSelector(getDiscoveredCaches);
const [isCacheModalOpen, setCacheModalOpen] = useState(false);
const [openCache, setOpenCache] = useState<Cache | null>(sampleCaches[0]);
// const currMsg = (discoveredCaches.length > 0) ? discoveredCaches[0] : null; const onClose = useCallback(() => {
// const [showOverlay, setShowOverlay] = useState(true); setCacheModalOpen(false);
setOpenCache(null);
}, [setCacheModalOpen, setOpenCache]);
const onSave = useCallback((cache: Cache) => {
dispatch(saveCache({ ...cache, found: new Date().toISOString() }));
onClose();
}, [dispatch, onClose]);
const onReport = useCallback((id) => {
dispatch(reportCache(id, '1', 'reported'));
onClose();
}, [dispatch, onClose]);
// const onSave = useCallback(() => { const onCalloutPress = useCallback((cache: Cache) => {
// setShowOverlay(false); setOpenCache(cache);
// dispatch(saveCache({ ...currMsg!, found: new Date().toISOString() })); setCacheModalOpen(true);
// setTimeout(() => setShowOverlay(true), 500) }, []);
// }, [setShowOverlay, currMsg]);
// const onReport = useCallback(() => {
// setShowOverlay(false);
// dispatch(reportCache(currMsg.id, 1, 'reported'));
// setTimeout(() => setShowOverlay(true), 500);
// }, []);
const [userLocation, setUserLocation] = useState<LatLng>(useSelector(getUserLatLng));
const onUserLocationChange = useCallback((e: EventUserLocation) => {
const { latitude, longitude } = e.nativeEvent.coordinate;
setUserLocation({ latitude, longitude });
}, [setUserLocation]);
const userLocation = useSelector(getUserLatLng);
// const onUserLocationChange = useCallback((e: EventUserLocation) => {
// const { latitude, longitude } = e.nativeEvent.coordinate;
// setUserLocation({ latitude, longitude });
// }, [setUserLocation]);
useEffect(() => { useEffect(() => {
dispatch(geoSearch(userLocation.latitude, userLocation.longitude, '1')); dispatch(geoSearch(userLocation, '1'));
}, []); }, []); // TODO: update deps
return ( return (
<> <>
<SimpleHeader title={'Discover'} /> <ScreenHeader title={'Discover'} />
<View style={{ flex: 1 }}> <View style={{ flex: 1 }}>
<CacheFoundModal
show={isCacheModalOpen}
cache={openCache!}
onClose={onClose}
onSave={() => onSave(openCache!)}
onReport={() => onReport(openCache!.id)}
/>
<MapView <MapView
provider={'google'} provider={'google'}
style={styles.map} style={styles.map}
showsUserLocation={true} showsUserLocation
region={{ region={{
latitude: userLocation.latitude, latitude: userLocation.latitude,
longitude: userLocation.longitude, longitude: userLocation.longitude,
latitudeDelta: 0.0100, latitudeDelta: 0.0100,
longitudeDelta: 0.0025, longitudeDelta: 0.0025,