commit a6cae80944948c7d8491212f71160199d31b8319 Author: neel Date: Tue Mar 25 17:38:42 2025 +0530 first commit diff --git a/.env b/.env new file mode 100644 index 0000000..7a31ee6 --- /dev/null +++ b/.env @@ -0,0 +1,3 @@ +EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_bWVhc3VyZWQtcGFudGhlci04Mi5jbGVyay5hY2NvdW50cy5kZXYk +DATABASE_URL=postgresql://jsm_uber_owner:npg_2j5wHrsMyStB@ep-rapid-field-a1prd9ss-pooler.ap-southeast-1.aws.neon.tech/jsm_uber?sslmode=require +EXPO_PUBLIC_SERVER_URL=http://192.168.29.1:3000 \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 0000000..5660a59 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,10 @@ +module.exports = { + extends: ["expo", "prettier"], + plugins: ["prettier"], + rules: { + "prettier/prettier": [ + "error", + { singleQuote: true, quoteProps: "preserve" }, + ], + }, +}; diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c9d575d --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files + +# dependencies +node_modules/ + +# Expo +.expo/ +dist/ +web-build/ +expo-env.d.ts + +# Native +*.orig.* +*.jks +*.p8 +*.p12 +*.key +*.mobileprovision + +# Metro +.metro-health-check* + +# debug +npm-debug.* +yarn-debug.* +yarn-error.* + +# macOS +.DS_Store +*.pem + +# local env files +.env*.local + +# typescript +*.tsbuildinfo + +app-example diff --git a/README.md b/README.md new file mode 100644 index 0000000..cd4feb8 --- /dev/null +++ b/README.md @@ -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. diff --git a/app.json b/app.json new file mode 100644 index 0000000..65d717b --- /dev/null +++ b/app.json @@ -0,0 +1,37 @@ +{ + "expo": { + "name": "Uber", + "slug": "uber", + "version": "1.0.0", + "orientation": "portrait", + "icon": "./assets/images/icon.png", + "scheme": "myapp", + "userInterfaceStyle": "automatic", + "splash":{ + "image":"./assets/images/splash.png", + "resizeMode":"cover", + "backgroundColor":"#2f80ed" + }, + "newArchEnabled": true, + "ios": { + "supportsTablet": true + }, + "android": { + "adaptiveIcon": { + "foregroundImage": "./assets/images/adaptive-icon.png", + "backgroundColor": "#ffffff" + } + }, + "web": { + "bundler": "metro", + "output": "server", + "favicon": "./assets/images/favicon.png" + }, + "plugins": [ + ["expo-router", {"origin":"https://192.168.29.1:3000"}] + ], + "experiments": { + "typedRoutes": true + } + } +} diff --git a/app/(auth)/_layout.tsx b/app/(auth)/_layout.tsx new file mode 100644 index 0000000..77918c0 --- /dev/null +++ b/app/(auth)/_layout.tsx @@ -0,0 +1,13 @@ + +import { Stack } from "expo-router"; + +const Layout=()=> { + return ( + + + + + + ); +} +export default Layout; \ No newline at end of file diff --git a/app/(auth)/sign-in.tsx b/app/(auth)/sign-in.tsx new file mode 100644 index 0000000..b102e33 --- /dev/null +++ b/app/(auth)/sign-in.tsx @@ -0,0 +1,79 @@ +import { ScrollView, Text, View, Image , } from "react-native"; +import tw from "twrnc"; +import InputField from "@/components/InputField"; +import { images,icons } from "@/constants"; +import { useCallback, useState } from "react"; +import CustomButton from "@/components/CustomButton"; +import { Link ,useRouter } from "expo-router"; +import OAuth from "@/components/OAuth"; +import { useSignIn } from "@clerk/clerk-expo"; +const SignIn = () => { + const { signIn, setActive, isLoaded } = useSignIn(); + const router = useRouter(); + const [form,setForm]= useState({ + email:"", + password:"", + }); + const onSignInPress =useCallback( async () => { + if (!isLoaded) { + console.log("Clerk is not loaded yet."); + return; + } + console.log("signIn object:", signIn); + try { + const signInAttempt = await signIn.create({ + identifier: form.email, + password :form.password , + }) + if (signInAttempt.status === "complete") { + await setActive({ session: signInAttempt.createdSessionId }) + console.log("Successfully signed in! Navigating to home..."); + router.push("/(root)/(tabs)/home") + } else { + console.error(JSON.stringify(signInAttempt, null, 2)) + } + } catch (err:any) { + console.error("Sign-in attempt did not complete:", JSON.stringify(err, null, 2)) + } + }, + [isLoaded,form.email,form.password] + ); + return ( + + + + + Welcome☺️ + + + setForm({... form, email:value})} + /> + setForm({... form, password:value})} + /> + + + + Don't Have an Account? + Sign Up + + { /* Verification Modal */ } + + + + ); +}; +export default SignIn; \ No newline at end of file diff --git a/app/(auth)/sign-up.tsx b/app/(auth)/sign-up.tsx new file mode 100644 index 0000000..93b795f --- /dev/null +++ b/app/(auth)/sign-up.tsx @@ -0,0 +1,134 @@ +import { ScrollView, Text, View, Image, Alert, } from "react-native"; +import tw from "twrnc"; +import InputField from "@/components/InputField"; +import { images, icons } from "@/constants"; +import { useState } from "react"; +import CustomButton from "@/components/CustomButton"; +import { Link, router } from "expo-router"; +import OAuth from "@/components/OAuth"; +import { useSignUp } from "@clerk/clerk-expo"; +import { ReactNativeModal } from "react-native-modal"; +import { fetchAPI } from "@/lib/fetch"; +import { TokenCache } from "@/lib/auth"; +const SignUp = () => { + const { isLoaded, signUp, setActive } = useSignUp(); + const [showSuccessModal,setShowSuccessModal]= useState(false); + const [form, setForm] = useState({ + name: "", + email: "", + password: "", + }); + const [verification, setVerification] = useState({ + state: "default", + error: "", + code: "", + }) + + const onSignUpPress = async () => { + if (!isLoaded){ return;} + try { + await signUp.create({ + emailAddress: form.email, + password: form.password, + }) + await signUp.prepareEmailAddressVerification({ strategy: "email_code" }) + setVerification({ + ...verification, + state: "pending" + }) + } catch (err:any) { + Alert.alert("Error",err.errors[0].longMessage) + } + } + const onVerifyPress = async () => { + if (!isLoaded) return; + + try { + const CompleteSignUp = await signUp.attemptEmailAddressVerification({ + code: verification.code, + }) + if (CompleteSignUp.status === "complete") { + await fetchAPI(`${process.env.EXPO_PUBLIC_SERVER_URL}/api/users`,{ + method:"POST", + headers:{"Content-Type": "application/json", + Authorization: `Bearer ${await TokenCache.getToken("__clerk_client_jwt")}`,}, + body:JSON.stringify({ + name:form.name, + email:form.email, + clerkId:CompleteSignUp.createdUserId, + }) + }) + await setActive({ session: CompleteSignUp.createdSessionId }); + setVerification({ ...verification, state: "success" }) + } else { + setVerification({ ...verification, error: "Verification failed.", state: "failed" }) + } + } catch (err: any) { + setVerification({ ...verification, error: err.errors[0].longMessage, state: "success" }) + } + } + return ( + + + + + Create Your Account + + + setForm({ ...form, name: value })} + /> + setForm({ ...form, email: value })} + /> + setForm({ ...form, password: value })} + /> + + + + Already Have an Account? + Log In + + {if(verification.state==="success") setShowSuccessModal(true)} } > + + Verification + We've sent a verification code to {form.email} + setVerification ({... verification,code}) } + /> + {verification.error && ( + {verification.error} + )} + + + + + + + Verified + You have successfully verified your account. + {setShowSuccessModal(false); router.push("/(root)/(tabs)/home")}} style={tw`mt-5`} /> + + + + + + ); +}; +export default SignUp; \ No newline at end of file diff --git a/app/(auth)/welcome.tsx b/app/(auth)/welcome.tsx new file mode 100644 index 0000000..5229c0c --- /dev/null +++ b/app/(auth)/welcome.tsx @@ -0,0 +1,47 @@ +import { router } from "expo-router"; +import { Text, TouchableOpacity, View, Image } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; +import tw from "twrnc"; +import Swiper from "react-native-swiper"; +import { useRef, useState } from "react"; +import { onboarding } from "@/constants"; +import CustomButton from "@/components/CustomButton"; +const Onboarding = () => { + const swiperRef = useRef(null); + const [activeIndex, setActiveIndex] = useState(0); + const isLastSlide = activeIndex === onboarding.length-1; + return ( + + { router.replace("/(auth)/sign-up"); }} + style={tw`w-full flex justify-end items-end p-5 `}> + Skip + + } + activeDot={ + + } + onIndexChanged={(index) => setActiveIndex(index)} + > + {onboarding.map((item) => + ( + + + {item.title} + + {item.description} + )) + } + + isLastSlide ? router.replace("/(auth)/sign-up") : swiperRef.current ?. scrollBy(1)} + style={tw`w-11/12 mt-10`}/> + + ) +} +export default Onboarding; \ No newline at end of file diff --git a/app/(root)/(tabs)/_layout.tsx b/app/(root)/(tabs)/_layout.tsx new file mode 100644 index 0000000..32d6acc --- /dev/null +++ b/app/(root)/(tabs)/_layout.tsx @@ -0,0 +1,24 @@ +import { icons } from "@/constants"; +import { Stack, Tabs } from "expo-router"; +import { Image, ImageSourcePropType, View } from "react-native"; +import tw from "twrnc"; +const TabIcon=({source,focused}:{source:ImageSourcePropType; focused:boolean;})=>{ + return( + + + + + + ); +}; +const Layout=()=> { +return( + + }} /> + }} /> + }} /> + }} /> + +) +} +export default Layout; \ No newline at end of file diff --git a/app/(root)/(tabs)/chat.tsx b/app/(root)/(tabs)/chat.tsx new file mode 100644 index 0000000..7e258e5 --- /dev/null +++ b/app/(root)/(tabs)/chat.tsx @@ -0,0 +1,10 @@ +import { Text } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; +const Chat =() => { + return( + + Chat + + ) +} +export default Chat; \ No newline at end of file diff --git a/app/(root)/(tabs)/home.tsx b/app/(root)/(tabs)/home.tsx new file mode 100644 index 0000000..dcea467 --- /dev/null +++ b/app/(root)/(tabs)/home.tsx @@ -0,0 +1,14 @@ +import { SignedIn, SignedOut, useUser } from '@clerk/clerk-expo'; +import { SafeAreaView, Text, View } from 'react-native'; + +export default function Page() { + const { user } = useUser() + + return ( + + + Hello {user?.emailAddresses[0].emailAddress} + + + ) +} \ No newline at end of file diff --git a/app/(root)/(tabs)/profile.tsx b/app/(root)/(tabs)/profile.tsx new file mode 100644 index 0000000..be74b84 --- /dev/null +++ b/app/(root)/(tabs)/profile.tsx @@ -0,0 +1,10 @@ +import { Text } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; +const profile =() => { + return( + + Profile + + ) +} +export default profile; \ No newline at end of file diff --git a/app/(root)/(tabs)/rides.tsx b/app/(root)/(tabs)/rides.tsx new file mode 100644 index 0000000..5f5d9de --- /dev/null +++ b/app/(root)/(tabs)/rides.tsx @@ -0,0 +1,10 @@ +import { Text } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; +const Rides =() => { + return( + + Rides + + ) +} +export default Rides; \ No newline at end of file diff --git a/app/(root)/_layout.tsx b/app/(root)/_layout.tsx new file mode 100644 index 0000000..532f186 --- /dev/null +++ b/app/(root)/_layout.tsx @@ -0,0 +1,10 @@ +import { Stack } from "expo-router"; + +const Layout=()=> { + return ( + + + + ); +} +export default Layout; diff --git a/app/+html.tsx b/app/+html.tsx new file mode 100644 index 0000000..15bbe41 --- /dev/null +++ b/app/+html.tsx @@ -0,0 +1,42 @@ +import { ScrollViewStyleReset } from "expo-router/html"; +import { type PropsWithChildren } from "react"; + +/** + * This file is web-only and used to configure the root HTML for every web page during static rendering. + * The contents of this function only run in Node.js environments and do not have access to the DOM or browser APIs. + */ +export default function Root({ children }: PropsWithChildren) { + return ( + + + + + + + {/* + Disable body scrolling on web. This makes ScrollView components work closer to how they do on native. + However, body scrolling is often nice to have for mobile web. If you want to enable it, remove this line. + */} + + + {/* Using raw CSS styles as an escape-hatch to ensure the background color never flickers in dark-mode. */} +