finger

finger
git clone https://git.prime8.dev/finger.git
Log | Files | Refs | README | LICENSE

commit 13155a0e6993d47560a6ee23f8f70190e903901c
parent d252ca32d1b200bf985d217ba338f5a1c8f7f74e
Author: Blubfish <101827184+Blubfish@users.noreply.github.com>
Date:   Tue, 24 Jun 2025 15:51:43 -0500

Add expo app, add splash, add icon, add safeAreaView, add Status bar


Diffstat:
A.expo/README.md | 8++++++++
A.expo/devices.json | 3+++
A.expo/types/router.d.ts | 14++++++++++++++
A.expo/web/cache/production/images/favicon/favicon-24272cdaeff82cc5facdaccd982a6f05b60c4504704bbf94c19a6388659880bb-contain-transparent/favicon-48.png | 0
M.gitignore | 10+++++++++-
Mapp.json | 15++++++++++-----
Dapp/(tabs)/_layout.tsx | 45---------------------------------------------
Dapp/(tabs)/explore.tsx | 110-------------------------------------------------------------------------------
Dapp/(tabs)/index.tsx | 75---------------------------------------------------------------------------
Mapp/+not-found.tsx | 42++++++++++++++++++++----------------------
Mapp/_layout.tsx | 27+++++++--------------------
Aapp/about.tsx | 22++++++++++++++++++++++
Aapp/index.tsx | 32++++++++++++++++++++++++++++++++
Massets/images/adaptive-icon.png | 0
Aassets/images/ios-dark.png | 0
Aassets/images/ios-light.png | 0
Aassets/images/ios-tinted.png | 0
Aassets/images/splash-dark.png | 0
Aassets/images/splash-light.png | 0
Dcomponents/Collapsible.tsx | 45---------------------------------------------
Dcomponents/ExternalLink.tsx | 24------------------------
Dcomponents/HapticTab.tsx | 18------------------
Dcomponents/HelloWave.tsx | 40----------------------------------------
Dcomponents/ParallaxScrollView.tsx | 82-------------------------------------------------------------------------------
Dcomponents/ThemedText.tsx | 60------------------------------------------------------------
Dcomponents/ThemedView.tsx | 14--------------
Dcomponents/ui/IconSymbol.ios.tsx | 32--------------------------------
Dcomponents/ui/IconSymbol.tsx | 41-----------------------------------------
Dcomponents/ui/TabBarBackground.ios.tsx | 19-------------------
Dcomponents/ui/TabBarBackground.tsx | 6------
Dconstants/Colors.ts | 26--------------------------
Dhooks/useColorScheme.ts | 1-
Dhooks/useColorScheme.web.ts | 21---------------------
Dhooks/useThemeColor.ts | 21---------------------
Dscripts/reset-project.js | 112-------------------------------------------------------------------------------
35 files changed, 125 insertions(+), 840 deletions(-)

