initial commit

This commit is contained in:
Juthatip McDevitt 2024-07-29 23:34:10 -05:00
parent bda2c41772
commit ea96109874
31 changed files with 19341 additions and 0 deletions

22
ev_station/.gitignore vendored Normal file
View file

@ -0,0 +1,22 @@
node_modules/
.expo/
dist/
npm-debug.*
*.jks
*.p8
*.p12
*.key
*.mobileprovision
*.orig.*
web-build/
# macOS
.DS_Store
# @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb
# The following patterns were generated by expo-cli
expo-env.d.ts
# @end expo-cli
.env

50
ev_station/README.md Normal file
View file

@ -0,0 +1,50 @@
# Welcome to your Expo app 👋
This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app).
## Get started
1. Install dependencies
```bash
npm install
```
2. Start the app
```bash
npx expo start
```
In the output, you'll find options to open the app in a
- [development build](https://docs.expo.dev/develop/development-builds/introduction/)
- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/)
- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/)
- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo
You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction).
## Get a fresh project
When you're ready, run:
```bash
npm run reset-project
```
This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing.
## Learn more
To learn more about developing your project with Expo, look at the following resources:
- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides).
- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web.
## Join the community
Join our community of developers creating universal apps.
- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute.
- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions.

42
ev_station/app.json Normal file
View file

@ -0,0 +1,42 @@
{
"expo": {
"name": "ev_station",
"slug": "ev_station",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"scheme": "myapp",
"userInterfaceStyle": "automatic",
"splash": {
"image": "./assets/images/splash.png",
"resizeMode": "contain",
"backgroundColor": "#ffffff"
},
"ios": {
"supportsTablet": true
},
"android": {
"adaptiveIcon": {
"foregroundImage": "./assets/images/adaptive-icon.png",
"backgroundColor": "#ffffff"
}
},
"web": {
"bundler": "metro",
"output": "static",
"favicon": "./assets/images/favicon.png"
},
"plugins": [
[
"expo-location",
{
"locationAlwaysAndWhenInUsePermission": "Allow ev_station to use your location."
}
],
"expo-router"
],
"experiments": {
"typedRoutes": true
}
}
}

View file

@ -0,0 +1,3 @@
import { createContext } from "react";
export const UserLocationContext = createContext(null);

View file

@ -0,0 +1,60 @@
import { View, Text, StyleSheet } from 'react-native'
import React from 'react'
import {createBottomTabNavigator} from "@react-navigation/bottom-tabs";
import HomeScreen from "../screen/HomeScreen"
import SaveScreen from "../screen/SaveScreen"
import ProfileScreen from "../screen/ProfileScreen"
import {Entypo, MaterialIcons, AntDesign} from '@expo/vector-icons';
const Tab = createBottomTabNavigator();
const HomeNavigator = () => {
return (
<Tab.Navigator screenOptions={{headerShown: false, tabBarHideOnKeyboard: true}}>
<Tab.Screen name='home' component={HomeScreen}
options={{
tabBarLabel: "Home",
tabBarActiveTintColor: '#379777',
tabBarInactiveTintColor: 'gray',
tabBarIcon:({focused}) =>
focused? (
<Entypo name="home" size={26} color="#379777" style={{marginTop: 5, marginBottom: 5}}/>
) : (
<Entypo name="home" size={26} color="gray" style={{marginTop: 5, marginBottom: 5}}/>
)
}}
/>
<Tab.Screen name='save' component={SaveScreen}
options={{
tabBarLabel: "Save",
tabBarActiveTintColor: '#379777',
tabBarInactiveTintColor: 'gray',
tabBarIcon:({focused}) =>
focused? (
<MaterialIcons name="data-saver-on" size={26} color="#379777" style={{marginTop: 5, marginBottom: 5}}/>
) : (
<MaterialIcons name="data-saver-on" size={26} color="gray" style={{marginTop: 5, marginBottom: 5}}/>
)
}}
/>
<Tab.Screen name='profile' component={ProfileScreen}
options={{
tabBarLabel: "Profile",
tabBarActiveTintColor: '#379777',
tabBarInactiveTintColor: 'gray',
tabBarIcon:({focused}) =>
focused? (
<AntDesign name="user" size={26} color="#379777" style={{marginTop: 5, marginBottom: 5}}/>
) : (
<AntDesign name="user" size={26} color="gray" style={{marginTop: 5, marginBottom: 5}}/>
)
}}
/>
</Tab.Navigator>
)
}
export default HomeNavigator
const styles = StyleSheet.create({})

