diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..6623142
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,20 @@
+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
\ No newline at end of file
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..4ec7238
--- /dev/null
+++ b/app.json
@@ -0,0 +1,36 @@
+{
+ "expo": {
+ "name": "uber-clone",
+ "slug": "uber-clone",
+ "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-router"
+ ],
+ "experiments": {
+ "typedRoutes": true
+ }
+ }
+}
diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx
new file mode 100644
index 0000000..22a49b6
--- /dev/null
+++ b/app/(tabs)/_layout.tsx
@@ -0,0 +1,37 @@
+import { Tabs } from 'expo-router';
+import React from 'react';
+
+import { TabBarIcon } from '@/components/navigation/TabBarIcon';
+import { Colors } from '@/constants/Colors';
+import { useColorScheme } from '@/hooks/useColorScheme';
+
+export default function TabLayout() {
+ const colorScheme = useColorScheme();
+
+ return (
+
+ (
+
+ ),
+ }}
+ />
+ (
+
+ ),
+ }}
+ />
+
+ );
+}
diff --git a/app/(tabs)/explore.tsx b/app/(tabs)/explore.tsx
new file mode 100644
index 0000000..e480218
--- /dev/null
+++ b/app/(tabs)/explore.tsx
@@ -0,0 +1,102 @@
+import Ionicons from '@expo/vector-icons/Ionicons';
+import { StyleSheet, Image, Platform } from 'react-native';
+
+import { Collapsible } from '@/components/Collapsible';
+import { ExternalLink } from '@/components/ExternalLink';
+import ParallaxScrollView from '@/components/ParallaxScrollView';
+import { ThemedText } from '@/components/ThemedText';
+import { ThemedView } from '@/components/ThemedView';
+
+export default function TabTwoScreen() {
+ return (
+ }>
+
+ Explore
+
+ This app includes example code to help you get started.
+
+
+ This app has two screens:{' '}
+ app/(tabs)/index.tsx and{' '}
+ app/(tabs)/explore.tsx
+
+
+ The layout file in app/(tabs)/_layout.tsx{' '}
+ sets up the tab navigator.
+
+
+ Learn more
+
+
+
+
+ You can open this project on Android, iOS, and the web. To open the web version, press{' '}
+ w in the terminal running this project.
+
+
+
+
+ For static images, you can use the @2x and{' '}
+ @3x suffixes to provide files for
+ different screen densities
+
+
+
+ Learn more
+
+
+
+
+ Open app/_layout.tsx to see how to load{' '}
+
+ custom fonts such as this one.
+
+
+
+ Learn more
+
+
+
+
+ This template has light and dark mode support. The{' '}
+ useColorScheme() hook lets you inspect
+ what the user's current color scheme is, and so you can adjust UI colors accordingly.
+
+
+ Learn more
+
+
+
+
+ This template includes an example of an animated component. The{' '}
+ components/HelloWave.tsx component uses
+ the powerful react-native-reanimated library
+ to create a waving hand animation.
+
+ {Platform.select({
+ ios: (
+
+ The components/ParallaxScrollView.tsx{' '}
+ component provides a parallax effect for the header image.
+
+ ),
+ })}
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ headerImage: {
+ color: '#808080',
+ bottom: -90,
+ left: -35,
+ position: 'absolute',
+ },
+ titleContainer: {
+ flexDirection: 'row',
+ gap: 8,
+ },
+});
diff --git a/app/(tabs)/index.tsx b/app/(tabs)/index.tsx
new file mode 100644
index 0000000..324aeb7
--- /dev/null
+++ b/app/(tabs)/index.tsx
@@ -0,0 +1,70 @@
+import { Image, StyleSheet, Platform } from 'react-native';
+
+import { HelloWave } from '@/components/HelloWave';
+import ParallaxScrollView from '@/components/ParallaxScrollView';
+import { ThemedText } from '@/components/ThemedText';
+import { ThemedView } from '@/components/ThemedView';
+
+export default function HomeScreen() {
+ return (
+
+ }>
+
+ Welcome!
+
+
+
+ Step 1: Try it
+
+ Edit app/(tabs)/index.tsx to see changes.
+ Press{' '}
+
+ {Platform.select({ ios: 'cmd + d', android: 'cmd + m' })}
+ {' '}
+ to open developer tools.
+
+
+
+ Step 2: Explore
+
+ Tap the Explore tab to learn more about what's included in this starter app.
+
+
+
+ Step 3: Get a fresh start
+
+ When you're ready, run{' '}
+ npm run reset-project to get a fresh{' '}
+ app directory. This will move the current{' '}
+ app to{' '}
+ app-example.
+
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ titleContainer: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: 8,
+ },
+ stepContainer: {
+ gap: 8,
+ marginBottom: 8,
+ },
+ reactLogo: {
+ height: 178,
+ width: 290,
+ bottom: 0,
+ left: 0,
+ position: 'absolute',
+ },
+});
diff --git a/app/+html.tsx b/app/+html.tsx
new file mode 100644
index 0000000..8b92456
--- /dev/null
+++ b/app/+html.tsx
@@ -0,0 +1,39 @@
+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. */}
+
+ {/* Add any additional elements that you want globally available on web... */}
+
+ {children}
+
+ );
+}
+
+const responsiveBackground = `
+body {
+ background-color: #fff;
+}
+@media (prefers-color-scheme: dark) {
+ body {
+ background-color: #000;
+ }
+}`;
diff --git a/app/+not-found.tsx b/app/+not-found.tsx
new file mode 100644
index 0000000..963b04f
--- /dev/null
+++ b/app/+not-found.tsx
@@ -0,0 +1,32 @@
+import { Link, Stack } from 'expo-router';
+import { StyleSheet } from 'react-native';
+
+import { ThemedText } from '@/components/ThemedText';
+import { ThemedView } from '@/components/ThemedView';
+
+export default function NotFoundScreen() {
+ return (
+ <>
+
+
+ This screen doesn't exist.
+
+ Go to home screen!
+
+
+ >
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ alignItems: 'center',
+ justifyContent: 'center',
+ padding: 20,
+ },
+ link: {
+ marginTop: 15,
+ paddingVertical: 15,
+ },
+});
diff --git a/app/_layout.tsx b/app/_layout.tsx
new file mode 100644
index 0000000..2e37cdd
--- /dev/null
+++ b/app/_layout.tsx
@@ -0,0 +1,37 @@
+import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
+import { useFonts } from 'expo-font';
+import { Stack } from 'expo-router';
+import * as SplashScreen from 'expo-splash-screen';
+import { useEffect } from 'react';
+import 'react-native-reanimated';
+
+import { useColorScheme } from '@/hooks/useColorScheme';
+
+// Prevent the splash screen from auto-hiding before asset loading is complete.
+SplashScreen.preventAutoHideAsync();
+
+export default function RootLayout() {
+ const colorScheme = useColorScheme();
+ const [loaded] = useFonts({
+ SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
+ });
+
+ useEffect(() => {
+ if (loaded) {
+ SplashScreen.hideAsync();
+ }
+ }, [loaded]);
+
+ if (!loaded) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/assets/fonts/SpaceMono-Regular.ttf b/assets/fonts/SpaceMono-Regular.ttf
new file mode 100755
index 0000000..28d7ff7
Binary files /dev/null and b/assets/fonts/SpaceMono-Regular.ttf differ
diff --git a/assets/images/adaptive-icon.png b/assets/images/adaptive-icon.png
new file mode 100644
index 0000000..03d6f6b
Binary files /dev/null and b/assets/images/adaptive-icon.png differ
diff --git a/assets/images/favicon.png b/assets/images/favicon.png
new file mode 100644
index 0000000..e75f697
Binary files /dev/null and b/assets/images/favicon.png differ
diff --git a/assets/images/icon.png b/assets/images/icon.png
new file mode 100644
index 0000000..a0b1526
Binary files /dev/null and b/assets/images/icon.png differ
diff --git a/assets/images/partial-react-logo.png b/assets/images/partial-react-logo.png
new file mode 100644
index 0000000..66fd957
Binary files /dev/null and b/assets/images/partial-react-logo.png differ
diff --git a/assets/images/react-logo.png b/assets/images/react-logo.png
new file mode 100644
index 0000000..9d72a9f
Binary files /dev/null and b/assets/images/react-logo.png differ
diff --git a/assets/images/react-logo@2x.png b/assets/images/react-logo@2x.png
new file mode 100644
index 0000000..2229b13
Binary files /dev/null and b/assets/images/react-logo@2x.png differ
diff --git a/assets/images/react-logo@3x.png b/assets/images/react-logo@3x.png
new file mode 100644
index 0000000..a99b203
Binary files /dev/null and b/assets/images/react-logo@3x.png differ
diff --git a/assets/images/splash.png b/assets/images/splash.png
new file mode 100644
index 0000000..0e89705
Binary files /dev/null and b/assets/images/splash.png differ
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 0000000..9d89e13
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1,6 @@
+module.exports = function (api) {
+ api.cache(true);
+ return {
+ presets: ['babel-preset-expo'],
+ };
+};
diff --git a/bun.lockb b/bun.lockb
new file mode 100755
index 0000000..4db1282
Binary files /dev/null and b/bun.lockb differ
diff --git a/components/Collapsible.tsx b/components/Collapsible.tsx
new file mode 100644
index 0000000..c326473
--- /dev/null
+++ b/components/Collapsible.tsx
@@ -0,0 +1,41 @@
+import Ionicons from '@expo/vector-icons/Ionicons';
+import { PropsWithChildren, useState } from 'react';
+import { StyleSheet, TouchableOpacity, useColorScheme } from 'react-native';
+
+import { ThemedText } from '@/components/ThemedText';
+import { ThemedView } from '@/components/ThemedView';
+import { Colors } from '@/constants/Colors';
+
+export function Collapsible({ children, title }: PropsWithChildren & { title: string }) {
+ const [isOpen, setIsOpen] = useState(false);
+ const theme = useColorScheme() ?? 'light';
+
+ return (
+
+ setIsOpen((value) => !value)}
+ activeOpacity={0.8}>
+
+ {title}
+
+ {isOpen && {children}}
+
+ );
+}
+
+const styles = StyleSheet.create({
+ heading: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ gap: 6,
+ },
+ content: {
+ marginTop: 6,
+ marginLeft: 24,
+ },
+});
diff --git a/components/ExternalLink.tsx b/components/ExternalLink.tsx
new file mode 100644
index 0000000..8f05675
--- /dev/null
+++ b/components/ExternalLink.tsx
@@ -0,0 +1,24 @@
+import { Link } from 'expo-router';
+import { openBrowserAsync } from 'expo-web-browser';
+import { type ComponentProps } from 'react';
+import { Platform } from 'react-native';
+
+type Props = Omit, 'href'> & { href: string };
+
+export function ExternalLink({ href, ...rest }: Props) {
+ return (
+ {
+ if (Platform.OS !== 'web') {
+ // Prevent the default behavior of linking to the default browser on native.
+ event.preventDefault();
+ // Open the link in an in-app browser.
+ await openBrowserAsync(href);
+ }
+ }}
+ />
+ );
+}
diff --git a/components/HelloWave.tsx b/components/HelloWave.tsx
new file mode 100644
index 0000000..f4b6ea5
--- /dev/null
+++ b/components/HelloWave.tsx
@@ -0,0 +1,37 @@
+import { StyleSheet } from 'react-native';
+import Animated, {
+ useSharedValue,
+ useAnimatedStyle,
+ withTiming,
+ withRepeat,
+ withSequence,
+} from 'react-native-reanimated';
+
+import { ThemedText } from '@/components/ThemedText';
+
+export function HelloWave() {
+ const rotationAnimation = useSharedValue(0);
+
+ rotationAnimation.value = withRepeat(
+ withSequence(withTiming(25, { duration: 150 }), withTiming(0, { duration: 150 })),
+ 4 // Run the animation 4 times
+ );
+
+ const animatedStyle = useAnimatedStyle(() => ({
+ transform: [{ rotate: `${rotationAnimation.value}deg` }],
+ }));
+
+ return (
+
+ 👋
+
+ );
+}
+
+const styles = StyleSheet.create({
+ text: {
+ fontSize: 28,
+ lineHeight: 32,
+ marginTop: -6,
+ },
+});
diff --git a/components/ParallaxScrollView.tsx b/components/ParallaxScrollView.tsx
new file mode 100644
index 0000000..0a35419
--- /dev/null
+++ b/components/ParallaxScrollView.tsx
@@ -0,0 +1,76 @@
+import type { PropsWithChildren, ReactElement } from 'react';
+import { StyleSheet, useColorScheme } from 'react-native';
+import Animated, {
+ interpolate,
+ useAnimatedRef,
+ useAnimatedStyle,
+ useScrollViewOffset,
+} from 'react-native-reanimated';
+
+import { ThemedView } from '@/components/ThemedView';
+
+const HEADER_HEIGHT = 250;
+
+type Props = PropsWithChildren<{
+ headerImage: ReactElement;
+ headerBackgroundColor: { dark: string; light: string };
+}>;
+
+export default function ParallaxScrollView({
+ children,
+ headerImage,
+ headerBackgroundColor,
+}: Props) {
+ const colorScheme = useColorScheme() ?? 'light';
+ const scrollRef = useAnimatedRef();
+ const scrollOffset = useScrollViewOffset(scrollRef);
+
+ const headerAnimatedStyle = useAnimatedStyle(() => {
+ return {
+ transform: [
+ {
+ translateY: interpolate(
+ scrollOffset.value,
+ [-HEADER_HEIGHT, 0, HEADER_HEIGHT],
+ [-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75]
+ ),
+ },
+ {
+ scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]),
+ },
+ ],
+ };
+ });
+
+ return (
+
+
+
+ {headerImage}
+
+ {children}
+
+
+ );
+}
+
+const styles = StyleSheet.create({
+ container: {
+ flex: 1,
+ },
+ header: {
+ height: 250,
+ overflow: 'hidden',
+ },
+ content: {
+ flex: 1,
+ padding: 32,
+ gap: 16,
+ overflow: 'hidden',
+ },
+});
diff --git a/components/ThemedText.tsx b/components/ThemedText.tsx
new file mode 100644
index 0000000..c0e1a78
--- /dev/null
+++ b/components/ThemedText.tsx
@@ -0,0 +1,60 @@
+import { Text, type TextProps, StyleSheet } from 'react-native';
+
+import { useThemeColor } from '@/hooks/useThemeColor';
+
+export type ThemedTextProps = TextProps & {
+ lightColor?: string;
+ darkColor?: string;
+ type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
+};
+
+export function ThemedText({
+ style,
+ lightColor,
+ darkColor,
+ type = 'default',
+ ...rest
+}: ThemedTextProps) {
+ const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');
+
+ return (
+
+ );
+}
+
+const styles = StyleSheet.create({
+ default: {
+ fontSize: 16,
+ lineHeight: 24,
+ },
+ defaultSemiBold: {
+ fontSize: 16,
+ lineHeight: 24,
+ fontWeight: '600',
+ },
+ title: {
+ fontSize: 32,
+ fontWeight: 'bold',
+ lineHeight: 32,
+ },
+ subtitle: {
+ fontSize: 20,
+ fontWeight: 'bold',
+ },
+ link: {
+ lineHeight: 30,
+ fontSize: 16,
+ color: '#0a7ea4',
+ },
+});
diff --git a/components/ThemedView.tsx b/components/ThemedView.tsx
new file mode 100644
index 0000000..4d2cb09
--- /dev/null
+++ b/components/ThemedView.tsx
@@ -0,0 +1,14 @@
+import { View, type ViewProps } from 'react-native';
+
+import { useThemeColor } from '@/hooks/useThemeColor';
+
+export type ThemedViewProps = ViewProps & {
+ lightColor?: string;
+ darkColor?: string;
+};
+
+export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) {
+ const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
+
+ return ;
+}
diff --git a/components/__tests__/ThemedText-test.tsx b/components/__tests__/ThemedText-test.tsx
new file mode 100644
index 0000000..1ac3225
--- /dev/null
+++ b/components/__tests__/ThemedText-test.tsx
@@ -0,0 +1,10 @@
+import * as React from 'react';
+import renderer from 'react-test-renderer';
+
+import { ThemedText } from '../ThemedText';
+
+it(`renders correctly`, () => {
+ const tree = renderer.create(Snapshot test!).toJSON();
+
+ expect(tree).toMatchSnapshot();
+});
diff --git a/components/__tests__/__snapshots__/ThemedText-test.tsx.snap b/components/__tests__/__snapshots__/ThemedText-test.tsx.snap
new file mode 100644
index 0000000..b68e53e
--- /dev/null
+++ b/components/__tests__/__snapshots__/ThemedText-test.tsx.snap
@@ -0,0 +1,24 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`renders correctly 1`] = `
+
+ Snapshot test!
+
+`;
diff --git a/components/navigation/TabBarIcon.tsx b/components/navigation/TabBarIcon.tsx
new file mode 100644
index 0000000..b7302c3
--- /dev/null
+++ b/components/navigation/TabBarIcon.tsx
@@ -0,0 +1,9 @@
+// You can explore the built-in icon families and icons on the web at https://icons.expo.fyi/
+
+import Ionicons from '@expo/vector-icons/Ionicons';
+import { type IconProps } from '@expo/vector-icons/build/createIconSet';
+import { type ComponentProps } from 'react';
+
+export function TabBarIcon({ style, ...rest }: IconProps['name']>) {
+ return ;
+}
diff --git a/constants/Colors.ts b/constants/Colors.ts
new file mode 100644
index 0000000..14e6784
--- /dev/null
+++ b/constants/Colors.ts
@@ -0,0 +1,26 @@
+/**
+ * Below are the colors that are used in the app. The colors are defined in the light and dark mode.
+ * There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc.
+ */
+
+const tintColorLight = '#0a7ea4';
+const tintColorDark = '#fff';
+
+export const Colors = {
+ light: {
+ text: '#11181C',
+ background: '#fff',
+ tint: tintColorLight,
+ icon: '#687076',
+ tabIconDefault: '#687076',
+ tabIconSelected: tintColorLight,
+ },
+ dark: {
+ text: '#ECEDEE',
+ background: '#151718',
+ tint: tintColorDark,
+ icon: '#9BA1A6',
+ tabIconDefault: '#9BA1A6',
+ tabIconSelected: tintColorDark,
+ },
+};
diff --git a/hooks/useColorScheme.ts b/hooks/useColorScheme.ts
new file mode 100644
index 0000000..17e3c63
--- /dev/null
+++ b/hooks/useColorScheme.ts
@@ -0,0 +1 @@
+export { useColorScheme } from 'react-native';
diff --git a/hooks/useColorScheme.web.ts b/hooks/useColorScheme.web.ts
new file mode 100644
index 0000000..6dcd80d
--- /dev/null
+++ b/hooks/useColorScheme.web.ts
@@ -0,0 +1,8 @@
+// NOTE: The default React Native styling doesn't support server rendering.
+// Server rendered styles should not change between the first render of the HTML
+// and the first render on the client. Typically, web developers will use CSS media queries
+// to render different styles on the client and server, these aren't directly supported in React Native
+// but can be achieved using a styling library like Nativewind.
+export function useColorScheme() {
+ return 'light';
+}
diff --git a/hooks/useThemeColor.ts b/hooks/useThemeColor.ts
new file mode 100644
index 0000000..ae43b47
--- /dev/null
+++ b/hooks/useThemeColor.ts
@@ -0,0 +1,22 @@
+/**
+ * Learn more about light and dark modes:
+ * https://docs.expo.dev/guides/color-schemes/
+ */
+
+import { useColorScheme } from 'react-native';
+
+import { Colors } from '@/constants/Colors';
+
+export function useThemeColor(
+ props: { light?: string; dark?: string },
+ colorName: keyof typeof Colors.light & keyof typeof Colors.dark
+) {
+ const theme = useColorScheme() ?? 'light';
+ const colorFromProps = props[theme];
+
+ if (colorFromProps) {
+ return colorFromProps;
+ } else {
+ return Colors[theme][colorName];
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..cee8491
--- /dev/null
+++ b/package.json
@@ -0,0 +1,49 @@
+{
+ "name": "uber-clone",
+ "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": {
+ "@expo/vector-icons": "^14.0.2",
+ "@react-navigation/native": "^6.0.2",
+ "expo": "~51.0.28",
+ "expo-constants": "~16.0.2",
+ "expo-font": "~12.0.9",
+ "expo-linking": "~6.3.1",
+ "expo-router": "~3.5.23",
+ "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.5",
+ "react-native-gesture-handler": "~2.16.1",
+ "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
+}
diff --git a/scripts/reset-project.js b/scripts/reset-project.js
new file mode 100755
index 0000000..4512e16
--- /dev/null
+++ b/scripts/reset-project.js
@@ -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 (
+
+ Edit app/index.tsx to edit this screen.
+
+ );
+}
+`;
+
+const layoutContent = `import { Stack } from "expo-router";
+
+export default function RootLayout() {
+ return (
+
+
+
+ );
+}
+`;
+
+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.');
+ });
+ });
+ });
+});
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..909e901
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,17 @@
+{
+ "extends": "expo/tsconfig.base",
+ "compilerOptions": {
+ "strict": true,
+ "paths": {
+ "@/*": [
+ "./*"
+ ]
+ }
+ },
+ "include": [
+ "**/*.ts",
+ "**/*.tsx",
+ ".expo/types/**/*.ts",
+ "expo-env.d.ts"
+ ]
+}