diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx index 7b2f6b9..9492e5c 100644 --- a/app/(tabs)/index.tsx +++ b/app/(tabs)/index.tsx @@ -1,5 +1,5 @@ import React, { useEffect, useState } from 'react'; -import { StyleSheet, ScrollView, SafeAreaView, View, StatusBar, TouchableOpacity, Image } from 'react-native'; +import { StyleSheet, ScrollView, SafeAreaView, View, StatusBar } from 'react-native'; // @ts-ignore import CalendarPicker from 'react-native-calendar-picker'; import Ionicons from '@expo/vector-icons/Ionicons'; @@ -12,40 +12,50 @@ import { useToken } from '@/context/AppProvider'; import { Request } from '@/services/request'; import List from '@/components/List'; import { useIsFocused } from '@react-navigation/core'; +import { useSelector } from 'react-redux'; export default function HomeScreen() { const colorScheme = useColorScheme() ?? 'light'; const isFocused = useIsFocused(); + const session = useSelector((state: any) => state.data.session); + const reloadCalendar = useSelector((state: any) => state.data.reloadCalendar); const { token, isLoading } = useToken(); const [ name, setName ] = useState( ' ' ); // Default empty space to prevent layout shifting const [ dates, setDates ] = useState( [] ); const [ types, setTypes ] = useState( [] ); + // Load session + useEffect( () => { + setName(session.name); + }, [session, isFocused]); + useEffect( () => { if (token) { - Request.post( 'calendar', { token: token } ).then( (response) => { - if (response.success) { - // Set name - setName( response.name ); - - // Set dates - let calendarDates: any[] = []; - response.dates.forEach( (date: any) => { - calendarDates.push( { - date: new Date( date.date ), - style: { backgroundColor: date.color }, - textStyle: { color: Colors.white }, - allowDisabled: true, - } ) - } ) - setDates( calendarDates ); - - // Set types - setTypes( response.types ); - } - } ) + loadCalendar(); } - }, [ isFocused ] ); + }, [ reloadCalendar, isFocused ] ); + + // Load calendar data + const loadCalendar = () => { + Request.post( 'calendar', { token: token } ).then( (response) => { + if (response.success) { + // Set dates + let calendarDates: any[] = []; + response.dates.forEach( (date: any) => { + calendarDates.push( { + date: new Date( date.date ), + style: { backgroundColor: date.color }, + textStyle: { color: Colors.white }, + allowDisabled: true, + } ) + } ) + setDates( calendarDates ); + + // Set types + setTypes( response.types ); + } + } ) + } return ( diff --git a/app/(tabs)/map.tsx b/app/(tabs)/map.tsx index aaba273..ddcd6b4 100644 --- a/app/(tabs)/map.tsx +++ b/app/(tabs)/map.tsx @@ -10,13 +10,13 @@ import { Colors } from '@/constants/Colors'; import { useColorScheme } from '@/hooks/useColorScheme'; import List from '@/components/List'; import { Request } from '@/services/request'; -import { useToken } from '@/context/AppProvider'; import { useIsFocused } from '@react-navigation/core'; +import { useSelector } from 'react-redux'; export default function MapScreen() { const colorScheme = useColorScheme() ?? 'light'; - const { token, isLoading } = useToken(); const isFocused = useIsFocused(); + const session = useSelector((state: any) => state.data.session); const [ types, setTypes ] = useState( [] ); const [ coordinates, setCoordinates ] = useState( [] ); @@ -24,17 +24,8 @@ export default function MapScreen() { // Load session useEffect( () => { - if (token) { - Request.post( 'sessions/get', { token: token } ).then( (response) => { - console.log('session', response); - if (response.success) { - const { session } = response; - - setCoordinates([session.coordinates.longitude, session.coordinates.latitude]); - } - } ) - } - }, [isFocused]); + setCoordinates([session.coordinates.longitude, session.coordinates.latitude]); + }, [session, isFocused]); // Load markers and types useEffect( () => { diff --git a/app/(tabs)/settings.tsx b/app/(tabs)/settings.tsx index f1c0d04..a4c5d34 100644 --- a/app/(tabs)/settings.tsx +++ b/app/(tabs)/settings.tsx @@ -10,6 +10,7 @@ import { } from 'react-native'; import { useRouter } from 'expo-router'; import Ionicons from '@expo/vector-icons/Ionicons'; +import { useSelector } from 'react-redux'; import { ThemedText } from '@/components/ThemedText'; import { ThemedView } from '@/components/ThemedView'; @@ -19,10 +20,13 @@ import { useToken } from '@/context/AppProvider'; import { Message } from '@/services/message'; import { Request } from '@/services/request'; import CustomModal from '@/components/EditModal'; +import { store } from '@/store/store'; +import { setSession, setReloadCalendar } from '@/store/dataStore'; export default function SettingsScreen() { const colorScheme = useColorScheme() ?? 'light'; const { token, isLoading } = useToken(); + const session = useSelector((state: any) => state.data.session); const router = useRouter(); // Name @@ -37,22 +41,13 @@ export default function SettingsScreen() { const [ addressModalVisible, setAddressModalVisible ] = useState( false ); useEffect( () => { - // Load current session - if (token) { - Request.post( 'sessions/get', { token: token } ).then( (response) => { - if (response.success) { - const { session } = response; - - setSessionData( session ); - } - } ) - } - }, [] ); + setSessionData( session ); + }, [session] ); // Set session data in view const setSessionData = (session: any) => { // Name - setName( session.name ) + setName( session.name ); // Address setZipcode( session.address.zipcode ); @@ -61,7 +56,7 @@ export default function SettingsScreen() { setCity( session.address.city ); } - // Handle + // Handle save settings const handleSave = (inputValues: Record) => { let postData: any = { token: token }; @@ -69,21 +64,27 @@ export default function SettingsScreen() { postData[ 'name' ] = inputValues.name; } + let addressChanged = false; + if (inputValues.zipcode) { postData[ 'zipcode' ] = inputValues.zipcode; + addressChanged = true; } if (inputValues.houseNumber) { postData[ 'houseNumber' ] = inputValues.houseNumber; + addressChanged = true; } Request.post( 'sessions/update', postData ) .then( (response) => { - console.log( 'save', response ); - if (response.success) { setSessionData( response.session ); + // Save to store + store.dispatch(setSession(response.session)) + store.dispatch(setReloadCalendar(addressChanged)) + Message.success( 'Opgeslagen!' ) } else { Message.error( response.message ); @@ -91,6 +92,7 @@ export default function SettingsScreen() { } ); }; + // Remove session data and logout const logout = () => { Alert.alert( 'Uitloggen', 'Weet je het zeker?', [ { diff --git a/app/_layout.tsx b/app/_layout.tsx index f5ed052..dafa18e 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -1,17 +1,18 @@ import { useFonts } from 'expo-font'; -import { Slot, Stack } from 'expo-router'; -import * as SplashScreen from 'expo-splash-screen'; +import { Slot, SplashScreen, Stack } from 'expo-router'; +import { Provider } from 'react-redux'; import { useEffect } from 'react'; import 'react-native-reanimated'; - -import { AppProvider } from '@/context/AppProvider'; import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native'; import Toast from 'react-native-toast-message'; import { AutocompleteDropdownContextProvider } from 'react-native-autocomplete-dropdown'; + +import { AppProvider } from '@/context/AppProvider'; import { useColorScheme } from '@/hooks/useColorScheme'; +import { store } from '@/store/store'; // Prevent the splash screen from auto-hiding before asset loading is complete. -// SplashScreen.preventAutoHideAsync(); +SplashScreen.preventAutoHideAsync(); export default function RootLayout() { const colorScheme = useColorScheme(); @@ -22,7 +23,7 @@ export default function RootLayout() { useEffect( () => { if (loaded) { - // SplashScreen.hideAsync(); + SplashScreen.hideAsync(); } }, [ loaded ] ); @@ -31,17 +32,19 @@ export default function RootLayout() { } return ( - - - - - - - - - - - - + + + + + + + + + + + + + + ); } diff --git a/app/index.tsx b/app/index.tsx index fa2ba07..06c499c 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -5,6 +5,8 @@ import { ThemedText } from '@/components/ThemedText'; import { ThemedView } from '@/components/ThemedView'; import { useToken } from '@/context/AppProvider'; import { Request } from '@/services/request'; +import { store } from '@/store/store'; +import { setSession } from '@/store/dataStore'; export default function OnboardStartScreen() { @@ -21,6 +23,9 @@ export default function OnboardStartScreen() { const fetchData = async () => { const response = await Request.post( 'sessions/get', { token: token } ); if (response.success) { + // Save to store + store.dispatch(setSession(response.session)) + // @ts-ignore router.replace( '/(tabs)' ); } else { diff --git a/package.json b/package.json index 587083a..c1dbf4e 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "@react-native-async-storage/async-storage": "1.23.1", "@react-native-community/cli-platform-ios": "^14.0.0", "@react-navigation/native": "^6.0.2", + "@reduxjs/toolkit": "^2.2.7", "@rnmapbox/maps": "^10.1.28", "@uiw/react-color-circle": "^2.3.0", "axios": "^1.7.2", @@ -51,7 +52,8 @@ "react-native-svg": "15.2.0", "react-native-toast-message": "^2.2.0", "react-native-web": "~0.19.10", - "react-native-webview": "13.8.6" + "react-native-webview": "13.8.6", + "react-redux": "^9.1.2" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/store/dataStore.tsx b/store/dataStore.tsx new file mode 100644 index 0000000..febb06a --- /dev/null +++ b/store/dataStore.tsx @@ -0,0 +1,38 @@ +// dataStore.js +import { createSlice } from '@reduxjs/toolkit'; + +const dataStore = createSlice( { + name: 'data', + initialState: { + session: { + token: '', + name: '', + device: '', + address: { + id: 0, + zipcode: '', + houseNumber: '', + street: '', + city: '', + }, + coordinates: { + latitude: '', + longitude: '', + }, + }, + reloadCalendar: true, + }, + reducers: { + setSession: (state, action) => { + state.session = action.payload; + }, + setReloadCalendar: (state, action) => { + state.reloadCalendar = action.payload; + }, + }, +} ); + +export const { setSession } = dataStore.actions; +export const { setReloadCalendar } = dataStore.actions; + +export default dataStore.reducer; diff --git a/store/store.tsx b/store/store.tsx new file mode 100644 index 0000000..606b840 --- /dev/null +++ b/store/store.tsx @@ -0,0 +1,8 @@ +import { configureStore } from '@reduxjs/toolkit'; +import dataReducer from './dataStore'; + +export const store = configureStore({ + reducer: { + data: dataReducer, + }, +}); \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 897f50e..08df0a1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1983,6 +1983,16 @@ dependencies: nanoid "^3.1.23" +"@reduxjs/toolkit@^2.2.7": + version "2.2.7" + resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-2.2.7.tgz#199e3d10ccb39267cb5aee92c0262fd9da7fdfb2" + integrity sha512-faI3cZbSdFb8yv9dhDTmGwclW0vk0z5o1cia+kf7gCbaCwHI5e+7tP57mJUv22pNcNbeA62GSrPpfrUfdXcQ6g== + dependencies: + immer "^10.0.3" + redux "^5.0.1" + redux-thunk "^3.1.0" + reselect "^5.1.0" + "@remix-run/node@^2.7.2": version "2.10.3" resolved "https://registry.npmjs.org/@remix-run/node/-/node-2.10.3.tgz" @@ -2408,6 +2418,11 @@ resolved "https://registry.npmjs.org/@types/urijs/-/urijs-1.19.25.tgz" integrity sha512-XOfUup9r3Y06nFAZh3WvO0rBU4OtlfPB/vgxpjg+NRdGU6CN6djdc6OEiH+PcqHCY6eFLo9Ista73uarf4gnBg== +"@types/use-sync-external-store@^0.0.3": + version "0.0.3" + resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.3.tgz#b6725d5f4af24ace33b36fafd295136e75509f43" + integrity sha512-EwmlvuaxPNej9+T4v5AuBPJa2x2UOJVdjCtDHgcDqitUeOtjnJKJ+apYjVcAoBEMjKW1VVFGZLUb5+qqa09XFA== + "@types/yargs-parser@*": version "21.0.3" resolved "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz" @@ -4976,6 +4991,11 @@ image-size@^1.0.2: dependencies: queue "6.0.2" +immer@^10.0.3: + version "10.1.1" + resolved "https://registry.yarnpkg.com/immer/-/immer-10.1.1.tgz#206f344ea372d8ea176891545ee53ccc062db7bc" + integrity sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw== + import-fresh@^2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/import-fresh/-/import-fresh-2.0.0.tgz" @@ -7538,6 +7558,14 @@ react-native@0.74.3: ws "^6.2.2" yargs "^17.6.2" +react-redux@^9.1.2: + version "9.1.2" + resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-9.1.2.tgz#deba38c64c3403e9abd0c3fbeab69ffd9d8a7e4b" + integrity sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w== + dependencies: + "@types/use-sync-external-store" "^0.0.3" + use-sync-external-store "^1.0.0" + react-refresh@^0.14.0, react-refresh@^0.14.2: version "0.14.2" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz" @@ -7613,6 +7641,16 @@ recyclerlistview@^3.0.0: prop-types "15.5.8" ts-object-utils "0.0.5" +redux-thunk@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3" + integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw== + +redux@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/redux/-/redux-5.0.1.tgz#97fa26881ce5746500125585d5642c77b6e9447b" + integrity sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w== + regenerate-unicode-properties@^10.1.0: version "10.1.1" resolved "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-10.1.1.tgz" @@ -7698,6 +7736,11 @@ requires-port@^1.0.0: resolved "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz" integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== +reselect@^5.1.0: + version "5.1.1" + resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.1.1.tgz#c766b1eb5d558291e5e550298adb0becc24bb72e" + integrity sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w== + resolve-cwd@^3.0.0: version "3.0.0" resolved "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz" @@ -8857,6 +8900,11 @@ use-latest-callback@^0.2.1: resolved "https://registry.npmjs.org/use-latest-callback/-/use-latest-callback-0.2.1.tgz" integrity sha512-QWlq8Is8BGWBf883QOEQP5HWYX/kMI+JTbJ5rdtvJLmXTIh9XoHIO3PQcmQl8BU44VKxow1kbQUHa6mQSMALDQ== +use-sync-external-store@^1.0.0: + version "1.2.2" + resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.2.2.tgz#c3b6390f3a30eba13200d2302dcdf1e7b57b2ef9" + integrity sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw== + util-deprecate@^1.0.1, util-deprecate@~1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"