76
ev_station/app/index.js Normal file
View file

@ -0,0 +1,76 @@
import { StatusBar } from "expo-status-bar";
import { View, Text } from "react-native";
import Login from "./screen/loginScreen/Login";
import { ClerkProvider, SignedIn, SignedOut } from '@clerk/clerk-expo';
import *as SecureStore from "expo-secure-store";
import * as Location from 'expo-location';
import { NavigationContainer } from "@react-navigation/native";
import HomeNavigator from "./Navigation/HomeNavigator";
import { useEffect, useState } from "react";
import { UserLocationContext } from "../app/Context/UserLocationContext";
const tokenCache = {
async getToken(key){
try {
return SecureStore.getItemAsync(key);
} catch (error) {
return null;
}
},
async saveToken(key, value){
try {
return SecureStore.setItemAsync(key, value);
} catch (error) {
return;
}
},
}
export default function Index() {
const PUBLIC_KEY = process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY;
//use current location
const [location, setLocation] = useState(null);
const [errorMsg, setErrorMsg] = useState(null);
useEffect(() => {
(async () => {
let { status } = await Location.requestForegroundPermissionsAsync();
if (status !== 'granted') {
setErrorMsg('Permission to access location was denied');
return;
}
let location = await Location.getCurrentPositionAsync({});
setLocation(location.coords);
})();
}, []);
let text = 'Waiting..';
if (errorMsg) {
text = errorMsg;
} else if (location) {
text = JSON.stringify(location);
}
return (
<ClerkProvider tokenCache={tokenCache} publishableKey={PUBLIC_KEY}>
<UserLocationContext.Provider value={{location, setLocation}}>
<View style={{flex: 1, backgroundColor: 'white'}}>
<StatusBar style="auto"/>
<SignedIn>
<NavigationContainer independent={true}>
<HomeNavigator />
</NavigationContainer>
</SignedIn>
<SignedOut>
<Login/>
</SignedOut>
</View>
</UserLocationContext.Provider>
</ClerkProvider>
);
}

View file

