Category view + detail

This commit is contained in:
Maarten 2024-08-01 11:53:43 +02:00
parent 5ebd10cd3a
commit aaedcec4a6
8 changed files with 3360 additions and 1599 deletions

View file

@ -4,23 +4,43 @@ import {
SafeAreaView, SafeAreaView,
StatusBar, StatusBar,
Image, Image,
TouchableOpacity TouchableOpacity,
} from 'react-native'; } from 'react-native';
import { AutocompleteDropdown } from 'react-native-autocomplete-dropdown';
import React, {useState} from 'react';
import {router} from 'expo-router';
import {AutocompleteDropdown} from 'react-native-autocomplete-dropdown';
import AsyncStorage from '@react-native-async-storage/async-storage';
import {ThemedText} from '@/components/ThemedText'; import {ThemedText} from '@/components/ThemedText';
import {ThemedView} from '@/components/ThemedView'; import {ThemedView} from '@/components/ThemedView';
import {Colors} from '@/constants/Colors'; import {Colors} from '@/constants/Colors';
import {useColorScheme} from '@/hooks/useColorScheme'; import {useColorScheme} from '@/hooks/useColorScheme';
import React from 'react'; import {Request} from '@/services/request';
import List from '@/components/List';
export default function ExploreScreen() { export default function ExploreScreen() {
const colorScheme = useColorScheme() ?? 'light'; const colorScheme = useColorScheme() ?? 'light';
const [types, setTypes] = useState([]);
// Load waste types
Request.get('waste-types').then((responseData) => {
const response = responseData.data;
setTypes(response);
});
// View item in sub screen
const viewItem = async (item: any) => {
await AsyncStorage.setItem('activeCategory', JSON.stringify(item));
router.push('/explore/category');
};
return ( return (
<SafeAreaView style={{flex: 1, backgroundColor: Colors[colorScheme].background,}}> <SafeAreaView style={{flex: 1, backgroundColor: Colors[colorScheme].background,}}>
<ThemedView style={styles.container}>
<ScrollView> <ScrollView>
<ThemedView style={styles.container}>
<ThemedView style={styles.titleContainer}> <ThemedView style={styles.titleContainer}>
<ThemedText type="title">Wat moet waar?</ThemedText> <ThemedText type="title">Wat moet waar?</ThemedText>
</ThemedView> </ThemedView>
@ -37,9 +57,9 @@ export default function ExploreScreen() {
closeOnSubmit={false} closeOnSubmit={false}
emptyResultText={'Niks gevonden'} emptyResultText={'Niks gevonden'}
dataSet={[ dataSet={[
{ id: '1', title: 'Alpha' }, {id: '1', title: 'Alpha'},
{ id: '2', title: 'Beta' }, {id: '2', title: 'Beta'},
{ id: '3', title: 'Gamma' }, {id: '3', title: 'Gamma'},
]} ]}
/> />
</ThemedView> </ThemedView>
@ -49,21 +69,18 @@ export default function ExploreScreen() {
</ThemedView> </ThemedView>
<ThemedView style={styles.listContainer}> <ThemedView style={styles.listContainer}>
<TouchableOpacity style={styles.listItem}> <List
<Image source={require('@/assets/images/paper.png')} style={styles.listImage} /> data={types}
<ThemedText type="default">Categorie 1</ThemedText> renderItem={(item: any, index: any) => (
</TouchableOpacity> <TouchableOpacity style={styles.listItem} key={index} onPress={() => viewItem(item)}>
<TouchableOpacity style={styles.listItem}> <Image source={require('@/assets/images/paper.png')} style={styles.listImage}/>
<Image source={require('@/assets/images/paper.png')} style={styles.listImage} /> <ThemedText type="default">{item.name}</ThemedText>
<ThemedText type="default">Categorie 2</ThemedText>
</TouchableOpacity>
<TouchableOpacity style={styles.listItem}>
<Image source={require('@/assets/images/paper.png')} style={styles.listImage} />
<ThemedText type="default">Categorie 3</ThemedText>
</TouchableOpacity> </TouchableOpacity>
)}
/>
</ThemedView>
</ThemedView> </ThemedView>
</ScrollView> </ScrollView>
</ThemedView>
</SafeAreaView> </SafeAreaView>
); );
} }

View file

@ -15,6 +15,7 @@ import {ThemedView} from '@/components/ThemedView';
import {Colors} from '@/constants/Colors'; import {Colors} from '@/constants/Colors';
import {useColorScheme} from '@/hooks/useColorScheme'; import {useColorScheme} from '@/hooks/useColorScheme';
import MapView from '@/components/map/map'; import MapView from '@/components/map/map';
import List from '@/components/List';
export default function MapScreen() { export default function MapScreen() {
const colorScheme = useColorScheme() ?? 'light'; const colorScheme = useColorScheme() ?? 'light';
@ -47,44 +48,14 @@ export default function MapScreen() {
}, },
]); ]);
const renderListItem = (item: any, index: any) => {
return (
<ThemedView style={styles.listItem} key={index}>
<View style={styles.listItemTitle}>
<Image source={item.image} style={styles.listImage}/>
<ThemedText type="default">{item.name}</ThemedText>
</View>
<Switch
trackColor={{false: '#767577', true: Colors.light.tint}}
thumbColor={'#fff'}
value={item.isEnabled}
onValueChange={() => toggleSwitch(index)}
/>
</ThemedView>
);
};
const toggleSwitch = (index: any) => { const toggleSwitch = (index: any) => {
const newData = types.map((item, key) => const newData = types.map((item, key) =>
key === index ? { ...item, isEnabled: !item.isEnabled } : item key === index ? {...item, isEnabled: !item.isEnabled} : item
); );
setTypes(newData); setTypes(newData);
}; };
const renderList = () => {
let list: any[] = [];
for (let i = 0; i < types.length; i++) {
const type = types[i];
list[i] = renderListItem(type, i);
}
return list;
}
return ( return (
<SafeAreaView style={{flex: 1, backgroundColor: Colors[colorScheme].background}}> <SafeAreaView style={{flex: 1, backgroundColor: Colors[colorScheme].background}}>
<ThemedView style={styles.container}> <ThemedView style={styles.container}>
@ -110,7 +81,23 @@ export default function MapScreen() {
</ThemedView> </ThemedView>
<ThemedView style={styles.listContainer}> <ThemedView style={styles.listContainer}>
{ renderList() } <List
data={types}
renderItem={(item: any, index: any) => (
<ThemedView style={styles.listItem} key={index}>
<View style={styles.listItemTitle}>
<Image source={item.image} style={styles.listImage}/>
<ThemedText type="default">{item.name}</ThemedText>
</View>
<Switch
trackColor={{false: '#767577', true: Colors.light.tint}}
thumbColor={'#fff'}
value={item.isEnabled}
onValueChange={() => toggleSwitch(index)}
/>
</ThemedView>
)}/>
</ThemedView> </ThemedView>
</ScrollView> </ScrollView>
</ThemedView> </ThemedView>
@ -140,6 +127,8 @@ const styles = StyleSheet.create({
}, },
listContainer: { listContainer: {
marginTop: 10, marginTop: 10,
paddingLeft: 25,
paddingRight: 25,
}, },
listItem: { listItem: {
flex: 1, flex: 1,
@ -147,8 +136,6 @@ const styles = StyleSheet.create({
justifyContent: 'space-between', justifyContent: 'space-between',
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingLeft: 25,
paddingRight: 25,
paddingBottom: 10, paddingBottom: 10,
marginBottom: 10, marginBottom: 10,
borderBottomWidth: 1, borderBottomWidth: 1,

63
app/explore/category.tsx Normal file
View file

@ -0,0 +1,63 @@
import {
SafeAreaView,
ScrollView,
StyleSheet,
Dimensions
} from 'react-native';
import {useState} from 'react';
import RenderHtml from 'react-native-render-html';
import {useNavigation} from '@react-navigation/native';
import AsyncStorage from '@react-native-async-storage/async-storage';
import {Colors} from '@/constants/Colors';
import {useColorScheme} from '@/hooks/useColorScheme';
import {ThemedView} from '@/components/ThemedView';
export default function CategoryScreen() {
const colorScheme = useColorScheme() ?? 'light';
const navigation = useNavigation();
const [description, setDescription] = useState('');
// Load item from storage
AsyncStorage.getItem('activeCategory').then((data) => {
const itemData: any = JSON.parse(data ?? '{}');
if (itemData != null) {
const {name, description} = itemData;
// Set description
// @ts-ignore
setDescription(description);
// Set page title
navigation.setOptions({title: name});
}
});
// HTML render props
const source = {html: description};
const width = Dimensions.get('window').width;
return (
<SafeAreaView style={{flex: 1, backgroundColor: Colors[colorScheme].background,}}>
<ScrollView style={styles.container}>
<ThemedView style={styles.htmlContainer}>
<RenderHtml
contentWidth={width}
source={source}
/>
</ThemedView>
</ScrollView>
</SafeAreaView>
);
}
const styles = StyleSheet.create({
container: {
padding: 25,
},
htmlContainer: {
paddingBottom: 50,
},
})

29
components/List.tsx Normal file
View file

@ -0,0 +1,29 @@
import {StyleSheet, View} from 'react-native';
import React from 'react';
interface ListProps {
data: any;
renderItem: Function;
}
const CustomList: React.FC<ListProps> = ({ data, renderItem }) => {
const renderList = () => {
let list: any[] = [];
for (let i = 0; i < data.length; i++) {
const item = data[i];
list[i] = renderItem(item, i);
}
return list;
};
return (
<View>
{renderList()}
</View>
);
};
export default CustomList;

955
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -16,9 +16,11 @@
}, },
"dependencies": { "dependencies": {
"@expo/vector-icons": "^14.0.2", "@expo/vector-icons": "^14.0.2",
"@react-native-async-storage/async-storage": "^1.24.0",
"@react-navigation/native": "^6.0.2", "@react-navigation/native": "^6.0.2",
"@teovilla/react-native-web-maps": "^0.9.5", "@teovilla/react-native-web-maps": "^0.9.5",
"@uiw/react-color-circle": "^2.3.0", "@uiw/react-color-circle": "^2.3.0",
"axios": "^1.7.2",
"date-fns": "^3.6.0", "date-fns": "^3.6.0",
"deprecated-react-native-prop-types": "^5.0.0", "deprecated-react-native-prop-types": "^5.0.0",
"expo": "~51.0.22", "expo": "~51.0.22",
@ -40,6 +42,7 @@
"react-native-leaflet-view": "^0.1.2", "react-native-leaflet-view": "^0.1.2",
"react-native-maps": "1.14.0", "react-native-maps": "1.14.0",
"react-native-reanimated": "~3.10.1", "react-native-reanimated": "~3.10.1",
"react-native-render-html": "^6.3.4",
"react-native-safe-area-context": "4.10.5", "react-native-safe-area-context": "4.10.5",
"react-native-screens": "3.31.1", "react-native-screens": "3.31.1",
"react-native-svg": "^15.4.0", "react-native-svg": "^15.4.0",

53
services/request.tsx Normal file
View file

@ -0,0 +1,53 @@
import axios from 'axios';
const API_URL = 'https://kliko.maartenvr98.nl/api/v1/';
const CONFIG = {
timeout: 3000,
};
export class Request {
/**
* Send GET request to API
*
* @param url
* @param headers
* @returns {Promise<AxiosResponse<any>>}
*/
static get(url: string, headers = {}) {
return axios.get(API_URL + url, {
...CONFIG,
...headers,
});
}
/**
* Send POST request to API
*
* @param url
* @param body
* @param headers
* @returns {Promise<AxiosResponse<any>>}
*/
static post(url: string, body = {}, headers = {}) {
return axios.post(API_URL + url, body, {
...CONFIG,
...headers,
});
}
/**
* Send PUT request to API
*
* @param url
* @param body
* @param headers
* @returns {Promise<AxiosResponse<any>>}
*/
static put(url: string, body = {}, headers = {}) {
return axios.put(API_URL + url, body, {
...CONFIG,
...headers,
});
}
}

3740
yarn.lock

File diff suppressed because it is too large Load diff