Skip to content

Commit

Permalink
Merge pull request #278 from COS301-SE-2024/chore/mobile/refactor2
Browse files Browse the repository at this point in the history
Chore/mobile/refactor2
  • Loading branch information
KamogeloMoeketse authored Aug 6, 2024
2 parents c2b44f5 + d397d9c commit 905f323
Show file tree
Hide file tree
Showing 49 changed files with 4,203 additions and 1,840 deletions.
63 changes: 33 additions & 30 deletions frontend/occupi-mobile4/app/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Stack } from 'expo-router';
import * as SplashScreen from 'expo-splash-screen';
import 'react-native-reanimated';
import { GluestackUIProvider } from "@gluestack-ui/themed";
import { ThemeProvider } from '@/components/ThemeContext';
import { NavBarProvider } from '@/components/NavBarProvider';
import { config } from "@gluestack-ui/config"; // Optional if you want to use default theme

Expand All @@ -32,36 +33,38 @@ export default function RootLayout() {

return (
<GluestackUIProvider config={config} theme={theme}>
<NavBarProvider>
<Stack>
<Stack.Screen name="index" options={{ headerShown: false }} />
<Stack.Screen name="login" options={{ headerShown: false }} />
<Stack.Screen name="signup" options={{ headerShown: false }} />
<Stack.Screen name="forgot-password" options={{ headerShown: false }} />
<Stack.Screen name="verify-otp" options={{ headerShown: false }} />
<Stack.Screen name="create-password" options={{ headerShown: false }} />
<Stack.Screen name="home" options={{ headerShown: false }} />
<Stack.Screen name="bookings" options={{ headerShown: false }} />
<Stack.Screen name="viewbookings" options={{ headerShown: false }} />
<Stack.Screen name="notifications" options={{ headerShown: false }} />
<Stack.Screen name="onboarding1" options={{ headerShown: false }} />
<Stack.Screen name="onboarding2" options={{ headerShown: false }} />
<Stack.Screen name="onboarding3" options={{ headerShown: false }} />
<Stack.Screen name="profile" options={{ headerShown: false }} />
<Stack.Screen name="welcome" options={{ headerShown: false }} />
<Stack.Screen name="settings" options={{ headerShown: false }} />
<Stack.Screen name="office-details" options={{ headerShown: false }} />
<Stack.Screen name="booking-details" options={{ headerShown: false }} />
<Stack.Screen name="viewbookingdetails" options={{ headerShown: false }} />
<Stack.Screen name="faqpage" options={{ headerShown: false }} />
<Stack.Screen name="set-notifications" options={{ headerShown: false }} />
<Stack.Screen name="set-security" options={{ headerShown: false }} />
<Stack.Screen name="set-appearance" options={{ headerShown: false }} />
<Stack.Screen name="notiftester" options={{ headerShown: false }} />
<Stack.Screen name="changepassword" options={{ headerShown: false }} />
<Stack.Screen name="set-details" options={{ headerShown: false }} />
</Stack>
</NavBarProvider>
<ThemeProvider>
<NavBarProvider>
<Stack>
<Stack.Screen name="index" options={{ headerShown: false }} />
<Stack.Screen name="login" options={{ headerShown: false }} />
<Stack.Screen name="signup" options={{ headerShown: false }} />
<Stack.Screen name="forgot-password" options={{ headerShown: false }} />
<Stack.Screen name="verify-otp" options={{ headerShown: false }} />
<Stack.Screen name="create-password" options={{ headerShown: false }} />
<Stack.Screen name="home" options={{ headerShown: false }} />
<Stack.Screen name="bookings" options={{ headerShown: false }} />
<Stack.Screen name="viewbookings" options={{ headerShown: false }} />
<Stack.Screen name="notifications" options={{ headerShown: false }} />
<Stack.Screen name="onboarding1" options={{ headerShown: false }} />
<Stack.Screen name="onboarding2" options={{ headerShown: false }} />
<Stack.Screen name="onboarding3" options={{ headerShown: false }} />
<Stack.Screen name="profile" options={{ headerShown: false }} />
<Stack.Screen name="welcome" options={{ headerShown: false }} />
<Stack.Screen name="settings" options={{ headerShown: false }} />
<Stack.Screen name="office-details" options={{ headerShown: false }} />
<Stack.Screen name="booking-details" options={{ headerShown: false }} />
<Stack.Screen name="viewbookingdetails" options={{ headerShown: false }} />
<Stack.Screen name="faqpage" options={{ headerShown: false }} />
<Stack.Screen name="set-details" options={{ headerShown: false }} />
<Stack.Screen name="set-notifications" options={{ headerShown: false }} />
<Stack.Screen name="set-security" options={{ headerShown: false }} />
<Stack.Screen name="set-appearance" options={{ headerShown: false }} />
<Stack.Screen name="notiftester" options={{ headerShown: false }} />
<Stack.Screen name="changepassword" options={{ headerShown: false }} />
</Stack>
</NavBarProvider>
</ThemeProvider>
</GluestackUIProvider>
);
}
47 changes: 30 additions & 17 deletions frontend/occupi-mobile4/components/NavBar.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,43 @@
import React, { useState } from 'react';
import React, { useState, useEffect } from 'react';
import { StyleSheet } from 'react-native';
import { Text, Button, Icon, CalendarDaysIcon, BellIcon } from '@gluestack-ui/themed';
import { Feather } from '@expo/vector-icons';
import { FontAwesome6, Ionicons } from '@expo/vector-icons';
import { router } from 'expo-router';
import { BlurView } from 'expo-blur';
import { useColorScheme } from 'react-native';
import * as SecureStore from 'expo-secure-store';
import { widthPercentageToDP as wp, heightPercentageToDP as hp } from 'react-native-responsive-screen';
import { useNavBar } from './NavBarProvider';
import { useTheme } from './ThemeContext';

