diff --git a/app/ProfileScreen.tsx b/app/ProfileScreen.tsx index 704cf59..7fbecc4 100644 --- a/app/ProfileScreen.tsx +++ b/app/ProfileScreen.tsx @@ -1,9 +1,11 @@ -import React, { useEffect, useState } from 'react'; -import { Platform } from "react-native"; -import { Button, TextInput, Dialog, Portal, Avatar, useTheme } from "react-native-paper"; +import React, { useEffect, useState, useContext } from 'react'; +import { Platform, View, TouchableOpacity, StyleSheet } from "react-native"; +import { Button, TextInput, Dialog, Portal, Avatar, useTheme, Text } from "react-native-paper"; import { Asset } from 'expo-asset'; import * as FileSystem from 'expo-file-system'; import * as ImagePicker from "expo-image-picker"; +import { themes } from '@/app/themes'; +import { featureFlags } from '@/featureFlags'; interface ProfileScreenProps { visible: boolean; @@ -13,12 +15,14 @@ interface ProfileScreenProps { image: string; setImage: (image: string) => void; setChanged: (dataChanged: boolean) => void; + setTheme: (theme: string) => void; onClose: () => void; } -const ProfileScreen: React.FC = ({ visible, name, setName, image, setImage, setChanged, onClose }) => { - const { colors } = useTheme(); +const ProfileScreen: React.FC = ({ visible, name, setName, image, setImage, setChanged, setTheme, onClose }) => { + const theme = useTheme(); const isNameEmpty = !name.trim(); + const themeColors = ['red', 'blue', 'yellow', 'green', 'orange', 'purple']; // Track the initial values when the component first mounts const [initialName, setInitialName] = useState(name); @@ -94,8 +98,8 @@ const ProfileScreen: React.FC = ({ visible, name, setName, i onClose(); } }} - style={{ backgroundColor: colors.background }}> - Edit Your Profile + style={{ backgroundColor: theme.colors.background }}> + Edit Your Profile = ({ visible, name, setName, i @@ -120,10 +124,26 @@ const ProfileScreen: React.FC = ({ visible, name, setName, i console.log("Name change"); } }} - style={{ marginBottom: 15, backgroundColor: colors.surface }} - placeholderTextColor={colors.onSurface} - theme={{ colors: { text: colors.onSurface } }} + style={{ marginBottom: 15, backgroundColor: theme.colors.surface }} + placeholderTextColor={theme.colors.onSurface} + theme={{ colors: { text: theme.colors.onSurface } }} /> + {featureFlags.enableThemeSelection && ( + <> + Choose Theme + + {themeColors.map((userTheme) => ( + {setTheme(userTheme); console.log("Changing Theme: ", userTheme)}} + > + + + ))} + + + )} + labelStyle={{ color: theme.colors.onPrimary }}>Save ); }; + +const styles = StyleSheet.create({ + themeContainer: { + flexDirection: 'row', + justifyContent: 'space-around', + marginTop: 10, + }, + themeButton: { + width: 50, + height: 50, + borderRadius: 25, + justifyContent: 'center', + alignItems: 'flex-start', + overflow: 'hidden', + borderWidth: 1, + borderColor: 'black', + }, + halfCircle: { + width: '50%', + height: '100%', + position: 'absolute', + bottom: 0, + }, +}); + export default ProfileScreen; diff --git a/app/_layout.tsx b/app/_layout.tsx index 279c411..7613c9f 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -6,17 +6,27 @@ import 'react-native-reanimated'; import { useColorScheme } from 'react-native'; import { PaperProvider, Provider } from "react-native-paper"; import { themes } from '@/app/themes' +import { UserProvider, useUser } from "@/context/UserContext"; // Prevent the splash screen from auto-hiding before asset loading is complete. SplashScreen.preventAutoHideAsync(); export default function RootLayout() { + return ( + + + + ); +} + +function InnerRootLayout() { + const { currentTheme } = useUser(); // Access the currentTheme from UserContext + console.log(currentTheme); const colorScheme = useColorScheme(); console.log(colorScheme); const [loaded] = useFonts({ SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), }); - useEffect(() => { if (loaded) { SplashScreen.hideAsync(); @@ -26,12 +36,17 @@ export default function RootLayout() { if (!loaded) { return null; } - const selectedTheme = 'green'; - //const [selectedTheme, setSelectedTheme] = useState<'red' | 'blue' | 'yellow' | 'green' | 'orange'>('red'); - const appTheme = themes[selectedTheme][colorScheme === 'dark' ? 'dark' : 'light']; + + // Ensure currentTheme is treated as a valid key, or fallback to 'blue' + const themeKey: 'blue' | 'green' | 'red' | 'yellow' | 'orange' = (currentTheme as 'blue' | 'green' | 'red' | 'yellow' | 'orange') || 'blue'; + + // Use the themeKey to index into the themes object + const appTheme = themes[themeKey][colorScheme === 'dark' ? 'dark' : 'light']; + + return ( - + diff --git a/app/index.tsx b/app/index.tsx index f5191f6..684508e 100644 --- a/app/index.tsx +++ b/app/index.tsx @@ -1,106 +1,28 @@ -import React, { useEffect, useState } from 'react'; -import {View, StyleSheet, Text, AppState } from "react-native"; +import React from 'react'; +import {View, StyleSheet, Text } from "react-native"; import { useTheme } from "react-native-paper"; -import { v4 as uuidv4 } from "uuid"; -import AsyncStorage from "@react-native-async-storage/async-storage"; import ProfileScreen from "@/app/ProfileScreen"; import StatusPage from "@/app/StatusPage"; import Nav from "@/app/Nav"; -import axios from "axios"; -export const API_URL = process.env.EXPO_PUBLIC_API_URL; +import { useUser } from "@/context/UserContext"; const Index = () => { const theme = useTheme(); - const [isProfileActive, setProfileActive] = useState(false); - const [userId, setUserId] = useState(""); - const [userName, setUserName] = useState(""); - const [userImage, setUserImage] = useState(""); - const [userStatus, setUserStatus] = useState("none"); - const [userDataChanged, setUserDataChanged] = useState(false); - const [isLoading, setIsLoading] = useState(true); // New loading state - const [appState, setAppState] = useState(AppState.currentState); - useEffect(() => { - const loadUserData = async () => { - try { - const storedUserId = await AsyncStorage.getItem("userId"); - const storedUserName = await AsyncStorage.getItem("userName"); - const storedUserImage = await AsyncStorage.getItem("userImage"); - console.log("User Id: ", storedUserId); - if (storedUserId) { - setUserId(storedUserId || uuidv4()); - setUserName(storedUserName || ""); - setUserImage(storedUserImage || ""); - setProfileActive(false); - } else { - setUserId(uuidv4()); - setUserName(""); - setUserImage(""); - setProfileActive(true); - } - console.log("Loading data ", userId); - } catch (error) { - console.error("Error loading user data:", error); - } finally { - setIsLoading(false); // Mark loading as complete - } - }; - loadUserData(); - }, []); - - useEffect(() => { - if (!userDataChanged) return; - - const saveUserData = async () => { - try { - console.log("Saving data ", userId); - await AsyncStorage.setItem("userId", userId); - await AsyncStorage.setItem("userName", userName); - await AsyncStorage.setItem("userImage", userImage); - setUserDataChanged(false); - } catch (error) { - console.error("Error saving user data:", error); - } - }; - saveUserData(); - }, [userDataChanged]); - - useEffect(() => { - const handleAppStateChange = (nextAppState: string) => { - //console.log("App state", appState); - //console.log("Next App state", nextAppState); - if (appState.match(/inactive|background/) && nextAppState === "active") { - // When the app comes to the foreground, fetch the status - if (!isLoading) { - fetchCurrentStatus().then() - } else { - console.log("Waiting for loading to complete before fetching status..."); - } - } - setAppState(AppState.currentState); - }; - - const listener = AppState.addEventListener("change", handleAppStateChange); - - // Cleanup listener on unmount - return () => { - listener.remove(); - }; - }, [appState]); - - const fetchCurrentStatus = async () => { - try { - const response = await axios.post(API_URL + "/get", { id: userId }); - console.log("response: ", response); - if (response.data?.status) { - setTimeout(() => { - setUserStatus("none"); // Reset status - }, 0) - } - } catch (error) { - console.error("Error fetching status:", error); - } - }; + const { + isProfileActive, + setProfileActive, + userId, + userName, + setUserName, + userImage, + setUserImage, + userStatus, + setUserStatus, + setUserDataChanged, + setTheme, + isLoading, + } = useUser(); if (isLoading) { console.log("Still loading"); @@ -131,6 +53,7 @@ const Index = () => { setName={setUserName} image={userImage} setImage={setUserImage} + setTheme={setTheme} setChanged={setUserDataChanged} onClose={() => setProfileActive(false)} /> diff --git a/context/UserContext.tsx b/context/UserContext.tsx new file mode 100644 index 0000000..aec5940 --- /dev/null +++ b/context/UserContext.tsx @@ -0,0 +1,155 @@ +import React, { createContext, useContext, useEffect, useState, ReactNode } from "react"; +import AsyncStorage from "@react-native-async-storage/async-storage"; +import { AppState } from "react-native"; +import { v4 as uuidv4 } from "uuid"; +import axios from "axios"; + +export const API_URL = process.env.EXPO_PUBLIC_API_URL; + +// Define context type +interface UserContextType { + isProfileActive: boolean; + setProfileActive: (active: boolean) => void; + userId: string; + userName: string; + setUserName: (name: string) => void; + userImage: string; + setUserImage: (image: string) => void; + userStatus: string; + setUserStatus: (status: string) => void; + setUserDataChanged: (changed: boolean) => void; + isLoading: boolean; + currentTheme: string; + setTheme: (theme: string) => void; +} + +// Create context with default values +const UserContext = createContext(undefined); + +// Define provider props type +interface UserProviderProps { + children: ReactNode; +} + +export const UserProvider: React.FC = ({ children }) => { + const [isProfileActive, setProfileActive] = useState(false); + const [userId, setUserId] = useState(""); + const [userName, setUserName] = useState(""); + const [userImage, setUserImage] = useState(""); + const [userStatus, setUserStatus] = useState("none"); + const [userDataChanged, setUserDataChanged] = useState(false); + const [isLoading, setIsLoading] = useState(true); + const [appState, setAppState] = useState(AppState.currentState); + const [currentTheme, setTheme] = useState(""); + + useEffect(() => { + const loadUserData = async () => { + try { + const storedUserId = await AsyncStorage.getItem("userId"); + const storedUserName = await AsyncStorage.getItem("userName"); + const storedUserImage = await AsyncStorage.getItem("userImage"); + const storedUserTheme = await AsyncStorage.getItem("theme"); + if (storedUserId) { + setUserId(storedUserId); + setUserName(storedUserName || ""); + setUserImage(storedUserImage || ""); + setTheme(storedUserTheme || "blue"); + setProfileActive(false); + } else { + setUserId(uuidv4()); + setUserName(""); + setUserImage(""); + setTheme("blue") + setProfileActive(true); + } + } catch (error) { + console.error("Error loading user data:", error); + } finally { + setIsLoading(false); + } + }; + + loadUserData(); + }, []); + + useEffect(() => { + if (!userDataChanged) return; + + const saveUserData = async () => { + try { + await AsyncStorage.setItem("userId", userId); + await AsyncStorage.setItem("userName", userName); + await AsyncStorage.setItem("userImage", userImage); + await AsyncStorage.setItem("theme", currentTheme); + setUserDataChanged(false); + } catch (error) { + console.error("Error saving user data:", error); + } + }; + + saveUserData(); + }, [userDataChanged]); + + useEffect(() => { + const handleAppStateChange = (nextAppState: string) => { + if (appState.match(/inactive|background/) && nextAppState === "active") { + if (!isLoading) { + fetchCurrentStatus(); + } else { + console.log("Waiting for loading to complete before fetching status..."); + } + } + setAppState(AppState.currentState); + }; + + const listener = AppState.addEventListener("change", handleAppStateChange); + + return () => { + listener.remove(); + }; + }, [appState]); + + const fetchCurrentStatus = async () => { + try { + const response = await axios.post(API_URL + "/get", { id: userId }); + if (response.data?.status) { + setTimeout(() => { + setUserStatus("none"); + }, 0); + } + } catch (error) { + console.error("Error fetching status:", error); + } + }; + + return ( + + {children} + + ); +}; + +// Custom hook to use context +export const useUser = (): UserContextType => { + const context = useContext(UserContext); + if (!context) { + throw new Error("useUser must be used within a UserProvider"); + } + return context; +}; diff --git a/featureFlags.ts b/featureFlags.ts new file mode 100644 index 0000000..39d109b --- /dev/null +++ b/featureFlags.ts @@ -0,0 +1,3 @@ +export const featureFlags = { + enableThemeSelection: true, // Toggle this to true or false to enable/disable the feature +};