This commit is contained in:
Maarten 2024-08-08 13:13:58 +02:00
parent fe82b69997
commit 3287b9a3b7
10 changed files with 813 additions and 813 deletions

View file

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

View file

@ -1,128 +1,128 @@
import {Stack} from 'expo-router'; import { Stack } from 'expo-router';
import {StyleSheet, TextInput, TouchableOpacity} from 'react-native'; import { StyleSheet, TextInput, TouchableOpacity } from 'react-native';
import {ThemedText} from '@/components/ThemedText'; import { ThemedText } from '@/components/ThemedText';
import {ThemedView} from '@/components/ThemedView'; import { ThemedView } from '@/components/ThemedView';
import {useToken} from '@/context/AppProvider'; import { useToken } from '@/context/AppProvider';
import {Colors} from '@/constants/Colors'; import { Colors } from '@/constants/Colors';
import React from 'react'; import React from 'react';
import {useColorScheme} from '@/hooks/useColorScheme'; import { useColorScheme } from '@/hooks/useColorScheme';
import {Message} from '@/services/message'; import { Message } from '@/services/message';
import {Request} from '@/services/request'; import { Request } from '@/services/request';
import { router } from 'expo-router'; import { router } from 'expo-router';
export default function OnboardStartScreen() { export default function OnboardStartScreen() {
const colorScheme = useColorScheme() ?? 'light'; const colorScheme = useColorScheme() ?? 'light';
const {setToken} = useToken(); const { setToken } = useToken();
const [name, setName] = React.useState('Maarten'); const [ name, setName ] = React.useState( 'Maarten' );
const [zipcode, setZipcode] = React.useState('6715GA'); const [ zipcode, setZipcode ] = React.useState( '6715GA' );
const [houseNumber, setHouseNumber] = React.useState('3'); const [ houseNumber, setHouseNumber ] = React.useState( '3' );
const start = () => { const start = () => {
if (name === '' || zipcode === '' || houseNumber === '') { if (name === '' || zipcode === '' || houseNumber === '') {
Message.error('Niet alle gegevens zijn ingevuld!'); Message.error( 'Niet alle gegevens zijn ingevuld!' );
return; return;
}
// TODO: Get device name
const deviceName = 'Test';
Request
.post( 'sessions/create', {
name: name,
zipcode: zipcode,
houseNumber: houseNumber,
device: deviceName,
} )
.then( (response) => {
if (!response.success) {
Message.error( response.message );
} else {
const token = response.token;
setToken( token );
router.replace( "/(tabs)" );
Message.success( response.message );
}
} );
} }
// TODO: Get device name return (
const deviceName = 'Test'; <>
<Stack.Screen options={{ title: 'Welkom' }}/>
<ThemedView style={styles.container}>
<ThemedView style={styles.heading}>
<ThemedText type="title">Welkom bij </ThemedText>
<ThemedText type="title" style={{ color: Colors[ colorScheme ].tint }}>Kliko</ThemedText>
</ThemedView>
Request <ThemedView style={styles.inputContainer}>
.post('sessions/create', { <ThemedText>Wat is je naam?</ThemedText>
name: name, <TextInput
zipcode: zipcode, style={styles.input}
houseNumber: houseNumber, onChangeText={setName}
device: deviceName, placeholder={'Je naam'}
}) value={name}
.then((response) => { />
if (!response.success) { </ThemedView>
Message.error(response.message);
} else {
const token = response.token;
setToken(token); <ThemedView style={styles.inputContainer}>
router.replace("/(tabs)"); <ThemedText>Wat is je postcode en huisnummer?</ThemedText>
<TextInput
Message.success(response.message); style={styles.input}
} onChangeText={setZipcode}
}); placeholder={'Postcode'}
} value={zipcode}
/>
return ( <TextInput
<> style={styles.input}
<Stack.Screen options={{title: 'Welkom'}}/> onChangeText={setHouseNumber}
<ThemedView style={styles.container}> placeholder={'Huisnummer'}
<ThemedView style={styles.heading}> value={houseNumber}
<ThemedText type="title">Welkom bij </ThemedText> keyboardType="numeric"
<ThemedText type="title" style={{color: Colors[colorScheme].tint}}>Kliko</ThemedText> />
</ThemedView> </ThemedView>
<ThemedView style={styles.inputContainer}>
<ThemedText>Wat is je naam?</ThemedText>
<TextInput
style={styles.input}
onChangeText={setName}
placeholder={'Je naam'}
value={name}
/>
</ThemedView>
<ThemedView style={styles.inputContainer}>
<ThemedText>Wat is je postcode en huisnummer?</ThemedText>
<TextInput
style={styles.input}
onChangeText={setZipcode}
placeholder={'Postcode'}
value={zipcode}
/>
<TextInput
style={styles.input}
onChangeText={setHouseNumber}
placeholder={'Huisnummer'}
value={houseNumber}
keyboardType="numeric"
/>
</ThemedView>
<TouchableOpacity style={{...styles.button, backgroundColor: Colors[colorScheme].tint}} onPress={start}> <TouchableOpacity style={{ ...styles.button, backgroundColor: Colors[ colorScheme ].tint }} onPress={start}>
<ThemedText style={{color: '#fff'}}>Start</ThemedText> <ThemedText style={{ color: '#fff' }}>Start</ThemedText>
</TouchableOpacity> </TouchableOpacity>
</ThemedView> </ThemedView>
</> </>
); );
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create( {
container: { container: {
padding: 20, padding: 20,
flex: 1, flex: 1,
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
}, },
heading: { heading: {
marginBottom: 30, marginBottom: 30,
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
}, },
inputContainer: { inputContainer: {
paddingTop: 20, paddingTop: 20,
}, },
input: { input: {
width: 250, width: 250,
borderWidth: 1, borderWidth: 1,
padding: 10, padding: 10,
paddingLeft: 20, paddingLeft: 20,
borderRadius: 3, borderRadius: 3,
marginBottom: 10, marginBottom: 10,
}, },
button: { button: {
borderRadius: 5, borderRadius: 5,
paddingTop: 10, paddingTop: 10,
paddingBottom: 10, paddingBottom: 10,
paddingLeft: 40, paddingLeft: 40,
paddingRight: 40, paddingRight: 40,
marginTop: 30, marginTop: 30,
}, },
}); } );

