diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..bb825cc --- /dev/null +++ b/.env.example @@ -0,0 +1,12 @@ +# 웹뷰로 띄울 stream-client-web 주소. +# 이 파일을 .env.local로 복사한 뒤 값을 자기 환경에 맞게 채운다. +# +# 실행 환경마다 주소가 다르다 (docs/local-development.md 참고): +# - iOS 시뮬레이터 http://localhost:5173 +# - Android 에뮬레이터 http://10.0.2.2:5173 +# - 실제 기기 http://{개발 PC의 LAN IP}:5173 +# +# LAN IP 확인: ipconfig getifaddr en0 +# 실제 기기는 PC와 같은 네트워크에 있어야 하고, 웹도 pnpm dev --host로 띄워야 한다. +# 배포 후에는 운영 URL로 바꾼다. +EXPO_PUBLIC_WEB_URL=http://192.168.0.2:5173 diff --git a/README.md b/README.md index 64640ce..23618d9 100644 --- a/README.md +++ b/README.md @@ -1,57 +1,61 @@ -# Welcome to your Expo app 👋 - -This is an [Expo](https://expo.dev) project created with [`create-expo-app`](https://www.npmjs.com/package/create-expo-app). - -## Get started - -1. Install dependencies - - ```bash - npm install - ``` +# stream-client-app -2. Start the app +국민대학교 소프트웨어융합대학 통합 플랫폼 'STREAM' 앱 (Expo + React Native + TypeScript). - ```bash - npx expo start - ``` +화면 UI는 [stream-client-web](https://github.com/billilge/stream-client-web)이 전담한다. 이 저장소는 그 웹을 WebView로 띄우는 **네이티브 셸**이다. 앞으로 푸시 알림, 토큰 저장처럼 웹이 할 수 없는 기능을 네이티브에서 맡는다. -In the output, you'll find options to open the app in a +## 개발 환경 -- [development build](https://docs.expo.dev/develop/development-builds/introduction/) -- [Android emulator](https://docs.expo.dev/workflow/android-studio-emulator/) -- [iOS simulator](https://docs.expo.dev/workflow/ios-simulator/) -- [Expo Go](https://expo.dev/go), a limited sandbox for trying out app development with Expo +- Node: `.nvmrc` 참고 (`nvm use`) +- 패키지 매니저: [pnpm](https://pnpm.io/) — Corepack으로 관리한다 (`corepack enable` 후 `pnpm install`만 실행하면 `packageManager` 필드에 고정된 버전이 자동으로 쓰인다) -You can start developing by editing the files inside the **app** directory. This project uses [file-based routing](https://docs.expo.dev/router/introduction). +```bash +corepack enable +pnpm install +cp .env.example .env.local # 웹 주소를 실행 환경에 맞게 채운다 +pnpm start +``` -## Get a fresh project +### 웹 주소 설정 -When you're ready, run: +WebView가 띄울 주소는 `.env.local`의 `EXPO_PUBLIC_WEB_URL`로 주입한다. 이 파일은 git에 올리지 않는다. -```bash -npm run reset-project -``` +**실행 환경마다 가리켜야 하는 주소가 다르다.** -This command will move the starter code to the **app-example** directory and create a blank **app** directory where you can start developing. +| 실행 환경 | 웹 주소 | +| --- | --- | +| iOS 시뮬레이터 | `http://localhost:5173` | +| Android 에뮬레이터 | `http://10.0.2.2:5173` | +| 실제 기기 (Expo Go) | `http://{개발 PC의 LAN IP}:5173` | -### Other setup steps +실제 기기에서는 `localhost`가 기기 자신을 가리켜 개발 PC에 닿지 않으므로 LAN IP를 써야 하고, 웹도 `pnpm dev --host`로 띄워야 한다. -- To set up ESLint for linting, run `npx expo lint`, or follow our guide on ["Using ESLint and Prettier"](https://docs.expo.dev/guides/using-eslint/) -- If you'd like to set up unit testing, follow our guide on ["Unit Testing with Jest"](https://docs.expo.dev/develop/unit-testing/) -- Learn more about the TypeScript setup in this template in our guide on ["Using TypeScript"](https://docs.expo.dev/guides/typescript/) +자세한 실행 방법·준비물·트러블슈팅은 **[docs/local-development.md](docs/local-development.md)** 참고. -## Learn more +## 스크립트 -To learn more about developing your project with Expo, look at the following resources: +| 명령 | 설명 | +| --- | --- | +| `pnpm start` | Expo 개발 서버 실행 | +| `pnpm ios` | iOS 시뮬레이터에서 실행 | +| `pnpm android` | Android 에뮬레이터에서 실행 | +| `pnpm web` | 웹에서 실행 | +| `pnpm lint` | Biome lint | +| `pnpm format` | Biome로 포맷 적용 | +| `pnpm check` | Biome lint + format 검증 | -- [Expo documentation](https://docs.expo.dev/): Learn fundamentals, or go into advanced topics with our [guides](https://docs.expo.dev/guides). -- [Learn Expo tutorial](https://docs.expo.dev/tutorial/introduction/): Follow a step-by-step tutorial where you'll create a project that runs on Android, iOS, and the web. +## 구조 -## Join the community +``` +src/ +├─ app/ expo-router 라우팅 껍데기 +├─ features/webview/ WebView 셸 화면 +├─ constants/ 설정 상수 +└─ utils/ 공용 유틸 +``` -Join our community of developers creating universal apps. +## 문서 -- [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute. -- [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions. -# stream-client-app +- [로컬 실행 가이드](docs/local-development.md) +- [코딩 컨벤션](docs/conventions/coding-style.md) +- [Git 컨벤션](docs/conventions/git-convention.md) diff --git a/assets/images/expo-badge-white.png b/assets/images/expo-badge-white.png deleted file mode 100644 index 2863067..0000000 Binary files a/assets/images/expo-badge-white.png and /dev/null differ diff --git a/assets/images/expo-badge.png b/assets/images/expo-badge.png deleted file mode 100644 index 5d5c5bb..0000000 Binary files a/assets/images/expo-badge.png and /dev/null differ diff --git a/assets/images/expo-logo.png b/assets/images/expo-logo.png deleted file mode 100644 index 6b1642a..0000000 Binary files a/assets/images/expo-logo.png and /dev/null differ diff --git a/assets/images/logo-glow.png b/assets/images/logo-glow.png deleted file mode 100644 index edc99be..0000000 Binary files a/assets/images/logo-glow.png and /dev/null differ diff --git a/assets/images/react-logo.png b/assets/images/react-logo.png deleted file mode 100644 index 9d72a9f..0000000 Binary files a/assets/images/react-logo.png and /dev/null differ diff --git a/assets/images/react-logo@2x.png b/assets/images/react-logo@2x.png deleted file mode 100644 index 2229b13..0000000 Binary files a/assets/images/react-logo@2x.png and /dev/null differ diff --git a/assets/images/react-logo@3x.png b/assets/images/react-logo@3x.png deleted file mode 100644 index a99b203..0000000 Binary files a/assets/images/react-logo@3x.png and /dev/null differ diff --git a/assets/images/tabIcons/explore.png b/assets/images/tabIcons/explore.png deleted file mode 100644 index 73d8258..0000000 Binary files a/assets/images/tabIcons/explore.png and /dev/null differ diff --git a/assets/images/tabIcons/explore@2x.png b/assets/images/tabIcons/explore@2x.png deleted file mode 100644 index 21b9bd2..0000000 Binary files a/assets/images/tabIcons/explore@2x.png and /dev/null differ diff --git a/assets/images/tabIcons/explore@3x.png b/assets/images/tabIcons/explore@3x.png deleted file mode 100644 index 422202d..0000000 Binary files a/assets/images/tabIcons/explore@3x.png and /dev/null differ diff --git a/assets/images/tabIcons/home.png b/assets/images/tabIcons/home.png deleted file mode 100644 index ad5699c..0000000 Binary files a/assets/images/tabIcons/home.png and /dev/null differ diff --git a/assets/images/tabIcons/home@2x.png b/assets/images/tabIcons/home@2x.png deleted file mode 100644 index 22a1f2c..0000000 Binary files a/assets/images/tabIcons/home@2x.png and /dev/null differ diff --git a/assets/images/tabIcons/home@3x.png b/assets/images/tabIcons/home@3x.png deleted file mode 100644 index f5d1f9a..0000000 Binary files a/assets/images/tabIcons/home@3x.png and /dev/null differ diff --git a/assets/images/tutorial-web.png b/assets/images/tutorial-web.png deleted file mode 100644 index e4a8c58..0000000 Binary files a/assets/images/tutorial-web.png and /dev/null differ diff --git a/docs/local-development.md b/docs/local-development.md new file mode 100644 index 0000000..5bafd17 --- /dev/null +++ b/docs/local-development.md @@ -0,0 +1,122 @@ +# 로컬 실행 가이드 + +> 이 앱은 `stream-client-web`을 WebView로 띄우는 셸이다. 따라서 **웹 서버와 앱을 함께 띄워야** 화면이 보인다. + +--- + +## 1. 준비 + +```bash +corepack enable +pnpm install +cp .env.example .env.local +``` + +`.env.local`의 `EXPO_PUBLIC_WEB_URL`에 웹 주소를 넣는다. **실행 환경마다 주소가 다르다** — 아래 표 참고. + +| 실행 환경 | 웹 주소 | 비고 | +| --- | --- | --- | +| iOS 시뮬레이터 | `http://localhost:5173` | 시뮬레이터가 맥의 네트워크를 그대로 쓴다 | +| Android 에뮬레이터 | `http://10.0.2.2:5173` | `10.0.2.2`가 호스트 PC의 `localhost`에 매핑된다 | +| 실제 기기 (Expo Go) | `http://{개발 PC의 LAN IP}:5173` | 기기와 PC가 같은 네트워크에 있어야 한다 | + +LAN IP 확인: + +```bash +ipconfig getifaddr en0 # 유선이면 en1 등 다른 인터페이스일 수 있다 +``` + +## 2. 웹 서버 띄우기 + +별도 터미널에서 `stream-client-web`을 실행한다. + +```bash +cd ../stream-client-web +pnpm dev +``` + +**실제 기기로 볼 때는 `--host`가 필요하다.** Vite dev 서버는 기본적으로 `localhost`에만 바인딩해서 다른 기기에서 닿지 않는다. + +```bash +pnpm dev --host +``` + +## 3. 앱 실행 + +```bash +pnpm start # QR 코드가 뜬다 +pnpm ios # iOS 시뮬레이터로 바로 실행 +pnpm android # Android 에뮬레이터로 바로 실행 +pnpm web # 브라우저에서 실행 +``` + +`.env.local`을 고쳤으면 **Expo 개발 서버를 재시작해야 반영된다.** `EXPO_PUBLIC_*` 값은 번들 시점에 주입되기 때문이다. + +--- + +## 실행 환경별 준비물 + +### 실제 기기 (권장 — 준비물이 가장 적다) + +기기에 **Expo Go** 앱만 설치하면 된다 (App Store / Play Store). `react-native-webview`는 Expo Go에 포함되어 있어 별도 네이티브 빌드가 필요 없다. + +1. `pnpm start` +2. 터미널의 QR 코드를 스캔 — iOS는 기본 카메라 앱, Android는 Expo Go 앱 안에서 +3. 기기와 PC가 같은 와이파이에 있어야 한다 + +### iOS 시뮬레이터 + +**Xcode가 필요하다** (App Store에서 설치, 용량이 크고 시간이 오래 걸린다). Command Line Tools만으로는 시뮬레이터가 없다. + +```bash +xcode-select -p # /Applications/Xcode.app/... 이 나와야 한다 +xcrun simctl list devices available # 사용 가능한 시뮬레이터 목록 +pnpm ios +``` + +`xcode-select -p`가 `/Library/Developer/CommandLineTools`를 가리키면 Xcode 설치 후 아래를 실행한다. + +```bash +sudo xcode-select -s /Applications/Xcode.app/Contents/Developer +``` + +### Android 에뮬레이터 + +**Android Studio가 필요하다.** 설치 후 Device Manager에서 가상 기기(AVD)를 하나 만든다. + +```bash +emulator -list-avds # 만든 AVD 목록 +pnpm android +``` + +### 웹 (`pnpm web`) + +브라우저에서 `react-native-web`으로 렌더링된다. 화면 확인용으로는 쓸 수 있지만 **하드웨어 백 버튼, 외부 링크 처리 같은 네이티브 동작은 검증되지 않는다.** 최종 확인은 기기나 시뮬레이터에서 한다. + +--- + +## 확인할 것 + +셸이 제대로 동작하는지 보려면 아래를 확인한다. + +- [ ] 웹 화면이 뜨는가 +- [ ] 로드 중 스피너가 보이고, 로드되면 사라지는가 +- [ ] Bottom Nav로 화면을 옮긴 뒤 **Android 하드웨어 백 버튼**이 앱 종료가 아니라 웹 뒤로 가기로 동작하는가 (Android 전용) +- [ ] 네트워크를 끊고 앱을 다시 열면 오류 화면과 `다시 시도` 버튼이 뜨는가 +- [ ] 외부 도메인 링크가 웹뷰 안이 아니라 시스템 브라우저로 열리는가 + +> 현재 웹의 `ScreenLayout`이 `375x812` 고정 프레임이라 화면이 꽉 차지 않고 가운데 정렬된 채 여백이 생긴다. 알려진 사항이며 web 레포에서 별도로 대응한다. + +--- + +## 잘 안 될 때 + +| 증상 | 원인 / 해결 | +| --- | --- | +| "웹 주소가 설정되지 않았습니다" 화면 | `.env.local`에 `EXPO_PUBLIC_WEB_URL`이 없다. 채운 뒤 Expo 서버를 **재시작**한다 | +| "페이지를 불러오지 못했습니다" 화면 | 웹 dev 서버가 꺼져 있거나, 주소가 실행 환경과 맞지 않는다(위 표 확인) | +| 실제 기기에서만 안 닿는다 | 웹을 `pnpm dev --host`로 띄웠는지, 기기와 PC가 같은 네트워크인지, 맥 방화벽이 막고 있지 않은지 확인 | +| `.env.local`을 고쳤는데 그대로다 | `EXPO_PUBLIC_*`은 번들 시점에 주입된다. Expo 개발 서버를 재시작한다 | +| LAN IP가 자꾸 바뀐다 | 공유기가 DHCP로 새 IP를 준 것이다. `ipconfig getifaddr en0`로 다시 확인해 `.env.local`을 갱신한다 | + +> 로컬 `http://` 접속은 Expo Go에서 동작한다. 나중에 자체 개발 빌드(EAS)로 넘어가면 iOS ATS 예외 설정이 필요할 수 있다. diff --git a/package.json b/package.json index 7d8a227..0e08f3f 100644 --- a/package.json +++ b/package.json @@ -3,18 +3,12 @@ "main": "expo-router/entry", "version": "1.0.0", "dependencies": { - "@expo/ui": "~57.0.15", "expo": "~57.0.19", "expo-constants": "~57.0.17", - "expo-device": "~57.0.1", - "expo-font": "~57.0.3", - "expo-glass-effect": "~57.0.1", - "expo-image": "~57.0.4", "expo-linking": "~57.0.9", "expo-router": "~57.0.18", "expo-splash-screen": "~57.0.8", "expo-status-bar": "~57.0.1", - "expo-symbols": "~57.0.2", "expo-system-ui": "~57.0.3", "expo-web-browser": "~57.0.2", "react": "19.2.3", @@ -26,6 +20,7 @@ "react-native-safe-area-context": "~5.7.0", "react-native-screens": "~4.26.0", "react-native-web": "~0.21.0", + "react-native-webview": "13.16.1", "react-native-worklets": "0.10.1" }, "devDependencies": { @@ -37,7 +32,6 @@ }, "scripts": { "start": "expo start", - "reset-project": "node ./scripts/reset-project.js", "android": "expo start --android", "ios": "expo start --ios", "web": "expo start --web", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d2bfe97..efb66cb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,27 +8,12 @@ importers: .: dependencies: - '@expo/ui': - specifier: ~57.0.15 - version: 57.0.15(@babel/core@7.29.7(supports-color@8.1.1))(@types/react@19.2.18)(expo@57.0.19)(react-dom@19.2.3(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) expo: specifier: ~57.0.19 - version: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + version: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) expo-constants: specifier: ~57.0.17 version: 57.0.17(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) - expo-device: - specifier: ~57.0.1 - version: 57.0.1(expo@57.0.19) - expo-font: - specifier: ~57.0.3 - version: 57.0.3(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - expo-glass-effect: - specifier: ~57.0.1 - version: 57.0.1(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - expo-image: - specifier: ~57.0.4 - version: 57.0.4(expo@57.0.19)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) expo-linking: specifier: ~57.0.9 version: 57.0.9(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) @@ -41,9 +26,6 @@ importers: expo-status-bar: specifier: ~57.0.1 version: 57.0.1(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) - expo-symbols: - specifier: ~57.0.2 - version: 57.0.2(expo-font@57.0.3)(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) expo-system-ui: specifier: ~57.0.3 version: 57.0.3(expo@57.0.19)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) @@ -77,6 +59,9 @@ importers: react-native-web: specifier: ~0.21.0 version: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react-native-webview: + specifier: 13.16.1 + version: 13.16.1(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react-native-worklets: specifier: 0.10.1 version: 0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) @@ -1567,11 +1552,6 @@ packages: expo: '*' react-native: '*' - expo-device@57.0.1: - resolution: {integrity: sha512-jyEMDUticH+dhcL3GHa2aiifOvGXJsmb3oVT2R2q4i8bN7Bddy61+NkpMmuS2VAZrvoLQwf0TJJ/1vi1ukvutA==} - peerDependencies: - expo: '*' - expo-file-system@57.0.6: resolution: {integrity: sha512-pm8PMYEW6BnVOCBJ7df9FcDmQtE1tqImuYphlfYe1ipQRLYtdCayRczJcbKIsnB3mqEyp4gC2gWmMbsAsAOjVg==} peerDependencies: @@ -1592,17 +1572,6 @@ packages: react: '*' react-native: '*' - expo-image@57.0.4: - resolution: {integrity: sha512-DLVNjqIMLb71wh0F27kRv7nA9i4g80fvkVugiimVgGnO7Iot+gRSv5VhrFJCvX/OlSzbGQqd/3ktlEaPGFqP7g==} - peerDependencies: - expo: '*' - react: '*' - react-native: '*' - react-native-web: '*' - peerDependenciesMeta: - react-native-web: - optional: true - expo-keep-awake@57.0.1: resolution: {integrity: sha512-28lkFImeXTS+bhAjuCFV7w7tW5bXg27BJVrxv+nC/nyYa86qEa0oFeHwqol6ha5k4pdVDQgBF09GM4A1k76Ssg==} peerDependencies: @@ -2593,6 +2562,12 @@ packages: react: ^18.0.0 || ^19.0.0 react-dom: ^18.0.0 || ^19.0.0 + react-native-webview@13.16.1: + resolution: {integrity: sha512-If0eHhoEdOYDcHsX+xBFwHMbWBGK1BvGDQDQdVkwtSIXiq1uiqjkpWVP2uQ1as94J0CzvFE9PUNDuhiX0Z6ubw==} + peerDependencies: + react: '*' + react-native: '*' + react-native-worklets@0.10.1: resolution: {integrity: sha512-62mRM19bDpfpdI8HLkEErcdOsrAPDtE9lA/sw+5lLRpzBHNhxaoj9QyY2KjXqUmirelxkX4zuPGTC3VdA0feJA==} peerDependencies: @@ -2940,10 +2915,6 @@ packages: engines: {node: '>=14.17'} hasBin: true - ua-parser-js@0.7.41: - resolution: {integrity: sha512-O3oYyCMPYgNNHuO7Jjk3uacJWZF8loBgwrfd/5LE/HyZ3lUIOdniQ7DNXJcIgZbwioZxk0fLfI4EVnetdiX5jg==} - hasBin: true - ua-parser-js@1.0.41: resolution: {integrity: sha512-LbBDqdIC5s8iROCUjMbW1f5dJQTEFB1+KO9ogbvlb3nm9n4YHa5p4KTvFPWvh2Hs8gZMBuiB1/8+pdfe/tDPug==} hasBin: true @@ -3689,7 +3660,7 @@ snapshots: connect: 3.7.0(supports-color@8.1.1) debug: 4.4.3(supports-color@8.1.1) dnssd-advertise: 1.1.6 - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) expo-server: 57.0.3 fetch-nodeshim: 0.4.10 getenv: 2.0.0 @@ -3788,7 +3759,7 @@ snapshots: '@expo/dom-webview@57.0.1(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': dependencies: - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) react: 19.2.3 react-native: 0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) @@ -3855,7 +3826,7 @@ snapshots: dependencies: '@expo/dom-webview': 57.0.1(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) anser: 1.4.10 - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) react: 19.2.3 react-native: 0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) stacktrace-parser: 0.1.11 @@ -3886,7 +3857,7 @@ snapshots: postcss: 8.5.26 resolve-from: 5.0.0 optionalDependencies: - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) transitivePeerDependencies: - bufferutil - supports-color @@ -3908,7 +3879,7 @@ snapshots: dependencies: '@expo/log-box': 57.0.4(@expo/dom-webview@57.0.1)(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) anser: 1.4.10 - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) pretty-format: 29.7.0 react: 19.2.3 react-native: 0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) @@ -3986,7 +3957,7 @@ snapshots: '@expo/router-server@57.0.9(@expo/metro-runtime@57.0.15)(expo-constants@57.0.17)(expo-font@57.0.3)(expo-router@57.0.18)(expo-server@57.0.3)(expo@57.0.19)(react-dom@19.2.3(react@19.2.3))(react@19.2.3)(supports-color@8.1.1)': dependencies: debug: 4.4.3(supports-color@8.1.1) - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) expo-constants: 57.0.17(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) expo-font: 57.0.3(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) expo-server: 57.0.3 @@ -4010,7 +3981,7 @@ snapshots: '@expo/ui@57.0.15(@babel/core@7.29.7(supports-color@8.1.1))(@types/react@19.2.18)(expo@57.0.19)(react-dom@19.2.3(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)': dependencies: - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) react: 19.2.3 react-native: 0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) sf-symbols-typescript: 2.2.0 @@ -4605,7 +4576,7 @@ snapshots: react-refresh: 0.14.2 optionalDependencies: '@babel/runtime': 7.29.7 - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) transitivePeerDependencies: - '@babel/core' - supports-color @@ -4887,7 +4858,7 @@ snapshots: expo-asset@57.0.16(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3): dependencies: '@expo/image-utils': 0.11.5(supports-color@8.1.1)(typescript@6.0.3) - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) expo-constants: 57.0.17(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) react: 19.2.3 react-native: 0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) @@ -4898,46 +4869,32 @@ snapshots: expo-constants@57.0.17(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1): dependencies: '@expo/env': 2.4.3(supports-color@8.1.1) - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) react-native: 0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) transitivePeerDependencies: - supports-color - expo-device@57.0.1(expo@57.0.19): - dependencies: - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) - ua-parser-js: 0.7.41 - expo-file-system@57.0.6(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1)): dependencies: - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) react-native: 0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) expo-font@57.0.3(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) fontfaceobserver: 2.3.0 react: 19.2.3 react-native: 0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) expo-glass-effect@57.0.1(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) - react: 19.2.3 - react-native: 0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) - - expo-image@57.0.4(expo@57.0.19)(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): - dependencies: - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) react: 19.2.3 react-native: 0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) - sf-symbols-typescript: 2.2.0 - optionalDependencies: - react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) expo-keep-awake@57.0.1(expo@57.0.19)(react@19.2.3): dependencies: - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) react: 19.2.3 expo-linking@57.0.9(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1): @@ -4987,7 +4944,7 @@ snapshots: color: 4.2.3 debug: 4.4.3(supports-color@8.1.1) escape-string-regexp: 4.0.0 - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) expo-constants: 57.0.17(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(supports-color@8.1.1) expo-glass-effect: 57.0.1(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) expo-linking: 57.0.9(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1) @@ -5028,7 +4985,7 @@ snapshots: dependencies: '@expo/config-plugins': 57.0.9(supports-color@8.1.1)(typescript@6.0.3) '@expo/image-utils': 0.11.5(supports-color@8.1.1)(typescript@6.0.3) - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) xml2js: 0.6.0 transitivePeerDependencies: - supports-color @@ -5036,14 +4993,14 @@ snapshots: expo-status-bar@57.0.1(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) react: 19.2.3 react-native: 0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) expo-symbols@57.0.2(expo-font@57.0.3)(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): dependencies: '@expo-google-fonts/material-symbols': 0.4.45 - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) expo-font: 57.0.3(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react: 19.2.3 react-native: 0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) @@ -5053,7 +5010,7 @@ snapshots: dependencies: '@react-native/normalize-colors': 0.86.3 debug: 4.4.3(supports-color@8.1.1) - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) react-native: 0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) optionalDependencies: react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) @@ -5062,10 +5019,10 @@ snapshots: expo-web-browser@57.0.2(expo@57.0.19)(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1)): dependencies: - expo: 57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) + expo: 57.0.19(2b41da233386ad1d2b715aba6998b7ec) react-native: 0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) - expo@57.0.19(@babel/core@7.29.7(supports-color@8.1.1))(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-router@57.0.18)(react-dom@19.2.3(react@19.2.3))(react-native-web@0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3))(react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3): + expo@57.0.19(2b41da233386ad1d2b715aba6998b7ec): dependencies: '@babel/runtime': 7.29.7 '@expo/cli': 57.0.21(@expo/dom-webview@57.0.1)(@expo/metro-runtime@57.0.15)(expo-constants@57.0.17)(expo-font@57.0.3)(expo-router@57.0.18)(expo@57.0.19)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1)(typescript@6.0.3) @@ -5096,6 +5053,7 @@ snapshots: '@expo/metro-runtime': 57.0.15(@expo/log-box@57.0.4)(expo@57.0.19)(react-dom@19.2.3(react@19.2.3))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) react-dom: 19.2.3(react@19.2.3) react-native-web: 0.21.2(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + react-native-webview: 13.16.1(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3) transitivePeerDependencies: - '@babel/core' - bufferutil @@ -5981,6 +5939,13 @@ snapshots: transitivePeerDependencies: - encoding + react-native-webview@13.16.1(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3): + dependencies: + escape-string-regexp: 4.0.0 + invariant: 2.2.4 + react: 19.2.3 + react-native: 0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1) + react-native-worklets@0.10.1(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(react-native@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(@react-native/metro-config@0.86.3(@babel/core@7.29.7(supports-color@8.1.1))(supports-color@8.1.1))(@types/react@19.2.18)(react@19.2.3)(supports-color@8.1.1))(react@19.2.3)(supports-color@8.1.1): dependencies: '@babel/core': 7.29.7(supports-color@8.1.1) @@ -6359,8 +6324,6 @@ snapshots: typescript@6.0.3: {} - ua-parser-js@0.7.41: {} - ua-parser-js@1.0.41: {} undici-types@8.3.0: {} diff --git a/scripts/reset-project.js b/scripts/reset-project.js deleted file mode 100644 index 16a975c..0000000 --- a/scripts/reset-project.js +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/env node - -/** - * This script is used to reset the project to a blank state. - * It deletes or moves the /src and /scripts directories to /example based on user input and creates a new /src/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 = ["src", "scripts"]; -const exampleDir = "example"; -const newAppDir = "src/app"; -const exampleDirPath = path.join(root, exampleDir); - -const indexContent = `import { Text, View, StyleSheet } from "react-native"; - -export default function Index() { - return ( - - Edit src/app/index.tsx to edit this screen. - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - alignItems: "center", - justifyContent: "center", - }, -}); -`; - -const layoutContent = `import { Stack } from "expo-router"; - -export default function RootLayout() { - return ; -} -`; - -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 /src/app directory - const newAppDirPath = path.join(root, newAppDir); - await fs.promises.mkdir(newAppDirPath, { recursive: true }); - console.log("\n📁 New /src/app directory created."); - - // Create index.tsx - const indexPath = path.join(newAppDirPath, "index.tsx"); - await fs.promises.writeFile(indexPath, indexContent); - console.log("📄 src/app/index.tsx created."); - - // Create _layout.tsx - const layoutPath = path.join(newAppDirPath, "_layout.tsx"); - await fs.promises.writeFile(layoutPath, layoutContent); - console.log("📄 src/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 src/app/index.tsx to edit the main screen.\n3. Put all your application code in /src, only screens and layout files should be in /src/app.${ - userInput === "y" - ? `\n4. 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 /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(); - } - }, -); diff --git a/src/app/_layout.tsx b/src/app/_layout.tsx index ec17660..c01a945 100644 --- a/src/app/_layout.tsx +++ b/src/app/_layout.tsx @@ -1,18 +1,14 @@ -import { DarkTheme, DefaultTheme, ThemeProvider } from "expo-router"; -import * as SplashScreen from "expo-splash-screen"; -import { useColorScheme } from "react-native"; +import "@/global.css"; -import { AnimatedSplashOverlay } from "@/components/animated-icon"; -import AppTabs from "@/components/app-tabs"; +import { Stack } from "expo-router"; +import { StatusBar } from "expo-status-bar"; +import { SafeAreaProvider } from "react-native-safe-area-context"; -SplashScreen.preventAutoHideAsync(); - -export default function TabLayout() { - const colorScheme = useColorScheme(); +export default function RootLayout() { return ( - - - - + + + + ); } diff --git a/src/app/explore.tsx b/src/app/explore.tsx deleted file mode 100644 index 05f284c..0000000 --- a/src/app/explore.tsx +++ /dev/null @@ -1,181 +0,0 @@ -import { Image } from "expo-image"; -import { SymbolView } from "expo-symbols"; -import { Platform, Pressable, ScrollView, StyleSheet } from "react-native"; -import { useSafeAreaInsets } from "react-native-safe-area-context"; - -import { ExternalLink } from "@/components/external-link"; -import { ThemedText } from "@/components/themed-text"; -import { ThemedView } from "@/components/themed-view"; -import { Collapsible } from "@/components/ui/collapsible"; -import { WebBadge } from "@/components/web-badge"; -import { BottomTabInset, MaxContentWidth, Spacing } from "@/constants/theme"; -import { useTheme } from "@/hooks/use-theme"; - -export default function TabTwoScreen() { - const safeAreaInsets = useSafeAreaInsets(); - const insets = { - ...safeAreaInsets, - bottom: safeAreaInsets.bottom + BottomTabInset + Spacing.three, - }; - const theme = useTheme(); - - const contentPlatformStyle = Platform.select({ - android: { - paddingTop: insets.top, - paddingLeft: insets.left, - paddingRight: insets.right, - paddingBottom: insets.bottom, - }, - web: { - paddingTop: Spacing.six, - paddingBottom: Spacing.four, - }, - }); - - return ( - - - - Explore - - This starter app includes example{"\n"}code to help you get started. - - - - pressed && styles.pressed}> - - Expo documentation - - - - - - - - - - This app has two screens: src/app/index.tsx and{" "} - src/app/explore.tsx - - - The layout file in src/app/_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 - - - - - - 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{" "} - src/components/ui/collapsible.tsx component uses - the powerful react-native-reanimated library to - animate opening this hint. - - - - {Platform.OS === "web" && } - - - ); -} - -const styles = StyleSheet.create({ - scrollView: { - flex: 1, - }, - contentContainer: { - flexDirection: "row", - justifyContent: "center", - }, - container: { - maxWidth: MaxContentWidth, - flexGrow: 1, - }, - titleContainer: { - gap: Spacing.three, - alignItems: "center", - paddingHorizontal: Spacing.four, - paddingVertical: Spacing.six, - }, - centerText: { - textAlign: "center", - }, - pressed: { - opacity: 0.7, - }, - linkButton: { - flexDirection: "row", - paddingHorizontal: Spacing.four, - paddingVertical: Spacing.two, - borderRadius: Spacing.five, - justifyContent: "center", - gap: Spacing.one, - alignItems: "center", - }, - sectionsWrapper: { - gap: Spacing.five, - paddingHorizontal: Spacing.four, - paddingTop: Spacing.three, - }, - collapsibleContent: { - alignItems: "center", - }, - imageTutorial: { - width: "100%", - aspectRatio: 296 / 171, - borderRadius: Spacing.three, - marginTop: Spacing.two, - }, - imageReact: { - width: 100, - height: 100, - alignSelf: "center", - }, -}); diff --git a/src/app/index.tsx b/src/app/index.tsx index 88d3d40..1e00607 100644 --- a/src/app/index.tsx +++ b/src/app/index.tsx @@ -1,98 +1,3 @@ -import * as Device from "expo-device"; -import { Platform, StyleSheet } from "react-native"; -import { SafeAreaView } from "react-native-safe-area-context"; +import WebViewScreen from "@/features/webview/WebViewScreen"; -import { AnimatedIcon } from "@/components/animated-icon"; -import { HintRow } from "@/components/hint-row"; -import { ThemedText } from "@/components/themed-text"; -import { ThemedView } from "@/components/themed-view"; -import { WebBadge } from "@/components/web-badge"; -import { BottomTabInset, MaxContentWidth, Spacing } from "@/constants/theme"; - -function getDevMenuHint() { - if (Platform.OS === "web") { - return use browser devtools; - } - if (Device.isDevice) { - return ( - - shake device or press m in terminal - - ); - } - const shortcut = Platform.OS === "android" ? "cmd+m (or ctrl+m)" : "cmd+d"; - return ( - - press {shortcut} - - ); -} - -export default function HomeScreen() { - return ( - - - - - - Welcome to Expo - - - - - get started - - - - src/app/index.tsx} - /> - - npm run reset-project} - /> - - - {Platform.OS === "web" && } - - - ); -} - -const styles = StyleSheet.create({ - container: { - flex: 1, - justifyContent: "center", - flexDirection: "row", - }, - safeArea: { - flex: 1, - paddingHorizontal: Spacing.four, - alignItems: "center", - gap: Spacing.three, - paddingBottom: BottomTabInset + Spacing.three, - maxWidth: MaxContentWidth, - }, - heroSection: { - alignItems: "center", - justifyContent: "center", - flex: 1, - paddingHorizontal: Spacing.four, - gap: Spacing.four, - }, - title: { - textAlign: "center", - }, - code: { - textTransform: "uppercase", - }, - stepContainer: { - gap: Spacing.three, - alignSelf: "stretch", - paddingHorizontal: Spacing.three, - paddingVertical: Spacing.four, - borderRadius: Spacing.four, - }, -}); +export default WebViewScreen; diff --git a/src/components/animated-icon.module.css b/src/components/animated-icon.module.css deleted file mode 100644 index f8156fe..0000000 --- a/src/components/animated-icon.module.css +++ /dev/null @@ -1,6 +0,0 @@ -.expoLogoBackground { - background-image: linear-gradient(180deg, #3c9ffe, #0274df); - border-radius: 40px; - width: 128px; - height: 128px; -} diff --git a/src/components/animated-icon.tsx b/src/components/animated-icon.tsx deleted file mode 100644 index bffda2e..0000000 --- a/src/components/animated-icon.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import { Image } from "expo-image"; -import * as SplashScreen from "expo-splash-screen"; -import { useState } from "react"; -import { Dimensions, StyleSheet, View } from "react-native"; -import Animated, { Easing, Keyframe } from "react-native-reanimated"; -import { scheduleOnRN } from "react-native-worklets"; - -const INITIAL_SCALE_FACTOR = Dimensions.get("screen").height / 90; -const DURATION = 600; - -export function AnimatedSplashOverlay() { - const [animate, setAnimate] = useState(false); - const [visible, setVisible] = useState(true); - - if (!visible) return null; - - const splashKeyframe = new Keyframe({ - 0: { - transform: [{ scale: 1 }], - opacity: 1, - }, - 20: { - opacity: 1, - }, - 70: { - opacity: 0, - easing: Easing.elastic(0.7), - }, - 100: { - opacity: 0, - transform: [{ scale: 1 }], - easing: Easing.elastic(0.7), - }, - }); - - const image = ; - - return animate ? ( - { - "worklet"; - if (finished) { - scheduleOnRN(setVisible, false); - } - })} - style={styles.splashOverlay} - > - {image} - - ) : ( - { - SplashScreen.hideAsync().finally(() => { - setAnimate(true); - }); - }} - style={styles.splashOverlay} - > - {image} - - ); -} - -const keyframe = new Keyframe({ - 0: { - transform: [{ scale: INITIAL_SCALE_FACTOR }], - }, - 100: { - transform: [{ scale: 1 }], - easing: Easing.elastic(0.7), - }, -}); - -const logoKeyframe = new Keyframe({ - 0: { - transform: [{ scale: 1.3 }], - opacity: 0, - }, - 40: { - transform: [{ scale: 1.3 }], - opacity: 0, - easing: Easing.elastic(0.7), - }, - 100: { - opacity: 1, - transform: [{ scale: 1 }], - easing: Easing.elastic(0.7), - }, -}); - -const glowKeyframe = new Keyframe({ - 0: { - transform: [{ rotateZ: "0deg" }], - }, - 100: { - transform: [{ rotateZ: "7200deg" }], - }, -}); - -export function AnimatedIcon() { - return ( - - - - - - - - - - - ); -} - -const styles = StyleSheet.create({ - imageContainer: { - justifyContent: "center", - alignItems: "center", - }, - glow: { - width: 201, - height: 201, - position: "absolute", - }, - iconContainer: { - justifyContent: "center", - alignItems: "center", - width: 128, - height: 128, - zIndex: 100, - }, - image: { - width: 76, - height: 71, - }, - background: { - borderRadius: 40, - experimental_backgroundImage: `linear-gradient(180deg, #3C9FFE, #0274DF)`, - width: 128, - height: 128, - position: "absolute", - }, - splashOverlay: { - ...StyleSheet.absoluteFill, - backgroundColor: "#208AEF", - alignItems: "center", - justifyContent: "center", - zIndex: 1000, - }, -}); diff --git a/src/components/animated-icon.web.tsx b/src/components/animated-icon.web.tsx deleted file mode 100644 index eb7b23c..0000000 --- a/src/components/animated-icon.web.tsx +++ /dev/null @@ -1,109 +0,0 @@ -import { Image } from "expo-image"; -import { StyleSheet, View } from "react-native"; -import Animated, { Easing, Keyframe } from "react-native-reanimated"; - -import classes from "./animated-icon.module.css"; - -const DURATION = 300; - -export function AnimatedSplashOverlay() { - return null; -} - -const keyframe = new Keyframe({ - 0: { - transform: [{ scale: 0 }], - }, - 60: { - transform: [{ scale: 1.2 }], - easing: Easing.elastic(1.2), - }, - 100: { - transform: [{ scale: 1 }], - easing: Easing.elastic(1.2), - }, -}); - -const logoKeyframe = new Keyframe({ - 0: { - opacity: 0, - }, - 60: { - transform: [{ scale: 1.2 }], - opacity: 0, - easing: Easing.elastic(1.2), - }, - 100: { - transform: [{ scale: 1 }], - opacity: 1, - easing: Easing.elastic(1.2), - }, -}); - -const glowKeyframe = new Keyframe({ - 0: { - transform: [{ rotateZ: "-180deg" }, { scale: 0.8 }], - opacity: 0, - }, - [DURATION / 1000]: { - transform: [{ rotateZ: "0deg" }, { scale: 1 }], - opacity: 1, - easing: Easing.elastic(0.7), - }, - 100: { - transform: [{ rotateZ: "7200deg" }], - }, -}); - -export function AnimatedIcon() { - return ( - - - - - - -
- - - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - alignItems: "center", - width: "100%", - zIndex: 1000, - position: "absolute", - top: 128 / 2 + 138, - }, - imageContainer: { - justifyContent: "center", - alignItems: "center", - }, - glow: { - width: 201, - height: 201, - position: "absolute", - }, - iconContainer: { - justifyContent: "center", - alignItems: "center", - width: 128, - height: 128, - }, - image: { - position: "absolute", - width: 76, - height: 71, - }, - background: { - width: 128, - height: 128, - position: "absolute", - }, -}); diff --git a/src/components/app-tabs.tsx b/src/components/app-tabs.tsx deleted file mode 100644 index 8170afb..0000000 --- a/src/components/app-tabs.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { NativeTabs } from "expo-router/unstable-native-tabs"; -import { useColorScheme } from "react-native"; - -import { Colors } from "@/constants/theme"; - -export default function AppTabs() { - const scheme = useColorScheme(); - const colors = Colors[scheme === "unspecified" ? "light" : scheme]; - - return ( - - - Home - - - - - Explore - - - - ); -} diff --git a/src/components/app-tabs.web.tsx b/src/components/app-tabs.web.tsx deleted file mode 100644 index c554bdf..0000000 --- a/src/components/app-tabs.web.tsx +++ /dev/null @@ -1,114 +0,0 @@ -import { - TabList, - type TabListProps, - TabSlot, - Tabs, - TabTrigger, - type TabTriggerSlotProps, -} from "expo-router/ui"; -import { SymbolView } from "expo-symbols"; -import { Pressable, StyleSheet, useColorScheme, View } from "react-native"; -import { Colors, MaxContentWidth, Spacing } from "@/constants/theme"; -import { ExternalLink } from "./external-link"; -import { ThemedText } from "./themed-text"; -import { ThemedView } from "./themed-view"; - -export default function AppTabs() { - return ( - - - - - - Home - - - Explore - - - - - ); -} - -export function TabButton({ children, isFocused, ...props }: TabTriggerSlotProps) { - return ( - pressed && styles.pressed}> - - - {children} - - - - ); -} - -export function CustomTabList(props: TabListProps) { - const scheme = useColorScheme(); - const colors = Colors[scheme === "unspecified" ? "light" : scheme]; - - return ( - - - - Expo Starter - - - {props.children} - - - - Docs - - - - - - ); -} - -const styles = StyleSheet.create({ - tabListContainer: { - position: "absolute", - width: "100%", - padding: Spacing.three, - justifyContent: "center", - alignItems: "center", - flexDirection: "row", - }, - innerContainer: { - paddingVertical: Spacing.two, - paddingHorizontal: Spacing.five, - borderRadius: Spacing.five, - flexDirection: "row", - alignItems: "center", - flexGrow: 1, - gap: Spacing.two, - maxWidth: MaxContentWidth, - }, - brandText: { - marginRight: "auto", - }, - pressed: { - opacity: 0.7, - }, - tabButtonView: { - paddingVertical: Spacing.one, - paddingHorizontal: Spacing.three, - borderRadius: Spacing.three, - }, - externalPressable: { - flexDirection: "row", - justifyContent: "center", - alignItems: "center", - gap: Spacing.one, - marginLeft: Spacing.three, - }, -}); diff --git a/src/components/external-link.tsx b/src/components/external-link.tsx deleted file mode 100644 index 53d895b..0000000 --- a/src/components/external-link.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import { type Href, Link } from "expo-router"; -import { openBrowserAsync, WebBrowserPresentationStyle } from "expo-web-browser"; -import type { ComponentProps } from "react"; - -type Props = Omit, "href"> & { href: Href & string }; - -export function ExternalLink({ href, ...rest }: Props) { - return ( - { - if (process.env.EXPO_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, { - presentationStyle: WebBrowserPresentationStyle.AUTOMATIC, - }); - } - }} - /> - ); -} diff --git a/src/components/hint-row.tsx b/src/components/hint-row.tsx deleted file mode 100644 index 04a4d9f..0000000 --- a/src/components/hint-row.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import type { ReactNode } from "react"; -import { StyleSheet, View } from "react-native"; -import { Spacing } from "@/constants/theme"; -import { ThemedText } from "./themed-text"; -import { ThemedView } from "./themed-view"; - -type HintRowProps = { - title?: string; - hint?: ReactNode; -}; - -export function HintRow({ title = "Try editing", hint = "app/index.tsx" }: HintRowProps) { - return ( - - {title} - - {hint} - - - ); -} - -const styles = StyleSheet.create({ - stepRow: { - flexDirection: "row", - justifyContent: "space-between", - }, - codeSnippet: { - borderRadius: Spacing.two, - paddingVertical: Spacing.half, - paddingHorizontal: Spacing.two, - }, -}); diff --git a/src/components/themed-text.tsx b/src/components/themed-text.tsx deleted file mode 100644 index d7310fb..0000000 --- a/src/components/themed-text.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import { Platform, StyleSheet, Text, type TextProps } from "react-native"; - -import { Fonts, type ThemeColor } from "@/constants/theme"; -import { useTheme } from "@/hooks/use-theme"; - -export type ThemedTextProps = TextProps & { - type?: "default" | "title" | "small" | "smallBold" | "subtitle" | "link" | "linkPrimary" | "code"; - themeColor?: ThemeColor; -}; - -export function ThemedText({ style, type = "default", themeColor, ...rest }: ThemedTextProps) { - const theme = useTheme(); - - return ( - - ); -} - -const styles = StyleSheet.create({ - small: { - fontSize: 14, - lineHeight: 20, - fontWeight: 500, - }, - smallBold: { - fontSize: 14, - lineHeight: 20, - fontWeight: 700, - }, - default: { - fontSize: 16, - lineHeight: 24, - fontWeight: 500, - }, - title: { - fontSize: 48, - fontWeight: 600, - lineHeight: 52, - }, - subtitle: { - fontSize: 32, - lineHeight: 44, - fontWeight: 600, - }, - link: { - lineHeight: 30, - fontSize: 14, - }, - linkPrimary: { - lineHeight: 30, - fontSize: 14, - color: "#3c87f7", - }, - code: { - fontFamily: Fonts.mono, - fontWeight: Platform.select({ android: 700 }) ?? 500, - fontSize: 12, - }, -}); diff --git a/src/components/themed-view.tsx b/src/components/themed-view.tsx deleted file mode 100644 index 990bb54..0000000 --- a/src/components/themed-view.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { View, type ViewProps } from "react-native"; - -import type { ThemeColor } from "@/constants/theme"; -import { useTheme } from "@/hooks/use-theme"; - -export type ThemedViewProps = ViewProps & { - lightColor?: string; - darkColor?: string; - type?: ThemeColor; -}; - -export function ThemedView({ style, lightColor, darkColor, type, ...otherProps }: ThemedViewProps) { - const theme = useTheme(); - - return ; -} diff --git a/src/components/ui/collapsible.tsx b/src/components/ui/collapsible.tsx deleted file mode 100644 index 76dc278..0000000 --- a/src/components/ui/collapsible.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import { SymbolView } from "expo-symbols"; -import { type PropsWithChildren, useState } from "react"; -import { Pressable, StyleSheet } from "react-native"; -import Animated, { FadeIn } from "react-native-reanimated"; - -import { ThemedText } from "@/components/themed-text"; -import { ThemedView } from "@/components/themed-view"; -import { Spacing } from "@/constants/theme"; -import { useTheme } from "@/hooks/use-theme"; - -export function Collapsible({ children, title }: PropsWithChildren & { title: string }) { - const [isOpen, setIsOpen] = useState(false); - const theme = useTheme(); - - return ( - - [styles.heading, pressed && styles.pressedHeading]} - onPress={() => setIsOpen((value) => !value)} - > - - - - - {title} - - {isOpen && ( - - - {children} - - - )} - - ); -} - -const styles = StyleSheet.create({ - heading: { - flexDirection: "row", - alignItems: "center", - gap: Spacing.two, - }, - pressedHeading: { - opacity: 0.7, - }, - button: { - width: Spacing.four, - height: Spacing.four, - borderRadius: 12, - justifyContent: "center", - alignItems: "center", - }, - content: { - marginTop: Spacing.three, - borderRadius: Spacing.three, - marginLeft: Spacing.four, - padding: Spacing.four, - }, -}); diff --git a/src/components/web-badge.tsx b/src/components/web-badge.tsx deleted file mode 100644 index 241ec8b..0000000 --- a/src/components/web-badge.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import { version } from "expo/package.json"; -import { Image } from "expo-image"; -import { StyleSheet, useColorScheme } from "react-native"; -import { Spacing } from "@/constants/theme"; -import { ThemedText } from "./themed-text"; -import { ThemedView } from "./themed-view"; - -export function WebBadge() { - const scheme = useColorScheme(); - - return ( - - - v{version} - - - - ); -} - -const styles = StyleSheet.create({ - container: { - padding: Spacing.five, - alignItems: "center", - gap: Spacing.two, - }, - versionText: { - textAlign: "center", - }, - badgeImage: { - width: 123, - aspectRatio: 123 / 24, - }, -}); diff --git a/src/constants/config.ts b/src/constants/config.ts new file mode 100644 index 0000000..91969f3 --- /dev/null +++ b/src/constants/config.ts @@ -0,0 +1,8 @@ +/** + * 웹뷰로 띄울 stream-client-web 주소. + * + * 개발 중에는 실행 환경(시뮬레이터·에뮬레이터·실제 기기)마다 가리켜야 하는 주소가 달라 + * 커밋에 박을 수 없으므로 `.env.local`의 `EXPO_PUBLIC_WEB_URL`로 주입한다. + * 환경별 주소는 `docs/local-development.md` 참고. + */ +export const WEB_URL = process.env.EXPO_PUBLIC_WEB_URL ?? ""; diff --git a/src/constants/theme.ts b/src/constants/theme.ts deleted file mode 100644 index 2a76d02..0000000 --- a/src/constants/theme.ts +++ /dev/null @@ -1,65 +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. - */ - -import "@/global.css"; - -import { Platform } from "react-native"; - -export const Colors = { - light: { - text: "#000000", - background: "#ffffff", - backgroundElement: "#F0F0F3", - backgroundSelected: "#E0E1E6", - textSecondary: "#60646C", - }, - dark: { - text: "#ffffff", - background: "#000000", - backgroundElement: "#212225", - backgroundSelected: "#2E3135", - textSecondary: "#B0B4BA", - }, -} as const; - -export type ThemeColor = keyof typeof Colors.light & keyof typeof Colors.dark; - -export const Fonts = Platform.select({ - ios: { - /** iOS `UIFontDescriptorSystemDesignDefault` */ - sans: "system-ui", - /** iOS `UIFontDescriptorSystemDesignSerif` */ - serif: "ui-serif", - /** iOS `UIFontDescriptorSystemDesignRounded` */ - rounded: "ui-rounded", - /** iOS `UIFontDescriptorSystemDesignMonospaced` */ - mono: "ui-monospace", - }, - default: { - sans: "normal", - serif: "serif", - rounded: "normal", - mono: "monospace", - }, - web: { - sans: "var(--font-display)", - serif: "var(--font-serif)", - rounded: "var(--font-rounded)", - mono: "var(--font-mono)", - }, -}); - -export const Spacing = { - half: 2, - one: 4, - two: 8, - three: 16, - four: 24, - five: 32, - six: 64, -} as const; - -export const BottomTabInset = Platform.select({ ios: 50, android: 80 }) ?? 0; -export const MaxContentWidth = 800; diff --git a/src/features/webview/WebViewScreen.tsx b/src/features/webview/WebViewScreen.tsx new file mode 100644 index 0000000..342e7f3 --- /dev/null +++ b/src/features/webview/WebViewScreen.tsx @@ -0,0 +1,122 @@ +import * as WebBrowser from "expo-web-browser"; +import { useCallback, useEffect, useRef, useState } from "react"; +import { ActivityIndicator, BackHandler, Linking, Platform, StyleSheet, View } from "react-native"; +import { useSafeAreaInsets } from "react-native-safe-area-context"; +import { WebView, type WebViewNavigation } from "react-native-webview"; +import type { ShouldStartLoadRequest } from "react-native-webview/lib/WebViewTypes"; +import { WEB_URL } from "@/constants/config"; +import WebViewMessage from "@/features/webview/components/WebViewMessage"; +import { isSameOrigin } from "@/utils/url"; + +// WebView는 서드파티 컴포넌트라 NativeWind의 className이 적용되지 않는다. style로 채운다. +const styles = StyleSheet.create({ + webView: { flex: 1 }, +}); + +export default function WebViewScreen() { + const insets = useSafeAreaInsets(); + const webViewRef = useRef(null); + const [isLoading, setIsLoading] = useState(true); + const [hasError, setHasError] = useState(false); + const [canGoBack, setCanGoBack] = useState(false); + + // 안드로이드 하드웨어 백 버튼은 기본적으로 앱을 종료한다. 웹 히스토리가 남아 있으면 뒤로 보낸다. + useEffect(() => { + if (Platform.OS !== "android") { + return; + } + + const subscription = BackHandler.addEventListener("hardwareBackPress", () => { + if (!canGoBack) { + return false; + } + webViewRef.current?.goBack(); + return true; + }); + + return () => subscription.remove(); + }, [canGoBack]); + + const handleRetry = useCallback(() => { + setHasError(false); + setIsLoading(true); + webViewRef.current?.reload(); + }, []); + + const handleNavigationStateChange = useCallback((navigation: WebViewNavigation) => { + setCanGoBack(navigation.canGoBack); + }, []); + + // 서비스 바깥 주소는 웹뷰 안에서 열지 않고 시스템 브라우저·기본 앱으로 넘긴다. + const handleShouldStartLoad = useCallback((request: ShouldStartLoadRequest) => { + const { url } = request; + + if (url.startsWith("about:") || isSameOrigin(url, WEB_URL)) { + return true; + } + + const open = + url.startsWith("http://") || url.startsWith("https://") + ? WebBrowser.openBrowserAsync(url) + : Linking.openURL(url); + + open.catch((error) => { + console.warn(`외부 링크를 열지 못했습니다: ${url}`, error); + }); + + return false; + }, []); + + if (!WEB_URL) { + return ( + + ); + } + + if (hasError) { + return ( + + ); + } + + return ( + // 세이프에어리어는 네이티브가 담당하고 웹은 주어진 영역을 100%로 채우기만 한다. + // 위아래 스트립은 맞닿는 웹 화면과 같은 색으로 칠해야 경계선이 보이지 않는데, + // 위는 웹 본문 배경 / 아래는 Bottom Nav 배경이라 색이 서로 달라 + // SafeAreaView 하나로는 칠할 수 없다. 인셋을 직접 재서 나눠 칠한다. + + + + setHasError(true)} + // 이미지·스크립트 같은 하위 리소스 실패까지 오류 화면으로 넘기지 않도록 본문 요청만 본다. + onHttpError={({ nativeEvent }) => { + if (nativeEvent.url === WEB_URL) { + setHasError(true); + } + }} + onLoadEnd={() => setIsLoading(false)} + onLoadStart={() => setIsLoading(true)} + onNavigationStateChange={handleNavigationStateChange} + onShouldStartLoadWithRequest={handleShouldStartLoad} + ref={webViewRef} + source={{ uri: WEB_URL }} + style={styles.webView} + /> + {isLoading && ( + + + + )} + + + + ); +} diff --git a/src/features/webview/components/WebViewMessage.tsx b/src/features/webview/components/WebViewMessage.tsx new file mode 100644 index 0000000..eb75797 --- /dev/null +++ b/src/features/webview/components/WebViewMessage.tsx @@ -0,0 +1,30 @@ +import { Pressable, Text, View } from "react-native"; +import { SafeAreaView } from "react-native-safe-area-context"; + +interface WebViewMessageProps { + title: string; + description: string; + /** 있으면 재시도 버튼을 노출한다. */ + onRetry?: () => void; +} + +/** 웹을 띄우지 못했을 때 웹뷰 대신 보여주는 안내 화면. */ +export default function WebViewMessage({ title, description, onRetry }: WebViewMessageProps) { + return ( + + {title} + {description} + {onRetry && ( + + + 다시 시도 + + + )} + + ); +} diff --git a/src/hooks/use-color-scheme.ts b/src/hooks/use-color-scheme.ts deleted file mode 100644 index 93a8fde..0000000 --- a/src/hooks/use-color-scheme.ts +++ /dev/null @@ -1 +0,0 @@ -export { useColorScheme } from "react-native"; diff --git a/src/hooks/use-color-scheme.web.ts b/src/hooks/use-color-scheme.web.ts deleted file mode 100644 index 66cccac..0000000 --- a/src/hooks/use-color-scheme.web.ts +++ /dev/null @@ -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/src/hooks/use-theme.ts b/src/hooks/use-theme.ts deleted file mode 100644 index 4b6210d..0000000 --- a/src/hooks/use-theme.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Learn more about light and dark modes: - * https://docs.expo.dev/guides/color-schemes/ - */ - -import { Colors } from "@/constants/theme"; -import { useColorScheme } from "@/hooks/use-color-scheme"; - -export function useTheme() { - const scheme = useColorScheme(); - const theme = scheme === "unspecified" ? "light" : scheme; - - return Colors[theme]; -} diff --git a/src/utils/url.ts b/src/utils/url.ts new file mode 100644 index 0000000..f8a8f1d --- /dev/null +++ b/src/utils/url.ts @@ -0,0 +1,16 @@ +/** + * URL에서 origin(`scheme://host:port`)만 뽑는다. + * + * RN에 내장된 `URL` 구현은 불완전해 `origin`/`hostname`을 신뢰할 수 없으므로 직접 파싱한다. + * 형식을 알아볼 수 없으면 빈 문자열을 돌려준다. + */ +export function getOrigin(url: string): string { + const matched = url.match(/^[a-zA-Z][\w+.-]*:\/\/[^/?#]*/); + return matched ? matched[0].toLowerCase() : ""; +} + +/** 두 URL이 같은 origin인지 판단한다. 어느 한쪽이라도 파싱되지 않으면 false다. */ +export function isSameOrigin(a: string, b: string): boolean { + const originA = getOrigin(a); + return originA !== "" && originA === getOrigin(b); +} diff --git a/tailwind.config.js b/tailwind.config.js index 407a91f..58480d0 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -3,7 +3,15 @@ module.exports = { content: ["./src/**/*.{js,jsx,ts,tsx}"], presets: [require("nativewind/preset")], theme: { - extend: {}, + extend: { + colors: { + // stream-client-web이 쓰는 WDS 배경 토큰 값을 그대로 옮긴 것. + // 세이프에어리어 스트립을 웹 화면과 같은 색으로 잇는 용도로만 쓴다. + // 웹에서 토큰 값이 바뀌면 여기도 함께 맞춰야 한다. + "web-background-alternative": "#F7F7F8", + "web-background-normal": "#FFFFFF", + }, + }, }, plugins: [], };