Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
754fb08327 | ||
|
|
05fe7bf107 | ||
|
|
fa107895b6 | ||
|
|
d31b0273cf | ||
|
|
2f358c25e8 | ||
|
|
d2eb511e1d | ||
|
|
f62a941bc6 | ||
|
|
d784d14486 | ||
|
|
7b0d0225f9 | ||
|
|
c1d25701ca | ||
|
|
5359120dbd | ||
|
|
ed6b3a0ec0 | ||
|
|
efd3284272 | ||
|
|
46bc48726c | ||
|
|
ff47e492e4 | ||
|
|
be969274b9 | ||
|
|
359c1cfcb5 | ||
|
|
6c3fc5dff7 | ||
|
|
99d3927680 | ||
|
|
55482dae5f | ||
|
|
395599abfa |
No files matched your search
@@ -1 +0,0 @@
|
|||||||
/* @EvanBacon
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
name: 🐛 Expo Config Plugin Bug Report
|
|
||||||
description: Report a reproducible bug with one of the Expo Config Plugins.
|
|
||||||
labels: bug
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: >-
|
|
||||||
Thanks for taking the time to file a bug report! Please fill out this
|
|
||||||
form as completely as possible.
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: >-
|
|
||||||
If you leave out sections there is a high likelihood your issue will be
|
|
||||||
closed. If you have a question, not a bug report, please post it on our
|
|
||||||
[forums](https://forums.expo.dev/) instead.
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Summary
|
|
||||||
description: Describe the issue in 1 or 2 sentences
|
|
||||||
placeholder: >-
|
|
||||||
Clearly describe what the expected behavior is vs. what is actually
|
|
||||||
happening. This should be as short as possible, while still
|
|
||||||
communicating all the necessary information. If your summary is just 'X
|
|
||||||
library/method isn't working', then you need to continue debugging
|
|
||||||
yourself and provide more information.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: dropdown
|
|
||||||
attributes:
|
|
||||||
label: Config Plugin
|
|
||||||
options:
|
|
||||||
- '@config-plugins/android-jsc-intl'
|
|
||||||
- '@config-plugins/apple-settings'
|
|
||||||
- '@config-plugins/detox'
|
|
||||||
- '@config-plugins/ffmpeg-kit-react-native'
|
|
||||||
- '@config-plugins/ios-stickers'
|
|
||||||
- '@config-plugins/react-native-adjust'
|
|
||||||
- '@config-plugins/react-native-blob-util'
|
|
||||||
- '@config-plugins/react-native-branch'
|
|
||||||
- '@config-plugins/react-native-callkeep'
|
|
||||||
- '@config-plugins/react-native-dynamic-app-icon'
|
|
||||||
- '@config-plugins/react-native-pdf'
|
|
||||||
- '@config-plugins/react-native-siri-shortcut'
|
|
||||||
- '@config-plugins/react-native-webrtc'
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: dropdown
|
|
||||||
attributes:
|
|
||||||
label: What platform(s) does this occur on?
|
|
||||||
multiple: true
|
|
||||||
options:
|
|
||||||
- Android
|
|
||||||
- iOS
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: SDK Version
|
|
||||||
description: What version of the Expo SDK are you using?
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Reproducible demo
|
|
||||||
description: >-
|
|
||||||
This should include as little code as possible, do not simply link your
|
|
||||||
entire project. If a reproducible demo is not provided, it is very
|
|
||||||
likely your issue will be closed. Read [here more
|
|
||||||
guidance](https://stackoverflow.com/help/mcve).
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: >-
|
|
||||||
Please make sure contributors can run your code and follow the steps
|
|
||||||
your provided in order to reproduce the bug.
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: >-
|
|
||||||
**Realize that it is up to you to debug your code and be as certain as
|
|
||||||
possible that the bug is with Expo, not with your own app.** [Here's an
|
|
||||||
excellent guide to debugging you can
|
|
||||||
follow](https://gist.github.com/brentvatne/5ac00cba0c70b7a06b89a56787d6bc4a).
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
---
|
|
||||||
blank_issues_enabled: false
|
|
||||||
contact_links:
|
|
||||||
- name: 🤔 Question about an Expo Config Plugin
|
|
||||||
about: >-
|
|
||||||
If you have a question about an Expo Config Plugin, please ask it on the
|
|
||||||
forums, discord, Twitter, or Stack Overflow.
|
|
||||||
url: https://forums.expo.io/
|
|
||||||
- about: E2E tests and automation for mobile
|
|
||||||
name: 📦 detox issues
|
|
||||||
url: https://github.com/wix/Detox/issues
|
|
||||||
- about: FFmpeg Kit for React Native
|
|
||||||
name: 📦 ffmpeg-kit-react-native issues
|
|
||||||
url: https://github.com/arthenica/ffmpeg-kit/issues
|
|
||||||
- about: Adjust React Native SDK
|
|
||||||
name: 📦 react-native-adjust issues
|
|
||||||
url: https://github.com/adjust/react_native_sdk/issues
|
|
||||||
- about: >-
|
|
||||||
A module provides upload, download, and files access API. Supports file
|
|
||||||
stream read/write for process large files.
|
|
||||||
name: 📦 react-native-blob-util issues
|
|
||||||
url: https://github.com/RonRadtke/react-native-blob-util/issues
|
|
||||||
- about: iOS 10 CallKit and Android ConnectionService Framework For React Native
|
|
||||||
name: 📦 react-native-callkeep issues
|
|
||||||
url: https://github.com/react-native-webrtc/react-native-callkeep/issues
|
|
||||||
- about: Programmatically change the app icon in React Native.
|
|
||||||
name: 📦 react-native-dynamic-app-icon issues
|
|
||||||
url: https://github.com/idearockers/react-native-dynamic-app-icon/issues
|
|
||||||
- about: A react native PDF view component, support ios and android platform
|
|
||||||
name: 📦 react-native-pdf issues
|
|
||||||
url: https://github.com/wonday/react-native-pdf/issues
|
|
||||||
- about: A React Native library that enables you to use iOS 12+ Siri Shortcuts.
|
|
||||||
name: 📦 react-native-siri-shortcut issues
|
|
||||||
url: https://github.com/Gustash/react-native-siri-shortcut/issues
|
|
||||||
- about: WebRTC for React Native
|
|
||||||
name: 📦 react-native-webrtc issues
|
|
||||||
url: https://github.com/react-native-webrtc/react-native-webrtc/issues
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
name: "⭐️ Config Plugin Request"
|
|
||||||
description: "Suggest an idea for a new Expo Config Plugin."
|
|
||||||
labels: enhancement
|
|
||||||
body:
|
|
||||||
- type: markdown
|
|
||||||
attributes:
|
|
||||||
value: "This repo is used for publishing out-of-tree plugins that should eventually be upstreamed in a particular package. If the plugin is a utility or used to improve some particular workflow i.e. updates version numbers, than it's probably better suited for a personal repo or utility package."
|
|
||||||
- type: input
|
|
||||||
attributes:
|
|
||||||
label: Library
|
|
||||||
description: What package does this Config Plugin configure.
|
|
||||||
placeholder: URL to a React Native package.
|
|
||||||
validations:
|
|
||||||
required: true
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Summary
|
|
||||||
description: Add some info on why this integration is important.
|
|
||||||
- type: textarea
|
|
||||||
attributes:
|
|
||||||
label: Any existing examples?
|
|
||||||
placeholder: Have you already created a local plugin for the requested library?
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
<!--
|
|
||||||
🚨 We use semantic release (a bot publishes everything automatically)
|
|
||||||
🚨 so please use conventional commit style for the PR title:
|
|
||||||
🚨 https://www.conventionalcommits.org/en/v1.0.0/
|
|
||||||
🚨 Example: feat(detox): add new step
|
|
||||||
-->
|
|
||||||
|
|
||||||
# Why
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Please describe the motivation for this PR, and link to relevant GitHub issues, forums posts, or feature requests.
|
|
||||||
-->
|
|
||||||
|
|
||||||
# How
|
|
||||||
|
|
||||||
<!--
|
|
||||||
How did you build this feature or fix this bug and why?
|
|
||||||
-->
|
|
||||||
|
|
||||||
# Test Plan
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Please describe how you tested this change and how a reviewer could reproduce your test, especially if this PR does not include automated tests! If possible, please also provide terminal output and/or screenshots demonstrating your test/reproduction.
|
|
||||||
-->
|
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
name: E2E Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch: {}
|
||||||
|
push:
|
||||||
|
branches: [master]
|
||||||
|
paths:
|
||||||
|
- .github/workflows/ios.yml
|
||||||
|
- apps/app/**
|
||||||
|
- packages/**
|
||||||
|
- yarn.lock
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- .github/workflows/ios.yml
|
||||||
|
- apps/app/**
|
||||||
|
- packages/**
|
||||||
|
- yarn.lock
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
ios:
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- name: 👀 Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: 🍺 Install required tools
|
||||||
|
run: |
|
||||||
|
brew tap wix/brew
|
||||||
|
brew install applesimutils
|
||||||
|
brew install watchman
|
||||||
|
echo "$(pwd)/bin" >> $GITHUB_PATH
|
||||||
|
- name: 💎 Setup Ruby and install gems
|
||||||
|
uses: ruby/setup-ruby@v1
|
||||||
|
with:
|
||||||
|
bundler-cache: true
|
||||||
|
- name: ♻️ Restore workspace node modules
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: node-modules-cache
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
# See "workspaces" → "packages" in the root package.json for the source of truth of
|
||||||
|
# which node_modules are affected by the root yarn.lock
|
||||||
|
node_modules
|
||||||
|
apps/*/node_modules
|
||||||
|
packages/*/node_modules
|
||||||
|
key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||||
|
- name: 🧶 Install node modules in root dir
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
- name: ♻️ Restore apps/app/ios/Pods from cache
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: pods-cache
|
||||||
|
with:
|
||||||
|
path: "apps/app/ios/Pods"
|
||||||
|
key: ${{ runner.os }}-app-pods-${{ hashFiles('apps/app/ios/Podfile.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-app-pods-
|
||||||
|
- run: yarn global add expo-cli
|
||||||
|
- name: 🔨 Prebuild
|
||||||
|
run: expo prebuild -p ios
|
||||||
|
working-directory: apps/app
|
||||||
|
- name: 🕵️ Debug CocoaPods lockfiles
|
||||||
|
run: git diff Podfile.lock Pods/Manifest.lock
|
||||||
|
working-directory: apps/app/ios
|
||||||
|
continue-on-error: true
|
||||||
|
- name: ⚛️ Display React Native config
|
||||||
|
run: yarn react-native config
|
||||||
|
working-directory: apps/app
|
||||||
|
- name: 🌳 Display pod environment
|
||||||
|
run: pod env
|
||||||
|
working-directory: apps/app/ios
|
||||||
|
- name: 🥥 Install pods in apps/app/ios
|
||||||
|
run: pod install
|
||||||
|
if: steps.pods-cache.outputs.cache-hit != 'true' || hashFiles('apps/app/ios/Podfile.lock') != hashFiles('apps/app/ios/Pods/Manifest.lock')
|
||||||
|
working-directory: apps/app/ios
|
||||||
|
- name: Clean Detox
|
||||||
|
run: yarn detox:clean
|
||||||
|
working-directory: apps/app
|
||||||
|
- name: Build iOS project for Detox
|
||||||
|
run: yarn build:ios-release
|
||||||
|
working-directory: apps/app
|
||||||
|
timeout-minutes: 30
|
||||||
|
- name: Run tests
|
||||||
|
run: yarn test:ios-release
|
||||||
|
working-directory: apps/app
|
||||||
|
- name: Store images of build failures
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: app-artifacts
|
||||||
|
path: apps/app/artifacts
|
||||||
|
|
||||||
|
android:
|
||||||
|
runs-on: macos-latest
|
||||||
|
steps:
|
||||||
|
- name: 👀 Checkout
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
with:
|
||||||
|
submodules: true
|
||||||
|
- name: 🍺 Install required tools
|
||||||
|
run: |
|
||||||
|
brew install watchman
|
||||||
|
echo "$(pwd)/bin" >> $GITHUB_PATH
|
||||||
|
- name: ♻️ Restore workspace node modules
|
||||||
|
uses: actions/cache@v2
|
||||||
|
id: node-modules-cache
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
# See "workspaces" → "packages" in the root package.json for the source of truth of
|
||||||
|
# which node_modules are affected by the root yarn.lock
|
||||||
|
node_modules
|
||||||
|
apps/*/node_modules
|
||||||
|
packages/*/node_modules
|
||||||
|
key: ${{ runner.os }}-modules-${{ hashFiles('yarn.lock') }}
|
||||||
|
- name: 🧶 Install node modules in root dir
|
||||||
|
run: yarn install --frozen-lockfile
|
||||||
|
- uses: actions/cache@v2
|
||||||
|
with:
|
||||||
|
path: ~/.gradle/caches
|
||||||
|
key: ${{ runner.os }}-gradle-v20210625-${{ hashFiles('android/*.gradle*') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gradle-v20210625
|
||||||
|
- run: yarn global add expo-cli
|
||||||
|
- name: 🔨 Prebuild
|
||||||
|
run: expo prebuild -p android
|
||||||
|
working-directory: apps/app
|
||||||
|
- name: ⚛️ Display React Native config
|
||||||
|
run: yarn react-native config
|
||||||
|
working-directory: apps/app
|
||||||
|
- name: Clean Detox
|
||||||
|
run: yarn detox:clean
|
||||||
|
working-directory: apps/app
|
||||||
|
- name: Build Android project for Detox
|
||||||
|
run: yarn build:android-release
|
||||||
|
working-directory: apps/app
|
||||||
|
timeout-minutes: 35
|
||||||
|
env:
|
||||||
|
GRADLE_OPTS: "-Dorg.gradle.internal.http.connectionTimeout=180000 -Dorg.gradle.internal.http.socketTimeout=180000 -Dorg.gradle.internal.network.retry.max.attempts=18 -Dorg.gradle.internal.network.retry.initial.backOff=2000"
|
||||||
|
- name: Run tests
|
||||||
|
uses: reactivecircus/android-emulator-runner@v2
|
||||||
|
with:
|
||||||
|
api-level: 29
|
||||||
|
avd-name: app
|
||||||
|
script: yarn test:android-release
|
||||||
|
working-directory: ./apps/app
|
||||||
|
- name: Store images of build failures
|
||||||
|
if: always()
|
||||||
|
uses: actions/upload-artifact@v2
|
||||||
|
with:
|
||||||
|
name: app-artifacts
|
||||||
|
path: apps/app/artifacts
|
||||||
@@ -2,7 +2,7 @@ name: Check Markdown links
|
|||||||
on:
|
on:
|
||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [master]
|
||||||
paths:
|
paths:
|
||||||
- .github/workflows/lint-md.yml
|
- .github/workflows/lint-md.yml
|
||||||
- "**.md"
|
- "**.md"
|
||||||
@@ -16,5 +16,5 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- run: yarn install --frozen-lockfile --check-files
|
- run: npm install
|
||||||
- run: npx remark --use validate-links . .github
|
- run: npx remark --use validate-links . .github
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
name: Test Packages
|
name: Test Packages
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [master]
|
||||||
pull_request:
|
pull_request:
|
||||||
types: [opened, synchronize]
|
types: [opened, synchronize]
|
||||||
|
|
||||||
@@ -10,7 +10,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node: ["18"]
|
node: ["16", "17"]
|
||||||
name: Build with Node ${{ matrix.node }}
|
name: Build with Node ${{ matrix.node }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -22,7 +22,7 @@ jobs:
|
|||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
- run: yarn install --frozen-lockfile --check-files
|
- run: yarn install --frozen-lockfile --check-files
|
||||||
- run: yarn lerna run prepare --stream
|
- run: yarn lerna run prepare --stream
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: "*"
|
path: "*"
|
||||||
key: v2-${{ github.sha }}-${{ matrix.node }}
|
key: v2-${{ github.sha }}-${{ matrix.node }}
|
||||||
@@ -32,20 +32,20 @@ jobs:
|
|||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
matrix:
|
matrix:
|
||||||
node: ["18"]
|
node: ["16", "17"]
|
||||||
package: [
|
package: [
|
||||||
# Manually add packages here
|
# Manually add packages here
|
||||||
detox,
|
detox,
|
||||||
ffmpeg-kit-react-native,
|
ffmpeg-kit-react-native,
|
||||||
ios-stickers,
|
react-native-ble-plx,
|
||||||
react-native-blob-util,
|
react-native-blob-util,
|
||||||
react-native-branch,
|
react-native-google-cast,
|
||||||
react-native-siri-shortcut,
|
|
||||||
react-native-pdf,
|
react-native-pdf,
|
||||||
|
# react-native-quick-actions,
|
||||||
]
|
]
|
||||||
name: Test ${{ matrix.package }} on Node ${{ matrix.node }}
|
name: Test ${{ matrix.package }} on Node ${{ matrix.node }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/cache@v4
|
- uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
path: "*"
|
path: "*"
|
||||||
key: v2-${{ github.sha }}-${{ matrix.node }}
|
key: v2-${{ github.sha }}-${{ matrix.node }}
|
||||||
@@ -53,12 +53,8 @@ jobs:
|
|||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v1
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node }}
|
node-version: ${{ matrix.node }}
|
||||||
- name: Lint ${{ matrix.package }}
|
|
||||||
run: yarn lint --max-warnings 0
|
|
||||||
working-directory: packages/${{ matrix.package }}
|
|
||||||
- name: Test ${{ matrix.package }}
|
- name: Test ${{ matrix.package }}
|
||||||
run: yarn test
|
run: cd packages/${{ matrix.package }} && yarn test
|
||||||
working-directory: packages/${{ matrix.package }}
|
|
||||||
env:
|
env:
|
||||||
CI: true
|
CI: true
|
||||||
EXPO_DEBUG: true
|
EXPO_DEBUG: true
|
||||||
@@ -51,16 +51,4 @@ xcuserdata
|
|||||||
# VSCode
|
# VSCode
|
||||||
.history/
|
.history/
|
||||||
|
|
||||||
# Ignore build folders
|
# packages/*/build/
|
||||||
|
|
||||||
packages/*/build/**
|
|
||||||
packages/*/plugin/build/**
|
|
||||||
packages/@*/*/build/**
|
|
||||||
|
|
||||||
|
|
||||||
# App native folders
|
|
||||||
|
|
||||||
apps/*/ios/*
|
|
||||||
apps/*/android/*
|
|
||||||
apps/*/web-build/*
|
|
||||||
apps/*/dist/*
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2.7.0
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
# Contributing to Expo Config Plugins
|
|
||||||
|
|
||||||
## Submitting a pull request
|
|
||||||
|
|
||||||
To submit a pull request:
|
|
||||||
|
|
||||||
1. Fork the [repository](https://github.com/expo/config-plugins) and create a feature branch. (Existing contributors can create feature branches without forking. Prefix the branch name with `@your-github-username/`.)
|
|
||||||
2. Run `yarn` in the root directory to generate the build files.
|
|
||||||
3. Use [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) including in the PR title.
|
|
||||||
4. Fill out the entire PR template. Ensure your code is tested locally and has continuous tests in CI.
|
|
||||||
5. Make sure all tests pass on GitHub Actions.
|
|
||||||
6. Wait for a review and adjust the code if necessary.
|
|
||||||
|
|
||||||
## Creating a plugin
|
|
||||||
|
|
||||||
- Plugins can be generated by running `yarn gen`.
|
|
||||||
- Add plugins to the example in [`apps/app`](/apps/app/package.json) to test them.
|
|
||||||
- The example app should always stay in a state of regeneration.
|
|
||||||
|
|
||||||
Be sure to run the following scripts in the **root directory** to update automation tools:
|
|
||||||
|
|
||||||
- Update dependabot: `yarn update-dependabot-config`
|
|
||||||
- Update the issue template: `yarn update-issue-template`
|
|
||||||
|
|
||||||
## Publishing a release
|
|
||||||
|
|
||||||
All publishing should be handled automatically whenever code is merged to `main`.
|
|
||||||
@@ -1,69 +1,10 @@
|
|||||||
# Expo Config Plugins
|
# Expo Config Plugins
|
||||||
|
|
||||||
- A collection of [Expo config plugins](https://docs.expo.dev/guides/config-plugins/) for configuring the `npx expo prebuild` command.
|
A collection of custom WIP config plugins for Expo dev clients. Use at your own discretion.
|
||||||
- This repo is to Expo config plugins as [DefinitelyTyped](https://github.com/DefinitelyTyped/DefinitelyTyped) is to TypeScript.
|
|
||||||
- We prefer packages ship their own Expo config plugin (to ensure versioning is aligned), but if they haven't adopted the system yet, the community can add a package here.
|
|
||||||
- All packages here are community maintained. Feel free to improve docs, packages, tests, etc.
|
|
||||||
- We maintain a 1-1 mapping of **native packages** ⇔ `@config-plugins/*`. This means there should be no general utility packages here -- those are better suited for a different repo / NPM namespace.
|
|
||||||
|
|
||||||
## First-party plugins
|
|
||||||
|
|
||||||
Here is a list of known packages that have a built-in Config Plugin.
|
## Creating a plugin
|
||||||
|
|
||||||
> Not all packages need a config plugin, packages that don't appear here might still work with managed EAS.
|
- Plugins can be generated by running `yarn gen`.
|
||||||
|
- Add plugins to the example in [`apps/app`](/apps/app/package.json) to test them.
|
||||||
- [React Native Firebase](https://rnfirebase.io/) (`@react-native-firebase/perf`, `@react-native-firebase/app`, `@react-native-firebase/crashlytics`)
|
- The example app should always stay in a state of regeneration.
|
||||||
- [react-native-google-cast](https://github.com/react-native-google-cast/react-native-google-cast)
|
|
||||||
- [react-native-nfc-manager](https://github.com/revtel/react-native-nfc-manager)
|
|
||||||
- [react-native-health](https://github.com/agencyenterprise/react-native-health)
|
|
||||||
- [@react-native-mapbox-gl/maps](https://github.com/rnmapbox/maps)
|
|
||||||
- [@logrocket/react-native](https://www.npmjs.com/package/@logrocket/react-native)
|
|
||||||
- [sentry-expo](https://www.npmjs.com/package/sentry-expo)
|
|
||||||
- [@leanplum/react-native-sdk](https://github.com/Leanplum/Leanplum-ReactNative-SDK#readme)
|
|
||||||
- [react-native-fbsdk-next](https://www.npmjs.com/package/react-native-fbsdk-next)
|
|
||||||
- [@stripe/stripe-react-native](https://www.npmjs.com/package/@stripe/stripe-react-native)
|
|
||||||
- [@react-native-voice/voice](https://www.npmjs.com/package/@react-native-voice/voice)
|
|
||||||
- [react-native-imglysdk](https://www.npmjs.com/package/react-native-imglysdk)
|
|
||||||
- [@viro-community/react-viro](https://github.com/virocommunity/viro) -- [Guide](https://viro-community.readme.io/docs/integrating-with-expo)
|
|
||||||
- [react-native-msal](https://www.npmjs.com/package/react-native-msal)
|
|
||||||
- [react-native-spotlight-search](https://www.npmjs.com/package/react-native-spotlight-search)
|
|
||||||
- [newrelic-react-native-agent](https://www.npmjs.com/package/newrelic-react-native-agent)
|
|
||||||
- [react-native-videoeditorsdk](https://www.npmjs.com/package/react-native-videoeditorsdk)
|
|
||||||
- [notifee](https://notifee.app/)
|
|
||||||
- [App Clips](https://github.com/bndkt/react-native-app-clip/)
|
|
||||||
- [iOS Safari Extensions](https://github.com/andrew-levy/react-native-safari-extension/)
|
|
||||||
- [react-native-ble-plx](https://github.com/dotintent/react-native-ble-plx)
|
|
||||||
|
|
||||||
### Complementary
|
|
||||||
|
|
||||||
More out-of-tree plugins which can be used to configure more packages.
|
|
||||||
|
|
||||||
- [onesignal-expo-plugin](https://github.com/OneSignal/onesignal-expo-plugin) (for use with `react-native-onesignal`)
|
|
||||||
- Configures **iOS Notification Service Extensions** for rich push notification and custom notification actions.
|
|
||||||
- [@lyrahealth-inc/react-native-orientation-plugin](https://www.npmjs.com/package/@lyrahealth-inc/react-native-orientation-plugin) (for use with `react-native-orientation-plugin`)
|
|
||||||
- [airship-expo-plugin](https://www.npmjs.com/package/airship-expo-plugin) (for use with `urbanairship-react-native`)
|
|
||||||
- [config-plugin-react-native-intercom](https://www.npmjs.com/package/config-plugin-react-native-intercom) (for use with `intercom-react-native`)
|
|
||||||
- [@allboatsrise/expo-marketingcloudsdk](https://www.npmjs.com/package/@allboatsrise/expo-marketingcloudsdk) (for use with `react-native-marketingcloudsdk`)
|
|
||||||
- [@ouvio/react-native-background-location-plugin](https://www.npmjs.com/package/@ouvio/react-native-background-location-plugin) (for use with `react-native-background-location-plugin`)
|
|
||||||
- [expo-community-flipper](https://www.npmjs.com/package/expo-community-flipper) (for use with Flipper)
|
|
||||||
- [expo-appcenter](https://www.npmjs.com/package/expo-appcenter) (for use with `appcenter`)
|
|
||||||
- [expo-react-native-freshchat](https://www.npmjs.com/package/expo-react-native-freshchat) (for use with `react-native-freshchat-sdk`)
|
|
||||||
- [react-native-keyevent-expo-config-plugin](https://github.com/chronsyn/react-native-keyevent-expo-config-plugin) (for use with `react-native-keyevent`)
|
|
||||||
- [watermelondb-expo-plugin](https://github.com/morrowdigital/watermelondb-expo-plugin) (for use with `@nozbe/watermelondb`)
|
|
||||||
|
|
||||||
### No Plugin Required
|
|
||||||
|
|
||||||
Just install and rebuild! If a package doesn't require any futher setup then it most likely doesn't need an Expo config plugin. Most packages work without a config plugin (including packages not listed below):
|
|
||||||
|
|
||||||
- [react-native-share](https://github.com/react-native-share/react-native-share)
|
|
||||||
- [@react-native-menu/menu](https://github.com/react-native-menu/menu)
|
|
||||||
- [react-native-blurhash](https://github.com/mrousavy/react-native-blurhash)
|
|
||||||
- [react-native-app-shortcuts](https://github.com/lokyoung/react-native-app-shortcuts)
|
|
||||||
- [react-native-multiple-image-picker](https://github.com/baronha/react-native-multiple-image-picker)
|
|
||||||
- [`@shopify/flash-list`](https://github.com/Shopify/flash-list)
|
|
||||||
|
|
||||||
> Feel free to [open a PR](https://github.com/expo/config-plugins/edit/main/README.md) with missing packages.
|
|
||||||
|
|
||||||
## Contributing!
|
|
||||||
|
|
||||||
See the [contributing guide](/CONTRIBUTING.md).
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"12bb71342c6255bbf50437ec8f4441c083f47cdb74bd89160c15e4f43e52a1cb": true,
|
||||||
|
"40b842e832070c58deac6aa9e08fa459302ee3f9da492c7e77d93d2fbf4a56fd": true
|
||||||
|
}
|
||||||
@@ -1,2 +1,80 @@
|
|||||||
|
node_modules/**/*
|
||||||
|
.expo/*
|
||||||
|
npm-debug.*
|
||||||
|
*.jks
|
||||||
|
*.p8
|
||||||
|
*.p12
|
||||||
|
*.key
|
||||||
|
*.mobileprovision
|
||||||
|
*.orig.*
|
||||||
|
web-build/
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
|
||||||
|
/artifacts
|
||||||
|
|
||||||
/scripts/start.command
|
/scripts/start.command
|
||||||
/artifacts
|
/ios
|
||||||
|
/android
|
||||||
|
# @generated expo-cli sync-e7dcf75f4e856f7b6f3239b3f3a7dd614ee755a8
|
||||||
|
# The following patterns were generated by expo-cli
|
||||||
|
|
||||||
|
# OSX
|
||||||
|
#
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Xcode
|
||||||
|
#
|
||||||
|
build/
|
||||||
|
*.pbxuser
|
||||||
|
!default.pbxuser
|
||||||
|
*.mode1v3
|
||||||
|
!default.mode1v3
|
||||||
|
*.mode2v3
|
||||||
|
!default.mode2v3
|
||||||
|
*.perspectivev3
|
||||||
|
!default.perspectivev3
|
||||||
|
xcuserdata
|
||||||
|
*.xccheckout
|
||||||
|
*.moved-aside
|
||||||
|
DerivedData
|
||||||
|
*.hmap
|
||||||
|
*.ipa
|
||||||
|
*.xcuserstate
|
||||||
|
project.xcworkspace
|
||||||
|
|
||||||
|
# Android/IntelliJ
|
||||||
|
#
|
||||||
|
build/
|
||||||
|
.idea
|
||||||
|
.gradle
|
||||||
|
local.properties
|
||||||
|
*.iml
|
||||||
|
*.hprof
|
||||||
|
|
||||||
|
# node.js
|
||||||
|
#
|
||||||
|
node_modules/
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
# BUCK
|
||||||
|
buck-out/
|
||||||
|
\.buckd/
|
||||||
|
*.keystore
|
||||||
|
!debug.keystore
|
||||||
|
|
||||||
|
# Bundle artifacts
|
||||||
|
*.jsbundle
|
||||||
|
|
||||||
|
# CocoaPods
|
||||||
|
/ios/Pods/
|
||||||
|
|
||||||
|
# Expo
|
||||||
|
.expo/
|
||||||
|
web-build/
|
||||||
|
dist/
|
||||||
|
|
||||||
|
# @end expo-cli
|
||||||
@@ -1,3 +1,8 @@
|
|||||||
export { default } from "./src/detox/App";
|
// export { default } from './src/react-native-quick-actions/App';
|
||||||
// export { default } from "./src/android-jsc-intl/App";
|
// export { default } from './src/react-native-dynamic-app-icon/App';
|
||||||
// export { default } from "./src/react-native-adjust/App";
|
// export { default } from './src/react-native-google-cast/App';
|
||||||
|
export { default } from './src/detox/App';
|
||||||
|
// export { default } from './src/react-native-ble-plx/App';
|
||||||
|
// export { default } from './src/react-native-webrtc/App';
|
||||||
|
// export { default } from './src/android-jsc-intl/App';
|
||||||
|
// export { default } from './src/ffmpeg-kit-react-native/App';
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"expo": {
|
"expo": {
|
||||||
"name": "detoxapp",
|
"name": "app",
|
||||||
"slug": "app",
|
"slug": "app",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"orientation": "portrait",
|
"orientation": "portrait",
|
||||||
"icon": "https://github.com/expo.png",
|
"icon": "./assets/icons/winter.png",
|
||||||
"splash": {
|
"splash": {
|
||||||
"image": "./assets/splash.png",
|
"image": "./assets/splash.png",
|
||||||
"resizeMode": "contain",
|
"resizeMode": "contain",
|
||||||
@@ -13,10 +13,12 @@
|
|||||||
"updates": {
|
"updates": {
|
||||||
"fallbackToCacheTimeout": 0
|
"fallbackToCacheTimeout": 0
|
||||||
},
|
},
|
||||||
"assetBundlePatterns": ["**/*"],
|
"assetBundlePatterns": [
|
||||||
|
"**/*"
|
||||||
|
],
|
||||||
"ios": {
|
"ios": {
|
||||||
"supportsTablet": true,
|
"supportsTablet": true,
|
||||||
"bundleIdentifier": "dev.bacon.app"
|
"bundleIdentifier": "com.bacon.app"
|
||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
"adaptiveIcon": {
|
"adaptiveIcon": {
|
||||||
@@ -29,11 +31,7 @@
|
|||||||
"favicon": "./assets/favicon.png"
|
"favicon": "./assets/favicon.png"
|
||||||
},
|
},
|
||||||
"plugins": [
|
"plugins": [
|
||||||
"@config-plugins/detox",
|
"@config-plugins/detox"
|
||||||
"@config-plugins/react-native-adjust",
|
|
||||||
"@config-plugins/react-native-callkeep",
|
|
||||||
"@config-plugins/android-jsc-intl",
|
|
||||||
"expo-localization"
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 83 KiB After Width: | Height: | Size: 83 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 145 KiB After Width: | Height: | Size: 145 KiB |
|
Before Width: | Height: | Size: 130 KiB After Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 230 KiB After Width: | Height: | Size: 230 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 133 KiB After Width: | Height: | Size: 133 KiB |
|
Before Width: | Height: | Size: 177 KiB After Width: | Height: | Size: 177 KiB |
|
Before Width: | Height: | Size: 179 KiB After Width: | Height: | Size: 179 KiB |
|
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 157 KiB |
|
Before Width: | Height: | Size: 182 KiB After Width: | Height: | Size: 182 KiB |
|
Before Width: | Height: | Size: 164 KiB After Width: | Height: | Size: 164 KiB |
|
Before Width: | Height: | Size: 176 KiB After Width: | Height: | Size: 176 KiB |
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = function(api) {
|
||||||
|
api.cache(true);
|
||||||
|
return {
|
||||||
|
presets: ['babel-preset-expo'],
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -1,21 +1,13 @@
|
|||||||
const iosName = "detoxapp";
|
const iosName = "app";
|
||||||
const simulator = "iPhone 16";
|
const emulator = process.env.CI ? "app" : "Pixel_3a_API_30";
|
||||||
|
const simulator = "iPhone 11";
|
||||||
const derivedDataPath = "ios/build";
|
const derivedDataPath = "ios/build";
|
||||||
const sdk = "iphonesimulator";
|
const sdk = "iphonesimulator";
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
testRunner: {
|
testRunner: "jest",
|
||||||
$0: "jest",
|
runnerConfig: require.resolve("./e2e/jest.config.js"),
|
||||||
args: {
|
specs: "e2e",
|
||||||
config: require.resolve("./e2e/jest.config.js"),
|
|
||||||
_: ["e2e"],
|
|
||||||
},
|
|
||||||
jest: {
|
|
||||||
setupTimeout: 300000,
|
|
||||||
reportSpecs: true,
|
|
||||||
reportWorkerAssign: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
behavior: {
|
behavior: {
|
||||||
init: {
|
init: {
|
||||||
exposeGlobals: false,
|
exposeGlobals: false,
|
||||||
@@ -55,7 +47,7 @@ module.exports = {
|
|||||||
emulator: {
|
emulator: {
|
||||||
type: "android.emulator",
|
type: "android.emulator",
|
||||||
device: {
|
device: {
|
||||||
avdName: "Pixel_6_Pro_API_33",
|
avdName: emulator,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { by, device, element, expect } from "detox";
|
import { device } from "detox";
|
||||||
import * as path from "path";
|
import * as path from "path";
|
||||||
|
|
||||||
import { createCapture } from "../lib";
|
import { createCapture } from "../lib";
|
||||||
@@ -6,7 +6,10 @@ import { createCapture } from "../lib";
|
|||||||
const projectRoot = path.resolve(__dirname, "../../");
|
const projectRoot = path.resolve(__dirname, "../../");
|
||||||
|
|
||||||
// https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/LanguageandLocaleIDs/LanguageandLocaleIDs.html
|
// https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPInternational/LanguageandLocaleIDs/LanguageandLocaleIDs.html
|
||||||
const languages = ["en-US", "nl-NL", "zh-CN"];
|
const languages = [
|
||||||
|
"en-US",
|
||||||
|
// "nl-NL", "zh-CN"
|
||||||
|
];
|
||||||
|
|
||||||
for (const locale of languages) {
|
for (const locale of languages) {
|
||||||
const { captureAsync, resetAppAsync } = createCapture({
|
const { captureAsync, resetAppAsync } = createCapture({
|
||||||
@@ -21,18 +24,8 @@ for (const locale of languages) {
|
|||||||
await device.reloadReactNative();
|
await device.reloadReactNative();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should have welcome screen", async () => {
|
it("should load app", async () => {
|
||||||
await expect(element(by.id("welcome"))).toBeVisible();
|
// await expect(element(by.id("welcome"))).toBeVisible();
|
||||||
await captureAsync();
|
|
||||||
});
|
|
||||||
|
|
||||||
xit("should show hello screen after tap", async () => {
|
|
||||||
await element(by.id("hello_button")).tap();
|
|
||||||
await captureAsync();
|
|
||||||
});
|
|
||||||
|
|
||||||
xit("should show world screen after tap", async () => {
|
|
||||||
await element(by.id("world_button")).tap();
|
|
||||||
await captureAsync();
|
await captureAsync();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
const {
|
||||||
|
DetoxCircusEnvironment,
|
||||||
|
SpecReporter,
|
||||||
|
WorkerAssignReporter,
|
||||||
|
} = require("detox/runners/jest-circus");
|
||||||
|
|
||||||
|
class CustomDetoxEnvironment extends DetoxCircusEnvironment {
|
||||||
|
constructor(config, context) {
|
||||||
|
super(config, context);
|
||||||
|
|
||||||
|
// Can be safely removed, if you are content with the default value (=300000ms)
|
||||||
|
this.initTimeout = 300000;
|
||||||
|
|
||||||
|
// This takes care of generating status logs on a per-spec basis. By default, Jest only reports at file-level.
|
||||||
|
// This is strictly optional.
|
||||||
|
this.registerListeners({
|
||||||
|
SpecReporter,
|
||||||
|
WorkerAssignReporter,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CustomDetoxEnvironment;
|
||||||
@@ -1,14 +1,11 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
|
testEnvironment: require.resolve("./environment.js"),
|
||||||
testRunner: require.resolve("jest-circus/runner"),
|
testRunner: require.resolve("jest-circus/runner"),
|
||||||
testTimeout: 120000,
|
testTimeout: 120000,
|
||||||
rootDir: "..",
|
testRegex: "/__tests__/.*(e2e)\\.[jt]sx?$",
|
||||||
testMatch: ["<rootDir>/**/__tests__/*"],
|
|
||||||
transform: {
|
transform: {
|
||||||
"^.+\\.[jt]sx?$": "ts-jest",
|
"^.+\\.[jt]sx?$": "ts-jest",
|
||||||
},
|
},
|
||||||
reporters: [require.resolve("detox/runners/jest/reporter")],
|
reporters: [require.resolve("detox/runners/jest/streamlineReporter")],
|
||||||
globalSetup: "detox/runners/jest/globalSetup",
|
|
||||||
globalTeardown: "detox/runners/jest/globalTeardown",
|
|
||||||
testEnvironment: "detox/runners/jest/testEnvironment",
|
|
||||||
verbose: true,
|
verbose: true,
|
||||||
};
|
};
|
||||||
@@ -4,10 +4,19 @@ import * as path from "path";
|
|||||||
import { setStatusBar } from "./setStatusBar";
|
import { setStatusBar } from "./setStatusBar";
|
||||||
|
|
||||||
export function getOutputDir(projectRoot: string, locale: string) {
|
export function getOutputDir(projectRoot: string, locale: string) {
|
||||||
return path.resolve(
|
const store = device.getPlatform() === "ios" ? "apple" : "google";
|
||||||
projectRoot,
|
return path.resolve(projectRoot, `merch/${store}/preview/${locale}/`);
|
||||||
`meta/screenshots/${device.getPlatform()}/${locale}/`
|
}
|
||||||
);
|
|
||||||
|
function resolveName(str: string) {
|
||||||
|
if (str && device.getPlatform() === "ios" && typeof str === "string") {
|
||||||
|
// Convert a name like: `E9EA2214-6B88-4223-9872-8232168088FB {"type":"iPhone 12 Pro Max"}`
|
||||||
|
// to `iPhone 12 Pro Max`
|
||||||
|
// Name is important for screenshot sorting
|
||||||
|
const match = str.match(/"type"\s?:\s?"(.*)"}/i)?.[1];
|
||||||
|
return match || str;
|
||||||
|
}
|
||||||
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function takeScreenshotAsync({
|
export async function takeScreenshotAsync({
|
||||||
@@ -27,8 +36,9 @@ export async function takeScreenshotAsync({
|
|||||||
if (index == null) {
|
if (index == null) {
|
||||||
index = fs.readdirSync(outputDir).length;
|
index = fs.readdirSync(outputDir).length;
|
||||||
}
|
}
|
||||||
|
const deviceName = resolveName(device.name);
|
||||||
|
|
||||||
const name = `APP_${device.name}_${index}`.toUpperCase();
|
const name = `APP_${deviceName}-${index}`;
|
||||||
const imgPath = path.resolve(outputDir, `${name}.png`);
|
const imgPath = path.resolve(outputDir, `${name}.png`);
|
||||||
const imagePath = await device.takeScreenshot(name);
|
const imagePath = await device.takeScreenshot(name);
|
||||||
await fs.promises.copyFile(imagePath, imgPath);
|
await fs.promises.copyFile(imagePath, imgPath);
|
||||||
@@ -58,7 +68,7 @@ export function createCapture({
|
|||||||
clearAsync,
|
clearAsync,
|
||||||
async resetAppAsync() {
|
async resetAppAsync() {
|
||||||
// Clear old screen shots
|
// Clear old screen shots
|
||||||
await clearAsync();
|
// await clearAsync();
|
||||||
// configure status bars
|
// configure status bars
|
||||||
|
|
||||||
setStatusBar();
|
setStatusBar();
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import "react-native-gesture-handler";
|
||||||
import { registerRootComponent } from "expo";
|
import { registerRootComponent } from "expo";
|
||||||
|
|
||||||
import Constants from "expo-constants";
|
import Constants from "expo-constants";
|
||||||
|
|||||||
|
After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 84 KiB |
|
After Width: | Height: | Size: 86 KiB |
@@ -0,0 +1,6 @@
|
|||||||
|
// Learn more https://docs.expo.io/guides/customizing-metro
|
||||||
|
const { getDefaultConfig } = require("@expo/metro-config");
|
||||||
|
|
||||||
|
const config = getDefaultConfig(__dirname);
|
||||||
|
|
||||||
|
module.exports = config;
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
"main": "./index.js",
|
"main": "./index.js",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"detox:clean": "detox clean-framework-cache && detox build-framework-cache",
|
||||||
"build:ios": "detox build -c ios.sim.debug",
|
"build:ios": "detox build -c ios.sim.debug",
|
||||||
"test:ios": "detox test -c ios.sim.debug",
|
"test:ios": "detox test -c ios.sim.debug",
|
||||||
"e2e:ios": "npm run build:ios && npm run test:ios",
|
"e2e:ios": "npm run build:ios && npm run test:ios",
|
||||||
@@ -13,7 +14,7 @@
|
|||||||
"test:android": "detox test -c android.emu.debug",
|
"test:android": "detox test -c android.emu.debug",
|
||||||
"e2e:android": "npm run build:android && npm run test:android",
|
"e2e:android": "npm run build:android && npm run test:android",
|
||||||
"build:android-release": "detox build -c android.emu.release",
|
"build:android-release": "detox build -c android.emu.release",
|
||||||
"test:android-release": "detox test -c android.emu.release",
|
"test:android-release": "detox test -c android.emu.release --headless",
|
||||||
"e2e:android-release": "npm run build:android-release && npm run test:android-release",
|
"e2e:android-release": "npm run build:android-release && npm run test:android-release",
|
||||||
"clean:android": "pushd android && ./gradlew clean && popd",
|
"clean:android": "pushd android && ./gradlew clean && popd",
|
||||||
"start": "expo start --dev-client",
|
"start": "expo start --dev-client",
|
||||||
@@ -21,32 +22,38 @@
|
|||||||
"android": "expo run:android"
|
"android": "expo run:android"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@config-plugins/android-jsc-intl": "*",
|
"@config-plugins/detox": "^1.0.0",
|
||||||
"@config-plugins/detox": "*",
|
"expo": "^43.0.0",
|
||||||
"@config-plugins/react-native-adjust": "*",
|
"expo-localization": "~11.0.0",
|
||||||
"@config-plugins/react-native-callkeep": "*",
|
"expo-splash-screen": "~0.13.5",
|
||||||
"detox": "^20.37.0",
|
"expo-status-bar": "~1.1.0",
|
||||||
"expo": "~53",
|
"expo-updates": "~0.10.13",
|
||||||
"expo-localization": "~16.1.5",
|
"ffmpeg-kit-react-native": "^4.5.0",
|
||||||
"expo-splash-screen": "~0.30.7",
|
|
||||||
"fbjs": "^0.8.18",
|
|
||||||
"i18n-js": "^3.8.0",
|
"i18n-js": "^3.8.0",
|
||||||
"luxon": "^1.27.0",
|
"react": "17.0.1",
|
||||||
"react": "19.0.0",
|
"react-native": "0.64.3",
|
||||||
"react-native": "0.79.2",
|
"react-native-reanimated": "~2.2.0",
|
||||||
"react-native-adjust": "^5.1.0",
|
"react-native-safe-area-context": "3.3.2",
|
||||||
"react-native-callkeep": "^4.3.16",
|
"react-native-screens": "~3.8.0",
|
||||||
"react-native-safe-area-context": "5.3.0"
|
"react-dom": "17.0.1",
|
||||||
|
"react-native-gesture-handler": "~1.10.2",
|
||||||
|
"react-native-web": "0.17.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.12.9",
|
||||||
"@babel/plugin-syntax-jsx": "^7.16.0",
|
"@babel/plugin-syntax-jsx": "^7.16.0",
|
||||||
"@types/jest": "^29.4.0",
|
"@expo/metro-config": "~0.3.0",
|
||||||
|
"@expo/xcpretty": "^4.1.0",
|
||||||
|
"@types/jest": "^26.0.19",
|
||||||
"@types/luxon": "^1.27.1",
|
"@types/luxon": "^1.27.1",
|
||||||
"babel-jest": "^29.5.0",
|
"@types/react": "~17.0.21",
|
||||||
"jest": "~29.7.0",
|
"@types/react-native": "~0.64.12",
|
||||||
"jest-circus": "^29.5.0",
|
"babel-jest": "~25.2.6",
|
||||||
"ts-jest": "^29.0.5",
|
"detox": "^19.1.0",
|
||||||
"typescript": "~5.8.3"
|
"jest": "^26.5.0",
|
||||||
|
"jest-circus": "^26.5.2",
|
||||||
|
"ts-jest": "^26.4.4",
|
||||||
|
"typescript": "~4.3.5"
|
||||||
},
|
},
|
||||||
"private": true
|
"private": true
|
||||||
}
|
}
|
||||||
@@ -9,7 +9,7 @@ set -eu
|
|||||||
|
|
||||||
iosName=$1
|
iosName=$1
|
||||||
# Debug or Release
|
# Debug or Release
|
||||||
configuration=${3:-"Debug"}
|
configuration=$2
|
||||||
# YES or NO
|
# YES or NO
|
||||||
UseModernBuildSystem=${3:-"NO"}
|
UseModernBuildSystem=${3:-"NO"}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ else
|
|||||||
commandFile=$(dirname "$0")/start.command
|
commandFile=$(dirname "$0")/start.command
|
||||||
cat > ${commandFile} << EOF
|
cat > ${commandFile} << EOF
|
||||||
cd "\$(dirname "\$0")/.."
|
cd "\$(dirname "\$0")/.."
|
||||||
# Run 'npx expo start --help' to get more parameters
|
# Run 'react-native start --help' to get more parameters
|
||||||
yarn start --port ${port}
|
yarn start --port ${port}
|
||||||
EOF
|
EOF
|
||||||
# execute the file in a new command line window
|
# execute the file in a new command line window
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import { Session } from 'ffmpeg-kit-react-native';
|
||||||
|
import React from 'react';
|
||||||
|
import { ActivityIndicator, SafeAreaView, ScrollView, Text, View } from 'react-native';
|
||||||
|
|
||||||
|
import { useMediaInfo, useResolvedValue } from './useMediaInfo';
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
const session = useMediaInfo('http://techslides.com/demos/sample-videos/small.mp4');
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
return <View style={{
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
}}><ActivityIndicator /><Text>Loading...</Text></View>
|
||||||
|
}
|
||||||
|
|
||||||
|
return <SessionInfo session={session} />
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function SessionInfo(props: { session: Session }) {
|
||||||
|
const { session } = props;
|
||||||
|
const duration = useResolvedValue(session.getDuration.bind(session));
|
||||||
|
const output = useResolvedValue(session.getOutput.bind(session));
|
||||||
|
return (
|
||||||
|
<SafeAreaView
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ScrollView>
|
||||||
|
<Text>Duration: {duration}</Text>
|
||||||
|
<Text>Output: {output}</Text>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { SafeAreaView, Text, StyleSheet } from "react-native";
|
|
||||||
import { Adjust, AdjustConfig } from "react-native-adjust";
|
|
||||||
|
|
||||||
export default function App() {
|
|
||||||
React.useEffect(() => {
|
|
||||||
const adjustConfig = new AdjustConfig(
|
|
||||||
"APP_TOKEN",
|
|
||||||
AdjustConfig.EnvironmentSandbox
|
|
||||||
);
|
|
||||||
Adjust.enable();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SafeAreaView style={styles.container}>
|
|
||||||
<Text>Adjust SDK</Text>
|
|
||||||
</SafeAreaView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
},
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Text, View } from 'react-native';
|
||||||
|
|
||||||
|
import useBluetoothDevices, { useManager, useManagerState } from './useBluetoothDevices';
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
const bleManager = useManager();
|
||||||
|
const state = useManagerState(bleManager);
|
||||||
|
|
||||||
|
console.log('Info:', state);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: 'blue'}}>
|
||||||
|
<Text style={{fontSize: 18, color: 'white'}}>State: {state}</Text>
|
||||||
|
{state === 'PoweredOn' && <DeviceScanner bleManager={bleManager} />}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function DeviceScanner({ bleManager }) {
|
||||||
|
const [devices] = useBluetoothDevices(bleManager);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
console.log(devices);
|
||||||
|
}, [devices]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View>
|
||||||
|
{devices.filter(({name}) => name).map((device, index) => (<Text key={`-${index}`} style={{fontSize: 14, color: 'white'}}>{device.name}</Text>))}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
import { useEffect, useMemo, useRef, useState } from "react";
|
||||||
|
import { BleManager, State, Device } from "react-native-ble-plx";
|
||||||
|
import { uniqBy } from "lodash";
|
||||||
|
|
||||||
|
export function useManager() {
|
||||||
|
return useMemo(() => new BleManager(), []);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useManagerState(bleManager: BleManager): State | null {
|
||||||
|
const [state, setState] = useState<State | null>(null);
|
||||||
|
const isMounted = useMounted();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!bleManager) return;
|
||||||
|
|
||||||
|
bleManager.onStateChange((state) => {
|
||||||
|
if (isMounted.current) {
|
||||||
|
setState(state);
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
}, [bleManager]);
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
function useMounted() {
|
||||||
|
const isMounted = useRef(true);
|
||||||
|
useEffect(() => {
|
||||||
|
isMounted.current = true;
|
||||||
|
return () => {
|
||||||
|
isMounted.current = false;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return isMounted;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useBluetoothDevices(
|
||||||
|
bleManager: BleManager
|
||||||
|
): [Device[], null | string] {
|
||||||
|
const isMoutned = useMounted();
|
||||||
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const [devices, setDevices] = useState<Device[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
bleManager.startDeviceScan(
|
||||||
|
[],
|
||||||
|
{ allowDuplicates: false },
|
||||||
|
(e, scannedDevice) => {
|
||||||
|
if (!isMoutned.current) return;
|
||||||
|
if (e) {
|
||||||
|
setError(e.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!scannedDevice) return;
|
||||||
|
|
||||||
|
setDevices((oldDevices) =>
|
||||||
|
uniqBy([...oldDevices, scannedDevice], "id")
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
bleManager.stopDeviceScan();
|
||||||
|
};
|
||||||
|
}, [bleManager]);
|
||||||
|
|
||||||
|
return [devices, error];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useBluetoothDevices;
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
import Entypo from '@expo/vector-icons/Entypo';
|
||||||
|
import React from 'react';
|
||||||
|
import { Image, SafeAreaView, Text, View } from 'react-native';
|
||||||
|
import AppIcon from 'react-native-dynamic-app-icon';
|
||||||
|
import TouchableBounce from 'react-native/Libraries/Components/Touchable/TouchableBounce';
|
||||||
|
|
||||||
|
function useIconName() {
|
||||||
|
const [icon, _setIcon] = React.useState(null);
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
let isMounted = true;
|
||||||
|
AppIcon.getIconName((result) => {
|
||||||
|
if (isMounted) _setIcon(result.iconName);
|
||||||
|
});
|
||||||
|
return () => (isMounted = false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const setIcon = React.useCallback(
|
||||||
|
(icon) => {
|
||||||
|
AppIcon.setAppIcon(icon);
|
||||||
|
_setIcon(icon || null);
|
||||||
|
},
|
||||||
|
[_setIcon]
|
||||||
|
);
|
||||||
|
return [icon === "default" ? null : icon, setIcon];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const icons = [
|
||||||
|
{ name: "Auto", source: require("../../assets/icons/winter.png"), iconId: null },
|
||||||
|
{ name: "Spring", source: require("../../assets/icons/spring.png"), iconId: '0' },
|
||||||
|
{ name: "Summer", source: require("../../assets/icons/summer.png"), iconId: '1' },
|
||||||
|
{ name: "Autumn", source: require("../../assets/icons/autumn.png"), iconId: '2' },
|
||||||
|
{ name: "Fall", source: require("../../assets/icons/fall.png"), iconId: '3' },
|
||||||
|
{ name: "Solstice", source: require("../../assets/icons/solstice.png"), iconId: '4' },
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
const [_icon, setIcon] = useIconName();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView
|
||||||
|
style={{
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View style={{ width: "100%" }}>
|
||||||
|
{icons.map((icon, index) => {
|
||||||
|
return (
|
||||||
|
<Item
|
||||||
|
onPress={() => {
|
||||||
|
setIcon(icon.iconId);
|
||||||
|
}}
|
||||||
|
isSelected={icon.iconId === _icon}
|
||||||
|
name={icon.name}
|
||||||
|
source={icon.source}
|
||||||
|
key={String(index)}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function Item({ ...props }) {
|
||||||
|
return (
|
||||||
|
<TouchableBounce
|
||||||
|
onPress={props.onPress}
|
||||||
|
style={{ marginVertical: 8, marginHorizontal: 24 }}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "stretch",
|
||||||
|
shadowColor: "#000",
|
||||||
|
shadowOffset: {
|
||||||
|
width: 0,
|
||||||
|
height: 4,
|
||||||
|
},
|
||||||
|
shadowOpacity: props.isSelected ? 0.35 : 0.2,
|
||||||
|
shadowRadius: 8.46,
|
||||||
|
|
||||||
|
backgroundColor: props.isSelected ? "#000" : "#F1F1F1",
|
||||||
|
elevation: 9,
|
||||||
|
paddingHorizontal: 12,
|
||||||
|
paddingVertical: 12,
|
||||||
|
borderRadius: 20,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
flex: 1,
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: "row",
|
||||||
|
flex: 1,
|
||||||
|
alignItems: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
source={props.source}
|
||||||
|
style={{
|
||||||
|
width: 64,
|
||||||
|
aspectRatio: 1,
|
||||||
|
resizeMode: "cover",
|
||||||
|
borderRadius: 16,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Text
|
||||||
|
style={{
|
||||||
|
marginLeft: 20,
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: "700",
|
||||||
|
color: props.isSelected ? "white" : "black",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.name}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
{props.isSelected && (
|
||||||
|
<Entypo
|
||||||
|
style={{ marginRight: 8 }}
|
||||||
|
name="check"
|
||||||
|
size={20}
|
||||||
|
color="white"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</TouchableBounce>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { View, Text, Button } from "react-native";
|
||||||
|
import GoogleCast, {
|
||||||
|
CastState,
|
||||||
|
CastButton,
|
||||||
|
useCastState,
|
||||||
|
useRemoteMediaClient,
|
||||||
|
} from "react-native-google-cast";
|
||||||
|
|
||||||
|
export default function HomeScreen() {
|
||||||
|
const castState = useCastState();
|
||||||
|
const client = useRemoteMediaClient();
|
||||||
|
|
||||||
|
GoogleCast.showIntroductoryOverlay();
|
||||||
|
|
||||||
|
const isConnected = castState === CastState.CONNECTED;
|
||||||
|
|
||||||
|
function cast() {
|
||||||
|
client
|
||||||
|
.loadMedia({
|
||||||
|
mediaInfo: {
|
||||||
|
contentUrl:
|
||||||
|
"https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/mp4/BigBuckBunny.mp4",
|
||||||
|
contentType: "video/mp4",
|
||||||
|
metadata: {
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url:
|
||||||
|
"https://commondatastorage.googleapis.com/gtv-videos-bucket/CastVideos/images/480x270/BigBuckBunny.jpg",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
title: "Big Buck Bunny",
|
||||||
|
subtitle:
|
||||||
|
"A large and lovable rabbit deals with three tiny bullies, led by a flying squirrel, who are determined to squelch his happiness.",
|
||||||
|
studio: "Blender Foundation",
|
||||||
|
type: "movie",
|
||||||
|
},
|
||||||
|
streamDuration: 596, // seconds
|
||||||
|
},
|
||||||
|
startTime: 10, // seconds
|
||||||
|
})
|
||||||
|
.then(console.log)
|
||||||
|
.catch(console.warn);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
|
||||||
|
<Text>State: {castState}</Text>
|
||||||
|
<CastButton style={{ width: 24, height: 24 }} />
|
||||||
|
<Button
|
||||||
|
title="Overlay"
|
||||||
|
onPress={() => {
|
||||||
|
GoogleCast.showIntroductoryOverlay();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
title="Expanded Controls"
|
||||||
|
onPress={() => {
|
||||||
|
GoogleCast.showExpandedControls();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
title="Cast"
|
||||||
|
disabled={!isConnected}
|
||||||
|
onPress={() => {
|
||||||
|
cast();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
title="Pause"
|
||||||
|
disabled={!isConnected}
|
||||||
|
onPress={() => {
|
||||||
|
// cast()
|
||||||
|
client.pause();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
title="Disconnect"
|
||||||
|
disabled={!isConnected}
|
||||||
|
onPress={() => {
|
||||||
|
GoogleCast.sessionManager.endCurrentSession(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { SafeAreaView, StyleSheet, useWindowDimensions } from 'react-native';
|
||||||
|
import Pdf from 'react-native-pdf';
|
||||||
|
|
||||||
|
const source = {
|
||||||
|
uri: "https://samples.leanpub.com/thereactnativebook-sample.pdf",
|
||||||
|
cache: true,
|
||||||
|
};
|
||||||
|
export default function App() {
|
||||||
|
const { width, height } = useWindowDimensions()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={styles.container}>
|
||||||
|
<Pdf
|
||||||
|
source={source}
|
||||||
|
onLoadComplete={(numberOfPages, filePath) => {
|
||||||
|
console.log(`Number of pages: ${numberOfPages}`);
|
||||||
|
}}
|
||||||
|
onPageChanged={(page, numberOfPages) => {
|
||||||
|
console.log(`Current page: ${page}`);
|
||||||
|
}}
|
||||||
|
onError={(error) => {
|
||||||
|
console.log(error);
|
||||||
|
}}
|
||||||
|
onPressLink={(uri) => {
|
||||||
|
console.log(`Link pressed: ${uri}`);
|
||||||
|
}}
|
||||||
|
style={{ flex: 1, width, height }}
|
||||||
|
/>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: "flex-start",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
|
||||||
|
});
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import QuickActions from "react-native-quick-actions";
|
||||||
|
import React from "react";
|
||||||
|
import { Text, View } from "react-native";
|
||||||
|
|
||||||
|
import { DeviceEventEmitter } from "react-native";
|
||||||
|
|
||||||
|
function useQuickAction(): QuickActions.ShortcutItem | null {
|
||||||
|
const [item, setItem] = React.useState<QuickActions.ShortcutItem | null>(null);
|
||||||
|
const isMounted = React.useRef(true)
|
||||||
|
React.useEffect(() => {
|
||||||
|
QuickActions.popInitialAction().then(action => {
|
||||||
|
if (isMounted.current) setItem(action);
|
||||||
|
})
|
||||||
|
const sub = DeviceEventEmitter.addListener("quickActionShortcut", data => {
|
||||||
|
if (isMounted.current) {
|
||||||
|
setItem(data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return () => {
|
||||||
|
isMounted.current = false;
|
||||||
|
sub.remove();
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const action = useQuickAction();
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
QuickActions.setShortcutItems([
|
||||||
|
{
|
||||||
|
title: 'Play Song',
|
||||||
|
subtitle: 'NDA - Billie Eilish',
|
||||||
|
icon: 'Play',
|
||||||
|
type: 'alpha',
|
||||||
|
} as any,
|
||||||
|
{
|
||||||
|
title: 'Shuffle',
|
||||||
|
icon: 'Shuffle',
|
||||||
|
type: 'beta',
|
||||||
|
} as any,
|
||||||
|
{
|
||||||
|
title: 'Like Song',
|
||||||
|
icon: 'Love',
|
||||||
|
type: 'gamma',
|
||||||
|
} as any,
|
||||||
|
])
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
|
||||||
|
<Text>Quick Actions</Text>
|
||||||
|
{action && <Text>{JSON.stringify(action, null, 2)}</Text>}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
export default App;
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
|
||||||
|
import React, { useState } from 'react';
|
||||||
|
import { Button, SafeAreaView, StatusBar, StyleSheet, View } from 'react-native';
|
||||||
|
import { mediaDevices, RTCView } from 'react-native-webrtc';
|
||||||
|
|
||||||
|
const App = () => {
|
||||||
|
const [stream, setStream] = useState(null);
|
||||||
|
const start = async () => {
|
||||||
|
console.log('start');
|
||||||
|
if (!stream) {
|
||||||
|
let s;
|
||||||
|
try {
|
||||||
|
s = await mediaDevices.getUserMedia({ video: true });
|
||||||
|
setStream(s);
|
||||||
|
} catch(e) {
|
||||||
|
console.error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const stop = () => {
|
||||||
|
console.log('stop');
|
||||||
|
if (stream) {
|
||||||
|
stream.release();
|
||||||
|
setStream(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<StatusBar barStyle="dark-content" />
|
||||||
|
<SafeAreaView style={styles.body}>
|
||||||
|
{
|
||||||
|
stream &&
|
||||||
|
<RTCView
|
||||||
|
streamURL={stream.toURL()}
|
||||||
|
style={styles.stream} />
|
||||||
|
}
|
||||||
|
<View
|
||||||
|
style={styles.footer}>
|
||||||
|
<Button
|
||||||
|
title = "Start"
|
||||||
|
onPress = {start} />
|
||||||
|
<Button
|
||||||
|
title = "Stop"
|
||||||
|
onPress = {stop} />
|
||||||
|
</View>
|
||||||
|
</SafeAreaView>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
body: {
|
||||||
|
...StyleSheet.absoluteFillObject
|
||||||
|
},
|
||||||
|
stream: {
|
||||||
|
flex: 1
|
||||||
|
},
|
||||||
|
footer: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default App;
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
const { createWebpackConfigAsync } = require("expo-yarn-workspaces/webpack");
|
||||||
|
|
||||||
|
module.exports = createWebpackConfigAsync;
|
||||||
@@ -1,123 +0,0 @@
|
|||||||
import withAppleSettings, {
|
|
||||||
ChildPane,
|
|
||||||
Group,
|
|
||||||
RadioGroup,
|
|
||||||
Slider,
|
|
||||||
Switch,
|
|
||||||
TextField,
|
|
||||||
Title,
|
|
||||||
} from "@config-plugins/apple-settings";
|
|
||||||
import { MultiValue } from "@config-plugins/apple-settings/build/models";
|
|
||||||
import { ConfigContext, ExpoConfig } from "expo/config";
|
|
||||||
import path from "node:path";
|
|
||||||
|
|
||||||
const folderName = path.basename(__dirname);
|
|
||||||
const cleanName = folderName.replace(/[_\s-]/g, "");
|
|
||||||
const appId = "dev.bacon." + cleanName;
|
|
||||||
|
|
||||||
function withWhiteLabel(config: Partial<ExpoConfig>) {
|
|
||||||
if (!config.extra) config.extra = {};
|
|
||||||
// Expose CI env variables to the app
|
|
||||||
config.extra.CI = process.env.CI;
|
|
||||||
|
|
||||||
if (!config.ios) config.ios = {};
|
|
||||||
config.ios.bundleIdentifier = appId;
|
|
||||||
if (!config.android) config.android = {};
|
|
||||||
config.android.package = appId;
|
|
||||||
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = ({ config }: ConfigContext): Partial<ExpoConfig> => {
|
|
||||||
config = withWhiteLabel(config);
|
|
||||||
|
|
||||||
config = withAppleSettings(config as ExpoConfig, {
|
|
||||||
// The name of the .plist file to generate. Root is the default and must be provided.
|
|
||||||
Root: {
|
|
||||||
// The locales object is optional. If provided, it will be used to generate the localized .strings files.
|
|
||||||
locales: {
|
|
||||||
// The Apple locale code. This will be used to generate the .strings file.
|
|
||||||
en: {
|
|
||||||
// Name of the localized key.
|
|
||||||
Name: "Text Field",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
// The page object is required. It will be used to generate the .plist file.
|
|
||||||
// The contents will be converted directly to plist.
|
|
||||||
page: {
|
|
||||||
// The `PreferenceSpecifiers` defines the UI elements to generate.
|
|
||||||
PreferenceSpecifiers: [
|
|
||||||
Title({
|
|
||||||
title: "Title",
|
|
||||||
value: "Default Title",
|
|
||||||
key: "title_preference",
|
|
||||||
}),
|
|
||||||
// Helper models can be used to generate the UI elements using
|
|
||||||
// an API that's similar to React Native.
|
|
||||||
TextField({
|
|
||||||
title: "Name",
|
|
||||||
key: "name_preference",
|
|
||||||
value: "",
|
|
||||||
keyboardType: "Alphabet",
|
|
||||||
autoCapitalize: "None",
|
|
||||||
autoCorrect: "No",
|
|
||||||
}),
|
|
||||||
Switch({
|
|
||||||
title: "Enabled",
|
|
||||||
key: "enabled_preference",
|
|
||||||
value: true,
|
|
||||||
}),
|
|
||||||
Slider({
|
|
||||||
key: "slider_preference",
|
|
||||||
value: 0.5,
|
|
||||||
}),
|
|
||||||
// Broken https://forums.developer.apple.com/forums/thread/764519
|
|
||||||
// RadioGroup({
|
|
||||||
// value: "option1",
|
|
||||||
// key: "radio_preference",
|
|
||||||
// items: [
|
|
||||||
// {
|
|
||||||
// title: "Option 1",
|
|
||||||
// value: "option1",
|
|
||||||
// },
|
|
||||||
|
|
||||||
// {
|
|
||||||
// title: "Option 2",
|
|
||||||
// value: "option2",
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// }),
|
|
||||||
// MultiValue({
|
|
||||||
// title: "Multi Value",
|
|
||||||
// key: "multi_value_preference",
|
|
||||||
// value: "alpha",
|
|
||||||
// items: [
|
|
||||||
// {
|
|
||||||
// title: "Alpha",
|
|
||||||
// value: "alpha",
|
|
||||||
// short: "α",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// title: "Beta",
|
|
||||||
// value: "beta",
|
|
||||||
// short: "β",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// title: "Delta",
|
|
||||||
// value: "delta",
|
|
||||||
// short: "Δ",
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// title: "Omega",
|
|
||||||
// value: "omega",
|
|
||||||
// short: "Ω",
|
|
||||||
// },
|
|
||||||
// ],
|
|
||||||
// }),
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"expo": {
|
|
||||||
"name": "apple-settings",
|
|
||||||
"icon": "https://github.com/expo.png",
|
|
||||||
"platforms": ["ios"],
|
|
||||||
"plugins": [["expo-build-properties", { "ios": { "ccacheEnabled": true } }]]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import { registerRootComponent } from "expo";
|
|
||||||
|
|
||||||
import App from "./src/App";
|
|
||||||
|
|
||||||
registerRootComponent(App);
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@config-plugins-app/apple-settings",
|
|
||||||
"main": "./index.js",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"scripts": {
|
|
||||||
"start": "expo start --dev-client",
|
|
||||||
"ios": "expo run:ios",
|
|
||||||
"android": "expo run:android"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@config-plugins/apple-settings": "*",
|
|
||||||
"@react-native-community/slider": "4.5.6",
|
|
||||||
"@react-native-picker/picker": "2.11.0",
|
|
||||||
"expo": "~53",
|
|
||||||
"expo-splash-screen": "~0.30.7",
|
|
||||||
"react": "19.0.0",
|
|
||||||
"react-native": "0.79.2"
|
|
||||||
},
|
|
||||||
"private": true
|
|
||||||
}
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
import { Button, Linking, StyleSheet, View } from "react-native";
|
|
||||||
|
|
||||||
import {
|
|
||||||
SettingsRadioGroup,
|
|
||||||
SettingsSlider,
|
|
||||||
SettingsSwitch,
|
|
||||||
SettingsTextInput,
|
|
||||||
SettingsTitle,
|
|
||||||
} from "./SettingsViews";
|
|
||||||
|
|
||||||
const App = () => {
|
|
||||||
return (
|
|
||||||
<View style={styles.container}>
|
|
||||||
{/* <SettingsTitle settingsKey="title_preference" /> */}
|
|
||||||
<SettingsTextInput
|
|
||||||
style={{
|
|
||||||
height: 40,
|
|
||||||
borderColor: "gray",
|
|
||||||
borderWidth: 1,
|
|
||||||
padding: 8,
|
|
||||||
}}
|
|
||||||
settingsKey="name_preference"
|
|
||||||
/>
|
|
||||||
<SettingsSwitch settingsKey="enabled_preference" />
|
|
||||||
<SettingsSlider settingsKey="slider_preference" />
|
|
||||||
<View />
|
|
||||||
<SettingsRadioGroup settingsKey="radio_preference">
|
|
||||||
<SettingsRadioGroup.Item label="Option 1" value="option1" />
|
|
||||||
<SettingsRadioGroup.Item label="Option 2" value="option2" />
|
|
||||||
</SettingsRadioGroup>
|
|
||||||
<View />
|
|
||||||
<Button
|
|
||||||
title="Launch Settings"
|
|
||||||
onPress={() => {
|
|
||||||
Linking.openSettings();
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "stretch",
|
|
||||||
paddingHorizontal: 56,
|
|
||||||
gap: 12,
|
|
||||||
},
|
|
||||||
value: {
|
|
||||||
fontSize: 24,
|
|
||||||
marginVertical: 12,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
export default App;
|
|
||||||
@@ -1,91 +0,0 @@
|
|||||||
import Slider from "@react-native-community/slider";
|
|
||||||
import { Picker } from "@react-native-picker/picker";
|
|
||||||
import React from "react";
|
|
||||||
import { Settings, Switch, Text, TextInput } from "react-native";
|
|
||||||
|
|
||||||
export function useSetting<T = string>(key: string): [T, (value: T) => void] {
|
|
||||||
const [value, setValue] = React.useState<T>(() => Settings.get(key));
|
|
||||||
React.useEffect(() => {
|
|
||||||
let isMounted = true;
|
|
||||||
const callback = Settings.watchKeys(key, () => {
|
|
||||||
if (isMounted) {
|
|
||||||
setValue(Settings.get(key));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return () => {
|
|
||||||
Settings.clearWatch(callback);
|
|
||||||
isMounted = false;
|
|
||||||
};
|
|
||||||
}, [key]);
|
|
||||||
|
|
||||||
return [
|
|
||||||
value,
|
|
||||||
(value) => {
|
|
||||||
Settings.set({ [key]: value });
|
|
||||||
setValue(value);
|
|
||||||
},
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SettingsSlider({
|
|
||||||
settingsKey,
|
|
||||||
...props
|
|
||||||
}: { settingsKey: string } & Omit<
|
|
||||||
React.ComponentProps<typeof Slider>,
|
|
||||||
"selectedValue" | "onValueChange"
|
|
||||||
>) {
|
|
||||||
const [value, setValue] = useSetting<number>(settingsKey);
|
|
||||||
return <Slider {...props} value={value ?? 0} onValueChange={setValue} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SettingsRadioGroup({
|
|
||||||
settingsKey,
|
|
||||||
...props
|
|
||||||
}: { settingsKey: string } & Omit<
|
|
||||||
React.ComponentProps<typeof Picker>,
|
|
||||||
"selectedValue" | "onValueChange"
|
|
||||||
>) {
|
|
||||||
const [value, setValue] = useSetting<string>(settingsKey);
|
|
||||||
return (
|
|
||||||
<Picker
|
|
||||||
{...props}
|
|
||||||
selectedValue={value ?? "option1"}
|
|
||||||
onValueChange={setValue}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
SettingsRadioGroup.Item = Picker.Item;
|
|
||||||
|
|
||||||
export function SettingsSwitch({
|
|
||||||
settingsKey,
|
|
||||||
...props
|
|
||||||
}: { settingsKey: string } & Omit<
|
|
||||||
React.ComponentProps<typeof Switch>,
|
|
||||||
"value" | "onValueChange"
|
|
||||||
>) {
|
|
||||||
const [value, setValue] = useSetting<boolean>(settingsKey);
|
|
||||||
return <Switch {...props} value={!!value} onValueChange={setValue} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SettingsTitle({
|
|
||||||
settingsKey,
|
|
||||||
...props
|
|
||||||
}: { settingsKey: string } & Omit<
|
|
||||||
React.ComponentProps<typeof Text>,
|
|
||||||
"children"
|
|
||||||
>) {
|
|
||||||
const [value] = useSetting<string>(settingsKey);
|
|
||||||
return <Text {...props} children={value} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function SettingsTextInput({
|
|
||||||
settingsKey,
|
|
||||||
...props
|
|
||||||
}: { settingsKey: string } & Omit<
|
|
||||||
React.ComponentProps<typeof TextInput>,
|
|
||||||
"value" | "onChangeText"
|
|
||||||
>) {
|
|
||||||
const [value, setValue] = useSetting<string>(settingsKey);
|
|
||||||
return <TextInput {...props} value={value} onChangeText={setValue} />;
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {},
|
|
||||||
"extends": "expo/tsconfig.base"
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
const path = require("path");
|
|
||||||
const folderName = path.basename(__dirname);
|
|
||||||
const cleanName = folderName.replace(/[_\s-]/g, "");
|
|
||||||
const appId = "dev.bacon." + cleanName;
|
|
||||||
|
|
||||||
module.exports = ({ config }) => {
|
|
||||||
if (!config.extra) config.extra = {};
|
|
||||||
// Expose CI env variables to the app
|
|
||||||
config.extra.CI = process.env.CI;
|
|
||||||
|
|
||||||
if (!config.ios) config.ios = {};
|
|
||||||
config.ios.bundleIdentifier = appId;
|
|
||||||
if (!config.android) config.android = {};
|
|
||||||
config.android.package = appId;
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"expo": {
|
|
||||||
"name": "ffmpeg",
|
|
||||||
"icon": "https://github.com/expo.png",
|
|
||||||
"splash": {
|
|
||||||
"image": "https://github.com/expo.png"
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
[
|
|
||||||
"@config-plugins/ffmpeg-kit-react-native",
|
|
||||||
{
|
|
||||||
"package": "min"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import { registerRootComponent } from "expo";
|
|
||||||
|
|
||||||
import App from "./src/App";
|
|
||||||
|
|
||||||
registerRootComponent(App);
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@config-plugins-app/ffmpeg-kit-react-native",
|
|
||||||
"main": "./index.js",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"scripts": {
|
|
||||||
"start": "expo start --dev-client",
|
|
||||||
"ios": "expo run:ios",
|
|
||||||
"android": "expo run:android"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@config-plugins/ffmpeg-kit-react-native": "*",
|
|
||||||
"expo": "~53",
|
|
||||||
"expo-splash-screen": "~0.30.7",
|
|
||||||
"ffmpeg-kit-react-native": "^6.0.2",
|
|
||||||
"react": "19.0.0",
|
|
||||||
"react-native": "0.79.2"
|
|
||||||
},
|
|
||||||
"private": true
|
|
||||||
}
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
import { Session } from "ffmpeg-kit-react-native";
|
|
||||||
import React from "react";
|
|
||||||
import {
|
|
||||||
ActivityIndicator,
|
|
||||||
SafeAreaView,
|
|
||||||
ScrollView,
|
|
||||||
Text,
|
|
||||||
View,
|
|
||||||
} from "react-native";
|
|
||||||
|
|
||||||
import { useMediaInfo, useResolvedValue } from "./useMediaInfo";
|
|
||||||
|
|
||||||
export default function App() {
|
|
||||||
const session = useMediaInfo(
|
|
||||||
"http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!session) {
|
|
||||||
return (
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ActivityIndicator />
|
|
||||||
<Text>Loading...</Text>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <SessionInfo session={session} />;
|
|
||||||
}
|
|
||||||
|
|
||||||
function SessionInfo(props: { session: Session }) {
|
|
||||||
const { session } = props;
|
|
||||||
const duration = useResolvedValue(session.getDuration.bind(session));
|
|
||||||
const output = useResolvedValue(session.getOutput.bind(session));
|
|
||||||
return (
|
|
||||||
<SafeAreaView
|
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ScrollView>
|
|
||||||
<Text>Duration: {duration}</Text>
|
|
||||||
<Text>Output: {output}</Text>
|
|
||||||
</ScrollView>
|
|
||||||
</SafeAreaView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {},
|
|
||||||
"extends": "expo/tsconfig.base"
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
const path = require("path");
|
|
||||||
const folderName = path.basename(__dirname);
|
|
||||||
const cleanName = folderName.replace(/[_\s-]/g, "");
|
|
||||||
const appId = "dev.bacon." + cleanName;
|
|
||||||
|
|
||||||
module.exports = ({ config }) => {
|
|
||||||
if (!config.extra) config.extra = {};
|
|
||||||
// Expose CI env variables to the app
|
|
||||||
config.extra.CI = process.env.CI;
|
|
||||||
|
|
||||||
if (!config.ios) config.ios = {};
|
|
||||||
config.ios.bundleIdentifier = appId;
|
|
||||||
if (!config.android) config.android = {};
|
|
||||||
config.android.package = appId;
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
{
|
|
||||||
"expo": {
|
|
||||||
"name": "stickers",
|
|
||||||
"icon": "https://github.com/expo.png",
|
|
||||||
"platforms": ["ios"],
|
|
||||||
"plugins": [
|
|
||||||
[
|
|
||||||
"@config-plugins/ios-stickers",
|
|
||||||
{
|
|
||||||
"name": "Stickers",
|
|
||||||
"stickerBundleId": "dev.bacon.iosstickers.stickers",
|
|
||||||
"icon": "./assets/imessage-icon.png",
|
|
||||||
"columns": 4,
|
|
||||||
"stickers": [
|
|
||||||
"./assets/stickers/annoyed.png",
|
|
||||||
"./assets/stickers/cuddle.png",
|
|
||||||
"./assets/stickers/crazy.png",
|
|
||||||
"./assets/stickers/cant.png",
|
|
||||||
"./assets/stickers/donno.png",
|
|
||||||
"./assets/stickers/cozzy.png",
|
|
||||||
"./assets/stickers/dork.png",
|
|
||||||
"./assets/stickers/focus.png",
|
|
||||||
"./assets/stickers/avocool.png",
|
|
||||||
"./assets/stickers/coder-girl.png",
|
|
||||||
"./assets/stickers/teapot.png"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
["expo-build-properties", { "ios": { "ccacheEnabled": true } }]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import { registerRootComponent } from "expo";
|
|
||||||
|
|
||||||
import App from "./src/App";
|
|
||||||
|
|
||||||
registerRootComponent(App);
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@config-plugins-app/ios-stickers",
|
|
||||||
"main": "./index.js",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"scripts": {
|
|
||||||
"start": "expo start --dev-client",
|
|
||||||
"prebuild": "expo prebuild -p ios --template ./expo-template-bare-minimum-50.0.17.tgz",
|
|
||||||
"ios": "expo run:ios",
|
|
||||||
"android": "expo run:android"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@config-plugins/ios-stickers": "*",
|
|
||||||
"expo": "~53",
|
|
||||||
"expo-splash-screen": "~0.30.7",
|
|
||||||
"react": "19.0.0",
|
|
||||||
"react-native": "0.79.2"
|
|
||||||
},
|
|
||||||
"private": true
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { Text, View } from "react-native";
|
|
||||||
|
|
||||||
export default function App() {
|
|
||||||
return (
|
|
||||||
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
|
|
||||||
<Text>Open iMessage to find the app</Text>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
const path = require("path");
|
|
||||||
const folderName = path.basename(__dirname);
|
|
||||||
const cleanName = folderName.replace(/[_\s-]/g, "");
|
|
||||||
const appId = "dev.bacon." + cleanName;
|
|
||||||
|
|
||||||
module.exports = ({ config }) => {
|
|
||||||
if (!config.extra) config.extra = {};
|
|
||||||
// Expose CI env variables to the app
|
|
||||||
config.extra.CI = process.env.CI;
|
|
||||||
|
|
||||||
if (!config.ios) config.ios = {};
|
|
||||||
config.ios.bundleIdentifier = appId;
|
|
||||||
if (!config.android) config.android = {};
|
|
||||||
config.android.package = appId;
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"expo": {
|
|
||||||
"name": "branch",
|
|
||||||
"icon": "https://github.com/expo.png",
|
|
||||||
"splash": {
|
|
||||||
"image": "https://github.com/expo.png"
|
|
||||||
},
|
|
||||||
"ios": {
|
|
||||||
"config": {
|
|
||||||
"branch": {
|
|
||||||
"apiKey": "key_live_f9f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"android": {
|
|
||||||
"config": {
|
|
||||||
"branch": {
|
|
||||||
"apiKey": "key_live_f9f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8f8"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
"@config-plugins/react-native-branch",
|
|
||||||
["expo-build-properties", { "ios": { "ccacheEnabled": true } }]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import { registerRootComponent } from "expo";
|
|
||||||
|
|
||||||
import App from "./src/App";
|
|
||||||
|
|
||||||
registerRootComponent(App);
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@config-plugins-app/react-native-branch",
|
|
||||||
"main": "./index.js",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"scripts": {
|
|
||||||
"start": "expo start --dev-client",
|
|
||||||
"ios": "expo run:ios",
|
|
||||||
"android": "expo run:android"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@config-plugins/react-native-branch": "*",
|
|
||||||
"expo": "~53",
|
|
||||||
"expo-splash-screen": "~0.30.7",
|
|
||||||
"react": "19.0.0",
|
|
||||||
"react-native": "0.79.2",
|
|
||||||
"react-native-branch": "^6.6.0"
|
|
||||||
},
|
|
||||||
"private": true
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { Text, View } from "react-native";
|
|
||||||
|
|
||||||
export default function App() {
|
|
||||||
return (
|
|
||||||
<View style={{ flex: 1, justifyContent: "center", alignItems: "center" }}>
|
|
||||||
<Text>Branch</Text>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
const path = require("path");
|
|
||||||
const folderName = path.basename(__dirname);
|
|
||||||
const cleanName = folderName.replace(/[_\s-]/g, "");
|
|
||||||
const appId = "dev.bacon." + cleanName;
|
|
||||||
|
|
||||||
module.exports = ({ config }) => {
|
|
||||||
if (!config.extra) config.extra = {};
|
|
||||||
// Expose CI env variables to the app
|
|
||||||
config.extra.CI = process.env.CI;
|
|
||||||
|
|
||||||
if (!config.ios) config.ios = {};
|
|
||||||
config.ios.bundleIdentifier = appId;
|
|
||||||
if (!config.android) config.android = {};
|
|
||||||
config.android.package = appId;
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"expo": {
|
|
||||||
"name": "app-icon",
|
|
||||||
"icon": "./assets/icons/winter.png",
|
|
||||||
"platforms": ["ios"],
|
|
||||||
"ios": {
|
|
||||||
"supportsTablet": true
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
[
|
|
||||||
"@config-plugins/react-native-dynamic-app-icon",
|
|
||||||
[
|
|
||||||
"./assets/icons/spring.png",
|
|
||||||
"./assets/icons/summer.png",
|
|
||||||
"./assets/icons/autumn.png",
|
|
||||||
"./assets/icons/fall.png",
|
|
||||||
"./assets/icons/solstice.png"
|
|
||||||
]
|
|
||||||
],
|
|
||||||
["expo-build-properties", { "ios": { "ccacheEnabled": true } }]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import { registerRootComponent } from "expo";
|
|
||||||
|
|
||||||
import App from "./src/App";
|
|
||||||
|
|
||||||
registerRootComponent(App);
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@config-plugins-app/react-native-dynamic-app-icon",
|
|
||||||
"main": "./index.js",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"scripts": {
|
|
||||||
"start": "expo start --dev-client",
|
|
||||||
"ios": "expo run:ios",
|
|
||||||
"android": "expo run:android"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@config-plugins/react-native-dynamic-app-icon": "*",
|
|
||||||
"expo": "~53",
|
|
||||||
"expo-splash-screen": "~0.30.7",
|
|
||||||
"react": "19.0.0",
|
|
||||||
"react-native": "0.79.2",
|
|
||||||
"react-native-dynamic-app-icon": "^1.1.0"
|
|
||||||
},
|
|
||||||
"private": true
|
|
||||||
}
|
|
||||||
@@ -1,159 +0,0 @@
|
|||||||
import Entypo from "@expo/vector-icons/Entypo";
|
|
||||||
import React from "react";
|
|
||||||
import { Image, SafeAreaView, Text, View } from "react-native";
|
|
||||||
import AppIcon from "react-native-dynamic-app-icon";
|
|
||||||
import TouchableBounce from "react-native/Libraries/Components/Touchable/TouchableBounce";
|
|
||||||
|
|
||||||
function useIconName() {
|
|
||||||
const [icon, _setIcon] = React.useState(null);
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
|
||||||
let isMounted = true;
|
|
||||||
AppIcon.getIconName((result) => {
|
|
||||||
if (isMounted) _setIcon(result.iconName);
|
|
||||||
});
|
|
||||||
return () => (isMounted = false);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const setIcon = React.useCallback(
|
|
||||||
(icon) => {
|
|
||||||
AppIcon.setAppIcon(icon);
|
|
||||||
_setIcon(icon || null);
|
|
||||||
},
|
|
||||||
[_setIcon]
|
|
||||||
);
|
|
||||||
return [icon === "default" ? null : icon, setIcon];
|
|
||||||
}
|
|
||||||
|
|
||||||
const icons = [
|
|
||||||
{
|
|
||||||
name: "Auto",
|
|
||||||
source: require("../assets/icons/winter.png"),
|
|
||||||
iconId: null,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Spring",
|
|
||||||
source: require("../assets/icons/spring.png"),
|
|
||||||
iconId: "0",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Summer",
|
|
||||||
source: require("../assets/icons/summer.png"),
|
|
||||||
iconId: "1",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Autumn",
|
|
||||||
source: require("../assets/icons/autumn.png"),
|
|
||||||
iconId: "2",
|
|
||||||
},
|
|
||||||
{ name: "Fall", source: require("../assets/icons/fall.png"), iconId: "3" },
|
|
||||||
{
|
|
||||||
name: "Solstice",
|
|
||||||
source: require("../assets/icons/solstice.png"),
|
|
||||||
iconId: "4",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function App() {
|
|
||||||
const [_icon, setIcon] = useIconName();
|
|
||||||
|
|
||||||
return (
|
|
||||||
<SafeAreaView
|
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: "center",
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View style={{ width: "100%" }}>
|
|
||||||
{icons.map((icon, index) => {
|
|
||||||
return (
|
|
||||||
<Item
|
|
||||||
onPress={() => {
|
|
||||||
setIcon(icon.iconId);
|
|
||||||
}}
|
|
||||||
isSelected={icon.iconId === _icon}
|
|
||||||
name={icon.name}
|
|
||||||
source={icon.source}
|
|
||||||
key={String(index)}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</View>
|
|
||||||
</SafeAreaView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function Item({ ...props }) {
|
|
||||||
return (
|
|
||||||
<TouchableBounce
|
|
||||||
onPress={props.onPress}
|
|
||||||
style={{ marginVertical: 8, marginHorizontal: 24 }}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flexDirection: "row",
|
|
||||||
justifyContent: "space-between",
|
|
||||||
alignItems: "stretch",
|
|
||||||
shadowColor: "#000",
|
|
||||||
shadowOffset: {
|
|
||||||
width: 0,
|
|
||||||
height: 4,
|
|
||||||
},
|
|
||||||
shadowOpacity: props.isSelected ? 0.35 : 0.2,
|
|
||||||
shadowRadius: 8.46,
|
|
||||||
|
|
||||||
backgroundColor: props.isSelected ? "#000" : "#F1F1F1",
|
|
||||||
elevation: 9,
|
|
||||||
paddingHorizontal: 12,
|
|
||||||
paddingVertical: 12,
|
|
||||||
borderRadius: 20,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flexDirection: "row",
|
|
||||||
flex: 1,
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<View
|
|
||||||
style={{
|
|
||||||
flexDirection: "row",
|
|
||||||
flex: 1,
|
|
||||||
alignItems: "center",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
source={props.source}
|
|
||||||
style={{
|
|
||||||
width: 64,
|
|
||||||
aspectRatio: 1,
|
|
||||||
resizeMode: "cover",
|
|
||||||
borderRadius: 16,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
style={{
|
|
||||||
marginLeft: 20,
|
|
||||||
fontSize: 18,
|
|
||||||
fontWeight: "700",
|
|
||||||
color: props.isSelected ? "white" : "black",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{props.name}
|
|
||||||
</Text>
|
|
||||||
</View>
|
|
||||||
{props.isSelected && (
|
|
||||||
<Entypo
|
|
||||||
style={{ marginRight: 8 }}
|
|
||||||
name="check"
|
|
||||||
size={20}
|
|
||||||
color="white"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
</TouchableBounce>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {},
|
|
||||||
"extends": "expo/tsconfig.base"
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
const path = require("path");
|
|
||||||
const folderName = path.basename(__dirname);
|
|
||||||
const cleanName = folderName.replace(/[_\s-]/g, "");
|
|
||||||
const appId = "dev.bacon." + cleanName;
|
|
||||||
|
|
||||||
module.exports = ({ config }) => {
|
|
||||||
if (!config.extra) config.extra = {};
|
|
||||||
// Expose CI env variables to the app
|
|
||||||
config.extra.CI = process.env.CI;
|
|
||||||
|
|
||||||
if (!config.ios) config.ios = {};
|
|
||||||
config.ios.bundleIdentifier = appId;
|
|
||||||
if (!config.android) config.android = {};
|
|
||||||
config.android.package = appId;
|
|
||||||
return config;
|
|
||||||
};
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
{
|
|
||||||
"expo": {
|
|
||||||
"name": "pdf",
|
|
||||||
"icon": "https://github.com/expo.png",
|
|
||||||
"splash": {
|
|
||||||
"image": "https://github.com/expo.png"
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
"@config-plugins/react-native-blob-util",
|
|
||||||
"@config-plugins/react-native-pdf",
|
|
||||||
["expo-build-properties", { "ios": { "ccacheEnabled": true } }]
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
import { registerRootComponent } from "expo";
|
|
||||||
|
|
||||||
import App from "./src/App";
|
|
||||||
|
|
||||||
registerRootComponent(App);
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@config-plugins-app/react-native-pdf",
|
|
||||||
"main": "./index.js",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"scripts": {
|
|
||||||
"start": "expo start --dev-client",
|
|
||||||
"ios": "expo run:ios",
|
|
||||||
"android": "expo run:android"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@config-plugins/react-native-blob-util": "*",
|
|
||||||
"@config-plugins/react-native-pdf": "*",
|
|
||||||
"expo": "~53",
|
|
||||||
"expo-splash-screen": "~0.30.7",
|
|
||||||
"react": "19.0.0",
|
|
||||||
"react-native": "0.79.2",
|
|
||||||
"react-native-blob-util": "^0.21.2",
|
|
||||||
"react-native-pdf": "^6.7.7"
|
|
||||||
},
|
|
||||||
"private": true
|
|
||||||
}
|
|
||||||