location-menu #5
@ -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<ProfileScreenProps> = ({ visible, name, setName, image, setImage, setChanged, onClose }) => {
|
||||
const { colors } = useTheme();
|
||||
const ProfileScreen: React.FC<ProfileScreenProps> = ({ 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<ProfileScreenProps> = ({ visible, name, setName, i
|
||||
onClose();
|
||||
}
|
||||
}}
|
||||
style={{ backgroundColor: colors.background }}>
|
||||
<Dialog.Title style={{ color: colors.onBackground, textAlign: 'center' }}>Edit Your Profile</Dialog.Title>
|
||||
style={{ backgroundColor: theme.colors.background }}>
|
||||
<Dialog.Title style={{ color: theme.colors.onBackground, textAlign: 'center' }}>Edit Your Profile</Dialog.Title>
|
||||
<Dialog.Content>
|
||||
<Avatar.Image
|
||||
size={100}
|
||||
@ -105,8 +109,8 @@ const ProfileScreen: React.FC<ProfileScreenProps> = ({ visible, name, setName, i
|
||||
<Button
|
||||
onPress={pickImage}
|
||||
mode="contained"
|
||||
style={{ backgroundColor: colors.primary, marginBottom: 10 }}
|
||||
labelStyle={{ color: colors.onPrimary }}
|
||||
style={{ backgroundColor: theme.colors.primary, marginBottom: 10 }}
|
||||
labelStyle={{ color: theme.colors.onPrimary }}
|
||||
>
|
||||
Change Profile Picture
|
||||
</Button>
|
||||
@ -120,10 +124,26 @@ const ProfileScreen: React.FC<ProfileScreenProps> = ({ 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 && (
|
||||
<>
|
||||
<Text style={{ color: theme.colors.onBackground, fontSize: 18, textAlign: 'center' }}>Choose Theme</Text>
|
||||
<View style={styles.themeContainer}>
|
||||
{themeColors.map((userTheme) => (
|
||||
<TouchableOpacity
|
||||
key={userTheme}
|
||||
style={[styles.themeButton, { backgroundColor: themes[userTheme as keyof typeof themes]['light'].colors.primary }]}
|
||||
onPress={() => {setTheme(userTheme); console.log("Changing Theme: ", userTheme)}}
|
||||
>
|
||||
<View style={[styles.halfCircle, { backgroundColor: themes[userTheme as keyof typeof themes]['dark'].colors.primary }]} />
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</Dialog.Content>
|
||||
<Dialog.Actions>
|
||||
<Button
|
||||
@ -131,17 +151,39 @@ const ProfileScreen: React.FC<ProfileScreenProps> = ({ visible, name, setName, i
|
||||
mode="contained"
|
||||
disabled={isNameEmpty} // Disable if name is empty
|
||||
style={{
|
||||
backgroundColor: isNameEmpty ? colors.tertiary : colors.secondary, // Dim the button
|
||||
backgroundColor: theme.colors.primary,
|
||||
opacity: isNameEmpty ? 0.5 : 1, // Visually dim the button
|
||||
}}
|
||||
labelStyle={{ color: colors.onPrimary }}
|
||||
>
|
||||
Save
|
||||
</Button>
|
||||
labelStyle={{ color: theme.colors.onPrimary }}>Save</Button>
|
||||
</Dialog.Actions>
|
||||
</Dialog>
|
||||
</Portal>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
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;
|
||||
|
@ -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 (
|
||||
<UserProvider>
|
||||
<InnerRootLayout />
|
||||
</UserProvider>
|
||||
);
|
||||
}
|
||||
|
||||
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,9 +36,14 @@ 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 (
|
||||
<Provider>
|
||||
<PaperProvider theme={appTheme}>
|
||||
|
113
app/index.tsx
113
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)}
|
||||
/>
|
||||
|
155
context/UserContext.tsx
Normal file
155
context/UserContext.tsx
Normal file
@ -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<UserContextType | undefined>(undefined);
|
||||
|
||||
// Define provider props type
|
||||
interface UserProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const UserProvider: React.FC<UserProviderProps> = ({ 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 (
|
||||
<UserContext.Provider
|
||||
value={{
|
||||
isProfileActive,
|
||||
setProfileActive,
|
||||
userId,
|
||||
userName,
|
||||
setUserName,
|
||||
userImage,
|
||||
setUserImage,
|
||||
userStatus,
|
||||
setUserStatus,
|
||||
setUserDataChanged,
|
||||
isLoading,
|
||||
currentTheme,
|
||||
setTheme,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</UserContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
// 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;
|
||||
};
|
3
featureFlags.ts
Normal file
3
featureFlags.ts
Normal file
@ -0,0 +1,3 @@
|
||||
export const featureFlags = {
|
||||
enableThemeSelection: true, // Toggle this to true or false to enable/disable the feature
|
||||
};
|
Loading…
Reference in New Issue
Block a user