@ -0,0 +1,19 @@
import { Image, StyleSheet, Text, View } from 'react-native'
import React from 'react'
import { useUser } from '@clerk/clerk-expo';
const HomeHeader = () => {
const {user} = useUser();
return (
<View style={{backgroundColor: 'white', paddingHorizontal: 20, paddingVertical: 10}}>
<View style={{flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center'}}>
<Image source={require('../../assets/images/ev_images/EV.png')} style={{width: 120, height: 60, objectFit: 'contain'}}/>
<Image source={{uri: user?.imageUrl}} style={{width: 40, height: 40, borderRadius: '50%'}}/>
</View>
</View>
)
}
export default HomeHeader
const styles = StyleSheet.create({})

View file

@ -0,0 +1,24 @@
import { Image, Pressable, StyleSheet, Text, View } from 'react-native';
import React from 'react';
import MapViewScreen from "../screen/mapView/MapViewScreen";
import HomeHeader from './HomeHeader';
import HomeSearch from './HomeSearch';
const HomeScreen = () => {
return (
<View style={{flex: 1, backgroundColor: 'white'}}>
<View style={{position: 'absolute', zIndex: 10, width: '100%'}}>
<HomeHeader />
<HomeSearch searchedLocation={(location) => console.log(location)}/>
</View>
<MapViewScreen />
</View>
)
}
export default HomeScreen
const styles = StyleSheet.create({})

View file

@ -0,0 +1,34 @@
import { StyleSheet, Text, View } from 'react-native'
import React from 'react'
import { GooglePlacesAutocomplete } from 'react-native-google-places-autocomplete';
const mapApiKey = process.env.EXPO_PUBLIC_GOOGLE_PLACE_API_KEY;
const HomeSearch = ({searchedLocation}) => {
return (
<View style={{marginVertical: 10, marginHorizontal: 10, borderRadius: 5}}>
<GooglePlacesAutocomplete
placeholder='Search EV charging station'
textInputProps={{
placeholderTextColor: '#45474B'
}}
enablePoweredByContainer={false}
fetchDetails={true}
onPress={(data, details = null) => {
searchedLocation(details?.geometry?.location)
}}
query={{
key: mapApiKey,
language: 'en',
}}
onFail={error => console.error(error)}
/>
</View>
)
}
export default HomeSearch
const styles = StyleSheet.create({})

View file

@ -0,0 +1,16 @@
import { StyleSheet, Text, View } from 'react-native'
import React from 'react'
const ProfileScreen = () => {
return (
<View style={{flex: 1, backgroundColor: 'white'}}>
<View style={{margin: 20}}>
<Text>ProfileScreen</Text>
</View>
</View>
)
}
export default ProfileScreen
const styles = StyleSheet.create({})

View file

@ -0,0 +1,16 @@
import { StyleSheet, Text, View } from 'react-native'
import React from 'react'
const SaveScreen = () => {
return (
<View style={{flex: 1, backgroundColor: 'white'}}>
<View style={{margin: 20}}>
<Text>SaveScreen</Text>
</View>
</View>
)
}
export default SaveScreen
const styles = StyleSheet.create({})

View file

@ -0,0 +1,42 @@
import { Image, Pressable, StyleSheet, Text, View } from 'react-native'
import React from 'react';
import *as WebBrowser from "expo-web-browser";
import { useOAuth } from '@clerk/clerk-expo';
import { useWarmUpBrowser } from '../../../hooks/warmUpBrowser'
WebBrowser.maybeCompleteAuthSession();
const Login = () => {
useWarmUpBrowser();
const {startOAuthFlow} = useOAuth({strategy: "oauth_google"})
const loginHandler = async() => {
try {
const {createdSessionId, signIn, signUp, setActive} = await startOAuthFlow();
if(createdSessionId){
setActive({session: createdSessionId});
}
} catch (error) {
console.error("oAuth error");
}
}
return (
<View style={{flex:1, justifyContent: 'center', alignItems: 'center'}}>
<Image source={require('../../../assets/images/ev_images/EV.png')}/>
<View style={{padding: 20, marginVertical: 10}}>
<Text style={{fontSize: 24, textAlign: 'center', paddingHorizontal: 5, fontWeight: 600, color: '#45474B'}}>Find reliable EV charging stations near you just one click</Text>
<Pressable onPress={loginHandler} style={{marginVertical: 50}}>
<View style={{justifyContent: 'center', alignItems: 'center', padding: 10, backgroundColor: '#379777', marginHorizontal: 100, borderRadius: 5}}>
<Text style={{fontSize: 20, fontWeight: 500, color: 'white'}}>Start</Text>
</View>
</Pressable>
</View>
</View>
)
}
export default Login
const styles = StyleSheet.create({})

View file

@ -0,0 +1,33 @@
import { Image, StyleSheet, Text, View } from 'react-native';
import React, { useContext } from 'react';
import MapView, { Marker, PROVIDER_DEFAULT} from 'react-native-maps';
import { UserLocationContext } from '@/app/Context/UserLocationContext';
const MapViewScreen = () => {
const {location, setLocation} = useContext(UserLocationContext)
return location?.latitude &&(
<View>
<MapView style={{width: '100%', height: '100%'}}
provider={PROVIDER_DEFAULT}
showsUserLocation={true}
region={{
latitude: location?.latitude,
longitude: location?.longitude,
latitudeDelta: 0.05,
longitudeDelta: 0.05
}}
>
<Marker coordinate={{latitude: location?.latitude, longitude: location?.longitude}}>
<Image source={require('../../../assets/images/ev_images/car.png')} style={{width: 70, height: 70}}/>
</Marker>
</MapView>
</View>
)
}
export default MapViewScreen
const styles = StyleSheet.create({})

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

View file

@ -0,0 +1,6 @@
module.exports = function (api) {
api.cache(true);
return {
presets: ['babel-preset-expo'],
};
};

View file

@ -0,0 +1,11 @@
import React, { useEffect } from 'react';
import *as WebBrowser from "expo-web-browser";
export const useWarmUpBrowser = () => {
React.useEffect(() => {
void WebBrowser.warmUpAsync();
return() => {
void WebBrowser.coolDownAsync();
}
}, [])
};

18742
ev_station/package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

55
ev_station/package.json Normal file
View file

@ -0,0 +1,55 @@
{
"name": "ev_station",
"main": "expo-router/entry",
"version": "1.0.0",
"scripts": {
"start": "expo start",
"reset-project": "node ./scripts/reset-project.js",
"android": "expo start --android",
"ios": "expo start --ios",
"web": "expo start --web",
"test": "jest --watchAll",
"lint": "expo lint"
},
"jest": {
"preset": "jest-expo"
},
"dependencies": {
"@clerk/clerk-expo": "^2.0.0",
"@expo/vector-icons": "^14.0.2",
"@react-navigation/native": "^6.1.18",
"expo": "~51.0.22",
"expo-constants": "~16.0.2",
"expo-font": "~12.0.9",
"expo-linking": "~6.3.1",
"expo-location": "~17.0.1",
"expo-router": "~3.5.18",
"expo-secure-store": "^13.0.2",
"expo-splash-screen": "~0.27.5",
"expo-status-bar": "~1.12.1",
"expo-system-ui": "~3.0.7",
"expo-web-browser": "~13.0.3",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-native": "0.74.3",
"react-native-gesture-handler": "~2.16.1",
"react-native-google-places-autocomplete": "^2.5.6",
"react-native-maps": "1.14.0",
"react-native-places-input": "^1.1.7",
"react-native-reanimated": "~3.10.1",
"react-native-safe-area-context": "4.10.5",
"react-native-screens": "3.31.1",
"react-native-web": "~0.19.10"
},
"devDependencies": {
"@babel/core": "^7.20.0",
"@types/jest": "^29.5.12",
"@types/react": "~18.2.45",
"@types/react-test-renderer": "^18.0.7",
"jest": "^29.2.1",
"jest-expo": "~51.0.3",
"react-test-renderer": "18.2.0",
"typescript": "~5.3.3"
},
"private": true
}

View file

@ -0,0 +1,73 @@
#!/usr/bin/env node
/**
* This script is used to reset the project to a blank state.
* It moves the /app directory to /app-example and creates a new /app directory with an index.tsx and _layout.tsx file.
* You can remove the `reset-project` script from package.json and safely delete this file after running it.
*/
const fs = require('fs');
const path = require('path');
const root = process.cwd();
const oldDirPath = path.join(root, 'app');
const newDirPath = path.join(root, 'app-example');
const newAppDirPath = path.join(root, 'app');
const indexContent = `import { Text, View } from "react-native";
export default function Index() {
return (
<View
style={{
flex: 1,
justifyContent: "center",
alignItems: "center",
}}
>
<Text>Edit app/index.tsx to edit this screen.</Text>
</View>
);
}
`;
const layoutContent = `import { Stack } from "expo-router";
export default function RootLayout() {
return (
<Stack>
<Stack.Screen name="index" />
</Stack>
);
}
`;
fs.rename(oldDirPath, newDirPath, (error) => {
if (error) {
return console.error(`Error renaming directory: ${error}`);
}
console.log('/app moved to /app-example.');
fs.mkdir(newAppDirPath, { recursive: true }, (error) => {
if (error) {
return console.error(`Error creating new app directory: ${error}`);
}
console.log('New /app directory created.');
const indexPath = path.join(newAppDirPath, 'index.tsx');
fs.writeFile(indexPath, indexContent, (error) => {
if (error) {
return console.error(`Error creating index.tsx: ${error}`);
}
console.log('app/index.tsx created.');
const layoutPath = path.join(newAppDirPath, '_layout.tsx');
fs.writeFile(layoutPath, layoutContent, (error) => {
if (error) {
return console.error(`Error creating _layout.tsx: ${error}`);
}
console.log('app/_layout.tsx created.');
});
});
});
});

17
ev_station/tsconfig.json Normal file
View file

@ -0,0 +1,17 @@
{
"extends": "expo/tsconfig.base",
"compilerOptions": {
"strict": true,
"paths": {
"@/*": [
"./*"
]
}
},
"include": [
"**/*.ts",
"**/*.tsx",
".expo/types/**/*.ts",
"expo-env.d.ts"
, "app/index.js" ]
}