View file

@ -6,55 +6,55 @@ import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme'; import { useColorScheme } from '@/hooks/useColorScheme';
export default function TabLayout() { export default function TabLayout() {
const colorScheme = useColorScheme(); const colorScheme = useColorScheme();
return ( return (
<Tabs <Tabs
screenOptions={{ screenOptions={{
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint, tabBarActiveTintColor: Colors[ colorScheme ?? 'light' ].tint,
tabBarShowLabel: false, tabBarShowLabel: false,
headerShown: false, headerShown: false,
tabBarStyle: { tabBarStyle: {
height: 70, height: 70,
elevation: 0, elevation: 0,
} }
}}> }}>
<Tabs.Screen <Tabs.Screen
name="index" name="index"
options={{ options={{
title: 'Home', title: 'Home',
tabBarIcon: ({ color, focused }) => ( tabBarIcon: ({ color, focused }) => (
<TabBarIcon name={focused ? 'calendar' : 'calendar-outline'} color={color} /> <TabBarIcon name={focused ? 'calendar' : 'calendar-outline'} color={color}/>
), ),
}} }}
/> />
<Tabs.Screen <Tabs.Screen
name="map" name="map"
options={{ options={{
title: 'Map', title: 'Map',
tabBarIcon: ({ color, focused }) => ( tabBarIcon: ({ color, focused }) => (
<TabBarIcon name={focused ? 'map' : 'map-outline'} color={color} /> <TabBarIcon name={focused ? 'map' : 'map-outline'} color={color}/>
), ),
}} }}
/> />
<Tabs.Screen <Tabs.Screen
name="explore" name="explore"
options={{ options={{
title: 'Explore', title: 'Explore',
tabBarIcon: ({ color, focused }) => ( tabBarIcon: ({ color, focused }) => (
<TabBarIcon name={focused ? 'trash' : 'trash-outline'} color={color} /> <TabBarIcon name={focused ? 'trash' : 'trash-outline'} color={color}/>
), ),
}} }}
/> />
<Tabs.Screen <Tabs.Screen
name="settings" name="settings"
options={{ options={{
title: 'Settings', title: 'Settings',
tabBarIcon: ({ color, focused }) => ( tabBarIcon: ({ color, focused }) => (
<TabBarIcon name={focused ? 'settings' : 'settings-outline'} color={color} /> <TabBarIcon name={focused ? 'settings' : 'settings-outline'} color={color}/>
), ),
}} }}
/> />
</Tabs> </Tabs>
); );
} }

View file

