···11-// Fallback for using MaterialIcons on Android and web.
22-33-import MaterialIcons from '@expo/vector-icons/MaterialIcons';
44-import { SymbolWeight, SymbolViewProps } from 'expo-symbols';
55-import { ComponentProps } from 'react';
66-import { OpaqueColorValue, type StyleProp, type TextStyle } from 'react-native';
77-88-type IconMapping = Record<SymbolViewProps['name'], ComponentProps<typeof MaterialIcons>['name']>;
99-type IconSymbolName = keyof typeof MAPPING;
1010-1111-/**
1212- * Add your SF Symbols to Material Icons mappings here.
1313- * - see Material Icons in the [Icons Directory](https://icons.expo.fyi).
1414- * - see SF Symbols in the [SF Symbols](https://developer.apple.com/sf-symbols/) app.
1515- */
1616-const MAPPING = {
1717- 'house.fill': 'home',
1818- 'paperplane.fill': 'send',
1919- 'chevron.left.forwardslash.chevron.right': 'code',
2020- 'chevron.right': 'chevron-right',
2121-} as IconMapping;
2222-2323-/**
2424- * An icon component that uses native SF Symbols on iOS, and Material Icons on Android and web.
2525- * This ensures a consistent look across platforms, and optimal resource usage.
2626- * Icon `name`s are based on SF Symbols and require manual mapping to Material Icons.
2727- */
2828-export function IconSymbol({
2929- name,
3030- size = 24,
3131- color,
3232- style,
3333-}: {
3434- name: IconSymbolName;
3535- size?: number;
3636- color: string | OpaqueColorValue;
3737- style?: StyleProp<TextStyle>;
3838- weight?: SymbolWeight;
3939-}) {
4040- return <MaterialIcons color={color} size={size} name={MAPPING[name]} style={style} />;
4141-}
-53
constants/theme.ts
···11-/**
22- * Below are the colors that are used in the app. The colors are defined in the light and dark mode.
33- * 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.
44- */
55-66-import { Platform } from 'react-native';
77-88-const tintColorLight = '#0a7ea4';
99-const tintColorDark = '#fff';
1010-1111-export const Colors = {
1212- light: {
1313- text: '#11181C',
1414- background: '#fff',
1515- tint: tintColorLight,
1616- icon: '#687076',
1717- tabIconDefault: '#687076',
1818- tabIconSelected: tintColorLight,
1919- },
2020- dark: {
2121- text: '#ECEDEE',
2222- background: '#151718',
2323- tint: tintColorDark,
2424- icon: '#9BA1A6',
2525- tabIconDefault: '#9BA1A6',
2626- tabIconSelected: tintColorDark,
2727- },
2828-};
2929-3030-export const Fonts = Platform.select({
3131- ios: {
3232- /** iOS `UIFontDescriptorSystemDesignDefault` */
3333- sans: 'system-ui',
3434- /** iOS `UIFontDescriptorSystemDesignSerif` */
3535- serif: 'ui-serif',
3636- /** iOS `UIFontDescriptorSystemDesignRounded` */
3737- rounded: 'ui-rounded',
3838- /** iOS `UIFontDescriptorSystemDesignMonospaced` */
3939- mono: 'ui-monospace',
4040- },
4141- default: {
4242- sans: 'normal',
4343- serif: 'serif',
4444- rounded: 'normal',
4545- mono: 'monospace',
4646- },
4747- web: {
4848- sans: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif",
4949- serif: "Georgia, 'Times New Roman', serif",
5050- rounded: "'SF Pro Rounded', 'Hiragino Maru Gothic ProN', Meiryo, 'MS PGothic', sans-serif",
5151- mono: "SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace",
5252- },
5353-});
-1
hooks/use-color-scheme.ts
···11-export { useColorScheme } from 'react-native';
-21
hooks/use-color-scheme.web.ts
···11-import { useEffect, useState } from 'react';
22-import { useColorScheme as useRNColorScheme } from 'react-native';
33-44-/**
55- * To support static rendering, this value needs to be re-calculated on the client side for web
66- */
77-export function useColorScheme() {
88- const [hasHydrated, setHasHydrated] = useState(false);
99-1010- useEffect(() => {
1111- setHasHydrated(true);
1212- }, []);
1313-1414- const colorScheme = useRNColorScheme();
1515-1616- if (hasHydrated) {
1717- return colorScheme;
1818- }
1919-2020- return 'light';
2121-}
-21
hooks/use-theme-color.ts
···11-/**
22- * Learn more about light and dark modes:
33- * https://docs.expo.dev/guides/color-schemes/
44- */
55-66-import { Colors } from '@/constants/theme';
77-import { useColorScheme } from '@/hooks/use-color-scheme';
88-99-export function useThemeColor(
1010- props: { light?: string; dark?: string },
1111- colorName: keyof typeof Colors.light & keyof typeof Colors.dark
1212-) {
1313- const theme = useColorScheme() ?? 'light';
1414- const colorFromProps = props[theme];
1515-1616- if (colorFromProps) {
1717- return colorFromProps;
1818- } else {
1919- return Colors[theme][colorName];
2020- }
2121-}
-112
scripts/reset-project.js
···11-#!/usr/bin/env node
22-33-/**
44- * This script is used to reset the project to a blank state.
55- * It deletes or moves the /app, /components, /hooks, /scripts, and /constants directories to /app-example based on user input and creates a new /app directory with an index.tsx and _layout.tsx file.
66- * You can remove the `reset-project` script from package.json and safely delete this file after running it.
77- */
88-99-const fs = require("fs");
1010-const path = require("path");
1111-const readline = require("readline");
1212-1313-const root = process.cwd();
1414-const oldDirs = ["app", "components", "hooks", "constants", "scripts"];
1515-const exampleDir = "app-example";
1616-const newAppDir = "app";
1717-const exampleDirPath = path.join(root, exampleDir);
1818-1919-const indexContent = `import { Text, View } from "react-native";
2020-2121-export default function Index() {
2222- return (
2323- <View
2424- style={{
2525- flex: 1,
2626- justifyContent: "center",
2727- alignItems: "center",
2828- }}
2929- >
3030- <Text>Edit app/index.tsx to edit this screen.</Text>
3131- </View>
3232- );
3333-}
3434-`;
3535-3636-const layoutContent = `import { Stack } from "expo-router";
3737-3838-export default function RootLayout() {
3939- return <Stack />;
4040-}
4141-`;
4242-4343-const rl = readline.createInterface({
4444- input: process.stdin,
4545- output: process.stdout,
4646-});
4747-4848-const moveDirectories = async (userInput) => {
4949- try {
5050- if (userInput === "y") {
5151- // Create the app-example directory
5252- await fs.promises.mkdir(exampleDirPath, { recursive: true });
5353- console.log(`๐ /${exampleDir} directory created.`);
5454- }
5555-5656- // Move old directories to new app-example directory or delete them
5757- for (const dir of oldDirs) {
5858- const oldDirPath = path.join(root, dir);
5959- if (fs.existsSync(oldDirPath)) {
6060- if (userInput === "y") {
6161- const newDirPath = path.join(root, exampleDir, dir);
6262- await fs.promises.rename(oldDirPath, newDirPath);
6363- console.log(`โก๏ธ /${dir} moved to /${exampleDir}/${dir}.`);
6464- } else {
6565- await fs.promises.rm(oldDirPath, { recursive: true, force: true });
6666- console.log(`โ /${dir} deleted.`);
6767- }
6868- } else {
6969- console.log(`โก๏ธ /${dir} does not exist, skipping.`);
7070- }
7171- }
7272-7373- // Create new /app directory
7474- const newAppDirPath = path.join(root, newAppDir);
7575- await fs.promises.mkdir(newAppDirPath, { recursive: true });
7676- console.log("\n๐ New /app directory created.");
7777-7878- // Create index.tsx
7979- const indexPath = path.join(newAppDirPath, "index.tsx");
8080- await fs.promises.writeFile(indexPath, indexContent);
8181- console.log("๐ app/index.tsx created.");
8282-8383- // Create _layout.tsx
8484- const layoutPath = path.join(newAppDirPath, "_layout.tsx");
8585- await fs.promises.writeFile(layoutPath, layoutContent);
8686- console.log("๐ app/_layout.tsx created.");
8787-8888- console.log("\nโ Project reset complete. Next steps:");
8989- console.log(
9090- `1. Run \`npx expo start\` to start a development server.\n2. Edit app/index.tsx to edit the main screen.${
9191- userInput === "y"
9292- ? `\n3. Delete the /${exampleDir} directory when you're done referencing it.`
9393- : ""
9494- }`
9595- );
9696- } catch (error) {
9797- console.error(`โ Error during script execution: ${error.message}`);
9898- }
9999-};
100100-101101-rl.question(
102102- "Do you want to move existing files to /app-example instead of deleting them? (Y/n): ",
103103- (answer) => {
104104- const userInput = answer.trim().toLowerCase() || "y";
105105- if (userInput === "y" || userInput === "n") {
106106- moveDirectories(userInput).finally(() => rl.close());
107107- } else {
108108- console.log("โ Invalid input. Please enter 'Y' or 'N'.");
109109- rl.close();
110110- }
111111- }
112112-);