const NavBar = () => {
let colorScheme = useColorScheme();
const styles = getStyles(colorScheme);
const colorscheme = useColorScheme();
const { theme } = useTheme();
const currentTheme = theme === "system" ? colorscheme : theme;
const styles = getStyles(currentTheme);
const [accentColour, setAccentColour] = useState<string>('greenyellow');
const { currentTab, setCurrentTab } = useNavBar();

const handleTabPress = (tabName, route) => {
setCurrentTab(tabName);
router.replace(route);
};

useEffect(() => {
const getSettings = async () => {
let accentcolour = await SecureStore.getItemAsync('accentColour');
setAccentColour(accentcolour);
};
getSettings();
}, []);
// console.log(currentTab);

return (
<BlurView
tint="light"
pb={hp('3%')}
backgroundColor={colorScheme === 'dark' ? 'black' : '#fff'}
backgroundColor={currentTheme === 'dark' ? 'black' : '#fff'}
intensity={20}
style={styles.container}
>
Expand All @@ -40,13 +53,13 @@ const NavBar = () => {
<Feather
name="home"
size={hp('3%')}
color={currentTab === 'Home' ? 'yellowgreen' : colorScheme === 'dark' ? 'white' : 'black'}
color={currentTab === 'Home' ? `${accentColour}` : currentTheme === 'dark' ? 'white' : 'black'}
/>
<Text
numberOfLines={1}
w={wp('9%')}
fontSize={wp('3%')}
color={currentTab === 'Home' ? 'yellowgreen' : colorScheme === 'dark' ? 'white' : 'black'}
color={currentTab === 'Home' ? `${accentColour}` : currentTheme === 'dark' ? 'white' : 'black'}
>
Home
</Text>
Expand All @@ -63,13 +76,13 @@ const NavBar = () => {
<Ionicons
name="receipt-outline"
size={24}
color={currentTab === 'ViewBookings' ? 'yellowgreen' : colorScheme === 'dark' ? 'white' : 'black'}
color={currentTab === 'ViewBookings' ? `${accentColour}` : currentTheme === 'dark' ? 'white' : 'black'}
/>
<Text
numberOfLines={1}
w={wp('19%')}
fontSize={wp('3%')}
color={currentTab === 'ViewBookings' ? 'yellowgreen' : colorScheme === 'dark' ? 'white' : 'black'}
color={currentTab === 'ViewBookings' ? `${accentColour}` : currentTheme === 'dark' ? 'white' : 'black'}
>
My bookings
</Text>
Expand All @@ -87,13 +100,13 @@ const NavBar = () => {
as={CalendarDaysIcon}
w={hp('3%')}
h={hp('3%')}
color={currentTab === 'Book' ? 'yellowgreen' : colorScheme === 'dark' ? 'white' : 'black'}
color={currentTab === 'Book' ? `${accentColour}` : currentTheme === 'dark' ? 'white' : 'black'}
/>
<Text
numberOfLines={1}
w={wp('7.4%')}
fontSize={wp('3%')}
color={currentTab === 'Book' ? 'yellowgreen' : colorScheme === 'dark' ? 'white' : 'black'}
color={currentTab === 'Book' ? `${accentColour}` : currentTheme === 'dark' ? 'white' : 'black'}
>
Book
</Text>
Expand All @@ -111,14 +124,14 @@ const NavBar = () => {
as={BellIcon}
w={hp('3%')}
h={hp('3%')}
color={currentTab === 'Notifications' ? 'yellowgreen' : colorScheme === 'dark' ? 'white' : 'black'}
color={currentTab === 'Notifications' ? `${accentColour}` : currentTheme === 'dark' ? 'white' : 'black'}
/>
<Text
pl={wp('1%')}
numberOfLines={1}
w={wp('20%')}
fontSize={wp('3%')}
color={currentTab === 'Notifications' ? 'yellowgreen' : colorScheme === 'dark' ? 'white' : 'black'}
color={currentTab === 'Notifications' ? `${accentColour}` : currentTheme === 'dark' ? 'white' : 'black'}
>
Notifications
</Text>
Expand All @@ -135,14 +148,14 @@ const NavBar = () => {
<FontAwesome6
name="user"
size={hp('3%')}
color={currentTab === 'Profile' ? 'yellowgreen' : colorScheme === 'dark' ? 'white' : 'black'}
color={currentTab === 'Profile' ? `${accentColour}` : currentTheme === 'dark' ? 'white' : 'black'}
/>
<Text
pl={wp('1%')}
numberOfLines={1}
w={wp('12%')}
fontSize={wp('3%')}
color={currentTab === 'Profile' ? 'yellowgreen' : colorScheme === 'dark' ? 'white' : 'black'}
color={currentTab === 'Profile' ? `${accentColour}` : currentTheme === 'dark' ? 'white' : 'black'}
>
Profile
</Text>
Expand All @@ -151,7 +164,7 @@ const NavBar = () => {
);
};

const getStyles = (colorScheme) => StyleSheet.create({
const getStyles = (currentTheme) => StyleSheet.create({
container: {
position: 'absolute',
bottom: 0,
Expand All @@ -161,10 +174,10 @@ const getStyles = (colorScheme) => StyleSheet.create({
paddingBottom: hp('3%'),
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: colorScheme === 'dark' ? 'rgba(0, 0, 0, 0.8)' : 'rgba(255, 255, 255, 0.5)',
backgroundColor: currentTheme === 'dark' ? 'rgba(0, 0, 0, 0.8)' : 'rgba(255, 255, 255, 0.5)',
paddingVertical: hp('1%'),
borderTopWidth: 1,
borderTopColor: colorScheme === 'dark' ? '#444' : '#ccc',
borderTopColor: currentTheme === 'dark' ? '#444' : '#ccc',
borderLeftColor: '#ccc',
borderRightColor: '#ccc',
}
Expand Down
72 changes: 72 additions & 0 deletions frontend/occupi-mobile4/components/ThemeContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// ThemeContext.tsx

import React, { createContext, useState, useEffect, ReactNode } from 'react';
import * as SecureStore from 'expo-secure-store';
import { View, ActivityIndicator, useColorScheme } from 'react-native';

// Define the shape of the context state
interface ThemeContextType {
theme: string;
setTheme: (theme: string) => void;
}

// Create the context
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);

// Create a provider component
export const ThemeProvider = ({ children }: { children: ReactNode }) => {
const [theme, setTheme] = useState<string | null>(null); // Start with null to indicate loading

useEffect(() => {
const fetchTheme = async () => {
try {
const storedTheme = await SecureStore.getItemAsync('Theme');
if (storedTheme) {
setTheme(storedTheme);
} else {
// If no theme is found, set a default theme
setTheme('light');
}
} catch (error) {
console.error('Failed to load theme from SecureStore:', error);
setTheme('light'); // Set a default theme in case of an error
}
};

fetchTheme();
}, []);

const updateTheme = async (newTheme: string) => {
console.log('updating theme');
try {
await SecureStore.setItemAsync('Theme', newTheme);
setTheme(newTheme);
} catch (error) {
console.error('Failed to save theme to SecureStore:', error);
}
};

if (theme === null) {
// Show a loading spinner while the theme is being fetched
return (
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
<ActivityIndicator size="large" color="#0000ff" />
</View>
);
}

return (
<ThemeContext.Provider value={{ theme, setTheme: updateTheme }}>
{children}
</ThemeContext.Provider>
);
};

// Custom hook to use the ThemeContext
export const useTheme = (): ThemeContextType => {
const context = React.useContext(ThemeContext);
if (!context) {
throw new Error('useTheme must be used within a ThemeProvider');
}
return context;
};
22 changes: 18 additions & 4 deletions frontend/occupi-mobile4/components/__tests__/NavBar-test.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,23 @@
import * as React from 'react';
import renderer from 'react-test-renderer';
import NavBar from '../NavBar';
import { useNavBar } from '../NavBarProvider';

it(`renders correctly`, () => {
const tree = renderer.create(<NavBar>Snapshot test!</NavBar>).toJSON();
// Mock the NavBarProvider module
jest.mock('../NavBarProvider', () => ({
useNavBar: jest.fn(),
}));

expect(tree).toMatchSnapshot();
});
describe('NavBar', () => {
it(`renders correctly`, () => {
// Mock the useNavBar hook implementation
(useNavBar as jest.Mock).mockReturnValue({
currentTab: 'Home',
setCurrentTab: jest.fn(),
});

const tree = renderer.create(<NavBar />).toJSON();

expect(tree).toMatchSnapshot();
});
});
Loading

0 comments on commit 905f323

Please sign in to comment.