@ -1,220 +1,220 @@
import { import {
StyleSheet, StyleSheet,
ScrollView, ScrollView,
SafeAreaView, SafeAreaView,
StatusBar, StatusBar,
Image, Image,
TouchableOpacity, TouchableOpacity,
View, View,
} from 'react-native'; } from 'react-native';
import React, {useState, useEffect, useRef} from 'react'; import React, { useState, useEffect, useRef } from 'react';
import {router} from 'expo-router'; import { router } from 'expo-router';
import type {AutocompleteDropdownRef} from 'react-native-autocomplete-dropdown' import type { AutocompleteDropdownRef } from 'react-native-autocomplete-dropdown'
import {AutocompleteDropdown} from 'react-native-autocomplete-dropdown'; import { AutocompleteDropdown } from 'react-native-autocomplete-dropdown';
import Modal from "react-native-modal"; import Modal from "react-native-modal";
import AsyncStorage from '@react-native-async-storage/async-storage'; 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 {Request} from '@/services/request'; import { Request } from '@/services/request';
import List from '@/components/List'; import List from '@/components/List';
export default function ExploreScreen() { export default function ExploreScreen() {
const colorScheme = useColorScheme() ?? 'light'; const colorScheme = useColorScheme() ?? 'light';
const [categories, setCategories] = useState([]); const [ categories, setCategories ] = useState( [] );
const [types, setTypes] = useState([]); const [ types, setTypes ] = useState( [] );
const [activeCategory, setActiveCategory] = useState<any | null>(null); const [ activeCategory, setActiveCategory ] = useState<any | null>( null );
const searchRef = useRef(null); const searchRef = useRef( null );
const dropdownController = useRef<AutocompleteDropdownRef | null>(null) const dropdownController = useRef<AutocompleteDropdownRef | null>( null )
useEffect(() => { useEffect( () => {
// Load categories // Load categories
Request.get('categories').then((response) => { Request.get( 'categories' ).then( (response) => {
const list: any = []; const list: any = [];
response.forEach((category: any, index: any) => { response.forEach( (category: any, index: any) => {
list.push({ list.push( {
id: index, id: index,
title: category.name, title: category.name,
category: category, category: category,
}) } )
}); } );
setCategories(list); setCategories( list );
}) } )
// Load waste types // Load waste types
Request.get('waste-types').then((response) => { Request.get( 'waste-types' ).then( (response) => {
setTypes(response); setTypes( response );
}); } );
}, []); }, [] );
// View selected item in modal // View selected item in modal
const selectItem = (item: any | null) => { const selectItem = (item: any | null) => {
if (item == null) { if (item == null) {
return; return;
}
// Clear select
dropdownController.current?.clear()
const { category } = item;
setActiveCategory( category );
} }
// Clear select // View item in sub screen
dropdownController.current?.clear() const viewItem = async (item: any) => {
await AsyncStorage.setItem( 'activeCategory', JSON.stringify( item ) );
const {category} = item; router.push( '/(explore)/category' );
setActiveCategory(category); };
}
// View item in sub screen return (
const viewItem = async (item: any) => { <SafeAreaView style={{ flex: 1, backgroundColor: Colors[ colorScheme ].background }}>
await AsyncStorage.setItem('activeCategory', JSON.stringify(item)); <ThemedView>
<ScrollView style={styles.container}>
<ThemedView style={styles.titleContainer}>
<ThemedText type="title">Wat moet waar?</ThemedText>
</ThemedView>
router.push('/(explore)/category'); <ThemedView style={styles.titleContainer}>
}; <ThemedText type="title" style={{ color: Colors[ colorScheme ].tint }}>en waarom?</ThemedText>
</ThemedView>
return ( <ThemedView style={styles.searchContainer}>
<SafeAreaView style={{flex: 1, backgroundColor: Colors[colorScheme].background}}> <ThemedText type="defaultSemiBold">Wat wilt u scheiden?</ThemedText>
<ThemedView> <AutocompleteDropdown
<ScrollView style={styles.container}> ref={searchRef}
<ThemedView style={styles.titleContainer}> controller={controller => {
<ThemedText type="title">Wat moet waar?</ThemedText> dropdownController.current = controller
</ThemedView> }}
textInputProps={{
placeholder: 'Wat wilt u scheiden?',
autoCorrect: false,
autoCapitalize: 'none',
}}
clearOnFocus={false}
closeOnBlur={false}
closeOnSubmit={false}
emptyResultText={'Niks gevonden'}
onSelectItem={selectItem}
dataSet={categories}
showClear={false}
/>
</ThemedView>
<ThemedView style={styles.titleContainer}> <ThemedView style={styles.categoriesContainer}>
<ThemedText type="title" style={{color: Colors[colorScheme].tint}}>en waarom?</ThemedText> <ThemedText type="defaultSemiBold">Of kies een categorie:</ThemedText>
</ThemedView> </ThemedView>
<ThemedView style={styles.searchContainer}> <ThemedView style={styles.listContainer}>
<ThemedText type="defaultSemiBold">Wat wilt u scheiden?</ThemedText> <List
<AutocompleteDropdown data={types}
ref={searchRef} renderItem={(item: any, index: any) => (
controller={controller => { <TouchableOpacity style={styles.listItem} key={index} onPress={() => viewItem( item )}>
dropdownController.current = controller <Image source={{ uri: item.image }} style={styles.listImage}/>
}} <ThemedText type="default">{item.name}</ThemedText>
textInputProps={{ </TouchableOpacity>
placeholder: 'Wat wilt u scheiden?', )}
autoCorrect: false, />
autoCapitalize: 'none', </ThemedView>
}} </ScrollView>
clearOnFocus={false} </ThemedView>
closeOnBlur={false}
closeOnSubmit={false}
emptyResultText={'Niks gevonden'}
onSelectItem={selectItem}
dataSet={categories}
showClear={false}
/>
</ThemedView>
<ThemedView style={styles.categoriesContainer}> <Modal isVisible={activeCategory !== null}>
<ThemedText type="defaultSemiBold">Of kies een categorie:</ThemedText> <ThemedView style={{ ...styles.modalView, backgroundColor: Colors[ colorScheme ].background }}>
</ThemedView> <ThemedText type="subtitle" style={{ marginBottom: 30, textAlign: 'center' }}>{activeCategory?.name}</ThemedText>
<ThemedView style={styles.listContainer}> <View style={styles.modalType}>
<List <Image source={{ uri: activeCategory?.type.image }} style={styles.modalTypeImage}/>
data={types} <ThemedText>{activeCategory?.type?.name}</ThemedText>
renderItem={(item: any, index: any) => ( </View>
<TouchableOpacity style={styles.listItem} key={index} onPress={() => viewItem(item)}>
<Image source={{uri: item.image}} style={styles.listImage}/>
<ThemedText type="default">{item.name}</ThemedText>
</TouchableOpacity>
)}
/>
</ThemedView>
</ScrollView>
</ThemedView>
<Modal isVisible={activeCategory !== null}> <View style={{ alignItems: 'center' }}>
<ThemedView style={{...styles.modalView, backgroundColor: Colors[colorScheme].background}}> <ThemedText type="defaultSemiBold">Opmerking:</ThemedText>
<ThemedText type="subtitle" style={{marginBottom: 30, textAlign: 'center'}}>{activeCategory?.name}</ThemedText> <ThemedText>{activeCategory?.description}</ThemedText>
</View>
<View style={styles.modalType}> <TouchableOpacity
<Image source={{uri: activeCategory?.type.image}} style={styles.modalTypeImage}/> onPress={() => setActiveCategory( null )}
<ThemedText>{activeCategory?.type?.name}</ThemedText> style={{ ...styles.modalClose, backgroundColor: Colors[ colorScheme ].tint }}>
</View> <ThemedText style={{ color: '#fff' }}>Sluiten</ThemedText>
</TouchableOpacity>
<View style={{alignItems: 'center'}}> </ThemedView>
<ThemedText type="defaultSemiBold">Opmerking:</ThemedText> </Modal>
<ThemedText>{activeCategory?.description}</ThemedText> </SafeAreaView>
</View> );
<TouchableOpacity
onPress={() => setActiveCategory(null)}
style={{...styles.modalClose, backgroundColor: Colors[colorScheme].tint}}>
<ThemedText style={{color: '#fff'}}>Sluiten</ThemedText>
</TouchableOpacity>
</ThemedView>
</Modal>
</SafeAreaView>
);
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create( {
container: { container: {
marginTop: StatusBar.currentHeight, marginTop: StatusBar.currentHeight,
padding: 25, padding: 25,
}, },
titleContainer: { titleContainer: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
gap: 8, gap: 8,
paddingBottom: 8, paddingBottom: 8,
}, },
searchContainer: { searchContainer: {
marginTop: 10, marginTop: 10,
}, },
categoriesContainer: { categoriesContainer: {
marginTop: 25, marginTop: 25,
}, },
listContainer: { listContainer: {
marginTop: 10, marginTop: 10,
paddingBottom: 50 paddingBottom: 50
}, },
listItem: { listItem: {
flex: 1, flex: 1,
display: 'flex', display: 'flex',
gap: 8, gap: 8,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingBottom: 10, paddingBottom: 10,
marginBottom: 10, marginBottom: 10,
borderBottomWidth: 1, borderBottomWidth: 1,
borderBottomColor: '#f2f2f2', borderBottomColor: '#f2f2f2',
}, },
listImage: { listImage: {
width: 30, width: 30,
height: 30, height: 30,
borderRadius: 5, borderRadius: 5,
marginRight: 8, marginRight: 8,
}, },
modalView: { modalView: {
margin: 20, margin: 20,
borderRadius: 5, borderRadius: 5,
padding: 35, padding: 35,
alignItems: 'center', alignItems: 'center',
textAlign: 'center', textAlign: 'center',
}, },
modalType: { modalType: {
flex: 1, flex: 1,
display: 'flex', display: 'flex',
gap: 8, gap: 8,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingBottom: 10, paddingBottom: 10,
marginBottom: 30, marginBottom: 30,
}, },
modalTypeImage: { modalTypeImage: {
width: 40, width: 40,
height: 40, height: 40,
borderRadius: 5, borderRadius: 5,
marginRight: 8, marginRight: 8,
}, },
modalClose: { modalClose: {
borderRadius: 5, borderRadius: 5,
paddingTop: 10, paddingTop: 10,
paddingBottom: 10, paddingBottom: 10,
paddingLeft: 40, paddingLeft: 40,
paddingRight: 40, paddingRight: 40,
marginTop: 30, marginTop: 30,
}, },
}); } );

