forked from cemaden-educacao/WPD-MobileApp
Daniel D'Angelo Resende Barros
4 years ago
48 changed files with 9828 additions and 104 deletions
-
1src/.gitignore
-
14src/App.js
-
BINsrc/app/assets/chuva_peq.png
-
BINsrc/app/assets/ddangelorb.png
-
BINsrc/app/assets/defesa_civil.png
-
BINsrc/app/assets/pontos_alagamento_peq.png
-
BINsrc/app/assets/previsao_tempo.png
-
35src/app/components/Button.js
-
35src/app/components/CategoryPickerItem.js
-
27src/app/components/Icon.js
-
78src/app/components/ImageInput.js
-
40src/app/components/ImageInputList.js
-
98src/app/components/Picker.js
-
20src/app/components/PickerItem.js
-
23src/app/components/Screen.js
-
14src/app/components/Text.js
-
40src/app/components/TextInput.js
-
16src/app/components/forms/ErrorMessage.js
-
16src/app/components/forms/Form.js
-
23src/app/components/forms/FormField.js
-
34src/app/components/forms/FormImagePicker.js
-
33src/app/components/forms/FormPicker.js
-
12src/app/components/forms/SubmitButton.js
-
5src/app/components/forms/index.js
-
69src/app/components/lists/ListItem.js
-
30src/app/components/lists/ListItemDeleteAction.js
-
18src/app/components/lists/ListItemSeparator.js
-
3src/app/components/lists/index.js
-
2src/app/config/colors.js
-
12src/app/config/styles.js
-
25src/app/hooks/useLocation.js
-
14src/app/navigation/AccountNavigator.js
-
77src/app/navigation/AppNavigator.js
-
13src/app/navigation/FeedNavigator.js
-
13src/app/navigation/MessagesNavigator.js
-
35src/app/navigation/NewListingButton.js
-
13src/app/navigation/OfficialMessagesNavigator.js
-
11src/app/navigation/navigationTheme.js
-
9src/app/navigation/routes.js
-
36src/app/screens/AccountScreen.js
-
63src/app/screens/MapFeedScreen.js
-
70src/app/screens/MessagesScreen.js
-
29src/app/screens/OfficialMessagesScreen.js
-
109src/app/screens/SharingDataScreen.js
-
53src/app/screens/ViewImageScreen.js
-
7956src/package-lock.json
-
20src/package.json
-
688src/yarn.lock
@ -0,0 +1 @@ |
|||||
|
node_modules |
@ -1,7 +1,13 @@ |
|||||
import React from 'react'; |
|
||||
|
import React from "react"; |
||||
|
import { NavigationContainer } from "@react-navigation/native"; |
||||
|
|
||||
import ViewImageScreen from "./app/screens/ViewImageScreen"; |
|
||||
|
import navigationTheme from "./app/navigation/navigationTheme"; |
||||
|
import AppNavigator from "./app/navigation/AppNavigator"; |
||||
|
|
||||
export default function App() { |
export default function App() { |
||||
return <ViewImageScreen />; |
|
||||
} |
|
||||
|
return ( |
||||
|
<NavigationContainer theme={navigationTheme}> |
||||
|
<AppNavigator /> |
||||
|
</NavigationContainer> |
||||
|
); |
||||
|
} |
After Width: 50 | Height: 51 | Size: 8.4 KiB |
After Width: 700 | Height: 727 | Size: 793 KiB |
After Width: 225 | Height: 225 | Size: 5.9 KiB |
After Width: 50 | Height: 49 | Size: 8.4 KiB |
After Width: 714 | Height: 924 | Size: 56 KiB |
@ -0,0 +1,35 @@ |
|||||
|
import React from "react"; |
||||
|
import { StyleSheet, Text, TouchableOpacity } from "react-native"; |
||||
|
|
||||
|
import colors from "../config/colors"; |
||||
|
|
||||
|
function AppButton({ title, onPress, color = "primary" }) { |
||||
|
return ( |
||||
|
<TouchableOpacity |
||||
|
style={[styles.button, { backgroundColor: colors[color] }]} |
||||
|
onPress={onPress} |
||||
|
> |
||||
|
<Text style={styles.text}>{title}</Text> |
||||
|
</TouchableOpacity> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
button: { |
||||
|
backgroundColor: colors.primary, |
||||
|
borderRadius: 25, |
||||
|
justifyContent: "center", |
||||
|
alignItems: "center", |
||||
|
padding: 15, |
||||
|
width: "100%", |
||||
|
marginVertical: 10, |
||||
|
}, |
||||
|
text: { |
||||
|
color: colors.white, |
||||
|
fontSize: 18, |
||||
|
textTransform: "uppercase", |
||||
|
fontWeight: "bold", |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default AppButton; |
@ -0,0 +1,35 @@ |
|||||
|
import React from "react"; |
||||
|
import { View, StyleSheet, TouchableOpacity } from "react-native"; |
||||
|
|
||||
|
import Icon from "./Icon"; |
||||
|
import Text from "./Text"; |
||||
|
|
||||
|
function CategoryPickerItem({ item, onPress }) { |
||||
|
return ( |
||||
|
<View style={styles.container}> |
||||
|
<TouchableOpacity onPress={onPress}> |
||||
|
<Icon |
||||
|
backgroundColor={item.backgroundColor} |
||||
|
name={item.icon} |
||||
|
size={120} |
||||
|
/> |
||||
|
</TouchableOpacity> |
||||
|
<Text style={styles.label}>{item.label}</Text> |
||||
|
</View> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
paddingHorizontal: 30, |
||||
|
paddingVertical: 15, |
||||
|
alignItems: "center", |
||||
|
width: "50%", |
||||
|
}, |
||||
|
label: { |
||||
|
marginTop: 5, |
||||
|
textAlign: "center", |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default CategoryPickerItem; |
@ -0,0 +1,27 @@ |
|||||
|
import React from "react"; |
||||
|
import { View } from "react-native"; |
||||
|
import { MaterialCommunityIcons } from "@expo/vector-icons"; |
||||
|
|
||||
|
function Icon({ |
||||
|
name, |
||||
|
size = 40, |
||||
|
backgroundColor = "#000", |
||||
|
iconColor = "#fff", |
||||
|
}) { |
||||
|
return ( |
||||
|
<View |
||||
|
style={{ |
||||
|
width: size, |
||||
|
height: size, |
||||
|
borderRadius: size / 2, |
||||
|
backgroundColor, |
||||
|
justifyContent: "center", |
||||
|
alignItems: "center", |
||||
|
}} |
||||
|
> |
||||
|
<MaterialCommunityIcons name={name} color={iconColor} size={size * 0.5} /> |
||||
|
</View> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
export default Icon; |
@ -0,0 +1,78 @@ |
|||||
|
import React, { useEffect } from "react"; |
||||
|
import { |
||||
|
View, |
||||
|
StyleSheet, |
||||
|
Image, |
||||
|
TouchableWithoutFeedback, |
||||
|
Alert, |
||||
|
} from "react-native"; |
||||
|
import { MaterialCommunityIcons } from "@expo/vector-icons"; |
||||
|
import * as ImagePicker from "expo-image-picker"; |
||||
|
|
||||
|
import colors from "../config/colors"; |
||||
|
|
||||
|
function ImageInput({ imageUri, onChangeImage }) { |
||||
|
useEffect(() => { |
||||
|
requestPermission(); |
||||
|
}, []); |
||||
|
|
||||
|
const requestPermission = async () => { |
||||
|
const { granted } = await ImagePicker.requestCameraRollPermissionsAsync(); |
||||
|
if (!granted) alert("You need to enable permission to access the library."); |
||||
|
}; |
||||
|
|
||||
|
const handlePress = () => { |
||||
|
if (!imageUri) selectImage(); |
||||
|
else |
||||
|
Alert.alert("Delete", "Are you sure you want to delete this image?", [ |
||||
|
{ text: "Yes", onPress: () => onChangeImage(null) }, |
||||
|
{ text: "No" }, |
||||
|
]); |
||||
|
}; |
||||
|
|
||||
|
const selectImage = async () => { |
||||
|
try { |
||||
|
const result = await ImagePicker.launchImageLibraryAsync({ |
||||
|
mediaTypes: ImagePicker.MediaTypeOptions.Images, |
||||
|
quality: 0.5, |
||||
|
}); |
||||
|
if (!result.cancelled) onChangeImage(result.uri); |
||||
|
} catch (error) { |
||||
|
console.log("Error reading an image", error); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<TouchableWithoutFeedback onPress={handlePress}> |
||||
|
<View style={styles.container}> |
||||
|
{!imageUri && ( |
||||
|
<MaterialCommunityIcons |
||||
|
color={colors.medium} |
||||
|
name="camera" |
||||
|
size={40} |
||||
|
/> |
||||
|
)} |
||||
|
{imageUri && <Image source={{ uri: imageUri }} style={styles.image} />} |
||||
|
</View> |
||||
|
</TouchableWithoutFeedback> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
alignItems: "center", |
||||
|
backgroundColor: colors.light, |
||||
|
borderRadius: 15, |
||||
|
height: 100, |
||||
|
justifyContent: "center", |
||||
|
marginVertical: 10, |
||||
|
overflow: "hidden", |
||||
|
width: 100, |
||||
|
}, |
||||
|
image: { |
||||
|
height: "100%", |
||||
|
width: "100%", |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default ImageInput; |
@ -0,0 +1,40 @@ |
|||||
|
import React, { useRef } from "react"; |
||||
|
import { View, StyleSheet, ScrollView } from "react-native"; |
||||
|
import ImageInput from "./ImageInput"; |
||||
|
|
||||
|
function ImageInputList({ imageUris = [], onRemoveImage, onAddImage }) { |
||||
|
const scrollView = useRef(); |
||||
|
|
||||
|
return ( |
||||
|
<View> |
||||
|
<ScrollView |
||||
|
ref={scrollView} |
||||
|
horizontal |
||||
|
onContentSizeChange={() => scrollView.current.scrollToEnd()} |
||||
|
> |
||||
|
<View style={styles.container}> |
||||
|
{imageUris.map((uri) => ( |
||||
|
<View key={uri} style={styles.image}> |
||||
|
<ImageInput |
||||
|
imageUri={uri} |
||||
|
onChangeImage={() => onRemoveImage(uri)} |
||||
|
/> |
||||
|
</View> |
||||
|
))} |
||||
|
<ImageInput onChangeImage={(uri) => onAddImage(uri)} /> |
||||
|
</View> |
||||
|
</ScrollView> |
||||
|
</View> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
flexDirection: "row", |
||||
|
}, |
||||
|
image: { |
||||
|
marginRight: 10, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default ImageInputList; |
@ -0,0 +1,98 @@ |
|||||
|
import React, { useState } from "react"; |
||||
|
import { |
||||
|
View, |
||||
|
StyleSheet, |
||||
|
TouchableWithoutFeedback, |
||||
|
Modal, |
||||
|
Button, |
||||
|
FlatList, |
||||
|
} from "react-native"; |
||||
|
import { MaterialCommunityIcons } from "@expo/vector-icons"; |
||||
|
|
||||
|
import Text from "./Text"; |
||||
|
import defaultStyles from "../config/styles"; |
||||
|
import PickerItem from "./PickerItem"; |
||||
|
import Screen from "./Screen"; |
||||
|
|
||||
|
function AppPicker({ |
||||
|
icon, |
||||
|
items, |
||||
|
numberOfColumns = 1, |
||||
|
onSelectItem, |
||||
|
PickerItemComponent = PickerItem, |
||||
|
placeholder, |
||||
|
selectedItem, |
||||
|
width = "100%", |
||||
|
}) { |
||||
|
const [modalVisible, setModalVisible] = useState(false); |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<TouchableWithoutFeedback onPress={() => setModalVisible(true)}> |
||||
|
<View style={[styles.container, { width }]}> |
||||
|
{icon && ( |
||||
|
<MaterialCommunityIcons |
||||
|
name={icon} |
||||
|
size={20} |
||||
|
color={defaultStyles.colors.medium} |
||||
|
style={styles.icon} |
||||
|
/> |
||||
|
)} |
||||
|
{selectedItem ? ( |
||||
|
<Text style={styles.text}>{selectedItem.label}</Text> |
||||
|
) : ( |
||||
|
<Text style={styles.placeholder}>{placeholder}</Text> |
||||
|
)} |
||||
|
|
||||
|
<MaterialCommunityIcons |
||||
|
name="chevron-down" |
||||
|
size={20} |
||||
|
color={defaultStyles.colors.medium} |
||||
|
/> |
||||
|
</View> |
||||
|
</TouchableWithoutFeedback> |
||||
|
<Modal visible={modalVisible} animationType="slide"> |
||||
|
<Screen> |
||||
|
<Button title="Close" onPress={() => setModalVisible(false)} /> |
||||
|
<FlatList |
||||
|
data={items} |
||||
|
keyExtractor={(item) => item.value.toString()} |
||||
|
numColumns={numberOfColumns} |
||||
|
renderItem={({ item }) => ( |
||||
|
<PickerItemComponent |
||||
|
item={item} |
||||
|
label={item.label} |
||||
|
onPress={() => { |
||||
|
setModalVisible(false); |
||||
|
onSelectItem(item); |
||||
|
}} |
||||
|
/> |
||||
|
)} |
||||
|
/> |
||||
|
</Screen> |
||||
|
</Modal> |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
backgroundColor: defaultStyles.colors.light, |
||||
|
borderRadius: 25, |
||||
|
flexDirection: "row", |
||||
|
padding: 15, |
||||
|
marginVertical: 10, |
||||
|
}, |
||||
|
icon: { |
||||
|
marginRight: 10, |
||||
|
}, |
||||
|
placeholder: { |
||||
|
color: defaultStyles.colors.medium, |
||||
|
flex: 1, |
||||
|
}, |
||||
|
text: { |
||||
|
flex: 1, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default AppPicker; |
@ -0,0 +1,20 @@ |
|||||
|
import React from "react"; |
||||
|
import { TouchableOpacity, StyleSheet } from "react-native"; |
||||
|
|
||||
|
import Text from "./Text"; |
||||
|
|
||||
|
function PickerItem({ item, onPress }) { |
||||
|
return ( |
||||
|
<TouchableOpacity onPress={onPress}> |
||||
|
<Text style={styles.text}>{item.label}</Text> |
||||
|
</TouchableOpacity> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
text: { |
||||
|
padding: 20, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default PickerItem; |
@ -0,0 +1,23 @@ |
|||||
|
import React from "react"; |
||||
|
import Constants from "expo-constants"; |
||||
|
import { StyleSheet, SafeAreaView, View } from "react-native"; |
||||
|
|
||||
|
function Screen({ children, style }) { |
||||
|
return ( |
||||
|
<SafeAreaView style={[styles.screen, style]}> |
||||
|
<View style={[styles.view, style]}>{children}</View> |
||||
|
</SafeAreaView> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
screen: { |
||||
|
paddingTop: Constants.statusBarHeight, |
||||
|
flex: 1, |
||||
|
}, |
||||
|
view: { |
||||
|
flex: 1, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default Screen; |
@ -0,0 +1,14 @@ |
|||||
|
import React from "react"; |
||||
|
import { Text } from "react-native"; |
||||
|
|
||||
|
import defaultStyles from "../config/styles"; |
||||
|
|
||||
|
function AppText({ children, style, ...otherProps }) { |
||||
|
return ( |
||||
|
<Text style={[defaultStyles.text, style]} {...otherProps}> |
||||
|
{children} |
||||
|
</Text> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
export default AppText; |
@ -0,0 +1,40 @@ |
|||||
|
import React from "react"; |
||||
|
import { View, TextInput, StyleSheet } from "react-native"; |
||||
|
import { MaterialCommunityIcons } from "@expo/vector-icons"; |
||||
|
|
||||
|
import defaultStyles from "../config/styles"; |
||||
|
|
||||
|
function AppTextInput({ icon, width = "100%", ...otherProps }) { |
||||
|
return ( |
||||
|
<View style={[styles.container, { width }]}> |
||||
|
{icon && ( |
||||
|
<MaterialCommunityIcons |
||||
|
name={icon} |
||||
|
size={20} |
||||
|
color={defaultStyles.colors.medium} |
||||
|
style={styles.icon} |
||||
|
/> |
||||
|
)} |
||||
|
<TextInput |
||||
|
placeholderTextColor={defaultStyles.colors.medium} |
||||
|
style={defaultStyles.text} |
||||
|
{...otherProps} |
||||
|
/> |
||||
|
</View> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
backgroundColor: defaultStyles.colors.light, |
||||
|
borderRadius: 25, |
||||
|
flexDirection: "row", |
||||
|
padding: 15, |
||||
|
marginVertical: 10, |
||||
|
}, |
||||
|
icon: { |
||||
|
marginRight: 10, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default AppTextInput; |
@ -0,0 +1,16 @@ |
|||||
|
import React from "react"; |
||||
|
import { StyleSheet } from "react-native"; |
||||
|
|
||||
|
import Text from "../Text"; |
||||
|
|
||||
|
function ErrorMessage({ error, visible }) { |
||||
|
if (!visible || !error) return null; |
||||
|
|
||||
|
return <Text style={styles.error}>{error}</Text>; |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
error: { color: "red" }, |
||||
|
}); |
||||
|
|
||||
|
export default ErrorMessage; |
@ -0,0 +1,16 @@ |
|||||
|
import React from "react"; |
||||
|
import { Formik } from "formik"; |
||||
|
|
||||
|
function AppForm({ initialValues, onSubmit, validationSchema, children }) { |
||||
|
return ( |
||||
|
<Formik |
||||
|
initialValues={initialValues} |
||||
|
onSubmit={onSubmit} |
||||
|
validationSchema={validationSchema} |
||||
|
> |
||||
|
{() => <>{children}</>} |
||||
|
</Formik> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
export default AppForm; |
@ -0,0 +1,23 @@ |
|||||
|
import React from "react"; |
||||
|
import { useFormikContext } from "formik"; |
||||
|
|
||||
|
import TextInput from "../TextInput"; |
||||
|
import ErrorMessage from "./ErrorMessage"; |
||||
|
|
||||
|
function AppFormField({ name, width, ...otherProps }) { |
||||
|
const { setFieldTouched, handleChange, errors, touched } = useFormikContext(); |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<TextInput |
||||
|
onBlur={() => setFieldTouched(name)} |
||||
|
onChangeText={handleChange(name)} |
||||
|
width={width} |
||||
|
{...otherProps} |
||||
|
/> |
||||
|
<ErrorMessage error={errors[name]} visible={touched[name]} /> |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
export default AppFormField; |
@ -0,0 +1,34 @@ |
|||||
|
import React from "react"; |
||||
|
import { useFormikContext } from "formik"; |
||||
|
|
||||
|
import ErrorMessage from "./ErrorMessage"; |
||||
|
import ImageInputList from "../ImageInputList"; |
||||
|
|
||||
|
function FormImagePicker({ name }) { |
||||
|
const { errors, setFieldValue, touched, values } = useFormikContext(); |
||||
|
const imageUris = values[name]; |
||||
|
|
||||
|
const handleAdd = (uri) => { |
||||
|
setFieldValue(name, [...imageUris, uri]); |
||||
|
}; |
||||
|
|
||||
|
const handleRemove = (uri) => { |
||||
|
setFieldValue( |
||||
|
name, |
||||
|
imageUris.filter((imageUri) => imageUri !== uri) |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<ImageInputList |
||||
|
imageUris={imageUris} |
||||
|
onAddImage={handleAdd} |
||||
|
onRemoveImage={handleRemove} |
||||
|
/> |
||||
|
<ErrorMessage error={errors[name]} visible={touched[name]} /> |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
export default FormImagePicker; |
@ -0,0 +1,33 @@ |
|||||
|
import React from "react"; |
||||
|
import { useFormikContext } from "formik"; |
||||
|
|
||||
|
import Picker from "../Picker"; |
||||
|
import ErrorMessage from "./ErrorMessage"; |
||||
|
|
||||
|
function AppFormPicker({ |
||||
|
items, |
||||
|
name, |
||||
|
numberOfColumns, |
||||
|
PickerItemComponent, |
||||
|
placeholder, |
||||
|
width, |
||||
|
}) { |
||||
|
const { errors, setFieldValue, touched, values } = useFormikContext(); |
||||
|
|
||||
|
return ( |
||||
|
<> |
||||
|
<Picker |
||||
|
items={items} |
||||
|
numberOfColumns={numberOfColumns} |
||||
|
onSelectItem={(item) => setFieldValue(name, item)} |
||||
|
PickerItemComponent={PickerItemComponent} |
||||
|
placeholder={placeholder} |
||||
|
selectedItem={values[name]} |
||||
|
width={width} |
||||
|
/> |
||||
|
<ErrorMessage error={errors[name]} visible={touched[name]} /> |
||||
|
</> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
export default AppFormPicker; |
@ -0,0 +1,12 @@ |
|||||
|
import React from "react"; |
||||
|
import { useFormikContext } from "formik"; |
||||
|
|
||||
|
import Button from "../Button"; |
||||
|
|
||||
|
function SubmitButton({ title }) { |
||||
|
const { handleSubmit } = useFormikContext(); |
||||
|
|
||||
|
return <Button title={title} onPress={handleSubmit} />; |
||||
|
} |
||||
|
|
||||
|
export default SubmitButton; |
@ -0,0 +1,5 @@ |
|||||
|
export { default as Form } from "./Form"; |
||||
|
export { default as FormField } from "./FormField"; |
||||
|
export { default as FormPicker } from "./FormPicker"; |
||||
|
export { default as ErrorMessage } from "./ErrorMessage"; |
||||
|
export { default as SubmitButton } from "./SubmitButton"; |
@ -0,0 +1,69 @@ |
|||||
|
import React from "react"; |
||||
|
import { View, StyleSheet, Image, TouchableHighlight } from "react-native"; |
||||
|
import { MaterialCommunityIcons } from "@expo/vector-icons"; |
||||
|
import Swipeable from "react-native-gesture-handler/Swipeable"; |
||||
|
|
||||
|
import Text from "../Text"; |
||||
|
import colors from "../../config/colors"; |
||||
|
|
||||
|
function ListItem({ |
||||
|
title, |
||||
|
subTitle, |
||||
|
image, |
||||
|
IconComponent, |
||||
|
onPress, |
||||
|
renderRightActions, |
||||
|
}) { |
||||
|
return ( |
||||
|
<Swipeable renderRightActions={renderRightActions}> |
||||
|
<TouchableHighlight underlayColor={colors.light} onPress={onPress}> |
||||
|
<View style={styles.container}> |
||||
|
{IconComponent} |
||||
|
{image && <Image style={styles.image} source={image} />} |
||||
|
<View style={styles.detailsContainer}> |
||||
|
<Text style={styles.title} numberOfLines={1}> |
||||
|
{title} |
||||
|
</Text> |
||||
|
{subTitle && ( |
||||
|
<Text style={styles.subTitle} numberOfLines={2}> |
||||
|
{subTitle} |
||||
|
</Text> |
||||
|
)} |
||||
|
</View> |
||||
|
<MaterialCommunityIcons |
||||
|
color={colors.medium} |
||||
|
name="chevron-right" |
||||
|
size={25} |
||||
|
/> |
||||
|
</View> |
||||
|
</TouchableHighlight> |
||||
|
</Swipeable> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
alignItems: "center", |
||||
|
flexDirection: "row", |
||||
|
padding: 15, |
||||
|
backgroundColor: colors.white, |
||||
|
}, |
||||
|
detailsContainer: { |
||||
|
flex: 1, |
||||
|
marginLeft: 10, |
||||
|
justifyContent: "center", |
||||
|
}, |
||||
|
image: { |
||||
|
width: 70, |
||||
|
height: 70, |
||||
|
borderRadius: 35, |
||||
|
}, |
||||
|
subTitle: { |
||||
|
color: colors.medium, |
||||
|
}, |
||||
|
title: { |
||||
|
fontWeight: "500", |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default ListItem; |
@ -0,0 +1,30 @@ |
|||||
|
import React from "react"; |
||||
|
import { View, StyleSheet, TouchableWithoutFeedback } from "react-native"; |
||||
|
import { MaterialCommunityIcons } from "@expo/vector-icons"; |
||||
|
|
||||
|
import colors from "../../config/colors"; |
||||
|
|
||||
|
function ListItemDeleteAction({ onPress }) { |
||||
|
return ( |
||||
|
<TouchableWithoutFeedback onPress={onPress}> |
||||
|
<View style={styles.container}> |
||||
|
<MaterialCommunityIcons |
||||
|
name="trash-can" |
||||
|
size={35} |
||||
|
color={colors.white} |
||||
|
/> |
||||
|
</View> |
||||
|
</TouchableWithoutFeedback> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
backgroundColor: colors.danger, |
||||
|
width: 70, |
||||
|
justifyContent: "center", |
||||
|
alignItems: "center", |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default ListItemDeleteAction; |
@ -0,0 +1,18 @@ |
|||||
|
import React from "react"; |
||||
|
import { StyleSheet, View } from "react-native"; |
||||
|
|
||||
|
import colors from "../../config/colors"; |
||||
|
|
||||
|
function ListItemSeparator() { |
||||
|
return <View style={styles.separator} />; |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
separator: { |
||||
|
width: "100%", |
||||
|
height: 1, |
||||
|
backgroundColor: colors.light, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default ListItemSeparator; |
@ -0,0 +1,3 @@ |
|||||
|
export { default as ListItem } from "./ListItem"; |
||||
|
export { default as ListItemDeleteAction } from "./ListItemDeleteAction"; |
||||
|
export { default as ListItemSeparator } from "./ListItemSeparator"; |
@ -0,0 +1,12 @@ |
|||||
|
import { Platform } from "react-native"; |
||||
|
|
||||
|
import colors from "./colors"; |
||||
|
|
||||
|
export default { |
||||
|
colors, |
||||
|
text: { |
||||
|
color: colors.dark, |
||||
|
fontSize: 18, |
||||
|
fontFamily: Platform.OS === "android" ? "Roboto" : "Avenir", |
||||
|
}, |
||||
|
}; |
@ -0,0 +1,25 @@ |
|||||
|
import { useEffect, useState } from "react"; |
||||
|
import * as Location from "expo-location"; |
||||
|
|
||||
|
export default useLocation = () => { |
||||
|
const [location, setLocation] = useState(); |
||||
|
|
||||
|
const getLocation = async () => { |
||||
|
try { |
||||
|
const { granted } = await Location.requestPermissionsAsync(); |
||||
|
if (!granted) return; |
||||
|
const { |
||||
|
coords: { latitude, longitude }, |
||||
|
} = await Location.getLastKnownPositionAsync(); |
||||
|
setLocation({ latitude, longitude }); |
||||
|
} catch (error) { |
||||
|
console.log(error); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
useEffect(() => { |
||||
|
getLocation(); |
||||
|
}, []); |
||||
|
|
||||
|
return location; |
||||
|
}; |
@ -0,0 +1,14 @@ |
|||||
|
import React from "react"; |
||||
|
import { createStackNavigator } from "@react-navigation/stack"; |
||||
|
import AccountScreen from "../screens/AccountScreen"; |
||||
|
import MessagesScreen from "../screens/MessagesScreen"; |
||||
|
|
||||
|
const Stack = createStackNavigator(); |
||||
|
|
||||
|
const AccountNavigator = () => ( |
||||
|
<Stack.Navigator> |
||||
|
<Stack.Screen name="Perfil" component={AccountScreen} /> |
||||
|
</Stack.Navigator> |
||||
|
); |
||||
|
|
||||
|
export default AccountNavigator; |
@ -0,0 +1,77 @@ |
|||||
|
import React from "react"; |
||||
|
import { createBottomTabNavigator } from "@react-navigation/bottom-tabs"; |
||||
|
import { MaterialCommunityIcons } from "@expo/vector-icons"; |
||||
|
|
||||
|
import AccountNavigator from "./AccountNavigator"; |
||||
|
import FeedNavigator from "./FeedNavigator"; |
||||
|
import MessagesNavigator from "./MessagesNavigator" |
||||
|
import OfficialMessagesNavigator from "./OfficialMessagesNavigator" |
||||
|
import SharingDataScreen from "../screens/SharingDataScreen"; |
||||
|
import NewListingButton from "./NewListingButton"; |
||||
|
import routes from "./routes"; |
||||
|
|
||||
|
const Tab = createBottomTabNavigator(); |
||||
|
|
||||
|
const AppNavigator = () => ( |
||||
|
<Tab.Navigator> |
||||
|
<Tab.Screen |
||||
|
name="Home" |
||||
|
component={FeedNavigator} |
||||
|
options={{ |
||||
|
tabBarIcon: ({ color, size }) => ( |
||||
|
<MaterialCommunityIcons name="home" color={color} size={size} /> |
||||
|
), |
||||
|
}} |
||||
|
/> |
||||
|
<Tab.Screen |
||||
|
name="Oficial" |
||||
|
component={OfficialMessagesNavigator} |
||||
|
options={{ |
||||
|
tabBarIcon: ({ color, size }) => ( |
||||
|
<MaterialCommunityIcons name="check-circle" color={color} size={size} /> |
||||
|
), |
||||
|
}} |
||||
|
/> |
||||
|
|
||||
|
<Tab.Screen |
||||
|
name="SharingData" |
||||
|
component={SharingDataScreen} |
||||
|
options={({ navigation }) => ({ |
||||
|
tabBarButton: () => ( |
||||
|
<NewListingButton |
||||
|
onPress={() => navigation.navigate(routes.SHARING_DATA)} |
||||
|
/> |
||||
|
), |
||||
|
tabBarIcon: ({ color, size }) => ( |
||||
|
<MaterialCommunityIcons |
||||
|
name="plus-circle" |
||||
|
color={color} |
||||
|
size={size} |
||||
|
/> |
||||
|
), |
||||
|
})} |
||||
|
/> |
||||
|
|
||||
|
|
||||
|
<Tab.Screen |
||||
|
name="Notificação" |
||||
|
component={MessagesNavigator} |
||||
|
options={{ |
||||
|
tabBarIcon: ({ color, size }) => ( |
||||
|
<MaterialCommunityIcons name="bell-outline" color={color} size={size} /> |
||||
|
), |
||||
|
}} |
||||
|
/> |
||||
|
<Tab.Screen |
||||
|
name="Perfil" |
||||
|
component={AccountNavigator} |
||||
|
options={{ |
||||
|
tabBarIcon: ({ color, size }) => ( |
||||
|
<MaterialCommunityIcons name="account" color={color} size={size} /> |
||||
|
), |
||||
|
}} |
||||
|
/> |
||||
|
</Tab.Navigator> |
||||
|
); |
||||
|
|
||||
|
export default AppNavigator; |
@ -0,0 +1,13 @@ |
|||||
|
import React from "react"; |
||||
|
import { createStackNavigator } from "@react-navigation/stack"; |
||||
|
import MapFeedScreen from "../screens/MapFeedScreen"; |
||||
|
|
||||
|
const Stack = createStackNavigator(); |
||||
|
|
||||
|
const FeedNavigator = () => ( |
||||
|
<Stack.Navigator> |
||||
|
<Stack.Screen name="Home" component={MapFeedScreen} /> |
||||
|
</Stack.Navigator> |
||||
|
); |
||||
|
|
||||
|
export default FeedNavigator; |
@ -0,0 +1,13 @@ |
|||||
|
import React from "react"; |
||||
|
import { createStackNavigator } from "@react-navigation/stack"; |
||||
|
import MessagesScreen from "../screens/MessagesScreen"; |
||||
|
|
||||
|
const Stack = createStackNavigator(); |
||||
|
|
||||
|
const MessagesNavigator = () => ( |
||||
|
<Stack.Navigator> |
||||
|
<Stack.Screen name="Notificação" component={MessagesScreen} /> |
||||
|
</Stack.Navigator> |
||||
|
); |
||||
|
|
||||
|
export default MessagesNavigator; |
@ -0,0 +1,35 @@ |
|||||
|
import React from "react"; |
||||
|
import { View, StyleSheet, TouchableOpacity } from "react-native"; |
||||
|
import { MaterialCommunityIcons } from "@expo/vector-icons"; |
||||
|
|
||||
|
import colors from "../config/colors"; |
||||
|
|
||||
|
function NewListingButton({ onPress }) { |
||||
|
return ( |
||||
|
<TouchableOpacity onPress={onPress}> |
||||
|
<View style={styles.container}> |
||||
|
<MaterialCommunityIcons |
||||
|
name="plus-circle" |
||||
|
color={colors.white} |
||||
|
size={40} |
||||
|
/> |
||||
|
</View> |
||||
|
</TouchableOpacity> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
alignItems: "center", |
||||
|
backgroundColor: colors.primary, |
||||
|
borderColor: colors.white, |
||||
|
borderRadius: 40, |
||||
|
borderWidth: 10, |
||||
|
bottom: 20, |
||||
|
height: 80, |
||||
|
justifyContent: "center", |
||||
|
width: 80, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default NewListingButton; |
@ -0,0 +1,13 @@ |
|||||
|
import React from "react"; |
||||
|
import { createStackNavigator } from "@react-navigation/stack"; |
||||
|
import OfficialMessagesScreen from "../screens/OfficialMessagesScreen"; |
||||
|
|
||||
|
const Stack = createStackNavigator(); |
||||
|
|
||||
|
const OfficialMessagesNavigator = () => ( |
||||
|
<Stack.Navigator> |
||||
|
<Stack.Screen name="Previsão do Tempo" component={OfficialMessagesScreen} /> |
||||
|
</Stack.Navigator> |
||||
|
); |
||||
|
|
||||
|
export default OfficialMessagesNavigator; |
@ -0,0 +1,11 @@ |
|||||
|
import { DefaultTheme } from "@react-navigation/native"; |
||||
|
import colors from "../config/colors"; |
||||
|
|
||||
|
export default { |
||||
|
...DefaultTheme, |
||||
|
colors: { |
||||
|
...DefaultTheme.colors, |
||||
|
primary: colors.primary, |
||||
|
background: colors.white, |
||||
|
}, |
||||
|
}; |
@ -0,0 +1,9 @@ |
|||||
|
export default Object.freeze({ |
||||
|
LISTING_DETAILS: "ListingDetails", |
||||
|
LISTING_EDIT: "ListingEdit", |
||||
|
LOGIN: "Login", |
||||
|
MESSAGES: "Messages", |
||||
|
REGISTER: "Register", |
||||
|
SHARING_DATA: "SharingData", |
||||
|
}); |
||||
|
|
@ -0,0 +1,36 @@ |
|||||
|
import React from "react"; |
||||
|
import { StyleSheet, View } from "react-native"; |
||||
|
|
||||
|
import { ListItem } from "../components/lists"; |
||||
|
import colors from "../config/colors"; |
||||
|
import Icon from "../components/Icon"; |
||||
|
import Screen from "../components/Screen"; |
||||
|
|
||||
|
function AccountScreen({ navigation }) { |
||||
|
return ( |
||||
|
<Screen style={styles.screen}> |
||||
|
<View style={styles.container}> |
||||
|
<ListItem |
||||
|
title="Daniel Barros" |
||||
|
subTitle="danieldrb@gmail.com" |
||||
|
image={require("../assets/ddangelorb.png")} |
||||
|
/> |
||||
|
</View> |
||||
|
<ListItem |
||||
|
title="Log Out" |
||||
|
IconComponent={<Icon name="logout" backgroundColor="#ffe66d" />} |
||||
|
/> |
||||
|
</Screen> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
screen: { |
||||
|
backgroundColor: colors.light, |
||||
|
}, |
||||
|
container: { |
||||
|
marginVertical: 20, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default AccountScreen; |
@ -0,0 +1,63 @@ |
|||||
|
import React from "react"; |
||||
|
import { StyleSheet, View } from "react-native"; |
||||
|
import MapView, { Marker } from "react-native-maps"; |
||||
|
|
||||
|
import colors from "../config/colors"; |
||||
|
|
||||
|
function MapFeedScreen(props) { |
||||
|
return ( |
||||
|
<View style={styles.container}> |
||||
|
<MapView |
||||
|
style={styles.mapStyle} |
||||
|
initialRegion={{ |
||||
|
latitude: -23.657090, |
||||
|
longitude: -46.699260, |
||||
|
latitudeDelta: 0.0922, |
||||
|
longitudeDelta: 0.0421, |
||||
|
}}> |
||||
|
<Marker |
||||
|
coordinate={{ latitude: -23.657090, longitude: -46.699260 }} |
||||
|
image={require("../assets/chuva_peq.png")} |
||||
|
/> |
||||
|
<Marker |
||||
|
coordinate={{ latitude: -23.656282, longitude: -46.682768 }} |
||||
|
image={require("../assets/chuva_peq.png")} |
||||
|
/> |
||||
|
<Marker |
||||
|
coordinate={{ latitude: -23.666712, longitude: -46.687650 }} |
||||
|
image={require("../assets/chuva_peq.png")} |
||||
|
/> |
||||
|
<Marker |
||||
|
coordinate={{ latitude: -23.660848, longitude: -46.704396 }} |
||||
|
image={require("../assets/chuva_peq.png")} |
||||
|
/> |
||||
|
|
||||
|
<Marker |
||||
|
coordinate={{ latitude: -23.634700, longitude: -46.721960 }} |
||||
|
image={require("../assets/pontos_alagamento_peq.png")} |
||||
|
/> |
||||
|
<Marker |
||||
|
coordinate={{ latitude: -23.650861, longitude: -46.721775 }} |
||||
|
image={require("../assets/pontos_alagamento_peq.png")} |
||||
|
/> |
||||
|
</MapView> |
||||
|
</View> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
backgroundColor: colors.black, |
||||
|
flex: 1, |
||||
|
}, |
||||
|
mapStyle: { |
||||
|
position: 'absolute', |
||||
|
top: 0, |
||||
|
left: 0, |
||||
|
right: 0, |
||||
|
bottom: 0 |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default MapFeedScreen; |
||||
|
|
@ -0,0 +1,70 @@ |
|||||
|
import React, { useState } from "react"; |
||||
|
import { FlatList, StyleSheet } from "react-native"; |
||||
|
|
||||
|
import Screen from "../components/Screen"; |
||||
|
import { |
||||
|
ListItem, |
||||
|
ListItemDeleteAction, |
||||
|
ListItemSeparator, |
||||
|
} from "../components/lists"; |
||||
|
|
||||
|
const initialMessages = [ |
||||
|
{ |
||||
|
id: 1, |
||||
|
title: "Defesa Civil", |
||||
|
description: "Enviado um aviso de chuvas intensas na região do M'Boi Mirim. Há possibilidade ...", |
||||
|
image: require("../assets/defesa_civil.png"), |
||||
|
}, |
||||
|
{ |
||||
|
id: 2, |
||||
|
title: "Defesa Civil", |
||||
|
description: |
||||
|
"Enviado um aviso de alagamento na região de Pinheiros. Há possibilidade de alagamentos. Evite a região.", |
||||
|
image: require("../assets/defesa_civil.png"), |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
function MessagesScreen(props) { |
||||
|
const [messages, setMessages] = useState(initialMessages); |
||||
|
const [refreshing, setRefreshing] = useState(false); |
||||
|
|
||||
|
const handleDelete = (message) => { |
||||
|
setMessages(messages.filter((m) => m.id !== message.id)); |
||||
|
}; |
||||
|
|
||||
|
return ( |
||||
|
<Screen> |
||||
|
<FlatList |
||||
|
data={messages} |
||||
|
keyExtractor={(message) => message.id.toString()} |
||||
|
renderItem={({ item }) => ( |
||||
|
<ListItem |
||||
|
title={item.title} |
||||
|
subTitle={item.description} |
||||
|
image={item.image} |
||||
|
onPress={() => console.log("Message selected", item)} |
||||
|
renderRightActions={() => ( |
||||
|
<ListItemDeleteAction onPress={() => handleDelete(item)} /> |
||||
|
)} |
||||
|
/> |
||||
|
)} |
||||
|
ItemSeparatorComponent={ListItemSeparator} |
||||
|
refreshing={refreshing} |
||||
|
onRefresh={() => { |
||||
|
setMessages([ |
||||
|
{ |
||||
|
id: 2, |
||||
|
title: "T2", |
||||
|
description: "D2", |
||||
|
image: require("../assets/ddangelorb.png"), |
||||
|
}, |
||||
|
]); |
||||
|
}} |
||||
|
/> |
||||
|
</Screen> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({}); |
||||
|
|
||||
|
export default MessagesScreen; |
@ -0,0 +1,29 @@ |
|||||
|
import React from "react"; |
||||
|
import { Image, StyleSheet, View } from "react-native"; |
||||
|
|
||||
|
import colors from "../config/colors"; |
||||
|
|
||||
|
function OfficialMessagesScreen(props) { |
||||
|
return ( |
||||
|
<View style={styles.container}> |
||||
|
<Image |
||||
|
resizeMode="contain" |
||||
|
style={styles.image} |
||||
|
source={require("../assets/previsao_tempo.png")} |
||||
|
/> |
||||
|
</View> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
backgroundColor: colors.black, |
||||
|
flex: 1, |
||||
|
}, |
||||
|
image: { |
||||
|
width: "100%", |
||||
|
height: "100%", |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default OfficialMessagesScreen; |
@ -0,0 +1,109 @@ |
|||||
|
import React from "react"; |
||||
|
import { StyleSheet } from "react-native"; |
||||
|
import * as Yup from "yup"; |
||||
|
|
||||
|
import { |
||||
|
Form, |
||||
|
FormField, |
||||
|
FormPicker as Picker, |
||||
|
SubmitButton, |
||||
|
} from "../components/forms"; |
||||
|
import CategoryPickerItem from "../components/CategoryPickerItem"; |
||||
|
import Screen from "../components/Screen"; |
||||
|
import FormImagePicker from "../components/forms/FormImagePicker"; |
||||
|
import useLocation from "../hooks/useLocation"; |
||||
|
|
||||
|
const validationSchema = Yup.object().shape({ |
||||
|
title: Yup.string().required().min(1).label("Title"), |
||||
|
price: Yup.number().required().min(1).max(10000).label("Price"), |
||||
|
description: Yup.string().label("Description"), |
||||
|
category: Yup.object().required().nullable().label("Category"), |
||||
|
images: Yup.array().min(1, "Please select at least one image."), |
||||
|
}); |
||||
|
|
||||
|
const categories = [ |
||||
|
{ |
||||
|
backgroundColor: "#2e53ff", |
||||
|
icon: "wave", |
||||
|
label: "Pontos de alagamento", |
||||
|
value: 1, |
||||
|
}, |
||||
|
{ |
||||
|
backgroundColor: "#2e53ff", |
||||
|
icon: "weather-pouring", |
||||
|
label: "Chuva", |
||||
|
value: 2, |
||||
|
}, |
||||
|
{ |
||||
|
backgroundColor: "#2e53ff", |
||||
|
icon: "waves", |
||||
|
label: "Diário do pluviômetro", |
||||
|
value: 3, |
||||
|
}, |
||||
|
{ |
||||
|
backgroundColor: "#2e53ff", |
||||
|
icon: "waves", |
||||
|
label: "Diário do pluviômetro", |
||||
|
value: 3, |
||||
|
}, |
||||
|
{ |
||||
|
backgroundColor: "#2e53ff", |
||||
|
icon: "water-percent", |
||||
|
label: "Nível do rio", |
||||
|
value: 4, |
||||
|
}, |
||||
|
]; |
||||
|
|
||||
|
function SharingDataScreen() { |
||||
|
const location = useLocation(); |
||||
|
|
||||
|
return ( |
||||
|
<Screen style={styles.container}> |
||||
|
<Form |
||||
|
initialValues={{ |
||||
|
title: "", |
||||
|
price: "", |
||||
|
description: "", |
||||
|
category: null, |
||||
|
images: [], |
||||
|
}} |
||||
|
onSubmit={(values) => console.log(location)} |
||||
|
validationSchema={validationSchema} |
||||
|
> |
||||
|
<FormImagePicker name="images" /> |
||||
|
<FormField maxLength={255} name="title" placeholder="Título" /> |
||||
|
<FormField |
||||
|
keyboardType="numeric" |
||||
|
maxLength={8} |
||||
|
name="price" |
||||
|
placeholder="Valor" |
||||
|
width={120} |
||||
|
/> |
||||
|
<Picker |
||||
|
items={categories} |
||||
|
name="category" |
||||
|
numberOfColumns={3} |
||||
|
PickerItemComponent={CategoryPickerItem} |
||||
|
placeholder="Categoria" |
||||
|
width="50%" |
||||
|
/> |
||||
|
<FormField |
||||
|
maxLength={255} |
||||
|
multiline |
||||
|
name="description" |
||||
|
numberOfLines={3} |
||||
|
placeholder="Descrição" |
||||
|
/> |
||||
|
<SubmitButton title="Post" /> |
||||
|
</Form> |
||||
|
</Screen> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
const styles = StyleSheet.create({ |
||||
|
container: { |
||||
|
padding: 10, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
export default SharingDataScreen; |
@ -1,53 +0,0 @@ |
|||||
import React from "react"; |
|
||||
import { Image, StyleSheet, View } from "react-native"; |
|
||||
import MapView from 'react-native-maps'; |
|
||||
|
|
||||
import colors from "../config/colors"; |
|
||||
|
|
||||
function ViewImageScreen(props) { |
|
||||
return ( |
|
||||
<View style={styles.container}> |
|
||||
<View style={styles.closeIcon}></View> |
|
||||
<View style={styles.deleteIcon}></View> |
|
||||
<MapView style={styles.mapStyle} /> |
|
||||
<Image |
|
||||
resizeMode="contain" |
|
||||
style={styles.image} |
|
||||
source={require("../assets/wp6.jpg")} |
|
||||
/> |
|
||||
</View> |
|
||||
); |
|
||||
} |
|
||||
|
|
||||
const styles = StyleSheet.create({ |
|
||||
closeIcon: { |
|
||||
width: 50, |
|
||||
height: 50, |
|
||||
backgroundColor: colors.primary, |
|
||||
position: "absolute", |
|
||||
top: 40, |
|
||||
left: 30, |
|
||||
}, |
|
||||
container: { |
|
||||
backgroundColor: colors.black, |
|
||||
flex: 1, |
|
||||
}, |
|
||||
deleteIcon: { |
|
||||
width: 50, |
|
||||
height: 50, |
|
||||
backgroundColor: colors.secondary, |
|
||||
position: "absolute", |
|
||||
top: 40, |
|
||||
right: 30, |
|
||||
}, |
|
||||
image: { |
|
||||
width: "100%", |
|
||||
height: "100%", |
|
||||
}, |
|
||||
mapStyle: { |
|
||||
width: 300, |
|
||||
height: 300, |
|
||||
}, |
|
||||
}); |
|
||||
|
|
||||
export default ViewImageScreen; |
|
7956
src/package-lock.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
688
src/yarn.lock
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue