From a2f1844553e8424474e4cb7c71ec4fea430e1270 Mon Sep 17 00:00:00 2001 From: Jakub Gonet Date: Thu, 3 Oct 2024 17:55:28 +0200 Subject: [PATCH 01/52] Shared app template (#578) Adds shared template to all test apps. New screen was enabled only for apps that had default code setup. The rest only have infra for using template. After merging it's needed to run `npm run copy-shared` script to get the apps to work. I looked into running the script automatically but didn't find an easy way to do that. If template change frequently, we may add a script that runs it for every defined app. We should add a way to include components at the top or at the bottom of the template, so each app may add its own set of tests (geolocalization, locale, etc) ## How does it work In `test-apps/` directory there is `shared/` directory with the common UI code for apps. To use this code in the app, use `copy.sh` script. It should use minimal set of dependencies, ideally nothing except react-native. If app uses expo-router, we additionally copy `shared/navigation/` directory which uses expo-router and `@expo/vector-icons`. If app wants to use this shared code, it should add an entry to `.gitignore` for shared directory under its own `src/` and run copy script. If shared code is updated, every app using it should rerun the copy script.
Screenshot
## Testing Ran all apps except RN76. ## Followups - Some way to include custom components in the main screen. --- packages/docs/docs/development.md | 12 + test-apps/expo-50-dev-client/.gitignore | 3 + test-apps/expo-50-dev-client/App.js | 21 +- .../expo-50-dev-client/package-lock.json | 41 +- test-apps/expo-50-dev-client/package.json | 8 +- test-apps/expo-51-dev-client/.gitignore | 5 +- .../expo-51-dev-client/app/(tabs)/_layout.tsx | 38 +- .../expo-51-dev-client/app/(tabs)/explore.tsx | 104 +- .../expo-51-dev-client/app/(tabs)/index.tsx | 71 +- .../expo-51-dev-client/app/+not-found.tsx | 33 +- test-apps/expo-51-dev-client/app/_layout.tsx | 22 +- .../components/Collapsible.tsx | 41 - .../components/ExternalLink.tsx | 24 - .../components/HelloWave.tsx | 37 - .../components/ParallaxScrollView.tsx | 76 - .../components/ThemedText.tsx | 60 - .../components/ThemedView.tsx | 14 - .../components/__tests__/ThemedText-test.tsx | 10 - .../__snapshots__/ThemedText-test.tsx.snap | 24 - .../components/navigation/TabBarIcon.tsx | 9 - .../expo-51-dev-client/constants/Colors.ts | 26 - .../expo-51-dev-client/hooks/useThemeColor.ts | 22 - .../expo51devclient.xcodeproj/project.pbxproj | 8 + .../expo-51-dev-client/package-lock.json | 6 + test-apps/expo-51-dev-client/package.json | 8 +- test-apps/expo-51/.gitignore | 5 +- test-apps/expo-51/app/(tabs)/_layout.tsx | 38 +- test-apps/expo-51/app/(tabs)/explore.tsx | 104 +- test-apps/expo-51/app/(tabs)/index.tsx | 71 +- test-apps/expo-51/app/+not-found.tsx | 33 +- test-apps/expo-51/components/Collapsible.tsx | 41 - test-apps/expo-51/components/ExternalLink.tsx | 24 - test-apps/expo-51/components/HelloWave.tsx | 37 - .../expo-51/components/ParallaxScrollView.tsx | 76 - test-apps/expo-51/components/ThemedText.tsx | 60 - test-apps/expo-51/components/ThemedView.tsx | 14 - .../components/__tests__/ThemedText-test.tsx | 10 - .../__snapshots__/ThemedText-test.tsx.snap | 24 - .../components/navigation/TabBarIcon.tsx | 9 - test-apps/expo-51/constants/Colors.ts | 26 - test-apps/expo-51/hooks/useThemeColor.ts | 22 - test-apps/expo-51/package-lock.json | 6 + test-apps/expo-51/package.json | 4 +- test-apps/expo-go/.gitignore | 3 + test-apps/expo-go/App.js | 27 +- test-apps/expo-go/package-lock.json | 6 + test-apps/expo-go/package.json | 4 +- test-apps/expo-router/.gitignore | 3 + test-apps/expo-router/app/index.js | 41 +- test-apps/expo-router/package.json | 3 +- test-apps/react-native-73/.gitignore | 3 + test-apps/react-native-73/package-lock.json | 6 + test-apps/react-native-73/package.json | 4 +- test-apps/react-native-73/yarn.lock | 5 + test-apps/react-native-74/.gitignore | 3 + test-apps/react-native-74/App.tsx | 119 +- test-apps/react-native-74/package-lock.json | 6 + test-apps/react-native-74/package.json | 4 +- test-apps/react-native-75/.gitignore | 3 + test-apps/react-native-75/package-lock.json | 10210 +++++++++++ test-apps/react-native-75/package.json | 4 +- test-apps/react-native-75/yarn.lock | 14703 ++++++---------- test-apps/react-native-76/.gitignore | 3 + test-apps/react-native-76/App.tsx | 119 +- test-apps/react-native-76/package-lock.json | 6 + test-apps/react-native-76/package.json | 4 +- test-apps/shared/copy.sh | 23 + test-apps/shared/package-lock.json | 13462 ++++++++++++++ test-apps/shared/package.json | 12 + test-apps/shared/src/Button.tsx | 35 + test-apps/shared/src/Colors.ts | 31 + test-apps/shared/src/MainScreen.tsx | 158 + test-apps/shared/src/Text.tsx | 24 + test-apps/shared/src/assets/radon.png | Bin 0 -> 74493 bytes test-apps/shared/src/navigation/NotFound.tsx | 37 + test-apps/shared/src/navigation/TabLayout.tsx | 57 + 76 files changed, 29392 insertions(+), 11063 deletions(-) delete mode 100644 test-apps/expo-51-dev-client/components/Collapsible.tsx delete mode 100644 test-apps/expo-51-dev-client/components/ExternalLink.tsx delete mode 100644 test-apps/expo-51-dev-client/components/HelloWave.tsx delete mode 100644 test-apps/expo-51-dev-client/components/ParallaxScrollView.tsx delete mode 100644 test-apps/expo-51-dev-client/components/ThemedText.tsx delete mode 100644 test-apps/expo-51-dev-client/components/ThemedView.tsx delete mode 100644 test-apps/expo-51-dev-client/components/__tests__/ThemedText-test.tsx delete mode 100644 test-apps/expo-51-dev-client/components/__tests__/__snapshots__/ThemedText-test.tsx.snap delete mode 100644 test-apps/expo-51-dev-client/components/navigation/TabBarIcon.tsx delete mode 100644 test-apps/expo-51-dev-client/constants/Colors.ts delete mode 100644 test-apps/expo-51-dev-client/hooks/useThemeColor.ts delete mode 100644 test-apps/expo-51/components/Collapsible.tsx delete mode 100644 test-apps/expo-51/components/ExternalLink.tsx delete mode 100644 test-apps/expo-51/components/HelloWave.tsx delete mode 100644 test-apps/expo-51/components/ParallaxScrollView.tsx delete mode 100644 test-apps/expo-51/components/ThemedText.tsx delete mode 100644 test-apps/expo-51/components/ThemedView.tsx delete mode 100644 test-apps/expo-51/components/__tests__/ThemedText-test.tsx delete mode 100644 test-apps/expo-51/components/__tests__/__snapshots__/ThemedText-test.tsx.snap delete mode 100644 test-apps/expo-51/components/navigation/TabBarIcon.tsx delete mode 100644 test-apps/expo-51/constants/Colors.ts delete mode 100644 test-apps/expo-51/hooks/useThemeColor.ts create mode 100644 test-apps/react-native-75/package-lock.json create mode 100755 test-apps/shared/copy.sh create mode 100644 test-apps/shared/package-lock.json create mode 100644 test-apps/shared/package.json create mode 100644 test-apps/shared/src/Button.tsx create mode 100644 test-apps/shared/src/Colors.ts create mode 100644 test-apps/shared/src/MainScreen.tsx create mode 100644 test-apps/shared/src/Text.tsx create mode 100644 test-apps/shared/src/assets/radon.png create mode 100644 test-apps/shared/src/navigation/NotFound.tsx create mode 100644 test-apps/shared/src/navigation/TabLayout.tsx diff --git a/packages/docs/docs/development.md b/packages/docs/docs/development.md index de204ef5b..5bb4e432f 100644 --- a/packages/docs/docs/development.md +++ b/packages/docs/docs/development.md @@ -87,3 +87,15 @@ src="/img/docs/restart_extension.png"/> For main extension code, you can set breakpoints in vscode and use debugger normally, logs will appear in the Debug Console panel. Unfortunately debugging isn't available for the frontend code, however you can use vscode's builtin chrome devtools to see logs or interact with the frontend portion of the project – for this you'll need to run command "Developer: Open Webview Developer Tools" from the command palette in the Extension Host window. + +## Shared app template + +In `test-apps/` directory there is `shared/` directory with the common UI code for +apps. To use this code in the app, use `copy.sh` script. It should use minimal +set of dependencies, ideally nothing except react-native. +If app uses expo-router, we additionally copy `shared/navigation/` directory +which uses expo-router and `@expo/vector-icons`. + +If app wants to use this shared code, it should add an entry to .gitignore for +shared directory under its own `src/` and run copy script. If shared code is +updated, every app using it should rerun the copy script. diff --git a/test-apps/expo-50-dev-client/.gitignore b/test-apps/expo-50-dev-client/.gitignore index 05647d55c..376e6c5a2 100644 --- a/test-apps/expo-50-dev-client/.gitignore +++ b/test-apps/expo-50-dev-client/.gitignore @@ -33,3 +33,6 @@ yarn-error.* # typescript *.tsbuildinfo + +# shared is copied via copy-shared +shared/ diff --git a/test-apps/expo-50-dev-client/App.js b/test-apps/expo-50-dev-client/App.js index 09f879b82..d693d63ad 100644 --- a/test-apps/expo-50-dev-client/App.js +++ b/test-apps/expo-50-dev-client/App.js @@ -1,20 +1,3 @@ -import { StatusBar } from 'expo-status-bar'; -import { StyleSheet, Text, View } from 'react-native'; +import { MainScreen } from "./shared/MainScreen"; -export default function App() { - return ( - - Open up App.js to start working on your app! - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - backgroundColor: '#fff', - alignItems: 'center', - justifyContent: 'center', - }, -}); +export default MainScreen; diff --git a/test-apps/expo-50-dev-client/package-lock.json b/test-apps/expo-50-dev-client/package-lock.json index 1788b02f5..ed1fe74e3 100644 --- a/test-apps/expo-50-dev-client/package-lock.json +++ b/test-apps/expo-50-dev-client/package-lock.json @@ -8,11 +8,14 @@ "name": "devclienttester", "version": "1.0.0", "dependencies": { + "@types/react": "~18.2.45", "expo": "~50.0.14", "expo-dev-client": "~3.3.11", "expo-status-bar": "~1.11.1", + "radon-ide": "^0.0.1", "react": "18.2.0", - "react-native": "0.73.6" + "react-native": "0.73.6", + "typescript": "^5.3.0" }, "devDependencies": { "@babel/core": "^7.20.0" @@ -6062,6 +6065,20 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/prop-types": { + "version": "15.7.13", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" + }, + "node_modules/@types/react": { + "version": "18.2.79", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.79.tgz", + "integrity": "sha512-RwGAGXPl9kSXwdNTafkOEuFrTBD5SA2B3iEB96xi8+xu5ddUa/cpvyVCSNn+asgLCTHkb5ZxN8gbuibYJi4s1w==", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.0.2" + } + }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", @@ -7286,6 +7303,11 @@ "node": ">=8" } }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" + }, "node_modules/dag-map": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/dag-map/-/dag-map-1.0.2.tgz", @@ -11455,6 +11477,11 @@ } ] }, + "node_modules/radon-ide": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/radon-ide/-/radon-ide-0.0.1.tgz", + "integrity": "sha512-I1+y/jw58SAq6fN6pijpc43QBTzmeNuDmK/zWTXPS6RlXFW1ICAdPo+UbW2BcNn11jtfDqaLiLL9Tfxge0w6Gg==" + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", @@ -12781,6 +12808,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/typescript": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/ua-parser-js": { "version": "1.0.37", "resolved": "https://registry.npmjs.org/ua-parser-js/-/ua-parser-js-1.0.37.tgz", diff --git a/test-apps/expo-50-dev-client/package.json b/test-apps/expo-50-dev-client/package.json index ca5c799a7..c1411e318 100644 --- a/test-apps/expo-50-dev-client/package.json +++ b/test-apps/expo-50-dev-client/package.json @@ -6,14 +6,18 @@ "start": "expo start", "android": "expo run:android", "ios": "expo run:ios", - "web": "expo start --web" + "web": "expo start --web", + "copy-shared": "../shared/copy.sh bare ./shared" }, "dependencies": { + "@types/react": "~18.2.45", "expo": "~50.0.14", + "expo-dev-client": "~3.3.11", "expo-status-bar": "~1.11.1", + "radon-ide": "^0.0.1", "react": "18.2.0", "react-native": "0.73.6", - "expo-dev-client": "~3.3.11" + "typescript": "^5.3.0" }, "devDependencies": { "@babel/core": "^7.20.0" diff --git a/test-apps/expo-51-dev-client/.gitignore b/test-apps/expo-51-dev-client/.gitignore index 6623142a2..46244b979 100644 --- a/test-apps/expo-51-dev-client/.gitignore +++ b/test-apps/expo-51-dev-client/.gitignore @@ -17,4 +17,7 @@ web-build/ # The following patterns were generated by expo-cli expo-env.d.ts -# @end expo-cli \ No newline at end of file +# @end expo-cli + +# shared is copied via copy-shared +shared/ diff --git a/test-apps/expo-51-dev-client/app/(tabs)/_layout.tsx b/test-apps/expo-51-dev-client/app/(tabs)/_layout.tsx index 22a49b62c..deef626f6 100644 --- a/test-apps/expo-51-dev-client/app/(tabs)/_layout.tsx +++ b/test-apps/expo-51-dev-client/app/(tabs)/_layout.tsx @@ -1,37 +1,3 @@ -import { Tabs } from 'expo-router'; -import React from 'react'; +import { TabLayout } from "@/shared/navigation/TabLayout"; -import { TabBarIcon } from '@/components/navigation/TabBarIcon'; -import { Colors } from '@/constants/Colors'; -import { useColorScheme } from '@/hooks/useColorScheme'; - -export default function TabLayout() { - const colorScheme = useColorScheme(); - - return ( - - ( - - ), - }} - /> - ( - - ), - }} - /> - - ); -} +export default TabLayout; diff --git a/test-apps/expo-51-dev-client/app/(tabs)/explore.tsx b/test-apps/expo-51-dev-client/app/(tabs)/explore.tsx index e480218ad..74a5d858b 100644 --- a/test-apps/expo-51-dev-client/app/(tabs)/explore.tsx +++ b/test-apps/expo-51-dev-client/app/(tabs)/explore.tsx @@ -1,102 +1,8 @@ -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'; +import { useScheme } from "@/shared/Colors"; +import { View } from "react-native"; 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 { colors } = useScheme(); -const styles = StyleSheet.create({ - headerImage: { - color: '#808080', - bottom: -90, - left: -35, - position: 'absolute', - }, - titleContainer: { - flexDirection: 'row', - gap: 8, - }, -}); + return ; +} diff --git a/test-apps/expo-51-dev-client/app/(tabs)/index.tsx b/test-apps/expo-51-dev-client/app/(tabs)/index.tsx index 324aeb761..8d2882718 100644 --- a/test-apps/expo-51-dev-client/app/(tabs)/index.tsx +++ b/test-apps/expo-51-dev-client/app/(tabs)/index.tsx @@ -1,70 +1,3 @@ -import { Image, StyleSheet, Platform } from 'react-native'; +import { MainScreen } from "@/shared/MainScreen"; -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', - }, -}); +export default MainScreen; diff --git a/test-apps/expo-51-dev-client/app/+not-found.tsx b/test-apps/expo-51-dev-client/app/+not-found.tsx index 963b04fbc..84722cdfe 100644 --- a/test-apps/expo-51-dev-client/app/+not-found.tsx +++ b/test-apps/expo-51-dev-client/app/+not-found.tsx @@ -1,32 +1,3 @@ -import { Link, Stack } from 'expo-router'; -import { StyleSheet } from 'react-native'; +import { NotFoundScreen } from "@/shared/navigation/NotFound"; -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, - }, -}); +export default NotFoundScreen; diff --git a/test-apps/expo-51-dev-client/app/_layout.tsx b/test-apps/expo-51-dev-client/app/_layout.tsx index 2e37cdd8d..807892e9c 100644 --- a/test-apps/expo-51-dev-client/app/_layout.tsx +++ b/test-apps/expo-51-dev-client/app/_layout.tsx @@ -1,11 +1,15 @@ -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 { + 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'; +import { useColorScheme } from "@/hooks/useColorScheme"; // Prevent the splash screen from auto-hiding before asset loading is complete. SplashScreen.preventAutoHideAsync(); @@ -13,7 +17,7 @@ SplashScreen.preventAutoHideAsync(); export default function RootLayout() { const colorScheme = useColorScheme(); const [loaded] = useFonts({ - SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), + SpaceMono: require("../assets/fonts/SpaceMono-Regular.ttf"), }); useEffect(() => { @@ -27,7 +31,7 @@ export default function RootLayout() { } return ( - + diff --git a/test-apps/expo-51-dev-client/components/Collapsible.tsx b/test-apps/expo-51-dev-client/components/Collapsible.tsx deleted file mode 100644 index c326473d5..000000000 --- a/test-apps/expo-51-dev-client/components/Collapsible.tsx +++ /dev/null @@ -1,41 +0,0 @@ -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/test-apps/expo-51-dev-client/components/ExternalLink.tsx b/test-apps/expo-51-dev-client/components/ExternalLink.tsx deleted file mode 100644 index 8f05675b2..000000000 --- a/test-apps/expo-51-dev-client/components/ExternalLink.tsx +++ /dev/null @@ -1,24 +0,0 @@ -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/test-apps/expo-51-dev-client/components/HelloWave.tsx b/test-apps/expo-51-dev-client/components/HelloWave.tsx deleted file mode 100644 index f4b6ea544..000000000 --- a/test-apps/expo-51-dev-client/components/HelloWave.tsx +++ /dev/null @@ -1,37 +0,0 @@ -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/test-apps/expo-51-dev-client/components/ParallaxScrollView.tsx b/test-apps/expo-51-dev-client/components/ParallaxScrollView.tsx deleted file mode 100644 index 0a3541990..000000000 --- a/test-apps/expo-51-dev-client/components/ParallaxScrollView.tsx +++ /dev/null @@ -1,76 +0,0 @@ -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/test-apps/expo-51-dev-client/components/ThemedText.tsx b/test-apps/expo-51-dev-client/components/ThemedText.tsx deleted file mode 100644 index c0e1a78f5..000000000 --- a/test-apps/expo-51-dev-client/components/ThemedText.tsx +++ /dev/null @@ -1,60 +0,0 @@ -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/test-apps/expo-51-dev-client/components/ThemedView.tsx b/test-apps/expo-51-dev-client/components/ThemedView.tsx deleted file mode 100644 index 4d2cb09d4..000000000 --- a/test-apps/expo-51-dev-client/components/ThemedView.tsx +++ /dev/null @@ -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 ; -} diff --git a/test-apps/expo-51-dev-client/components/__tests__/ThemedText-test.tsx b/test-apps/expo-51-dev-client/components/__tests__/ThemedText-test.tsx deleted file mode 100644 index 1ac322506..000000000 --- a/test-apps/expo-51-dev-client/components/__tests__/ThemedText-test.tsx +++ /dev/null @@ -1,10 +0,0 @@ -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/test-apps/expo-51-dev-client/components/__tests__/__snapshots__/ThemedText-test.tsx.snap b/test-apps/expo-51-dev-client/components/__tests__/__snapshots__/ThemedText-test.tsx.snap deleted file mode 100644 index b68e53e9c..000000000 --- a/test-apps/expo-51-dev-client/components/__tests__/__snapshots__/ThemedText-test.tsx.snap +++ /dev/null @@ -1,24 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders correctly 1`] = ` - - Snapshot test! - -`; diff --git a/test-apps/expo-51-dev-client/components/navigation/TabBarIcon.tsx b/test-apps/expo-51-dev-client/components/navigation/TabBarIcon.tsx deleted file mode 100644 index b7302c3f0..000000000 --- a/test-apps/expo-51-dev-client/components/navigation/TabBarIcon.tsx +++ /dev/null @@ -1,9 +0,0 @@ -// 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/test-apps/expo-51-dev-client/constants/Colors.ts b/test-apps/expo-51-dev-client/constants/Colors.ts deleted file mode 100644 index 14e67844d..000000000 --- a/test-apps/expo-51-dev-client/constants/Colors.ts +++ /dev/null @@ -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/test-apps/expo-51-dev-client/hooks/useThemeColor.ts b/test-apps/expo-51-dev-client/hooks/useThemeColor.ts deleted file mode 100644 index ae43b47b6..000000000 --- a/test-apps/expo-51-dev-client/hooks/useThemeColor.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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/test-apps/expo-51-dev-client/ios/expo51devclient.xcodeproj/project.pbxproj b/test-apps/expo-51-dev-client/ios/expo51devclient.xcodeproj/project.pbxproj index 1c708df11..d8bdc8708 100644 --- a/test-apps/expo-51-dev-client/ios/expo51devclient.xcodeproj/project.pbxproj +++ b/test-apps/expo-51-dev-client/ios/expo51devclient.xcodeproj/project.pbxproj @@ -391,6 +391,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CC = ""; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "c++20"; CLANG_CXX_LIBRARY = "libc++"; @@ -417,6 +418,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = NO; + CXX = ""; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -435,6 +437,8 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.4; + LD = ""; + LDPLUSPLUS = ""; LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; MTL_ENABLE_DEBUG_INFO = YES; @@ -453,6 +457,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + CC = ""; CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; CLANG_CXX_LANGUAGE_STANDARD = "c++20"; CLANG_CXX_LIBRARY = "libc++"; @@ -479,6 +484,7 @@ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; COPY_PHASE_STRIP = YES; + CXX = ""; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu99; @@ -490,6 +496,8 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; IPHONEOS_DEPLOYMENT_TARGET = 13.4; + LD = ""; + LDPLUSPLUS = ""; LD_RUNPATH_SEARCH_PATHS = "/usr/lib/swift $(inherited)"; LIBRARY_SEARCH_PATHS = "$(SDKROOT)/usr/lib/swift\"$(inherited)\""; MTL_ENABLE_DEBUG_INFO = NO; diff --git a/test-apps/expo-51-dev-client/package-lock.json b/test-apps/expo-51-dev-client/package-lock.json index b4ef43499..01c2000c1 100644 --- a/test-apps/expo-51-dev-client/package-lock.json +++ b/test-apps/expo-51-dev-client/package-lock.json @@ -20,6 +20,7 @@ "expo-status-bar": "~1.12.1", "expo-system-ui": "~3.0.4", "expo-web-browser": "~13.0.3", + "radon-ide": "^0.0.1", "react": "18.2.0", "react-dom": "18.2.0", "react-native": "0.74.1", @@ -15192,6 +15193,11 @@ } ] }, + "node_modules/radon-ide": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/radon-ide/-/radon-ide-0.0.1.tgz", + "integrity": "sha512-I1+y/jw58SAq6fN6pijpc43QBTzmeNuDmK/zWTXPS6RlXFW1ICAdPo+UbW2BcNn11jtfDqaLiLL9Tfxge0w6Gg==" + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", diff --git a/test-apps/expo-51-dev-client/package.json b/test-apps/expo-51-dev-client/package.json index cd1282366..d2b0e4ffd 100644 --- a/test-apps/expo-51-dev-client/package.json +++ b/test-apps/expo-51-dev-client/package.json @@ -9,7 +9,8 @@ "ios": "expo run:ios", "web": "expo start --web", "test": "jest --watchAll", - "lint": "expo lint" + "lint": "expo lint", + "copy-shared": "../shared/copy.sh expo-router ./shared" }, "jest": { "preset": "jest-expo" @@ -19,6 +20,7 @@ "@react-navigation/native": "^6.0.2", "expo": "~51.0.2", "expo-constants": "~16.0.1", + "expo-dev-client": "~4.0.13", "expo-font": "~12.0.4", "expo-linking": "~6.3.1", "expo-router": "~3.5.11", @@ -26,6 +28,7 @@ "expo-status-bar": "~1.12.1", "expo-system-ui": "~3.0.4", "expo-web-browser": "~13.0.3", + "radon-ide": "^0.0.1", "react": "18.2.0", "react-dom": "18.2.0", "react-native": "0.74.1", @@ -33,8 +36,7 @@ "react-native-reanimated": "~3.10.1", "react-native-safe-area-context": "4.10.1", "react-native-screens": "3.31.1", - "react-native-web": "~0.19.10", - "expo-dev-client": "~4.0.13" + "react-native-web": "~0.19.10" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/test-apps/expo-51/.gitignore b/test-apps/expo-51/.gitignore index 6623142a2..46244b979 100644 --- a/test-apps/expo-51/.gitignore +++ b/test-apps/expo-51/.gitignore @@ -17,4 +17,7 @@ web-build/ # The following patterns were generated by expo-cli expo-env.d.ts -# @end expo-cli \ No newline at end of file +# @end expo-cli + +# shared is copied via copy-shared +shared/ diff --git a/test-apps/expo-51/app/(tabs)/_layout.tsx b/test-apps/expo-51/app/(tabs)/_layout.tsx index 22a49b62c..deef626f6 100644 --- a/test-apps/expo-51/app/(tabs)/_layout.tsx +++ b/test-apps/expo-51/app/(tabs)/_layout.tsx @@ -1,37 +1,3 @@ -import { Tabs } from 'expo-router'; -import React from 'react'; +import { TabLayout } from "@/shared/navigation/TabLayout"; -import { TabBarIcon } from '@/components/navigation/TabBarIcon'; -import { Colors } from '@/constants/Colors'; -import { useColorScheme } from '@/hooks/useColorScheme'; - -export default function TabLayout() { - const colorScheme = useColorScheme(); - - return ( - - ( - - ), - }} - /> - ( - - ), - }} - /> - - ); -} +export default TabLayout; diff --git a/test-apps/expo-51/app/(tabs)/explore.tsx b/test-apps/expo-51/app/(tabs)/explore.tsx index e480218ad..74a5d858b 100644 --- a/test-apps/expo-51/app/(tabs)/explore.tsx +++ b/test-apps/expo-51/app/(tabs)/explore.tsx @@ -1,102 +1,8 @@ -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'; +import { useScheme } from "@/shared/Colors"; +import { View } from "react-native"; 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 { colors } = useScheme(); -const styles = StyleSheet.create({ - headerImage: { - color: '#808080', - bottom: -90, - left: -35, - position: 'absolute', - }, - titleContainer: { - flexDirection: 'row', - gap: 8, - }, -}); + return ; +} diff --git a/test-apps/expo-51/app/(tabs)/index.tsx b/test-apps/expo-51/app/(tabs)/index.tsx index 324aeb761..8d2882718 100644 --- a/test-apps/expo-51/app/(tabs)/index.tsx +++ b/test-apps/expo-51/app/(tabs)/index.tsx @@ -1,70 +1,3 @@ -import { Image, StyleSheet, Platform } from 'react-native'; +import { MainScreen } from "@/shared/MainScreen"; -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', - }, -}); +export default MainScreen; diff --git a/test-apps/expo-51/app/+not-found.tsx b/test-apps/expo-51/app/+not-found.tsx index 963b04fbc..84722cdfe 100644 --- a/test-apps/expo-51/app/+not-found.tsx +++ b/test-apps/expo-51/app/+not-found.tsx @@ -1,32 +1,3 @@ -import { Link, Stack } from 'expo-router'; -import { StyleSheet } from 'react-native'; +import { NotFoundScreen } from "@/shared/navigation/NotFound"; -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, - }, -}); +export default NotFoundScreen; diff --git a/test-apps/expo-51/components/Collapsible.tsx b/test-apps/expo-51/components/Collapsible.tsx deleted file mode 100644 index c326473d5..000000000 --- a/test-apps/expo-51/components/Collapsible.tsx +++ /dev/null @@ -1,41 +0,0 @@ -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/test-apps/expo-51/components/ExternalLink.tsx b/test-apps/expo-51/components/ExternalLink.tsx deleted file mode 100644 index 8f05675b2..000000000 --- a/test-apps/expo-51/components/ExternalLink.tsx +++ /dev/null @@ -1,24 +0,0 @@ -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/test-apps/expo-51/components/HelloWave.tsx b/test-apps/expo-51/components/HelloWave.tsx deleted file mode 100644 index f4b6ea544..000000000 --- a/test-apps/expo-51/components/HelloWave.tsx +++ /dev/null @@ -1,37 +0,0 @@ -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/test-apps/expo-51/components/ParallaxScrollView.tsx b/test-apps/expo-51/components/ParallaxScrollView.tsx deleted file mode 100644 index 0a3541990..000000000 --- a/test-apps/expo-51/components/ParallaxScrollView.tsx +++ /dev/null @@ -1,76 +0,0 @@ -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/test-apps/expo-51/components/ThemedText.tsx b/test-apps/expo-51/components/ThemedText.tsx deleted file mode 100644 index c0e1a78f5..000000000 --- a/test-apps/expo-51/components/ThemedText.tsx +++ /dev/null @@ -1,60 +0,0 @@ -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/test-apps/expo-51/components/ThemedView.tsx b/test-apps/expo-51/components/ThemedView.tsx deleted file mode 100644 index 4d2cb09d4..000000000 --- a/test-apps/expo-51/components/ThemedView.tsx +++ /dev/null @@ -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 ; -} diff --git a/test-apps/expo-51/components/__tests__/ThemedText-test.tsx b/test-apps/expo-51/components/__tests__/ThemedText-test.tsx deleted file mode 100644 index 1ac322506..000000000 --- a/test-apps/expo-51/components/__tests__/ThemedText-test.tsx +++ /dev/null @@ -1,10 +0,0 @@ -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/test-apps/expo-51/components/__tests__/__snapshots__/ThemedText-test.tsx.snap b/test-apps/expo-51/components/__tests__/__snapshots__/ThemedText-test.tsx.snap deleted file mode 100644 index b68e53e9c..000000000 --- a/test-apps/expo-51/components/__tests__/__snapshots__/ThemedText-test.tsx.snap +++ /dev/null @@ -1,24 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`renders correctly 1`] = ` - - Snapshot test! - -`; diff --git a/test-apps/expo-51/components/navigation/TabBarIcon.tsx b/test-apps/expo-51/components/navigation/TabBarIcon.tsx deleted file mode 100644 index b7302c3f0..000000000 --- a/test-apps/expo-51/components/navigation/TabBarIcon.tsx +++ /dev/null @@ -1,9 +0,0 @@ -// 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/test-apps/expo-51/constants/Colors.ts b/test-apps/expo-51/constants/Colors.ts deleted file mode 100644 index 14e67844d..000000000 --- a/test-apps/expo-51/constants/Colors.ts +++ /dev/null @@ -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/test-apps/expo-51/hooks/useThemeColor.ts b/test-apps/expo-51/hooks/useThemeColor.ts deleted file mode 100644 index ae43b47b6..000000000 --- a/test-apps/expo-51/hooks/useThemeColor.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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/test-apps/expo-51/package-lock.json b/test-apps/expo-51/package-lock.json index dce7205be..78623b8bd 100644 --- a/test-apps/expo-51/package-lock.json +++ b/test-apps/expo-51/package-lock.json @@ -19,6 +19,7 @@ "expo-status-bar": "~1.12.1", "expo-system-ui": "^3.0.4", "expo-web-browser": "~13.0.3", + "radon-ide": "^0.0.1", "react": "18.2.0", "react-dom": "18.2.0", "react-native": "^0.74.1", @@ -15286,6 +15287,11 @@ } ] }, + "node_modules/radon-ide": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/radon-ide/-/radon-ide-0.0.1.tgz", + "integrity": "sha512-I1+y/jw58SAq6fN6pijpc43QBTzmeNuDmK/zWTXPS6RlXFW1ICAdPo+UbW2BcNn11jtfDqaLiLL9Tfxge0w6Gg==" + }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", diff --git a/test-apps/expo-51/package.json b/test-apps/expo-51/package.json index f310435df..6c40e516a 100644 --- a/test-apps/expo-51/package.json +++ b/test-apps/expo-51/package.json @@ -9,7 +9,8 @@ "ios": "expo start --ios", "web": "expo start --web", "test": "jest --watchAll", - "lint": "expo lint" + "lint": "expo lint", + "copy-shared": "../shared/copy.sh expo-router ./shared" }, "jest": { "preset": "jest-expo" @@ -26,6 +27,7 @@ "expo-status-bar": "~1.12.1", "expo-system-ui": "^3.0.4", "expo-web-browser": "~13.0.3", + "radon-ide": "^0.0.1", "react": "18.2.0", "react-dom": "18.2.0", "react-native": "^0.74.1", diff --git a/test-apps/expo-go/.gitignore b/test-apps/expo-go/.gitignore index 05647d55c..376e6c5a2 100644 --- a/test-apps/expo-go/.gitignore +++ b/test-apps/expo-go/.gitignore @@ -33,3 +33,6 @@ yarn-error.* # typescript *.tsbuildinfo + +# shared is copied via copy-shared +shared/ diff --git a/test-apps/expo-go/App.js b/test-apps/expo-go/App.js index 843f208d8..d693d63ad 100644 --- a/test-apps/expo-go/App.js +++ b/test-apps/expo-go/App.js @@ -1,26 +1,3 @@ -import { StatusBar } from 'expo-status-bar'; -import { StyleSheet, Text, View, Button } from 'react-native'; +import { MainScreen } from "./shared/MainScreen"; -function handleClick() { - alert('Button Clicked'); - console.log('Button clicked'); -} - -export default function App() { - return ( - - Open up App.js to start working on your app hello! - - + + {!isRewinding && ( +
+ + + + +
+ + + +
+ +
+ )} + + ); +} diff --git a/packages/vscode-extension/src/webview/components/ReplayUI.css b/packages/vscode-extension/src/webview/components/ReplayUI.css new file mode 100644 index 000000000..fd46a8c85 --- /dev/null +++ b/packages/vscode-extension/src/webview/components/ReplayUI.css @@ -0,0 +1,230 @@ +.vhs-noise { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + overflow: hidden; + z-index: 400; + opacity: 0.8; + pointer-events: none; + opacity: 1; + z-index: 450; +} + +.vhs-noise:before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + /* small bitmap displaying a white noise */ + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAMAAAAp4XiDAAAAUVBMVEWFhYWDg4N3d3dtbW17e3t1dXWBgYGHh4d5eXlzc3OLi4ubm5uVlZWPj4+NjY19fX2JiYl/f39ra2uRkZGZmZlpaWmXl5dvb29xcXGTk5NnZ2c8TV1mAAAAG3RSTlNAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEAvEOwtAAAFVklEQVR4XpWWB67c2BUFb3g557T/hRo9/WUMZHlgr4Bg8Z4qQgQJlHI4A8SzFVrapvmTF9O7dmYRFZ60YiBhJRCgh1FYhiLAmdvX0CzTOpNE77ME0Zty/nWWzchDtiqrmQDeuv3powQ5ta2eN0FY0InkqDD73lT9c9lEzwUNqgFHs9VQce3TVClFCQrSTfOiYkVJQBmpbq2L6iZavPnAPcoU0dSw0SUTqz/GtrGuXfbyyBniKykOWQWGqwwMA7QiYAxi+IlPdqo+hYHnUt5ZPfnsHJyNiDtnpJyayNBkF6cWoYGAMY92U2hXHF/C1M8uP/ZtYdiuj26UdAdQQSXQErwSOMzt/XWRWAz5GuSBIkwG1H3FabJ2OsUOUhGC6tK4EMtJO0ttC6IBD3kM0ve0tJwMdSfjZo+EEISaeTr9P3wYrGjXqyC1krcKdhMpxEnt5JetoulscpyzhXN5FRpuPHvbeQaKxFAEB6EN+cYN6xD7RYGpXpNndMmZgM5Dcs3YSNFDHUo2LGfZuukSWyUYirJAdYbF3MfqEKmjM+I2EfhA94iG3L7uKrR+GdWD73ydlIB+6hgref1QTlmgmbM3/LeX5GI1Ux1RWpgxpLuZ2+I+IjzZ8wqE4nilvQdkUdfhzI5QDWy+kw5Wgg2pGpeEVeCCA7b85BO3F9DzxB3cdqvBzWcmzbyMiqhzuYqtHRVG2y4x+KOlnyqla8AoWWpuBoYRxzXrfKuILl6SfiWCbjxoZJUaCBj1CjH7GIaDbc9kqBY3W/Rgjda1iqQcOJu2WW+76pZC9QG7M00dffe9hNnseupFL53r8F7YHSwJWUKP2q+k7RdsxyOB11n0xtOvnW4irMMFNV4H0uqwS5ExsmP9AxbDTc9JwgneAT5vTiUSm1E7BSflSt3bfa1tv8Di3R8n3Af7MNWzs49hmauE2wP+ttrq+AsWpFG2awvsuOqbipWHgtuvuaAE+A1Z/7gC9hesnr+7wqCwG8c5yAg3AL1fm8T9AZtp/bbJGwl1pNrE7RuOX7PeMRUERVaPpEs+yqeoSmuOlokqw49pgomjLeh7icHNlG19yjs6XXOMedYm5xH2YxpV2tc0Ro2jJfxC50ApuxGob7lMsxfTbeUv07TyYxpeLucEH1gNd4IKH2LAg5TdVhlCafZvpskfncCfx8pOhJzd76bJWeYFnFciwcYfubRc12Ip/ppIhA1/mSZ/RxjFDrJC5xifFjJpY2Xl5zXdguFqYyTR1zSp1Y9p+tktDYYSNflcxI0iyO4TPBdlRcpeqjK/piF5bklq77VSEaA+z8qmJTFzIWiitbnzR794USKBUaT0NTEsVjZqLaFVqJoPN9ODG70IPbfBHKK+/q/AWR0tJzYHRULOa4MP+W/HfGadZUbfw177G7j/OGbIs8TahLyynl4X4RinF793Oz+BU0saXtUHrVBFT/DnA3ctNPoGbs4hRIjTok8i+algT1lTHi4SxFvONKNrgQFAq2/gFnWMXgwffgYMJpiKYkmW3tTg3ZQ9Jq+f8XN+A5eeUKHWvJWJ2sgJ1Sop+wwhqFVijqWaJhwtD8MNlSBeWNNWTa5Z5kPZw5+LbVT99wqTdx29lMUH4OIG/D86ruKEauBjvH5xy6um/Sfj7ei6UUVk4AIl3MyD4MSSTOFgSwsH/QJWaQ5as7ZcmgBZkzjjU1UrQ74ci1gWBCSGHtuV1H2mhSnO3Wp/3fEV5a+4wz//6qy8JxjZsmxxy5+4w9CDNJY09T072iKG0EnOS0arEYgXqYnXcYHwjTtUNAcMelOd4xpkoqiTYICWFq0JSiPfPDQdnt+4/wuqcXY47QILbgAAAABJRU5ErkJggg==); + pointer-events: none; + will-change: background-position; + animation: noise 1s infinite alternate; +} + +.crt-lines { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + background-image: linear-gradient(30deg, + rgba(17, 20, 53, 0.3), + rgba(118, 255, 241, 0.3)); + background-repeat: repeat; + background-size: 100% 10%; + animation: bgscroll 2s linear infinite; +} + +.vhs-lines { + position: absolute; + left: 0; + top: 0; + width: 100%; + height: 100%; + pointer-events: none; + z-index: 300; + opacity: 0.5; + will-change: opacity; + animation: opacity 3s linear infinite; +} + +.vhs-lines:before { + content: ''; + position: absolute; + left: 0; + top: 0; + right: 0; + bottom: 0; + pointer-events: none; + background: linear-gradient(to bottom, transparent 50%, rgba(0, 0, 0, .5) 51%); + background-size: 100% 4px; + will-change: background, background-size; + animation: scanlines 0.2s linear infinite; +} + +.vhs-bg { + position: absolute; + top: 0; + left: 0; + color: #fff; + font-size: 2rem; + width: 100%; + height: 100%; + background: grey; + opacity: 0.5; +} + +.vhs-bg .vhs-noise:before { + background-size: 150%; +} + +.vhs-text { + position: absolute; + overflow: hidden; + right: 10%; + top: 15%; + will-change: text-shadow; + filter: blur(1px); + font-family: 'Courier new', fixed; + animation: rgbText 1s steps(9) 0s infinite alternate; +} + +@keyframes noise { + + 0%, + 100% { + background-position: 0 0; + } + + 10% { + background-position: -5% -10%; + } + + 20% { + background-position: -15% 5%; + } + + 30% { + background-position: 7% -25%; + } + + 40% { + background-position: 20% 25%; + } + + 50% { + background-position: -25% 10%; + } + + 60% { + background-position: 15% 5%; + } + + 70% { + background-position: 0 15%; + } + + 80% { + background-position: 25% 35%; + } + + 90% { + background-position: -10% 10%; + } +} + +@keyframes opacity { + 0% { + opacity: 0.6; + } + + 20% { + opacity: 0.3; + } + + 35% { + opacity: 0.5; + } + + 50% { + opacity: 0.8; + } + + 60% { + opacity: 0.4; + } + + 80% { + opacity: 0.7; + } + + 100% { + opacity: 0.6; + } +} + +@keyframes bgscroll { + 100% { + background-position: 0% 100%; + } +} + +@keyframes scanlines { + from { + background: linear-gradient(-30deg, to bottom, transparent 80%, rgba(0, 0, 0, .5) 51%); + background-size: 100% 10%; + } + + to { + background: linear-gradient(-30deg, to bottom, rgba(0, 0, 0, .5) 50%, transparent 51%); + background-size: 100% 5%; + } +} + +@keyframes rgbText { + 0% { + text-shadow: -1px 1px 8px rgba(255, 255, 255, 0.6), 1px -1px 8px rgba(255, 255, 235, 0.7), 0px 0 1px rgba(251, 0, 231, 0.8), 0 0px 3px rgba(0, 233, 235, 0.8), 0px 0 3px rgba(0, 242, 14, 0.8), 0 0px 3px rgba(244, 45, 0, 0.8), 0px 0 3px rgba(59, 0, 226, 0.8); + } + + 25% { + text-shadow: -1px 1px 8px rgba(255, 255, 255, 0.6), 1px -1px 8px rgba(255, 255, 235, 0.7), 0px 0 1px rgba(251, 0, 231, 0.8), 0 0px 3px rgba(0, 233, 235, 0.8), 0px 0 3px rgba(0, 242, 14, 0.8), 0 0px 3px rgba(244, 45, 0, 0.8), 0px 0 3px rgba(59, 0, 226, 0.8); + } + + 45% { + text-shadow: -1px 1px 8px rgba(255, 255, 255, 0.6), 1px -1px 8px rgba(255, 255, 235, 0.7), 5px 0 1px rgba(251, 0, 231, 0.8), 0 5px 1px rgba(0, 233, 235, 0.8), -5px 0 1px rgba(0, 242, 14, 0.8), 0 -5px 1px rgba(244, 45, 0, 0.8), 5px 0 1px rgba(59, 0, 226, 0.8); + } + + 50% { + text-shadow: -1px 1px 8px rgba(255, 255, 255, 0.6), 1px -1px 8px rgba(255, 255, 235, 0.7), -5px 0 1px rgba(251, 0, 231, 0.8), 0 -5px 1px rgba(0, 233, 235, 0.8), 5px 0 1px rgba(0, 242, 14, 0.8), 0 5px 1px rgba(244, 45, 0, 0.8), -5px 0 1px rgba(59, 0, 226, 0.8); + } + + 55% { + text-shadow: -1px 1px 8px rgba(255, 255, 255, 0.6), 1px -1px 8px rgba(255, 255, 235, 0.7), 0px 0 3px rgba(251, 0, 231, 0.8), 0 0px 3px rgba(0, 233, 235, 0.8), 0px 0 3px rgba(0, 242, 14, 0.8), 0 0px 3px rgba(244, 45, 0, 0.8), 0px 0 3px rgba(59, 0, 226, 0.8); + } + + 90% { + text-shadow: -1px 1px 8px rgba(255, 255, 255, 0.6), 1px -1px 8px rgba(255, 255, 235, 0.7), -5px 0 1px rgba(251, 0, 231, 0.8), 0 5px 1px rgba(0, 233, 235, 0.8), 5px 0 1px rgba(0, 242, 14, 0.8), 0 -5px 1px rgba(244, 45, 0, 0.8), 5px 0 1px rgba(59, 0, 226, 0.8); + } + + 100% { + text-shadow: -1px 1px 8px rgba(255, 255, 255, 0.6), 1px -1px 8px rgba(255, 255, 235, 0.7), 5px 0 1px rgba(251, 0, 231, 0.8), 0 -5px 1px rgba(0, 233, 235, 0.8), -5px 0 1px rgba(0, 242, 14, 0.8), 0 5px 1px rgba(244, 45, 0, 0.8), -5px 0 1px rgba(59, 0, 226, 0.8); + } +} + +@keyframes type { + + 0%, + 19% { + opacity: 0; + } + + 20%, + 100% { + opacity: 1; + } +} \ No newline at end of file diff --git a/packages/vscode-extension/src/webview/components/ReplayUI.tsx b/packages/vscode-extension/src/webview/components/ReplayUI.tsx new file mode 100644 index 000000000..748cc111b --- /dev/null +++ b/packages/vscode-extension/src/webview/components/ReplayUI.tsx @@ -0,0 +1,44 @@ +import { useRef, useState } from "react"; +import "./ReplayUI.css"; +import ReplayOverlay from "./ReplayOverlay"; +import { RecordingData } from "../../common/Project"; + +function VHSRewind() { + return ( +
+
+
+
+
+
+ {"\u25C0\u25C0"} +
REWIND +
+
+
+ ); +} + +type ReplayVideoProps = { + replayData: RecordingData; + onClose: () => void; +}; + +export default function ReplayUI({ replayData, onClose }: ReplayVideoProps) { + const videoRef = useRef(null); + const [isRewinding, setIsRewinding] = useState(false); + + return ( + <> + +