View file

@ -1,171 +1,171 @@
import React, {useEffect, useState} from 'react'; import React, { useEffect, useState } from 'react';
import {StyleSheet, ScrollView, SafeAreaView, View, StatusBar, TouchableOpacity, Image} from 'react-native'; import { StyleSheet, ScrollView, SafeAreaView, View, StatusBar, TouchableOpacity, Image } from 'react-native';
// @ts-ignore // @ts-ignore
import CalendarPicker from 'react-native-calendar-picker'; import CalendarPicker from 'react-native-calendar-picker';
import Ionicons from '@expo/vector-icons/Ionicons'; import Ionicons from '@expo/vector-icons/Ionicons';
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 {useToken} from '@/context/AppProvider'; import { useToken } from '@/context/AppProvider';
import {Request} from '@/services/request'; import { Request } from '@/services/request';
import List from '@/components/List'; import List from '@/components/List';
import { useIsFocused } from '@react-navigation/core'; import { useIsFocused } from '@react-navigation/core';
export default function HomeScreen() { export default function HomeScreen() {
const colorScheme = useColorScheme() ?? 'light'; const colorScheme = useColorScheme() ?? 'light';
const isFocused = useIsFocused(); const isFocused = useIsFocused();
const {token, isLoading} = useToken(); const { token, isLoading } = useToken();
const [name, setName] = useState(' '); // Default empty space to prevent layout shifting const [ name, setName ] = useState( ' ' ); // Default empty space to prevent layout shifting
const [dates, setDates] = useState<any | null>([]); const [ dates, setDates ] = useState<any | null>( [] );
const [types, setTypes] = useState<any | null>([]); const [ types, setTypes ] = useState<any | null>( [] );
useEffect(() => { useEffect( () => {
if (token) { if (token) {
Request.post('calendar', {token: token}).then((response) => { Request.post( 'calendar', { token: token } ).then( (response) => {
if (response.success) { if (response.success) {
// Set name // Set name
setName(response.name); setName( response.name );
// Set dates // Set dates
let calendarDates: any[] = []; let calendarDates: any[] = [];
response.dates.forEach((date: any) => { response.dates.forEach( (date: any) => {
calendarDates.push({ calendarDates.push( {
date: new Date(date.date), date: new Date( date.date ),
style: {backgroundColor: date.color}, style: { backgroundColor: date.color },
textStyle: {color: Colors.white}, textStyle: { color: Colors.white },
allowDisabled: true, allowDisabled: true,
}) } )
}) } )
setDates(calendarDates); setDates( calendarDates );
// Set types // Set types
setTypes(response.types); setTypes( response.types );
}
} )
} }
}) }, [ isFocused ] );
}
}, [isFocused]);
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}>
<ScrollView> <ScrollView>
<ThemedView style={styles.titleContainer}> <ThemedView style={styles.titleContainer}>
<ThemedText type="title">{getGreeting()},</ThemedText> <ThemedText type="title">{getGreeting()},</ThemedText>
</ThemedView> </ThemedView>
<ThemedView style={styles.titleContainer}> <ThemedView style={styles.titleContainer}>
<ThemedText type="title" style={{color: Colors[colorScheme].tint}}>{name}</ThemedText> <ThemedText type="title" style={{ color: Colors[ colorScheme ].tint }}>{name}</ThemedText>
</ThemedView> </ThemedView>
<ThemedView style={styles.calendarContainer}> <ThemedView style={styles.calendarContainer}>
<CalendarPicker <CalendarPicker
showDayStragglers={true} showDayStragglers={true}
enableDateChange={false} enableDateChange={false}
customDatesStyles={dates} customDatesStyles={dates}
todayTextStyle={styles.today} todayTextStyle={styles.today}
previousComponent={ previousComponent={
<Ionicons size={28} name="chevron-back"/> <Ionicons size={28} name="chevron-back"/>
} }
nextComponent={ nextComponent={
<Ionicons size={28} name="chevron-forward"/> <Ionicons size={28} name="chevron-forward"/>
} }
weekdays={["Zo", "Ma", "Di", "Woe", "Do", "Vrij", "Zat"]} weekdays={[ "Zo", "Ma", "Di", "Woe", "Do", "Vrij", "Zat" ]}
months={[ months={[
"Januari", "Januari",
"Februari", "Februari",
"Maart", "Maart",
"April", "April",
"Mei", "Mei",
"Juni", "Juni",
"Juli", "Juli",
"Augustus", "Augustus",
"September", "September",
"Oktober", "Oktober",
"November", "November",
"December", "December",
]} ]}
/> />
</ThemedView> </ThemedView>
<ThemedView style={styles.legendaContainer}> <ThemedView style={styles.legendaContainer}>
<ThemedText type='subtitle'>Legenda:</ThemedText> <ThemedText type='subtitle'>Legenda:</ThemedText>
<List <List
data={types} data={types}
viewStyle={styles.legendaList} viewStyle={styles.legendaList}
renderItem={(type: any, index: any) => ( renderItem={(type: any, index: any) => (
<View style={styles.legendaItem} key={index}> <View style={styles.legendaItem} key={index}>
<View style={{...styles.circle, backgroundColor: type.color}}/> <View style={{ ...styles.circle, backgroundColor: type.color }}/>
<ThemedText type="default">{ type.name }</ThemedText> <ThemedText type="default">{type.name}</ThemedText>
</View> </View>
)} )}
/> />
</ThemedView> </ThemedView>
</ScrollView> </ScrollView>
</ThemedView> </ThemedView>
</SafeAreaView> </SafeAreaView>
); );
} }
function getGreeting() { function getGreeting() {
const myDate = new Date(); const myDate = new Date();
const hours = myDate.getHours(); const hours = myDate.getHours();
if (hours < 12) { if (hours < 12) {
return 'Goedemorgen'; return 'Goedemorgen';
} else if (hours >= 12 && hours <= 17) { } else if (hours >= 12 && hours <= 17) {
return 'Goedemiddag'; return 'Goedemiddag';
} else if (hours >= 17 && hours <= 24) { } else if (hours >= 17 && hours <= 24) {
return 'Goedenavond'; return 'Goedenavond';
} }
return 'Hallo'; return 'Hallo';
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create( {
container: { container: {
padding: 25, padding: 25,
marginTop: StatusBar.currentHeight, marginTop: StatusBar.currentHeight,
}, },
titleContainer: { titleContainer: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
gap: 8, gap: 8,
paddingBottom: 8, paddingBottom: 8,
}, },
calendarContainer: { calendarContainer: {
marginTop: 15, marginTop: 15,
}, },
legendaContainer: { legendaContainer: {
marginTop: 40, marginTop: 40,
}, },
legendaList: { legendaList: {
marginTop: 15, marginTop: 15,
flex: 1, flex: 1,
flexDirection: 'column', flexDirection: 'column',
alignContent: 'center', alignContent: 'center',
gap: 15, gap: 15,
}, },
legendaItem: { legendaItem: {
flex: 1, flex: 1,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
gap: 8, gap: 8,
flexGrow: 1, flexGrow: 1,
}, },
circle: { circle: {
width: 25, width: 25,
height: 25, height: 25,
borderRadius: 50, borderRadius: 50,
}, },
today: { today: {
borderWidth: 3, borderWidth: 3,
borderColor: Colors.dark.grey, borderColor: Colors.dark.grey,
borderRadius: 25, borderRadius: 25,
textAlign: 'center', textAlign: 'center',
paddingTop: 8, paddingTop: 8,
width: 40, width: 40,
height: 40, height: 40,
}, },
}); } );

View file

@ -1,194 +1,194 @@
import React, {useEffect, useState} from 'react'; import React, { useEffect, useState } from 'react';
import {Image, SafeAreaView, ScrollView, StatusBar, StyleSheet, Switch, View, Dimensions} from 'react-native'; import { Image, SafeAreaView, ScrollView, StatusBar, StyleSheet, Switch, View, Dimensions } from 'react-native';
import Mapbox, {Callout, Camera, MapView, PointAnnotation} from "@rnmapbox/maps"; import Mapbox, { Callout, Camera, MapView, PointAnnotation } from "@rnmapbox/maps";
Mapbox.setAccessToken("pk.eyJ1IjoibWFhcnRlbnZyOTgiLCJhIjoiY2x6ZDFqMGp1MGVyejJrczhqcXpvYm9iYiJ9.XvYcL62dWiJQiFmG6mOoug"); Mapbox.setAccessToken( "pk.eyJ1IjoibWFhcnRlbnZyOTgiLCJhIjoiY2x6ZDFqMGp1MGVyejJrczhqcXpvYm9iYiJ9.XvYcL62dWiJQiFmG6mOoug" );
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 List from '@/components/List'; import List from '@/components/List';
import {Request} from '@/services/request'; import { Request } from '@/services/request';
export default function MapScreen() { export default function MapScreen() {
const colorScheme = useColorScheme() ?? 'light'; const colorScheme = useColorScheme() ?? 'light';
const [types, setTypes] = useState<any>([]); const [ types, setTypes ] = useState<any>( [] );
const [markers, setMarkers] = useState<any>([]); const [ markers, setMarkers ] = useState<any>( [] );
// Load markers and types // Load markers and types
useEffect(() => { useEffect( () => {
Mapbox.setTelemetryEnabled(false); Mapbox.setTelemetryEnabled( false );
Request.get('locations').then((response) => { Request.get( 'locations' ).then( (response) => {
const {locations, types} = response; const { locations, types } = response;
// Set types // Set types
const typesList: any[] = []; const typesList: any[] = [];
types.forEach((type: any) => { types.forEach( (type: any) => {
typesList.push({ typesList.push( {
name: type.name, name: type.name,
image: type.image, image: type.image,
isEnabled: false, isEnabled: false,
type: type.config_name, type: type.config_name,
}); } );
}) } )
setTypes(typesList); setTypes( typesList );
// Set markers // Set markers
setMarkers(locations); setMarkers( locations );
}) } )
}, []); }, [] );
// Enable/disable type // Enable/disable type
const toggleSwitch = (index: any) => { const toggleSwitch = (index: any) => {
const newData = types.map((item: any, key: any) => const newData = types.map( (item: any, key: any) =>
key === index ? {...item, isEnabled: !item.isEnabled} : item key === index ? { ...item, isEnabled: !item.isEnabled } : item
); );
setTypes(newData); setTypes( newData );
}; };
// Get all types that are active // Get all types that are active
const getActiveTypes = (): Array<String> => { const getActiveTypes = (): Array<String> => {
const list: any[] = []; const list: any[] = [];
types.forEach((type: any) => { types.forEach( (type: any) => {
if (type.isEnabled) { if (type.isEnabled) {
list.push(type.type); list.push( type.type );
} }
}); } );
return list; return list;
} }
// Get all markers that needs to be visible // Get all markers that needs to be visible
const activeMarkers = () => { const activeMarkers = () => {
return markers.filter((marker: any) => { return markers.filter( (marker: any) => {
return getActiveTypes().includes(marker.waste_type); return getActiveTypes().includes( marker.waste_type );
}); } );
} }
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}>
<ThemedView> <ThemedView>
<ThemedView style={styles.titleContainer}> <ThemedView style={styles.titleContainer}>
<ThemedText type="title">Afvalcontainers</ThemedText> <ThemedText type="title">Afvalcontainers</ThemedText>
</ThemedView>
<ThemedView style={styles.titleContainer}>
<ThemedText type="title" style={{color: Colors[colorScheme].tint}}>in de buurt</ThemedText>
</ThemedView>
<ThemedView style={styles.mapContainer}>
<MapView
style={styles.map}
logoEnabled={false}
scaleBarEnabled={false}
attributionEnabled={false}
>
<Camera
centerCoordinate={[5.630960, 52.043420]}
zoomLevel={13}
animationMode={'none'}
/>
{activeMarkers().map((marker: any, index: any) => (
<PointAnnotation
key={marker.id.toString()}
id={marker.id.toString()}
coordinate={marker.coordinate}
>
<Callout title={marker.number}>
<ThemedView style={styles.callout}>
<ThemedText>{marker.description + ' - ' + marker.street}</ThemedText>
</ThemedView> </ThemedView>
</Callout>
</PointAnnotation>
))}
</MapView>
</ThemedView>
</ThemedView>
<ScrollView style={styles.listContainer}> <ThemedView style={styles.titleContainer}>
<List <ThemedText type="title" style={{ color: Colors[ colorScheme ].tint }}>in de buurt</ThemedText>
data={types} </ThemedView>
renderItem={(item: any, index: any) => (
<ThemedView style={styles.listItem} key={index}>
<View style={styles.listItemTitle}>
<Image source={{uri: item.image}} style={styles.listImage}/>
<ThemedText type="default">{item.name}</ThemedText>
</View>
<Switch <ThemedView style={styles.mapContainer}>
trackColor={{false: '#767577', true: Colors[colorScheme].tint}} <MapView
thumbColor={'#fff'} style={styles.map}
value={item.isEnabled} logoEnabled={false}
onValueChange={() => toggleSwitch(index)} scaleBarEnabled={false}
/> attributionEnabled={false}
</ThemedView> >
)}/> <Camera
</ScrollView> centerCoordinate={[ 5.630960, 52.043420 ]}
</ThemedView> zoomLevel={13}
</SafeAreaView> animationMode={'none'}
); />
{activeMarkers().map( (marker: any, index: any) => (
<PointAnnotation
key={marker.id.toString()}
id={marker.id.toString()}
coordinate={marker.coordinate}
>
<Callout title={marker.number}>
<ThemedView style={styles.callout}>
<ThemedText>{marker.description + ' - ' + marker.street}</ThemedText>
</ThemedView>
</Callout>
</PointAnnotation>
) )}
</MapView>
</ThemedView>
</ThemedView>
<ScrollView style={styles.listContainer}>
<List
data={types}
renderItem={(item: any, index: any) => (
<ThemedView style={styles.listItem} key={index}>
<View style={styles.listItemTitle}>
<Image source={{ uri: item.image }} style={styles.listImage}/>
<ThemedText type="default">{item.name}</ThemedText>
</View>
<Switch
trackColor={{ false: '#767577', true: Colors[ colorScheme ].tint }}
thumbColor={'#fff'}
value={item.isEnabled}
onValueChange={() => toggleSwitch( index )}
/>
</ThemedView>
)}/>
</ScrollView>
</ThemedView>
</SafeAreaView>
);
} }
const styles = StyleSheet.create({ const styles = StyleSheet.create( {
container: { container: {
marginTop: StatusBar.currentHeight, marginTop: StatusBar.currentHeight,
paddingTop: 25, paddingTop: 25,
flex: 1, flex: 1,
}, },
titleContainer: { titleContainer: {
paddingLeft: 25, paddingLeft: 25,
paddingRight: 25, paddingRight: 25,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
gap: 8, gap: 8,
paddingBottom: 8, paddingBottom: 8,
}, },
mapContainer: { mapContainer: {
marginTop: 15, marginTop: 15,
height: 400, height: 400,
}, },
map: { map: {
width: Dimensions.get('window').width, width: Dimensions.get( 'window' ).width,
height: 400, height: 400,
flex: 1, flex: 1,
}, },
listContainer: { listContainer: {
flex: 1, flex: 1,
paddingTop: 20, paddingTop: 20,
paddingLeft: 25, paddingLeft: 25,
paddingRight: 25, paddingRight: 25,
}, },
listItem: { listItem: {
flex: 1, flex: 1,
display: 'flex', display: 'flex',
justifyContent: 'space-between', justifyContent: 'space-between',
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
paddingBottom: 10, paddingBottom: 10,
marginBottom: 10, marginBottom: 10,
borderBottomWidth: 1, borderBottomWidth: 1,
borderBottomColor: '#f2f2f2', borderBottomColor: '#f2f2f2',
}, },
listItemTitle: { listItemTitle: {
flex: 1, flex: 1,
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
}, },
listImage: { listImage: {
width: 30, width: 30,
height: 30, height: 30,
borderRadius: 5, borderRadius: 5,
marginRight: 8, marginRight: 8,
}, },
callout: { callout: {
width: 200, width: 200,
padding: 5, padding: 5,
}, },
}); } );

View file

@ -64,7 +64,7 @@ export default function SettingsScreen() {
// Handle // Handle
const handleSave = (inputValues: Record<string, string>) => { const handleSave = (inputValues: Record<string, string>) => {
let postData: any = {token: token}; let postData: any = { token: token };
if (inputValues.name) { if (inputValues.name) {
postData[ 'name' ] = inputValues.name; postData[ 'name' ] = inputValues.name;
@ -80,7 +80,7 @@ export default function SettingsScreen() {
Request.post( 'sessions/update', postData ) Request.post( 'sessions/update', postData )
.then( (response) => { .then( (response) => {
console.log('save', response); console.log( 'save', response );
if (response.success) { if (response.success) {
setSessionData( response.session ); setSessionData( response.session );
@ -90,9 +90,9 @@ export default function SettingsScreen() {
Message.error( response.message ); Message.error( response.message );
} }
} ) } )
.catch((error) => { .catch( (error) => {
console.log('error', error); console.log( 'error', error );
}) } )
}; };
const logout = () => { const logout = () => {

View file

@ -6,26 +6,26 @@ import { type PropsWithChildren } from 'react';
* The contents of this function only run in Node.js environments and do not have access to the DOM or browser APIs. * The contents of this function only run in Node.js environments and do not have access to the DOM or browser APIs.
*/ */
export default function Root({ children }: PropsWithChildren) { export default function Root({ children }: PropsWithChildren) {
return ( return (
<html lang="en"> <html lang="en">
<head> <head>
<meta charSet="utf-8" /> <meta charSet="utf-8"/>
<meta httpEquiv="X-UA-Compatible" content="IE=edge" /> <meta httpEquiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>
{/* {/*
Disable body scrolling on web. This makes ScrollView components work closer to how they do on native. Disable body scrolling on web. This makes ScrollView components work closer to how they do on native.
However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line. However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line.
*/} */}
<ScrollViewStyleReset /> <ScrollViewStyleReset/>
{/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */} {/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */}
<style dangerouslySetInnerHTML={{ __html: responsiveBackground }} /> <style dangerouslySetInnerHTML={{ __html: responsiveBackground }}/>
{/* Add any additional <head> elements that you want globally available on web... */} {/* Add any additional <head> elements that you want globally available on web... */}
</head> </head>
<body>{children}</body> <body>{children}</body>
</html> </html>
); );
} }
const responsiveBackground = ` const responsiveBackground = `

View file

@ -1,47 +1,47 @@
import {useFonts} from 'expo-font'; import { useFonts } from 'expo-font';
import {Slot, Stack} from 'expo-router'; import { Slot, Stack } from 'expo-router';
import * as SplashScreen from 'expo-splash-screen'; import * as SplashScreen from 'expo-splash-screen';
import {useEffect} from 'react'; import { useEffect } from 'react';
import 'react-native-reanimated'; import 'react-native-reanimated';
import {AppProvider} from '@/context/AppProvider'; import { AppProvider } from '@/context/AppProvider';
import {DarkTheme, DefaultTheme, ThemeProvider} from '@react-navigation/native'; import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
import Toast from 'react-native-toast-message'; import Toast from 'react-native-toast-message';
import {AutocompleteDropdownContextProvider} from 'react-native-autocomplete-dropdown'; import { AutocompleteDropdownContextProvider } from 'react-native-autocomplete-dropdown';
import {useColorScheme} from '@/hooks/useColorScheme'; import { useColorScheme } from '@/hooks/useColorScheme';
// Prevent the splash screen from auto-hiding before asset loading is complete. // Prevent the splash screen from auto-hiding before asset loading is complete.
// SplashScreen.preventAutoHideAsync(); // SplashScreen.preventAutoHideAsync();
export default function RootLayout() { export default function RootLayout() {
const colorScheme = useColorScheme(); const colorScheme = useColorScheme();
const [loaded] = useFonts({ const [ loaded ] = useFonts( {
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), SpaceMono: require( '../assets/fonts/SpaceMono-Regular.ttf' ),
}); } );
useEffect(() => { useEffect( () => {
if (loaded) { if (loaded) {
// SplashScreen.hideAsync(); // SplashScreen.hideAsync();
}
}, [ loaded ] );
if (!loaded) {
return null;
} }
}, [loaded]);
if (!loaded) { return (
return null; <AppProvider>
} <AutocompleteDropdownContextProvider>
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
return ( <Stack>
<AppProvider> <Stack.Screen name="(tabs)" options={{ headerShown: false }}/>
<AutocompleteDropdownContextProvider> <Stack.Screen name="(onboarding)/start" options={{ headerShown: false }}/>
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}> <Stack.Screen name="index" options={{ headerShown: false }}/>
<Stack> </Stack>
<Stack.Screen name="(tabs)" options={{headerShown: false}}/> <Toast/>
<Stack.Screen name="(onboarding)/start" options={{headerShown: false}}/> </ThemeProvider>
<Stack.Screen name="index" options={{headerShown: false}}/> </AutocompleteDropdownContextProvider>
</Stack> </AppProvider>
<Toast/> );
</ThemeProvider>
</AutocompleteDropdownContextProvider>
</AppProvider>
);
} }

View file

@ -1,45 +1,45 @@
import React, {useEffect} from 'react'; import React, { useEffect } from 'react';
import {Redirect, router, useRouter} from 'expo-router'; import { Redirect, router, useRouter } from 'expo-router';
import {ThemedText} from '@/components/ThemedText'; import { ThemedText } from '@/components/ThemedText';
import {ThemedView} from '@/components/ThemedView'; import { ThemedView } from '@/components/ThemedView';
import {useToken} from '@/context/AppProvider'; import { useToken } from '@/context/AppProvider';
import {Request} from '@/services/request'; import { Request } from '@/services/request';
export default function OnboardStartScreen() { export default function OnboardStartScreen() {
const { token, isLoading } = useToken(); const { token, isLoading } = useToken();
const router = useRouter(); const router = useRouter();
const loadingScreen = () => ( const loadingScreen = () => (
<ThemedView> <ThemedView>
<ThemedText>Laden...</ThemedText> <ThemedText>Laden...</ThemedText>
</ThemedView> </ThemedView>
); );
useEffect(() => { useEffect( () => {
const fetchData = async () => { const fetchData = async () => {
const response = await Request.post('sessions/get', { token: token }); const response = await Request.post( 'sessions/get', { token: token } );
if (response.success) { if (response.success) {
// @ts-ignore // @ts-ignore
router.replace('/(tabs)/settings'); router.replace( '/(tabs)/settings' );
} else { } else {
router.replace('/(onboarding)/start'); router.replace( '/(onboarding)/start' );
} }
}; };
if (!isLoading && token) { if (!isLoading && token) {
fetchData(); fetchData();
}
}, [ isLoading, token, router ] );
if (isLoading) {
return loadingScreen();
}
if (!token) {
return <Redirect href="/(onboarding)/start"/>;
} }
}, [isLoading, token, router]);
if (isLoading) {
return loadingScreen(); return loadingScreen();
}
if (!token) {
return <Redirect href="/(onboarding)/start" />;
}
return loadingScreen();
} }