diff --git a/.expo/README.md b/.expo/README.md @@ -0,0 +1,8 @@ +> Why do I have a folder named ".expo" in my project? +The ".expo" folder is created when an Expo project is started using "expo start" command. +> What do the files contain? +- "devices.json": contains information about devices that have recently opened this project. This is used to populate the "Development sessions" list in your development builds. +- "settings.json": contains the server configuration that is used to serve the application manifest. +> Should I commit the ".expo" folder? +No, you should not share the ".expo" folder. It does not contain any information that is relevant for other developers working on the project, it is specific to your machine. +Upon project creation, the ".expo" folder is already added to your ".gitignore" file. diff --git a/.expo/devices.json b/.expo/devices.json @@ -0,0 +1,3 @@ +{ + "devices": [] +} diff --git a/.expo/types/router.d.ts b/.expo/types/router.d.ts @@ -0,0 +1,14 @@ +/* eslint-disable */ +import * as Router from 'expo-router'; + +export * from 'expo-router'; + +declare module 'expo-router' { + export namespace ExpoRouter { + export interface __routes<T extends string | object = string> { + hrefInputParams: { pathname: Router.RelativePathString, params?: Router.UnknownInputParams } | { pathname: Router.ExternalPathString, params?: Router.UnknownInputParams } | { pathname: `/`; params?: Router.UnknownInputParams; } | { pathname: `/about`; params?: Router.UnknownInputParams; } | { pathname: `/_sitemap`; params?: Router.UnknownInputParams; } | { pathname: `/+not-found`, params: Router.UnknownInputParams & { } }; + hrefOutputParams: { pathname: Router.RelativePathString, params?: Router.UnknownOutputParams } | { pathname: Router.ExternalPathString, params?: Router.UnknownOutputParams } | { pathname: `/`; params?: Router.UnknownOutputParams; } | { pathname: `/about`; params?: Router.UnknownOutputParams; } | { pathname: `/_sitemap`; params?: Router.UnknownOutputParams; } | { pathname: `/+not-found`, params: Router.UnknownOutputParams & { } }; + href: Router.RelativePathString | Router.ExternalPathString | `/${`?${string}` | `#${string}` | ''}` | `/about${`?${string}` | `#${string}` | ''}` | `/_sitemap${`?${string}` | `#${string}` | ''}` | { pathname: Router.RelativePathString, params?: Router.UnknownInputParams } | { pathname: Router.ExternalPathString, params?: Router.UnknownInputParams } | { pathname: `/`; params?: Router.UnknownInputParams; } | { pathname: `/about`; params?: Router.UnknownInputParams; } | { pathname: `/_sitemap`; params?: Router.UnknownInputParams; } | `/+not-found` | { pathname: `/+not-found`, params: Router.UnknownInputParams & { } }; + } + } +} diff --git a/.expo/web/cache/production/images/favicon/favicon-24272cdaeff82cc5facdaccd982a6f05b60c4504704bbf94c19a6388659880bb-contain-transparent/favicon-48.png b/.expo/web/cache/production/images/favicon/favicon-24272cdaeff82cc5facdaccd982a6f05b60c4504704bbf94c19a6388659880bb-contain-transparent/favicon-48.png Binary files differ. diff --git a/.gitignore b/.gitignore @@ -1 +1,8 @@ -node_modules +node_modules + +# @generated expo-cli sync-2b81b286409207a5da26e14c78851eb30d8ccbdb +# The following patterns were generated by expo-cli + +expo-env.d.ts +# @end expo-cli +.expo +\ No newline at end of file diff --git a/app.json b/app.json @@ -4,12 +4,13 @@ "slug": "finger", "version": "1.0.0", "orientation": "portrait", - "icon": "./assets/images/icon.png", + "icon": "./assets/icon.png", "scheme": "finger", "userInterfaceStyle": "automatic", "newArchEnabled": true, "ios": { - "supportsTablet": true + "supportsTablet": true, + "icon": "./assets/ios-light.png" }, "android": { "adaptiveIcon": { @@ -28,10 +29,14 @@ [ "expo-splash-screen", { - "image": "./assets/images/splash-icon.png", - "imageWidth": 200, "resizeMode": "contain", - "backgroundColor": "#ffffff" + "backgroundColor": "#232323", + "image": "./assets/images/splash-light.png", + "dark": { + "image": "./assets/images/splash-dark.png", + "backgroundColor": "#000000" + }, + "imageWidth": 200 } ] ], diff --git a/app/(tabs)/_layout.tsx b/app/(tabs)/_layout.tsx @@ -1,45 +0,0 @@ -import { Tabs } from 'expo-router'; -import React from 'react'; -import { Platform } from 'react-native'; - -import { HapticTab } from '@/components/HapticTab'; -import { IconSymbol } from '@/components/ui/IconSymbol'; -import TabBarBackground from '@/components/ui/TabBarBackground'; -import { Colors } from '@/constants/Colors'; -import { useColorScheme } from '@/hooks/useColorScheme'; - -export default function TabLayout() { - const colorScheme = useColorScheme(); - - return ( - <Tabs - screenOptions={{ - tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint, - headerShown: false, - tabBarButton: HapticTab, - tabBarBackground: TabBarBackground, - tabBarStyle: Platform.select({ - ios: { - // Use a transparent background on iOS to show the blur effect - position: 'absolute', - }, - default: {}, - }), - }}> - <Tabs.Screen - name="index" - options={{ - title: 'Home', - tabBarIcon: ({ color }) => <IconSymbol size={28} name="house.fill" color={color} />, - }} - /> - <Tabs.Screen - name="explore" - options={{ - title: 'Explore', - tabBarIcon: ({ color }) => <IconSymbol size={28} name="paperplane.fill" color={color} />, - }} - /> - </Tabs> - ); -} diff --git a/app/(tabs)/explore.tsx b/app/(tabs)/explore.tsx @@ -1,110 +0,0 @@ -import { Image } from 'expo-image'; -import { Platform, StyleSheet } 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'; -import { IconSymbol } from '@/components/ui/IconSymbol'; - -export default function TabTwoScreen() { - return ( - <ParallaxScrollView - headerBackgroundColor={{ light: '#D0D0D0', dark: '#353636' }} - headerImage={ - <IconSymbol - size={310} - color="#808080" - name="chevron.left.forwardslash.chevron.right" - style={styles.headerImage} - /> - }> - <ThemedView style={styles.titleContainer}> - <ThemedText type="title">Explore</ThemedText> - </ThemedView> - <ThemedText>This app includes example code to help you get started.</ThemedText> - <Collapsible title="File-based routing"> - <ThemedText> - This app has two screens:{' '} - <ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> and{' '} - <ThemedText type="defaultSemiBold">app/(tabs)/explore.tsx</ThemedText> - </ThemedText> - <ThemedText> - The layout file in <ThemedText type="defaultSemiBold">app/(tabs)/_layout.tsx</ThemedText>{' '} - sets up the tab navigator. - </ThemedText> - <ExternalLink href="https://docs.expo.dev/router/introduction"> - <ThemedText type="link">Learn more</ThemedText> - </ExternalLink> - </Collapsible> - <Collapsible title="Android, iOS, and web support"> - <ThemedText> - You can open this project on Android, iOS, and the web. To open the web version, press{' '} - <ThemedText type="defaultSemiBold">w</ThemedText> in the terminal running this project. - </ThemedText> - </Collapsible> - <Collapsible title="Images"> - <ThemedText> - For static images, you can use the <ThemedText type="defaultSemiBold">@2x</ThemedText> and{' '} - <ThemedText type="defaultSemiBold">@3x</ThemedText> suffixes to provide files for - different screen densities - </ThemedText> - <Image source={require('@/assets/images/react-logo.png')} style={{ alignSelf: 'center' }} /> - <ExternalLink href="https://reactnative.dev/docs/images"> - <ThemedText type="link">Learn more</ThemedText> - </ExternalLink> - </Collapsible> - <Collapsible title="Custom fonts"> - <ThemedText> - Open <ThemedText type="defaultSemiBold">app/_layout.tsx</ThemedText> to see how to load{' '} - <ThemedText style={{ fontFamily: 'SpaceMono' }}> - custom fonts such as this one. - </ThemedText> - </ThemedText> - <ExternalLink href="https://docs.expo.dev/versions/latest/sdk/font"> - <ThemedText type="link">Learn more</ThemedText> - </ExternalLink> - </Collapsible> - <Collapsible title="Light and dark mode components"> - <ThemedText> - This template has light and dark mode support. The{' '} - <ThemedText type="defaultSemiBold">useColorScheme()</ThemedText> hook lets you inspect - what the user&apos;s current color scheme is, and so you can adjust UI colors accordingly. - </ThemedText> - <ExternalLink href="https://docs.expo.dev/develop/user-interface/color-themes/"> - <ThemedText type="link">Learn more</ThemedText> - </ExternalLink> - </Collapsible> - <Collapsible title="Animations"> - <ThemedText> - This template includes an example of an animated component. The{' '} - <ThemedText type="defaultSemiBold">components/HelloWave.tsx</ThemedText> component uses - the powerful <ThemedText type="defaultSemiBold">react-native-reanimated</ThemedText>{' '} - library to create a waving hand animation. - </ThemedText> - {Platform.select({ - ios: ( - <ThemedText> - The <ThemedText type="defaultSemiBold">components/ParallaxScrollView.tsx</ThemedText>{' '} - component provides a parallax effect for the header image. - </ThemedText> - ), - })} - </Collapsible> - </ParallaxScrollView> - ); -} - -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 @@ -1,75 +0,0 @@ -import { Image } from 'expo-image'; -import { Platform, StyleSheet } 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 ( - <ParallaxScrollView - headerBackgroundColor={{ light: '#A1CEDC', dark: '#1D3D47' }} - headerImage={ - <Image - source={require('@/assets/images/partial-react-logo.png')} - style={styles.reactLogo} - /> - }> - <ThemedView style={styles.titleContainer}> - <ThemedText type="title">Welcome!</ThemedText> - <HelloWave /> - </ThemedView> - <ThemedView style={styles.stepContainer}> - <ThemedText type="subtitle">Step 1: Try it</ThemedText> - <ThemedText> - Edit <ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> to see changes. - Press{' '} - <ThemedText type="defaultSemiBold"> - {Platform.select({ - ios: 'cmd + d', - android: 'cmd + m', - web: 'F12', - })} - </ThemedText>{' '} - to open developer tools. - </ThemedText> - </ThemedView> - <ThemedView style={styles.stepContainer}> - <ThemedText type="subtitle">Step 2: Explore</ThemedText> - <ThemedText> - {`Tap the Explore tab to learn more about what's included in this starter app.`} - </ThemedText> - </ThemedView> - <ThemedView style={styles.stepContainer}> - <ThemedText type="subtitle">Step 3: Get a fresh start</ThemedText> - <ThemedText> - {`When you're ready, run `} - <ThemedText type="defaultSemiBold">npm run reset-project</ThemedText> to get a fresh{' '} - <ThemedText type="defaultSemiBold">app</ThemedText> directory. This will move the current{' '} - <ThemedText type="defaultSemiBold">app</ThemedText> to{' '} - <ThemedText type="defaultSemiBold">app-example</ThemedText>. - </ThemedText> - </ThemedView> - </ParallaxScrollView> - ); -} - -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/+not-found.tsx b/app/+not-found.tsx @@ -1,32 +1,29 @@ -import { Link, Stack } from 'expo-router'; -import { StyleSheet } from 'react-native'; +import {StyleSheet} from 'react-native'; +import {Link, Stack} from 'expo-router'; +import { SafeAreaView } from "react-native-safe-area-context"; -import { ThemedText } from '@/components/ThemedText'; -import { ThemedView } from '@/components/ThemedView'; - -export default function NotFoundScreen() { - return ( - <> - <Stack.Screen options={{ title: 'Oops!' }} /> - <ThemedView style={styles.container}> - <ThemedText type="title">This screen does not exist.</ThemedText> - <Link href="/" style={styles.link}> - <ThemedText type="link">Go to home screen!</ThemedText> - </Link> - </ThemedView> - </> - ); +export default function NotFoundScreen(){ + return ( + <SafeAreaView style={styles.container}> + <Stack.Screen options={{ title: 'Page Not Found' }} /> + <Link href="/" style={styles.button}>Go back to home </Link> + </SafeAreaView> + ) } const styles = StyleSheet.create({ container: { flex: 1, + backgroundColor: '#25292e', alignItems: 'center', justifyContent: 'center', - padding: 20, }, - link: { - marginTop: 15, - paddingVertical: 15, + text: { + color: '#fff', + }, + button: { + fontSize: 20, + textDecorationLine: 'underline', + color: '#fff', }, -}); +}); +\ No newline at end of file diff --git a/app/_layout.tsx b/app/_layout.tsx @@ -1,29 +1,16 @@ -import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native'; -import { useFonts } from 'expo-font'; import { Stack } from 'expo-router'; +import { SafeAreaView } from "react-native-safe-area-context"; import { StatusBar } from 'expo-status-bar'; -import 'react-native-reanimated'; - -import { useColorScheme } from '@/hooks/useColorScheme'; export default function RootLayout() { - const colorScheme = useColorScheme(); - const [loaded] = useFonts({ - SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), - }); - - if (!loaded) { - // Async font loading only occurs in development. - return null; - } - return ( - <ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}> + <SafeAreaView> + <StatusBar style="auto" /> <Stack> - <Stack.Screen name="(tabs)" options={{ headerShown: false }} /> - <Stack.Screen name="+not-found" /> + <Stack.Screen name="index" options={{ title: 'Home' }} /> + <Stack.Screen name="about" options={{ title: 'About' }} /> </Stack> - <StatusBar style="auto" /> - </ThemeProvider> + </SafeAreaView> ); } + diff --git a/app/about.tsx b/app/about.tsx @@ -0,0 +1,22 @@ +import { Text, StyleSheet } from 'react-native'; +import { SafeAreaView } from "react-native-safe-area-context"; + +export default function AboutScreen() { + return ( + <SafeAreaView style={styles.container}> + <Text style={styles.text}>About screen</Text> + </SafeAreaView> + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#25292e', + justifyContent: 'center', + alignItems: 'center', + }, + text: { + color: '#fff', + }, +}); diff --git a/app/index.tsx b/app/index.tsx @@ -0,0 +1,31 @@ +import { Text, StyleSheet } from "react-native"; +import { Link } from "expo-router"; +import { SafeAreaView } from "react-native-safe-area-context"; + +export default function Index() { + return ( + <SafeAreaView style={styles.container}> + <Text style={styles.text}>Hello Sleepy Boy</Text> + <Link href="/about" style={styles.button}> + Go to About + </Link> + </SafeAreaView> + ); +} + +const styles = StyleSheet.create({ + container: { + flex: 1, + backgroundColor: '#25292e', + alignItems: 'center', + justifyContent: 'center', + }, + text: { + color: '#fff', + }, + button: { + fontSize: 20, + textDecorationLine: 'underline', + color: '#fff', + }, +}); +\ No newline at end of file diff --git a/assets/images/adaptive-icon.png b/assets/images/adaptive-icon.png Binary files differ. diff --git a/assets/images/ios-dark.png b/assets/images/ios-dark.png Binary files differ. diff --git a/assets/images/ios-light.png b/assets/images/ios-light.png Binary files differ. diff --git a/assets/images/ios-tinted.png b/assets/images/ios-tinted.png Binary files differ. diff --git a/assets/images/splash-dark.png b/assets/images/splash-dark.png Binary files differ. diff --git a/assets/images/splash-light.png b/assets/images/splash-light.png Binary files differ. diff --git a/components/Collapsible.tsx b/components/Collapsible.tsx @@ -1,45 +0,0 @@ -import { PropsWithChildren, useState } from 'react'; -import { StyleSheet, TouchableOpacity } from 'react-native'; - -import { ThemedText } from '@/components/ThemedText'; -import { ThemedView } from '@/components/ThemedView'; -import { IconSymbol } from '@/components/ui/IconSymbol'; -import { Colors } from '@/constants/Colors'; -import { useColorScheme } from '@/hooks/useColorScheme'; - -export function Collapsible({ children, title }: PropsWithChildren & { title: string }) { - const [isOpen, setIsOpen] = useState(false); - const theme = useColorScheme() ?? 'light'; - - return ( - <ThemedView> - <TouchableOpacity - style={styles.heading} - onPress={() => setIsOpen((value) => !value)} - activeOpacity={0.8}> - <IconSymbol - name="chevron.right" - size={18} - weight="medium" - color={theme === 'light' ? Colors.light.icon : Colors.dark.icon} - style={{ transform: [{ rotate: isOpen ? '90deg' : '0deg' }] }} - /> - - <ThemedText type="defaultSemiBold">{title}</ThemedText> - </TouchableOpacity> - {isOpen && <ThemedView style={styles.content}>{children}</ThemedView>} - </ThemedView> - ); -} - -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 @@ -1,24 +0,0 @@ -import { Href, Link } from 'expo-router'; -import { openBrowserAsync } from 'expo-web-browser'; -import { type ComponentProps } from 'react'; -import { Platform } from 'react-native'; - -type Props = Omit<ComponentProps<typeof Link>, 'href'> & { href: Href & string }; - -export function ExternalLink({ href, ...rest }: Props) { - return ( - <Link - target="_blank" - {...rest} - href={href} - onPress={async (event) => { - 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/HapticTab.tsx b/components/HapticTab.tsx @@ -1,18 +0,0 @@ -import { BottomTabBarButtonProps } from '@react-navigation/bottom-tabs'; -import { PlatformPressable } from '@react-navigation/elements'; -import * as Haptics from 'expo-haptics'; - -export function HapticTab(props: BottomTabBarButtonProps) { - return ( - <PlatformPressable - {...props} - onPressIn={(ev) => { - if (process.env.EXPO_OS === 'ios') { - // Add a soft haptic feedback when pressing down on the tabs. - Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light); - } - props.onPressIn?.(ev); - }} - /> - ); -} diff --git a/components/HelloWave.tsx b/components/HelloWave.tsx @@ -1,40 +0,0 @@ -import { useEffect } from 'react'; -import { StyleSheet } from 'react-native'; -import Animated, { - useAnimatedStyle, - useSharedValue, - withRepeat, - withSequence, - withTiming, -} from 'react-native-reanimated'; - -import { ThemedText } from '@/components/ThemedText'; - -export function HelloWave() { - const rotationAnimation = useSharedValue(0); - - useEffect(() => { - rotationAnimation.value = withRepeat( - withSequence(withTiming(25, { duration: 150 }), withTiming(0, { duration: 150 })), - 4 // Run the animation 4 times - ); - }, [rotationAnimation]); - - const animatedStyle = useAnimatedStyle(() => ({ - transform: [{ rotate: `${rotationAnimation.value}deg` }], - })); - - return ( - <Animated.View style={animatedStyle}> - <ThemedText style={styles.text}>šŸ‘‹</ThemedText> - </Animated.View> - ); -} - -const styles = StyleSheet.create({ - text: { - fontSize: 28, - lineHeight: 32, - marginTop: -6, - }, -}); diff --git a/components/ParallaxScrollView.tsx b/components/ParallaxScrollView.tsx @@ -1,82 +0,0 @@ -import type { PropsWithChildren, ReactElement } from 'react'; -import { StyleSheet } from 'react-native'; -import Animated, { - interpolate, - useAnimatedRef, - useAnimatedStyle, - useScrollViewOffset, -} from 'react-native-reanimated'; - -import { ThemedView } from '@/components/ThemedView'; -import { useBottomTabOverflow } from '@/components/ui/TabBarBackground'; -import { useColorScheme } from '@/hooks/useColorScheme'; - -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<Animated.ScrollView>(); - const scrollOffset = useScrollViewOffset(scrollRef); - const bottom = useBottomTabOverflow(); - 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 ( - <ThemedView style={styles.container}> - <Animated.ScrollView - ref={scrollRef} - scrollEventThrottle={16} - scrollIndicatorInsets={{ bottom }} - contentContainerStyle={{ paddingBottom: bottom }}> - <Animated.View - style={[ - styles.header, - { backgroundColor: headerBackgroundColor[colorScheme] }, - headerAnimatedStyle, - ]}> - {headerImage} - </Animated.View> - <ThemedView style={styles.content}>{children}</ThemedView> - </Animated.ScrollView> - </ThemedView> - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - }, - header: { - height: HEADER_HEIGHT, - overflow: 'hidden', - }, - content: { - flex: 1, - padding: 32, - gap: 16, - overflow: 'hidden', - }, -}); diff --git a/components/ThemedText.tsx b/components/ThemedText.tsx @@ -1,60 +0,0 @@ -import { StyleSheet, Text, type TextProps } 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 ( - <Text - style={[ - { color }, - type === 'default' ? styles.default : undefined, - type === 'title' ? styles.title : undefined, - type === 'defaultSemiBold' ? styles.defaultSemiBold : undefined, - type === 'subtitle' ? styles.subtitle : undefined, - type === 'link' ? styles.link : undefined, - style, - ]} - {...rest} - /> - ); -} - -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 @@ -1,14 +0,0 @@ -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 <View style={[{ backgroundColor }, style]} {...otherProps} />; -} diff --git a/components/ui/IconSymbol.ios.tsx b/components/ui/IconSymbol.ios.tsx @@ -1,32 +0,0 @@ -import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols'; -import { StyleProp, ViewStyle } from 'react-native'; - -export function IconSymbol({ - name, - size = 24, - color, - style, - weight = 'regular', -}: { - name: SymbolViewProps['name']; - size?: number; - color: string; - style?: StyleProp<ViewStyle>; - weight?: SymbolWeight; -}) { - return ( - <SymbolView - weight={weight} - tintColor={color} - resizeMode="scaleAspectFit" - name={name} - style={[ - { - width: size, - height: size, - }, - style, - ]} - /> - ); -} diff --git a/components/ui/IconSymbol.tsx b/components/ui/IconSymbol.tsx @@ -1,41 +0,0 @@ -// Fallback for using MaterialIcons on Android and web. - -import MaterialIcons from '@expo/vector-icons/MaterialIcons'; -import { SymbolWeight, SymbolViewProps } from 'expo-symbols'; -import { ComponentProps } from 'react'; -import { OpaqueColorValue, type StyleProp, type TextStyle } from 'react-native'; - -type IconMapping = Record<SymbolViewProps['name'], ComponentProps<typeof MaterialIcons>['name']>; -type IconSymbolName = keyof typeof MAPPING; - -/** - * Add your SF Symbols to Material Icons mappings here. - * - see Material Icons in the [Icons Directory](https://icons.expo.fyi). - * - see SF Symbols in the [SF Symbols](https://developer.apple.com/sf-symbols/) app. - */ -const MAPPING = { - 'house.fill': 'home', - 'paperplane.fill': 'send', - 'chevron.left.forwardslash.chevron.right': 'code', - 'chevron.right': 'chevron-right', -} as IconMapping; - -/** - * An icon component that uses native SF Symbols on iOS, and Material Icons on Android and web. - * This ensures a consistent look across platforms, and optimal resource usage. - * Icon `name`s are based on SF Symbols and require manual mapping to Material Icons. - */ -export function IconSymbol({ - name, - size = 24, - color, - style, -}: { - name: IconSymbolName; - size?: number; - color: string | OpaqueColorValue; - style?: StyleProp<TextStyle>; - weight?: SymbolWeight; -}) { - return <MaterialIcons color={color} size={size} name={MAPPING[name]} style={style} />; -} diff --git a/components/ui/TabBarBackground.ios.tsx b/components/ui/TabBarBackground.ios.tsx @@ -1,19 +0,0 @@ -import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs'; -import { BlurView } from 'expo-blur'; -import { StyleSheet } from 'react-native'; - -export default function BlurTabBarBackground() { - return ( - <BlurView - // System chrome material automatically adapts to the system's theme - // and matches the native tab bar appearance on iOS. - tint="systemChromeMaterial" - intensity={100} - style={StyleSheet.absoluteFill} - /> - ); -} - -export function useBottomTabOverflow() { - return useBottomTabBarHeight(); -} diff --git a/components/ui/TabBarBackground.tsx b/components/ui/TabBarBackground.tsx @@ -1,6 +0,0 @@ -// This is a shim for web and Android where the tab bar is generally opaque. -export default undefined; - -export function useBottomTabOverflow() { - return 0; -} diff --git a/constants/Colors.ts b/constants/Colors.ts @@ -1,26 +0,0 @@ -/** - * 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 @@ -1 +0,0 @@ -export { useColorScheme } from 'react-native'; diff --git a/hooks/useColorScheme.web.ts b/hooks/useColorScheme.web.ts @@ -1,21 +0,0 @@ -import { useEffect, useState } from 'react'; -import { useColorScheme as useRNColorScheme } from 'react-native'; - -/** - * To support static rendering, this value needs to be re-calculated on the client side for web - */ -export function useColorScheme() { - const [hasHydrated, setHasHydrated] = useState(false); - - useEffect(() => { - setHasHydrated(true); - }, []); - - const colorScheme = useRNColorScheme(); - - if (hasHydrated) { - return colorScheme; - } - - return 'light'; -} diff --git a/hooks/useThemeColor.ts b/hooks/useThemeColor.ts @@ -1,21 +0,0 @@ -/** - * Learn more about light and dark modes: - * https://docs.expo.dev/guides/color-schemes/ - */ - -import { Colors } from '@/constants/Colors'; -import { useColorScheme } from '@/hooks/useColorScheme'; - -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/scripts/reset-project.js b/scripts/reset-project.js @@ -1,112 +0,0 @@ -#!/usr/bin/env node - -/** - * This script is used to reset the project to a blank state. - * 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. - * 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 readline = require("readline"); - -const root = process.cwd(); -const oldDirs = ["app", "components", "hooks", "constants", "scripts"]; -const exampleDir = "app-example"; -const newAppDir = "app"; -const exampleDirPath = path.join(root, exampleDir); - -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 />; -} -`; - -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, -}); - -const moveDirectories = async (userInput) => { - try { - if (userInput === "y") { - // Create the app-example directory - await fs.promises.mkdir(exampleDirPath, { recursive: true }); - console.log(`šŸ“ /${exampleDir} directory created.`); - } - - // Move old directories to new app-example directory or delete them - for (const dir of oldDirs) { - const oldDirPath = path.join(root, dir); - if (fs.existsSync(oldDirPath)) { - if (userInput === "y") { - const newDirPath = path.join(root, exampleDir, dir); - await fs.promises.rename(oldDirPath, newDirPath); - console.log(`āž”ļø /${dir} moved to /${exampleDir}/${dir}.`); - } else { - await fs.promises.rm(oldDirPath, { recursive: true, force: true }); - console.log(`āŒ /${dir} deleted.`); - } - } else { - console.log(`āž”ļø /${dir} does not exist, skipping.`); - } - } - - // Create new /app directory - const newAppDirPath = path.join(root, newAppDir); - await fs.promises.mkdir(newAppDirPath, { recursive: true }); - console.log("\nšŸ“ New /app directory created."); - - // Create index.tsx - const indexPath = path.join(newAppDirPath, "index.tsx"); - await fs.promises.writeFile(indexPath, indexContent); - console.log("šŸ“„ app/index.tsx created."); - - // Create _layout.tsx - const layoutPath = path.join(newAppDirPath, "_layout.tsx"); - await fs.promises.writeFile(layoutPath, layoutContent); - console.log("šŸ“„ app/_layout.tsx created."); - - console.log("\nāœ… Project reset complete. Next steps:"); - console.log( - `1. Run \`npx expo start\` to start a development server.\n2. Edit app/index.tsx to edit the main screen.${ - userInput === "y" - ? `\n3. Delete the /${exampleDir} directory when you're done referencing it.` - : "" - }` - ); - } catch (error) { - console.error(`āŒ Error during script execution: ${error.message}`); - } -}; - -rl.question( - "Do you want to move existing files to /app-example instead of deleting them? (Y/n): ", - (answer) => { - const userInput = answer.trim().toLowerCase() || "y"; - if (userInput === "y" || userInput === "n") { - moveDirectories(userInput).finally(() => rl.close()); - } else { - console.log("āŒ Invalid input. Please enter 'Y' or 'N'."); - rl.close(); - } - } -);