mirror of
https://gitcode.com/gh_mirrors/re/react-native-pushy.git
synced 2025-09-18 00:06:10 +08:00
Compare commits
136 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
147d4e6cc1 | ||
![]() |
a13d6aa21a | ||
![]() |
60e446d2b3 | ||
![]() |
b85889cf22 | ||
![]() |
a8705ca0e4 | ||
![]() |
04ac3f3384 | ||
![]() |
94431ee6f7 | ||
![]() |
2467b0c119 | ||
![]() |
b71626d7d4 | ||
![]() |
20d09529d2 | ||
![]() |
34bc16ad70 | ||
![]() |
a40d627edf | ||
![]() |
462a342172 | ||
![]() |
94d2e18900 | ||
![]() |
d000c40e0f | ||
![]() |
5659c79726 | ||
![]() |
b20d987473 | ||
![]() |
14c9c0b1f5 | ||
![]() |
10178e1e64 | ||
![]() |
6d980a4c04 | ||
![]() |
084fbf35ee | ||
![]() |
d666e8c0f3 | ||
![]() |
5e89fb4a25 | ||
![]() |
ab01b6010a | ||
![]() |
92bc830d98 | ||
![]() |
7531e8ca3a | ||
![]() |
b2305cff3f | ||
![]() |
c5cdb6031b | ||
![]() |
20ebf8979e | ||
![]() |
3929fc2f8e | ||
![]() |
31ee269717 | ||
![]() |
adcd57b6b5 | ||
![]() |
c595e4e54a | ||
![]() |
4f80f96a8d | ||
![]() |
a77c3c85f3 | ||
![]() |
2357a0b78d | ||
![]() |
88bacdfb15 | ||
![]() |
90fee03c45 | ||
![]() |
7243512d3c | ||
![]() |
11b383d10b | ||
![]() |
af65b2660c | ||
![]() |
28b8e122af | ||
![]() |
0fdb33ab10 | ||
![]() |
a5893d8022 | ||
![]() |
54036c0f16 | ||
![]() |
454fc28c08 | ||
![]() |
3355751bd5 | ||
![]() |
e960b67cff | ||
![]() |
93b775dd5a | ||
![]() |
b6202e43d3 | ||
![]() |
13d8cf7b80 | ||
![]() |
7780f9985e | ||
![]() |
de69746937 | ||
![]() |
7cce998277 | ||
![]() |
9faeaf881f | ||
![]() |
e0f526aac8 | ||
![]() |
da2046b213 | ||
![]() |
78186ddf1e | ||
![]() |
d9f1c2edb2 | ||
![]() |
4fb0c691e6 | ||
![]() |
b62e6d64cf | ||
![]() |
9af0538a2a | ||
![]() |
e63fa0fdb6 | ||
![]() |
6244bb9af2 | ||
![]() |
9fd6293037 | ||
![]() |
e9e67b011c | ||
![]() |
5996a7aa75 | ||
![]() |
ad9b0778ba | ||
![]() |
3e60cfd80f | ||
![]() |
782b0e5227 | ||
![]() |
d1ecfb3f93 | ||
![]() |
571c2819b9 | ||
![]() |
1f75688b92 | ||
![]() |
de332c1796 | ||
![]() |
80e0451983 | ||
![]() |
b512ae18b7 | ||
![]() |
fe75a2ca9e | ||
![]() |
d84ad103fb | ||
![]() |
208034fa7d | ||
![]() |
c6430a9ed4 | ||
![]() |
f2aec36705 | ||
![]() |
71e5b947d3 | ||
![]() |
5dc8d8defc | ||
![]() |
30687dddf2 | ||
![]() |
57ddcc7758 | ||
![]() |
a93bbe553d | ||
![]() |
356050d839 | ||
![]() |
5df3cb65b3 | ||
![]() |
699eb0ea05 | ||
![]() |
36c4ff085c | ||
![]() |
8c13dd69b1 | ||
![]() |
2df04cb377 | ||
![]() |
e39d4fa370 | ||
![]() |
94cf96a0e5 | ||
![]() |
22c4b01ead | ||
![]() |
f655a1d954 | ||
![]() |
2bdc64ba1b | ||
![]() |
f5242017b9 | ||
![]() |
42e2051290 | ||
![]() |
a66f354c3b | ||
![]() |
592e13b77b | ||
![]() |
2845a4302a | ||
![]() |
84ef668102 | ||
![]() |
c63e1501fe | ||
![]() |
45bfa2560e | ||
![]() |
ab9c40bf2e | ||
![]() |
d184beabaf | ||
![]() |
68a6235145 | ||
![]() |
a98a48d665 | ||
![]() |
4bf004a274 | ||
![]() |
f01716bcb2 | ||
![]() |
4f9e1495c8 | ||
![]() |
9c06bac91b | ||
![]() |
4923aff184 | ||
![]() |
f6a38c1f48 | ||
![]() |
92cfbb3fdf | ||
![]() |
64ef8e129d | ||
![]() |
ce51b2ca81 | ||
![]() |
6c1662fce1 | ||
![]() |
d9e4575964 | ||
![]() |
190103687c | ||
![]() |
f6f055be64 | ||
![]() |
05e5c5a1a7 | ||
![]() |
296498e20a | ||
![]() |
2a3b8e5707 | ||
![]() |
e86df57476 | ||
![]() |
40b2e9dea0 | ||
![]() |
1afc896306 | ||
![]() |
36533d43c4 | ||
![]() |
e5405b4977 | ||
![]() |
9d93faab31 | ||
![]() |
7229f8847a | ||
![]() |
1daafb0142 | ||
![]() |
c1679a4cea | ||
![]() |
7ab7dffb0f | ||
![]() |
8622935bdf |
29
.eslintrc
29
.eslintrc
@@ -1,29 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "eslint-config-airbnb/base",
|
|
||||||
"parser": "babel-eslint",
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"node": true,
|
|
||||||
"mocha": true
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
// Disable for console/alert
|
|
||||||
"no-console": 0,
|
|
||||||
"no-alert": 0,
|
|
||||||
|
|
||||||
"indent": [2, 2, {"SwitchCase": 1}]
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
"import"
|
|
||||||
],
|
|
||||||
"settings": {
|
|
||||||
"import/parser": "babel-eslint",
|
|
||||||
"import/resolve": {
|
|
||||||
"moduleDirectory": ["node_modules", "src"]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"globals": {
|
|
||||||
"__DEV__": true,
|
|
||||||
"__OPTION__": true
|
|
||||||
}
|
|
||||||
}
|
|
4
.eslintrc.js
Normal file
4
.eslintrc.js
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
extends: '@react-native',
|
||||||
|
};
|
6
.github/workflows/e2e_ios.yml
vendored
6
.github/workflows/e2e_ios.yml
vendored
@@ -26,16 +26,16 @@ concurrency:
|
|||||||
jobs:
|
jobs:
|
||||||
ios:
|
ios:
|
||||||
name: iOS
|
name: iOS
|
||||||
runs-on: macos-12
|
runs-on: macos-14-arm64
|
||||||
# TODO matrix across APIs, at least 11 and 15 (lowest to highest)
|
# TODO matrix across APIs, at least 11 and 15 (lowest to highest)
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
env:
|
env:
|
||||||
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
|
||||||
steps:
|
steps:
|
||||||
# Set up tool versions
|
# Set up tool versions
|
||||||
- uses: actions/setup-node@v3
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: 16
|
node-version: 18
|
||||||
|
|
||||||
- name: Configure JDK 1.11
|
- name: Configure JDK 1.11
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
|
@@ -41,14 +41,17 @@ export const testFunctionDefaultRegion = functions.https.onCall(data => {
|
|||||||
|
|
||||||
const { type, asError, inputData } = data;
|
const { type, asError, inputData } = data;
|
||||||
if (!Object.hasOwnProperty.call(SAMPLE_DATA, type)) {
|
if (!Object.hasOwnProperty.call(SAMPLE_DATA, type)) {
|
||||||
throw new functions.https.HttpsError('invalid-argument', 'Invalid test requested.');
|
throw new functions.https.HttpsError(
|
||||||
|
'invalid-argument',
|
||||||
|
'Invalid test requested.',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const outputData = SAMPLE_DATA[type];
|
const outputData = SAMPLE_DATA[type];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
assert.deepEqual(outputData, inputData);
|
assert.deepEqual(outputData, inputData);
|
||||||
} catch (e) {
|
} catch (e: any) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
throw new functions.https.HttpsError(
|
throw new functions.https.HttpsError(
|
||||||
'invalid-argument',
|
'invalid-argument',
|
||||||
|
2
.gitignore
vendored
2
.gitignore
vendored
@@ -46,3 +46,5 @@ Example/**/.pushy
|
|||||||
Example/testHotUpdate/artifacts
|
Example/testHotUpdate/artifacts
|
||||||
|
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
|
Example/testHotUpdate/.yarn
|
||||||
|
android/bin
|
||||||
|
@@ -47,4 +47,6 @@ Example
|
|||||||
yarn.lock
|
yarn.lock
|
||||||
|
|
||||||
domains.json
|
domains.json
|
||||||
endpoints.json
|
endpoints.json
|
||||||
|
|
||||||
|
tea.yaml
|
@@ -1,4 +1,6 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
trailingComma: 'all',
|
arrowParens: 'avoid',
|
||||||
|
bracketSameLine: true,
|
||||||
singleQuote: true,
|
singleQuote: true,
|
||||||
|
trailingComma: 'all',
|
||||||
};
|
};
|
||||||
|
BIN
.yarn/install-state.gz
Normal file
BIN
.yarn/install-state.gz
Normal file
Binary file not shown.
1
.yarnrc.yml
Normal file
1
.yarnrc.yml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
nodeLinker: node-modules
|
@@ -1,4 +1,4 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
extends: '@react-native-community',
|
extends: '@react-native',
|
||||||
};
|
};
|
||||||
|
1
Example/testHotUpdate/.yarnrc.yml
Normal file
1
Example/testHotUpdate/.yarnrc.yml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
nodeLinker: node-modules
|
@@ -1,226 +1,87 @@
|
|||||||
plugins {
|
apply plugin: "com.android.application"
|
||||||
id 'com.android.application'
|
apply plugin: "com.facebook.react"
|
||||||
id 'org.jetbrains.kotlin.android'
|
apply plugin: "kotlin-android"
|
||||||
|
apply plugin: "kotlin-android-extensions"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is the configuration block to customize your React Native Android app.
|
||||||
|
* By default you don't need to apply any configuration, just uncomment the lines you need.
|
||||||
|
*/
|
||||||
|
react {
|
||||||
|
/* Folders */
|
||||||
|
// The root of your project, i.e. where "package.json" lives. Default is '..'
|
||||||
|
// root = file("../")
|
||||||
|
// The folder where the react-native NPM package is. Default is ../node_modules/react-native
|
||||||
|
// reactNativeDir = file("../node_modules/react-native")
|
||||||
|
// The folder where the react-native Codegen package is. Default is ../node_modules/@react-native/codegen
|
||||||
|
// codegenDir = file("../node_modules/@react-native/codegen")
|
||||||
|
// The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js
|
||||||
|
// cliFile = file("../node_modules/react-native/cli.js")
|
||||||
|
|
||||||
|
/* Variants */
|
||||||
|
// The list of variants to that are debuggable. For those we're going to
|
||||||
|
// skip the bundling of the JS bundle and the assets. By default is just 'debug'.
|
||||||
|
// If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants.
|
||||||
|
// debuggableVariants = ["liteDebug", "prodDebug"]
|
||||||
|
|
||||||
|
/* Bundling */
|
||||||
|
// A list containing the node command and its flags. Default is just 'node'.
|
||||||
|
// nodeExecutableAndArgs = ["node"]
|
||||||
|
//
|
||||||
|
// The command to run when bundling. By default is 'bundle'
|
||||||
|
// bundleCommand = "ram-bundle"
|
||||||
|
//
|
||||||
|
// The path to the CLI configuration file. Default is empty.
|
||||||
|
// bundleConfig = file(../rn-cli.config.js)
|
||||||
|
//
|
||||||
|
// The name of the generated asset file containing your JS bundle
|
||||||
|
// bundleAssetName = "MyApplication.android.bundle"
|
||||||
|
//
|
||||||
|
// The entry file for bundle generation. Default is 'index.android.js' or 'index.js'
|
||||||
|
// entryFile = file("../js/MyApplication.android.js")
|
||||||
|
//
|
||||||
|
// A list of extra flags to pass to the 'bundle' commands.
|
||||||
|
// See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle
|
||||||
|
// extraPackagerArgs = []
|
||||||
|
|
||||||
|
/* Hermes Commands */
|
||||||
|
// The hermes compiler command to run. By default it is 'hermesc'
|
||||||
|
// hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc"
|
||||||
|
//
|
||||||
|
// The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map"
|
||||||
|
// hermesFlags = ["-O", "-output-source-map"]
|
||||||
}
|
}
|
||||||
|
|
||||||
import com.android.build.OutputFile
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
|
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
|
||||||
* and bundleReleaseJsAndAssets).
|
|
||||||
* These basically call `react-native bundle` with the correct arguments during the Android build
|
|
||||||
* cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the
|
|
||||||
* bundle directly from the development server. Below you can see all the possible configurations
|
|
||||||
* and their defaults. If you decide to add a configuration block, make sure to add it before the
|
|
||||||
* `apply from: "../../node_modules/react-native/react.gradle"` line.
|
|
||||||
*
|
|
||||||
* project.ext.react = [
|
|
||||||
* // the name of the generated asset file containing your JS bundle
|
|
||||||
* bundleAssetName: "index.android.bundle",
|
|
||||||
*
|
|
||||||
* // the entry file for bundle generation. If none specified and
|
|
||||||
* // "index.android.js" exists, it will be used. Otherwise "index.js" is
|
|
||||||
* // default. Can be overridden with ENTRY_FILE environment variable.
|
|
||||||
* entryFile: "index.android.js",
|
|
||||||
*
|
|
||||||
* // https://reactnative.dev/docs/performance#enable-the-ram-format
|
|
||||||
* bundleCommand: "ram-bundle",
|
|
||||||
*
|
|
||||||
* // whether to bundle JS and assets in debug mode
|
|
||||||
* bundleInDebug: false,
|
|
||||||
*
|
|
||||||
* // whether to bundle JS and assets in release mode
|
|
||||||
* bundleInRelease: true,
|
|
||||||
*
|
|
||||||
* // whether to bundle JS and assets in another build variant (if configured).
|
|
||||||
* // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants
|
|
||||||
* // The configuration property can be in the following formats
|
|
||||||
* // 'bundleIn${productFlavor}${buildType}'
|
|
||||||
* // 'bundleIn${buildType}'
|
|
||||||
* // bundleInFreeDebug: true,
|
|
||||||
* // bundleInPaidRelease: true,
|
|
||||||
* // bundleInBeta: true,
|
|
||||||
*
|
|
||||||
* // whether to disable dev mode in custom build variants (by default only disabled in release)
|
|
||||||
* // for example: to disable dev mode in the staging build type (if configured)
|
|
||||||
* devDisabledInStaging: true,
|
|
||||||
* // The configuration property can be in the following formats
|
|
||||||
* // 'devDisabledIn${productFlavor}${buildType}'
|
|
||||||
* // 'devDisabledIn${buildType}'
|
|
||||||
*
|
|
||||||
* // the root of your project, i.e. where "package.json" lives
|
|
||||||
* root: "../../",
|
|
||||||
*
|
|
||||||
* // where to put the JS bundle asset in debug mode
|
|
||||||
* jsBundleDirDebug: "$buildDir/intermediates/assets/debug",
|
|
||||||
*
|
|
||||||
* // where to put the JS bundle asset in release mode
|
|
||||||
* jsBundleDirRelease: "$buildDir/intermediates/assets/release",
|
|
||||||
*
|
|
||||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
|
||||||
* // require('./image.png')), in debug mode
|
|
||||||
* resourcesDirDebug: "$buildDir/intermediates/res/merged/debug",
|
|
||||||
*
|
|
||||||
* // where to put drawable resources / React Native assets, e.g. the ones you use via
|
|
||||||
* // require('./image.png')), in release mode
|
|
||||||
* resourcesDirRelease: "$buildDir/intermediates/res/merged/release",
|
|
||||||
*
|
|
||||||
* // by default the gradle tasks are skipped if none of the JS files or assets change; this means
|
|
||||||
* // that we don't look at files in android/ or ios/ to determine whether the tasks are up to
|
|
||||||
* // date; if you have any other folders that you want to ignore for performance reasons (gradle
|
|
||||||
* // indexes the entire tree), add them here. Alternatively, if you have JS files in android/
|
|
||||||
* // for example, you might want to remove it from here.
|
|
||||||
* inputExcludes: ["android/**", "ios/**"],
|
|
||||||
*
|
|
||||||
* // override which node gets called and with what additional arguments
|
|
||||||
* nodeExecutableAndArgs: ["node"],
|
|
||||||
*
|
|
||||||
* // supply additional arguments to the packager
|
|
||||||
* extraPackagerArgs: []
|
|
||||||
* ]
|
|
||||||
*/
|
|
||||||
|
|
||||||
project.ext.react = [
|
|
||||||
enableHermes: false, // clean and rebuild if changing
|
|
||||||
]
|
|
||||||
|
|
||||||
apply from: "../../node_modules/react-native/react.gradle"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set this to true to create two separate APKs instead of one:
|
|
||||||
* - An APK that only works on ARM devices
|
|
||||||
* - An APK that only works on x86 devices
|
|
||||||
* The advantage is the size of the APK is reduced by about 4MB.
|
|
||||||
* Upload all the APKs to the Play Store and people will download
|
|
||||||
* the correct one based on the CPU architecture of their device.
|
|
||||||
*/
|
|
||||||
def enableSeparateBuildPerCPUArchitecture = false
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run Proguard to shrink the Java bytecode in release builds.
|
|
||||||
*/
|
*/
|
||||||
def enableProguardInReleaseBuilds = false
|
def enableProguardInReleaseBuilds = false
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The preferred build flavor of JavaScriptCore.
|
* The preferred build flavor of JavaScriptCore (JSC)
|
||||||
*
|
*
|
||||||
* For example, to use the international variant, you can use:
|
* For example, to use the international variant, you can use:
|
||||||
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
|
* `def jscFlavor = 'org.webkit:android-jsc-intl:+'`
|
||||||
*
|
*
|
||||||
* The international variant includes ICU i18n library and necessary data
|
* The international variant includes ICU i18n library and necessary data
|
||||||
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
|
* allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that
|
||||||
* give correct results when using with locales other than en-US. Note that
|
* give correct results when using with locales other than en-US. Note that
|
||||||
* this variant is about 6MiB larger per architecture than default.
|
* this variant is about 6MiB larger per architecture than default.
|
||||||
*/
|
*/
|
||||||
def jscFlavor = 'org.webkit:android-jsc:+'
|
def jscFlavor = 'org.webkit:android-jsc:+'
|
||||||
|
|
||||||
/**
|
|
||||||
* Whether to enable the Hermes VM.
|
|
||||||
*
|
|
||||||
* This should be set on project.ext.react and that value will be read here. If it is not set
|
|
||||||
* on project.ext.react, JavaScript will not be compiled to Hermes Bytecode
|
|
||||||
* and the benefits of using Hermes will therefore be sharply reduced.
|
|
||||||
*/
|
|
||||||
def enableHermes = project.ext.react.get("enableHermes", false);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Architectures to build native code for.
|
|
||||||
*/
|
|
||||||
def reactNativeArchitectures() {
|
|
||||||
def value = project.getProperties().get("reactNativeArchitectures")
|
|
||||||
return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"]
|
|
||||||
}
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
ndkVersion rootProject.ext.ndkVersion
|
ndkVersion rootProject.ext.ndkVersion
|
||||||
|
|
||||||
compileSdkVersion rootProject.ext.compileSdkVersion
|
compileSdkVersion rootProject.ext.compileSdkVersion
|
||||||
|
|
||||||
|
namespace "com.awesomeproject"
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.awesomeproject"
|
applicationId "com.awesomeproject"
|
||||||
minSdkVersion rootProject.ext.minSdkVersion
|
minSdkVersion rootProject.ext.minSdkVersion
|
||||||
targetSdkVersion rootProject.ext.targetSdkVersion
|
targetSdkVersion rootProject.ext.targetSdkVersion
|
||||||
versionCode 1
|
versionCode 1
|
||||||
versionName "1.0"
|
versionName "1.0"
|
||||||
testBuildType System.getProperty('testBuildType', 'debug')
|
|
||||||
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
|
|
||||||
buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled().toString()
|
|
||||||
|
|
||||||
if (isNewArchitectureEnabled()) {
|
|
||||||
// We configure the NDK build only if you decide to opt-in for the New Architecture.
|
|
||||||
externalNativeBuild {
|
|
||||||
ndkBuild {
|
|
||||||
arguments "APP_PLATFORM=android-21",
|
|
||||||
"APP_STL=c++_shared",
|
|
||||||
"NDK_TOOLCHAIN_VERSION=clang",
|
|
||||||
"GENERATED_SRC_DIR=$buildDir/generated/source",
|
|
||||||
"PROJECT_BUILD_DIR=$buildDir",
|
|
||||||
"REACT_ANDROID_DIR=$rootDir/../node_modules/react-native/ReactAndroid",
|
|
||||||
"REACT_ANDROID_BUILD_DIR=$rootDir/../node_modules/react-native/ReactAndroid/build",
|
|
||||||
"NODE_MODULES_DIR=$rootDir/../node_modules"
|
|
||||||
cFlags "-Wall", "-Werror", "-fexceptions", "-frtti", "-DWITH_INSPECTOR=1"
|
|
||||||
cppFlags "-std=c++17"
|
|
||||||
// Make sure this target name is the same you specify inside the
|
|
||||||
// src/main/jni/Android.mk file for the `LOCAL_MODULE` variable.
|
|
||||||
targets "awesomeproject_appmodules"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!enableSeparateBuildPerCPUArchitecture) {
|
|
||||||
ndk {
|
|
||||||
abiFilters (*reactNativeArchitectures())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isNewArchitectureEnabled()) {
|
|
||||||
// We configure the NDK build only if you decide to opt-in for the New Architecture.
|
|
||||||
externalNativeBuild {
|
|
||||||
ndkBuild {
|
|
||||||
path "$projectDir/src/main/jni/Android.mk"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
def reactAndroidProjectDir = project(':ReactAndroid').projectDir
|
|
||||||
def packageReactNdkDebugLibs = tasks.register("packageReactNdkDebugLibs", Copy) {
|
|
||||||
dependsOn(":ReactAndroid:packageReactNdkDebugLibsForBuck")
|
|
||||||
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
|
|
||||||
into("$buildDir/react-ndk/exported")
|
|
||||||
}
|
|
||||||
def packageReactNdkReleaseLibs = tasks.register("packageReactNdkReleaseLibs", Copy) {
|
|
||||||
dependsOn(":ReactAndroid:packageReactNdkReleaseLibsForBuck")
|
|
||||||
from("$reactAndroidProjectDir/src/main/jni/prebuilt/lib")
|
|
||||||
into("$buildDir/react-ndk/exported")
|
|
||||||
}
|
|
||||||
afterEvaluate {
|
|
||||||
// If you wish to add a custom TurboModule or component locally,
|
|
||||||
// you should uncomment this line.
|
|
||||||
// preBuild.dependsOn("generateCodegenArtifactsFromSchema")
|
|
||||||
preDebugBuild.dependsOn(packageReactNdkDebugLibs)
|
|
||||||
preReleaseBuild.dependsOn(packageReactNdkReleaseLibs)
|
|
||||||
|
|
||||||
// Due to a bug inside AGP, we have to explicitly set a dependency
|
|
||||||
// between configureNdkBuild* tasks and the preBuild tasks.
|
|
||||||
// This can be removed once this is solved: https://issuetracker.google.com/issues/207403732
|
|
||||||
configureNdkBuildRelease.dependsOn(preReleaseBuild)
|
|
||||||
configureNdkBuildDebug.dependsOn(preDebugBuild)
|
|
||||||
reactNativeArchitectures().each { architecture ->
|
|
||||||
tasks.findByName("configureNdkBuildDebug[${architecture}]")?.configure {
|
|
||||||
dependsOn("preDebugBuild")
|
|
||||||
}
|
|
||||||
tasks.findByName("configureNdkBuildRelease[${architecture}]")?.configure {
|
|
||||||
dependsOn("preReleaseBuild")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
splits {
|
|
||||||
abi {
|
|
||||||
reset()
|
|
||||||
enable enableSeparateBuildPerCPUArchitecture
|
|
||||||
universalApk false // If true, also generate a universal APK
|
|
||||||
include (*reactNativeArchitectures())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
signingConfigs {
|
signingConfigs {
|
||||||
debug {
|
debug {
|
||||||
@@ -240,89 +101,25 @@ android {
|
|||||||
signingConfig signingConfigs.debug
|
signingConfig signingConfigs.debug
|
||||||
minifyEnabled enableProguardInReleaseBuilds
|
minifyEnabled enableProguardInReleaseBuilds
|
||||||
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro"
|
||||||
proguardFile "${rootProject.projectDir}/../node_modules/detox/android/detox/proguard-rules-app.pro"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// applicationVariants are e.g. debug, release
|
|
||||||
applicationVariants.all { variant ->
|
|
||||||
variant.outputs.each { output ->
|
|
||||||
// For each separate APK per architecture, set a unique version code as described here:
|
|
||||||
// https://developer.android.com/studio/build/configure-apk-splits.html
|
|
||||||
// Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc.
|
|
||||||
def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4]
|
|
||||||
def abi = output.getFilter(OutputFile.ABI)
|
|
||||||
if (abi != null) { // null for the universal-debug, universal-release variants
|
|
||||||
output.versionCodeOverride =
|
|
||||||
defaultConfig.versionCode * 1000 + versionCodes.get(abi)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
androidTestImplementation('com.wix:detox:+')
|
// The version of react-native is set by the React Native Gradle Plugin
|
||||||
implementation 'androidx.appcompat:appcompat:1.1.0'
|
implementation("com.facebook.react:react-android")
|
||||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
|
||||||
|
|
||||||
//noinspection GradleDynamicVersion
|
|
||||||
implementation "com.facebook.react:react-native:+" // From node_modules
|
|
||||||
|
|
||||||
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0"
|
|
||||||
|
|
||||||
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") {
|
|
||||||
exclude group:'com.facebook.fbjni'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}")
|
||||||
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
|
debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") {
|
||||||
exclude group:'com.facebook.flipper'
|
|
||||||
exclude group:'com.squareup.okhttp3', module:'okhttp'
|
exclude group:'com.squareup.okhttp3', module:'okhttp'
|
||||||
}
|
}
|
||||||
|
|
||||||
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
|
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}")
|
||||||
exclude group:'com.facebook.flipper'
|
if (hermesEnabled.toBoolean()) {
|
||||||
}
|
implementation("com.facebook.react:hermes-android")
|
||||||
|
|
||||||
if (enableHermes) {
|
|
||||||
//noinspection GradleDynamicVersion
|
|
||||||
implementation("com.facebook.react:hermes-engine:+") { // From node_modules
|
|
||||||
exclude group:'com.facebook.fbjni'
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
implementation jscFlavor
|
implementation jscFlavor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isNewArchitectureEnabled()) {
|
|
||||||
// If new architecture is enabled, we let you build RN from source
|
|
||||||
// Otherwise we fallback to a prebuilt .aar bundled in the NPM package.
|
|
||||||
// This will be applied to all the imported transtitive dependency.
|
|
||||||
configurations.all {
|
|
||||||
resolutionStrategy.dependencySubstitution {
|
|
||||||
substitute(module("com.facebook.react:react-native"))
|
|
||||||
.using(project(":ReactAndroid"))
|
|
||||||
.because("On New Architecture we're building React Native from source")
|
|
||||||
substitute(module("com.facebook.react:hermes-engine"))
|
|
||||||
.using(project(":ReactAndroid:hermes-engine"))
|
|
||||||
.because("On New Architecture we're building Hermes from source")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run this once to be able to run the application with BUCK
|
|
||||||
// puts all compile dependencies into folder libs for BUCK to use
|
|
||||||
task copyDownloadableDepsToLibs(type: Copy) {
|
|
||||||
from configurations.implementation
|
|
||||||
into 'libs'
|
|
||||||
}
|
|
||||||
|
|
||||||
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)
|
||||||
|
|
||||||
def isNewArchitectureEnabled() {
|
|
||||||
// To opt-in for the New Architecture, you can either:
|
|
||||||
// - Set `newArchEnabled` to true inside the `gradle.properties` file
|
|
||||||
// - Invoke gradle with `-newArchEnabled=true`
|
|
||||||
// - Set an environment variable `ORG_GRADLE_PROJECT_newArchEnabled=true`
|
|
||||||
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
|
|
||||||
}
|
|
||||||
|
@@ -2,6 +2,8 @@
|
|||||||
package="com.awesomeproject">
|
package="com.awesomeproject">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".MainApplication"
|
android:name=".MainApplication"
|
||||||
@@ -19,8 +21,14 @@
|
|||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:exported="true">
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
<data android:scheme="testhotupdate" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
@@ -2,7 +2,8 @@ package com.awesomeproject;
|
|||||||
|
|
||||||
import com.facebook.react.ReactActivity;
|
import com.facebook.react.ReactActivity;
|
||||||
import com.facebook.react.ReactActivityDelegate;
|
import com.facebook.react.ReactActivityDelegate;
|
||||||
import com.facebook.react.ReactRootView;
|
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||||
|
import com.facebook.react.defaults.DefaultReactActivityDelegate;
|
||||||
|
|
||||||
public class MainActivity extends ReactActivity {
|
public class MainActivity extends ReactActivity {
|
||||||
|
|
||||||
@@ -16,33 +17,16 @@ public class MainActivity extends ReactActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and
|
* Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
|
||||||
* you can specify the renderer you wish to use - the new renderer (Fabric) or the old renderer
|
* DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
|
||||||
* (Paper).
|
* (aka React 18) with two boolean flags.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected ReactActivityDelegate createReactActivityDelegate() {
|
protected ReactActivityDelegate createReactActivityDelegate() {
|
||||||
return new MainActivityDelegate(this, getMainComponentName());
|
return new DefaultReactActivityDelegate(
|
||||||
}
|
this,
|
||||||
|
getMainComponentName(),
|
||||||
public static class MainActivityDelegate extends ReactActivityDelegate {
|
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
|
||||||
public MainActivityDelegate(ReactActivity activity, String mainComponentName) {
|
DefaultNewArchitectureEntryPoint.getFabricEnabled());
|
||||||
super(activity, mainComponentName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected ReactRootView createRootView() {
|
|
||||||
ReactRootView reactRootView = new ReactRootView(getContext());
|
|
||||||
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
|
|
||||||
reactRootView.setIsFabric(BuildConfig.IS_NEW_ARCHITECTURE_ENABLED);
|
|
||||||
return reactRootView;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean isConcurrentRootEnabled() {
|
|
||||||
// If you opted-in for the New Architecture, we enable Concurrent Root (i.e. React 18).
|
|
||||||
// More on this on https://reactjs.org/blog/2022/03/29/react-v18.html
|
|
||||||
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,104 +1,70 @@
|
|||||||
package com.awesomeproject;
|
package com.awesomeproject;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.annotation.Nullable;
|
|
||||||
|
|
||||||
import com.facebook.react.PackageList;
|
import com.facebook.react.PackageList;
|
||||||
import com.facebook.react.ReactApplication;
|
import com.facebook.react.ReactApplication;
|
||||||
import com.facebook.react.ReactInstanceManager;
|
|
||||||
import com.facebook.react.ReactNativeHost;
|
import com.facebook.react.ReactNativeHost;
|
||||||
import com.facebook.react.ReactPackage;
|
import com.facebook.react.ReactPackage;
|
||||||
import com.facebook.react.config.ReactFeatureFlags;
|
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint;
|
||||||
import com.facebook.react.shell.MainReactPackage;
|
import com.facebook.react.defaults.DefaultReactNativeHost;
|
||||||
import com.facebook.soloader.SoLoader;
|
import com.facebook.soloader.SoLoader;
|
||||||
import com.awesomeproject.newarchitecture.MainApplicationReactNativeHost;
|
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import cn.reactnative.modules.update.UpdateContext;
|
import cn.reactnative.modules.update.UpdateContext;
|
||||||
import cn.reactnative.modules.update.UpdatePackage;
|
|
||||||
|
|
||||||
public class MainApplication extends Application implements ReactApplication {
|
public class MainApplication extends Application implements ReactApplication {
|
||||||
|
|
||||||
private final ReactNativeHost mReactNativeHost =
|
private final ReactNativeHost mReactNativeHost =
|
||||||
new ReactNativeHost(this) {
|
new DefaultReactNativeHost(this) {
|
||||||
@Override
|
@Override
|
||||||
public boolean getUseDeveloperSupport() {
|
public boolean getUseDeveloperSupport() {
|
||||||
return BuildConfig.DEBUG;
|
return BuildConfig.DEBUG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Override
|
||||||
@Override
|
protected String getJSBundleFile() {
|
||||||
protected String getJSBundleFile() {
|
return UpdateContext.getBundleUrl(MainApplication.this);
|
||||||
return UpdateContext.getBundleUrl(MainApplication.this);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected List<ReactPackage> getPackages() {
|
protected List<ReactPackage> getPackages() {
|
||||||
@SuppressWarnings("UnnecessaryLocalVariable")
|
@SuppressWarnings("UnnecessaryLocalVariable")
|
||||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
List<ReactPackage> packages = new PackageList(this).getPackages();
|
||||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
// Packages that cannot be autolinked yet can be added manually here, for example:
|
||||||
// packages.add(new MyReactNativePackage());
|
// packages.add(new MyReactNativePackage());
|
||||||
return packages;
|
return packages;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String getJSMainModuleName() {
|
protected boolean isNewArchEnabled() {
|
||||||
return "index";
|
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
@Override
|
||||||
|
protected Boolean isHermesEnabled() {
|
||||||
|
return BuildConfig.IS_HERMES_ENABLED;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getJSMainModuleName() {
|
||||||
|
return "index";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
private final ReactNativeHost mNewArchitectureNativeHost =
|
|
||||||
new MainApplicationReactNativeHost(this);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ReactNativeHost getReactNativeHost() {
|
public ReactNativeHost getReactNativeHost() {
|
||||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
return mReactNativeHost;
|
||||||
return mNewArchitectureNativeHost;
|
|
||||||
} else {
|
|
||||||
return mReactNativeHost;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate() {
|
public void onCreate() {
|
||||||
super.onCreate();
|
super.onCreate();
|
||||||
// If you opted-in for the New Architecture, we enable the TurboModule system
|
|
||||||
ReactFeatureFlags.useTurboModules = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
|
||||||
SoLoader.init(this, /* native exopackage */ false);
|
SoLoader.init(this, /* native exopackage */ false);
|
||||||
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||||
}
|
// If you opted-in for the New Architecture, we load the native entry point for this app.
|
||||||
|
DefaultNewArchitectureEntryPoint.load();
|
||||||
/**
|
|
||||||
* Loads Flipper in React Native templates. Call this in the onCreate method with something like
|
|
||||||
* initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
|
||||||
*
|
|
||||||
* @param context
|
|
||||||
* @param reactInstanceManager
|
|
||||||
*/
|
|
||||||
private static void initializeFlipper(
|
|
||||||
Context context, ReactInstanceManager reactInstanceManager) {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
try {
|
|
||||||
/*
|
|
||||||
We use reflection here to pick up the class that initializes Flipper,
|
|
||||||
since Flipper library is not available in release mode
|
|
||||||
*/
|
|
||||||
Class<?> aClass = Class.forName("com.awesomeproject.ReactNativeFlipper");
|
|
||||||
aClass
|
|
||||||
.getMethod("initializeFlipper", Context.class, ReactInstanceManager.class)
|
|
||||||
.invoke(null, context, reactInstanceManager);
|
|
||||||
} catch (ClassNotFoundException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} catch (InvocationTargetException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,116 +0,0 @@
|
|||||||
package com.awesomeproject.newarchitecture;
|
|
||||||
|
|
||||||
import android.app.Application;
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
import com.facebook.react.PackageList;
|
|
||||||
import com.facebook.react.ReactInstanceManager;
|
|
||||||
import com.facebook.react.ReactNativeHost;
|
|
||||||
import com.facebook.react.ReactPackage;
|
|
||||||
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
|
|
||||||
import com.facebook.react.bridge.JSIModulePackage;
|
|
||||||
import com.facebook.react.bridge.JSIModuleProvider;
|
|
||||||
import com.facebook.react.bridge.JSIModuleSpec;
|
|
||||||
import com.facebook.react.bridge.JSIModuleType;
|
|
||||||
import com.facebook.react.bridge.JavaScriptContextHolder;
|
|
||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
|
||||||
import com.facebook.react.bridge.UIManager;
|
|
||||||
import com.facebook.react.fabric.ComponentFactory;
|
|
||||||
import com.facebook.react.fabric.CoreComponentsRegistry;
|
|
||||||
import com.facebook.react.fabric.FabricJSIModuleProvider;
|
|
||||||
import com.facebook.react.fabric.ReactNativeConfig;
|
|
||||||
import com.facebook.react.uimanager.ViewManagerRegistry;
|
|
||||||
import com.awesomeproject.BuildConfig;
|
|
||||||
import com.awesomeproject.newarchitecture.components.MainComponentsRegistry;
|
|
||||||
import com.awesomeproject.newarchitecture.modules.MainApplicationTurboModuleManagerDelegate;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link ReactNativeHost} that helps you load everything needed for the New Architecture, both
|
|
||||||
* TurboModule delegates and the Fabric Renderer.
|
|
||||||
*
|
|
||||||
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
|
|
||||||
* `newArchEnabled` property). Is ignored otherwise.
|
|
||||||
*/
|
|
||||||
public class MainApplicationReactNativeHost extends ReactNativeHost {
|
|
||||||
public MainApplicationReactNativeHost(Application application) {
|
|
||||||
super(application);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean getUseDeveloperSupport() {
|
|
||||||
return BuildConfig.DEBUG;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected List<ReactPackage> getPackages() {
|
|
||||||
List<ReactPackage> packages = new PackageList(this).getPackages();
|
|
||||||
// Packages that cannot be autolinked yet can be added manually here, for example:
|
|
||||||
// packages.add(new MyReactNativePackage());
|
|
||||||
// TurboModules must also be loaded here providing a valid TurboReactPackage implementation:
|
|
||||||
// packages.add(new TurboReactPackage() { ... });
|
|
||||||
// If you have custom Fabric Components, their ViewManagers should also be loaded here
|
|
||||||
// inside a ReactPackage.
|
|
||||||
return packages;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getJSMainModuleName() {
|
|
||||||
return "index";
|
|
||||||
}
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
@Override
|
|
||||||
protected ReactPackageTurboModuleManagerDelegate.Builder
|
|
||||||
getReactPackageTurboModuleManagerDelegateBuilder() {
|
|
||||||
// Here we provide the ReactPackageTurboModuleManagerDelegate Builder. This is necessary
|
|
||||||
// for the new architecture and to use TurboModules correctly.
|
|
||||||
return new MainApplicationTurboModuleManagerDelegate.Builder();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected JSIModulePackage getJSIModulePackage() {
|
|
||||||
return new JSIModulePackage() {
|
|
||||||
@Override
|
|
||||||
public List<JSIModuleSpec> getJSIModules(
|
|
||||||
final ReactApplicationContext reactApplicationContext,
|
|
||||||
final JavaScriptContextHolder jsContext) {
|
|
||||||
final List<JSIModuleSpec> specs = new ArrayList<>();
|
|
||||||
|
|
||||||
// Here we provide a new JSIModuleSpec that will be responsible of providing the
|
|
||||||
// custom Fabric Components.
|
|
||||||
specs.add(
|
|
||||||
new JSIModuleSpec() {
|
|
||||||
@Override
|
|
||||||
public JSIModuleType getJSIModuleType() {
|
|
||||||
return JSIModuleType.UIManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JSIModuleProvider<UIManager> getJSIModuleProvider() {
|
|
||||||
final ComponentFactory componentFactory = new ComponentFactory();
|
|
||||||
CoreComponentsRegistry.register(componentFactory);
|
|
||||||
|
|
||||||
// Here we register a Components Registry.
|
|
||||||
// The one that is generated with the template contains no components
|
|
||||||
// and just provides you the one from React Native core.
|
|
||||||
MainComponentsRegistry.register(componentFactory);
|
|
||||||
|
|
||||||
final ReactInstanceManager reactInstanceManager = getReactInstanceManager();
|
|
||||||
|
|
||||||
ViewManagerRegistry viewManagerRegistry =
|
|
||||||
new ViewManagerRegistry(
|
|
||||||
reactInstanceManager.getOrCreateViewManagers(reactApplicationContext));
|
|
||||||
|
|
||||||
return new FabricJSIModuleProvider(
|
|
||||||
reactApplicationContext,
|
|
||||||
componentFactory,
|
|
||||||
ReactNativeConfig.DEFAULT_CONFIG,
|
|
||||||
viewManagerRegistry);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return specs;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,36 +0,0 @@
|
|||||||
package com.awesomeproject.newarchitecture.components;
|
|
||||||
|
|
||||||
import com.facebook.jni.HybridData;
|
|
||||||
import com.facebook.proguard.annotations.DoNotStrip;
|
|
||||||
import com.facebook.react.fabric.ComponentFactory;
|
|
||||||
import com.facebook.soloader.SoLoader;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class responsible to load the custom Fabric Components. This class has native methods and needs a
|
|
||||||
* corresponding C++ implementation/header file to work correctly (already placed inside the jni/
|
|
||||||
* folder for you).
|
|
||||||
*
|
|
||||||
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
|
|
||||||
* `newArchEnabled` property). Is ignored otherwise.
|
|
||||||
*/
|
|
||||||
@DoNotStrip
|
|
||||||
public class MainComponentsRegistry {
|
|
||||||
static {
|
|
||||||
SoLoader.loadLibrary("fabricjni");
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotStrip private final HybridData mHybridData;
|
|
||||||
|
|
||||||
@DoNotStrip
|
|
||||||
private native HybridData initHybrid(ComponentFactory componentFactory);
|
|
||||||
|
|
||||||
@DoNotStrip
|
|
||||||
private MainComponentsRegistry(ComponentFactory componentFactory) {
|
|
||||||
mHybridData = initHybrid(componentFactory);
|
|
||||||
}
|
|
||||||
|
|
||||||
@DoNotStrip
|
|
||||||
public static MainComponentsRegistry register(ComponentFactory componentFactory) {
|
|
||||||
return new MainComponentsRegistry(componentFactory);
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,48 +0,0 @@
|
|||||||
package com.awesomeproject.newarchitecture.modules;
|
|
||||||
|
|
||||||
import com.facebook.jni.HybridData;
|
|
||||||
import com.facebook.react.ReactPackage;
|
|
||||||
import com.facebook.react.ReactPackageTurboModuleManagerDelegate;
|
|
||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
|
||||||
import com.facebook.soloader.SoLoader;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class responsible to load the TurboModules. This class has native methods and needs a
|
|
||||||
* corresponding C++ implementation/header file to work correctly (already placed inside the jni/
|
|
||||||
* folder for you).
|
|
||||||
*
|
|
||||||
* <p>Please note that this class is used ONLY if you opt-in for the New Architecture (see the
|
|
||||||
* `newArchEnabled` property). Is ignored otherwise.
|
|
||||||
*/
|
|
||||||
public class MainApplicationTurboModuleManagerDelegate
|
|
||||||
extends ReactPackageTurboModuleManagerDelegate {
|
|
||||||
|
|
||||||
private static volatile boolean sIsSoLibraryLoaded;
|
|
||||||
|
|
||||||
protected MainApplicationTurboModuleManagerDelegate(
|
|
||||||
ReactApplicationContext reactApplicationContext, List<ReactPackage> packages) {
|
|
||||||
super(reactApplicationContext, packages);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected native HybridData initHybrid();
|
|
||||||
|
|
||||||
native boolean canCreateTurboModule(String moduleName);
|
|
||||||
|
|
||||||
public static class Builder extends ReactPackageTurboModuleManagerDelegate.Builder {
|
|
||||||
protected MainApplicationTurboModuleManagerDelegate build(
|
|
||||||
ReactApplicationContext context, List<ReactPackage> packages) {
|
|
||||||
return new MainApplicationTurboModuleManagerDelegate(context, packages);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected synchronized void maybeLoadOtherSoLibraries() {
|
|
||||||
if (!sIsSoLibraryLoaded) {
|
|
||||||
// If you change the name of your application .so file in the Android.mk file,
|
|
||||||
// make sure you update the name here as well.
|
|
||||||
SoLoader.loadLibrary("awesomeproject_appmodules");
|
|
||||||
sIsSoLibraryLoaded = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,48 +0,0 @@
|
|||||||
THIS_DIR := $(call my-dir)
|
|
||||||
|
|
||||||
include $(REACT_ANDROID_DIR)/Android-prebuilt.mk
|
|
||||||
|
|
||||||
# If you wish to add a custom TurboModule or Fabric component in your app you
|
|
||||||
# will have to include the following autogenerated makefile.
|
|
||||||
# include $(GENERATED_SRC_DIR)/codegen/jni/Android.mk
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
|
|
||||||
LOCAL_PATH := $(THIS_DIR)
|
|
||||||
|
|
||||||
# You can customize the name of your application .so file here.
|
|
||||||
LOCAL_MODULE := awesomeproject_appmodules
|
|
||||||
|
|
||||||
LOCAL_C_INCLUDES := $(LOCAL_PATH)
|
|
||||||
LOCAL_SRC_FILES := $(wildcard $(LOCAL_PATH)/*.cpp)
|
|
||||||
LOCAL_EXPORT_C_INCLUDES := $(LOCAL_PATH)
|
|
||||||
|
|
||||||
# If you wish to add a custom TurboModule or Fabric component in your app you
|
|
||||||
# will have to uncomment those lines to include the generated source
|
|
||||||
# files from the codegen (placed in $(GENERATED_SRC_DIR)/codegen/jni)
|
|
||||||
#
|
|
||||||
# LOCAL_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni
|
|
||||||
# LOCAL_SRC_FILES += $(wildcard $(GENERATED_SRC_DIR)/codegen/jni/*.cpp)
|
|
||||||
# LOCAL_EXPORT_C_INCLUDES += $(GENERATED_SRC_DIR)/codegen/jni
|
|
||||||
|
|
||||||
# Here you should add any native library you wish to depend on.
|
|
||||||
LOCAL_SHARED_LIBRARIES := \
|
|
||||||
libfabricjni \
|
|
||||||
libfbjni \
|
|
||||||
libfolly_runtime \
|
|
||||||
libglog \
|
|
||||||
libjsi \
|
|
||||||
libreact_codegen_rncore \
|
|
||||||
libreact_debug \
|
|
||||||
libreact_nativemodule_core \
|
|
||||||
libreact_render_componentregistry \
|
|
||||||
libreact_render_core \
|
|
||||||
libreact_render_debug \
|
|
||||||
libreact_render_graphics \
|
|
||||||
librrc_view \
|
|
||||||
libruntimeexecutor \
|
|
||||||
libturbomodulejsijni \
|
|
||||||
libyoga
|
|
||||||
|
|
||||||
LOCAL_CFLAGS := -DLOG_TAG=\"ReactNative\" -fexceptions -frtti -std=c++17 -Wall
|
|
||||||
|
|
||||||
include $(BUILD_SHARED_LIBRARY)
|
|
@@ -1,24 +0,0 @@
|
|||||||
#include "MainApplicationModuleProvider.h"
|
|
||||||
|
|
||||||
#include <rncore.h>
|
|
||||||
|
|
||||||
namespace facebook {
|
|
||||||
namespace react {
|
|
||||||
|
|
||||||
std::shared_ptr<TurboModule> MainApplicationModuleProvider(
|
|
||||||
const std::string moduleName,
|
|
||||||
const JavaTurboModule::InitParams ¶ms) {
|
|
||||||
// Here you can provide your own module provider for TurboModules coming from
|
|
||||||
// either your application or from external libraries. The approach to follow
|
|
||||||
// is similar to the following (for a library called `samplelibrary`:
|
|
||||||
//
|
|
||||||
// auto module = samplelibrary_ModuleProvider(moduleName, params);
|
|
||||||
// if (module != nullptr) {
|
|
||||||
// return module;
|
|
||||||
// }
|
|
||||||
// return rncore_ModuleProvider(moduleName, params);
|
|
||||||
return rncore_ModuleProvider(moduleName, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace react
|
|
||||||
} // namespace facebook
|
|
@@ -1,16 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <ReactCommon/JavaTurboModule.h>
|
|
||||||
|
|
||||||
namespace facebook {
|
|
||||||
namespace react {
|
|
||||||
|
|
||||||
std::shared_ptr<TurboModule> MainApplicationModuleProvider(
|
|
||||||
const std::string moduleName,
|
|
||||||
const JavaTurboModule::InitParams ¶ms);
|
|
||||||
|
|
||||||
} // namespace react
|
|
||||||
} // namespace facebook
|
|
@@ -1,45 +0,0 @@
|
|||||||
#include "MainApplicationTurboModuleManagerDelegate.h"
|
|
||||||
#include "MainApplicationModuleProvider.h"
|
|
||||||
|
|
||||||
namespace facebook {
|
|
||||||
namespace react {
|
|
||||||
|
|
||||||
jni::local_ref<MainApplicationTurboModuleManagerDelegate::jhybriddata>
|
|
||||||
MainApplicationTurboModuleManagerDelegate::initHybrid(
|
|
||||||
jni::alias_ref<jhybridobject>) {
|
|
||||||
return makeCxxInstance();
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainApplicationTurboModuleManagerDelegate::registerNatives() {
|
|
||||||
registerHybrid({
|
|
||||||
makeNativeMethod(
|
|
||||||
"initHybrid", MainApplicationTurboModuleManagerDelegate::initHybrid),
|
|
||||||
makeNativeMethod(
|
|
||||||
"canCreateTurboModule",
|
|
||||||
MainApplicationTurboModuleManagerDelegate::canCreateTurboModule),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<TurboModule>
|
|
||||||
MainApplicationTurboModuleManagerDelegate::getTurboModule(
|
|
||||||
const std::string name,
|
|
||||||
const std::shared_ptr<CallInvoker> jsInvoker) {
|
|
||||||
// Not implemented yet: provide pure-C++ NativeModules here.
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::shared_ptr<TurboModule>
|
|
||||||
MainApplicationTurboModuleManagerDelegate::getTurboModule(
|
|
||||||
const std::string name,
|
|
||||||
const JavaTurboModule::InitParams ¶ms) {
|
|
||||||
return MainApplicationModuleProvider(name, params);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool MainApplicationTurboModuleManagerDelegate::canCreateTurboModule(
|
|
||||||
std::string name) {
|
|
||||||
return getTurboModule(name, nullptr) != nullptr ||
|
|
||||||
getTurboModule(name, {.moduleName = name}) != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace react
|
|
||||||
} // namespace facebook
|
|
@@ -1,38 +0,0 @@
|
|||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
#include <ReactCommon/TurboModuleManagerDelegate.h>
|
|
||||||
#include <fbjni/fbjni.h>
|
|
||||||
|
|
||||||
namespace facebook {
|
|
||||||
namespace react {
|
|
||||||
|
|
||||||
class MainApplicationTurboModuleManagerDelegate
|
|
||||||
: public jni::HybridClass<
|
|
||||||
MainApplicationTurboModuleManagerDelegate,
|
|
||||||
TurboModuleManagerDelegate> {
|
|
||||||
public:
|
|
||||||
// Adapt it to the package you used for your Java class.
|
|
||||||
static constexpr auto kJavaDescriptor =
|
|
||||||
"Lcom/awesomeproject/newarchitecture/modules/MainApplicationTurboModuleManagerDelegate;";
|
|
||||||
|
|
||||||
static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject>);
|
|
||||||
|
|
||||||
static void registerNatives();
|
|
||||||
|
|
||||||
std::shared_ptr<TurboModule> getTurboModule(
|
|
||||||
const std::string name,
|
|
||||||
const std::shared_ptr<CallInvoker> jsInvoker) override;
|
|
||||||
std::shared_ptr<TurboModule> getTurboModule(
|
|
||||||
const std::string name,
|
|
||||||
const JavaTurboModule::InitParams ¶ms) override;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test-only method. Allows user to verify whether a TurboModule can be
|
|
||||||
* created by instances of this class.
|
|
||||||
*/
|
|
||||||
bool canCreateTurboModule(std::string name);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace react
|
|
||||||
} // namespace facebook
|
|
@@ -1,61 +0,0 @@
|
|||||||
#include "MainComponentsRegistry.h"
|
|
||||||
|
|
||||||
#include <CoreComponentsRegistry.h>
|
|
||||||
#include <fbjni/fbjni.h>
|
|
||||||
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
|
|
||||||
#include <react/renderer/components/rncore/ComponentDescriptors.h>
|
|
||||||
|
|
||||||
namespace facebook {
|
|
||||||
namespace react {
|
|
||||||
|
|
||||||
MainComponentsRegistry::MainComponentsRegistry(ComponentFactory *delegate) {}
|
|
||||||
|
|
||||||
std::shared_ptr<ComponentDescriptorProviderRegistry const>
|
|
||||||
MainComponentsRegistry::sharedProviderRegistry() {
|
|
||||||
auto providerRegistry = CoreComponentsRegistry::sharedProviderRegistry();
|
|
||||||
|
|
||||||
// Custom Fabric Components go here. You can register custom
|
|
||||||
// components coming from your App or from 3rd party libraries here.
|
|
||||||
//
|
|
||||||
// providerRegistry->add(concreteComponentDescriptorProvider<
|
|
||||||
// AocViewerComponentDescriptor>());
|
|
||||||
return providerRegistry;
|
|
||||||
}
|
|
||||||
|
|
||||||
jni::local_ref<MainComponentsRegistry::jhybriddata>
|
|
||||||
MainComponentsRegistry::initHybrid(
|
|
||||||
jni::alias_ref<jclass>,
|
|
||||||
ComponentFactory *delegate) {
|
|
||||||
auto instance = makeCxxInstance(delegate);
|
|
||||||
|
|
||||||
auto buildRegistryFunction =
|
|
||||||
[](EventDispatcher::Weak const &eventDispatcher,
|
|
||||||
ContextContainer::Shared const &contextContainer)
|
|
||||||
-> ComponentDescriptorRegistry::Shared {
|
|
||||||
auto registry = MainComponentsRegistry::sharedProviderRegistry()
|
|
||||||
->createComponentDescriptorRegistry(
|
|
||||||
{eventDispatcher, contextContainer});
|
|
||||||
|
|
||||||
auto mutableRegistry =
|
|
||||||
std::const_pointer_cast<ComponentDescriptorRegistry>(registry);
|
|
||||||
|
|
||||||
mutableRegistry->setFallbackComponentDescriptor(
|
|
||||||
std::make_shared<UnimplementedNativeViewComponentDescriptor>(
|
|
||||||
ComponentDescriptorParameters{
|
|
||||||
eventDispatcher, contextContainer, nullptr}));
|
|
||||||
|
|
||||||
return registry;
|
|
||||||
};
|
|
||||||
|
|
||||||
delegate->buildRegistryFunction = buildRegistryFunction;
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainComponentsRegistry::registerNatives() {
|
|
||||||
registerHybrid({
|
|
||||||
makeNativeMethod("initHybrid", MainComponentsRegistry::initHybrid),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace react
|
|
||||||
} // namespace facebook
|
|
@@ -1,32 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <ComponentFactory.h>
|
|
||||||
#include <fbjni/fbjni.h>
|
|
||||||
#include <react/renderer/componentregistry/ComponentDescriptorProviderRegistry.h>
|
|
||||||
#include <react/renderer/componentregistry/ComponentDescriptorRegistry.h>
|
|
||||||
|
|
||||||
namespace facebook {
|
|
||||||
namespace react {
|
|
||||||
|
|
||||||
class MainComponentsRegistry
|
|
||||||
: public facebook::jni::HybridClass<MainComponentsRegistry> {
|
|
||||||
public:
|
|
||||||
// Adapt it to the package you used for your Java class.
|
|
||||||
constexpr static auto kJavaDescriptor =
|
|
||||||
"Lcom/awesomeproject/newarchitecture/components/MainComponentsRegistry;";
|
|
||||||
|
|
||||||
static void registerNatives();
|
|
||||||
|
|
||||||
MainComponentsRegistry(ComponentFactory *delegate);
|
|
||||||
|
|
||||||
private:
|
|
||||||
static std::shared_ptr<ComponentDescriptorProviderRegistry const>
|
|
||||||
sharedProviderRegistry();
|
|
||||||
|
|
||||||
static jni::local_ref<jhybriddata> initHybrid(
|
|
||||||
jni::alias_ref<jclass>,
|
|
||||||
ComponentFactory *delegate);
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace react
|
|
||||||
} // namespace facebook
|
|
@@ -1,11 +0,0 @@
|
|||||||
#include <fbjni/fbjni.h>
|
|
||||||
#include "MainApplicationTurboModuleManagerDelegate.h"
|
|
||||||
#include "MainComponentsRegistry.h"
|
|
||||||
|
|
||||||
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
|
|
||||||
return facebook::jni::initialize(vm, [] {
|
|
||||||
facebook::react::MainApplicationTurboModuleManagerDelegate::
|
|
||||||
registerNatives();
|
|
||||||
facebook::react::MainComponentsRegistry::registerNatives();
|
|
||||||
});
|
|
||||||
}
|
|
@@ -1,58 +1,24 @@
|
|||||||
import org.apache.tools.ant.taskdefs.condition.Os
|
|
||||||
|
|
||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext {
|
ext {
|
||||||
buildToolsVersion = "31.0.0"
|
buildToolsVersion = "33.0.0"
|
||||||
minSdkVersion = 21
|
minSdkVersion = 23
|
||||||
compileSdkVersion = 31
|
compileSdkVersion = 33
|
||||||
targetSdkVersion = 31
|
targetSdkVersion = 33
|
||||||
kotlinVersion = '1.6.10'
|
|
||||||
|
|
||||||
if (System.properties['os.arch'] == "aarch64") {
|
// We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP.
|
||||||
// For M1 Users we need to use the NDK 24 which added support for aarch64
|
ndkVersion = "23.1.7779620"
|
||||||
ndkVersion = "24.0.8215888"
|
|
||||||
} else {
|
kotlin_version = '1.7.20'
|
||||||
// Otherwise we default to the side-by-side NDK version from AGP.
|
|
||||||
ndkVersion = "21.4.7075529"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
repositories {
|
repositories {
|
||||||
google()
|
google()
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath("com.android.tools.build:gradle:7.1.1")
|
classpath("com.android.tools.build:gradle")
|
||||||
classpath("com.facebook.react:react-native-gradle-plugin")
|
classpath("com.facebook.react:react-native-gradle-plugin")
|
||||||
classpath("de.undercouch:gradle-download-task:5.0.1")
|
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version")
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
|
||||||
// in the individual module build.gradle files
|
|
||||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
allprojects {
|
|
||||||
repositories {
|
|
||||||
maven {
|
|
||||||
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
|
|
||||||
url("$rootDir/../node_modules/react-native/android")
|
|
||||||
}
|
|
||||||
maven {
|
|
||||||
// Android JSC is installed from npm
|
|
||||||
url("$rootDir/../node_modules/jsc-android/dist")
|
|
||||||
}
|
|
||||||
mavenCentral {
|
|
||||||
// We don't want to fetch react-native from Maven Central as there are
|
|
||||||
// older versions over there.
|
|
||||||
content {
|
|
||||||
excludeGroup "com.facebook.react"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
google()
|
|
||||||
maven {
|
|
||||||
url("$rootDir/../node_modules/detox/Detox-android")
|
|
||||||
}
|
|
||||||
maven { url 'https://www.jitpack.io' }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -25,7 +25,7 @@ android.useAndroidX=true
|
|||||||
android.enableJetifier=true
|
android.enableJetifier=true
|
||||||
|
|
||||||
# Version of flipper SDK to use with React Native
|
# Version of flipper SDK to use with React Native
|
||||||
FLIPPER_VERSION=0.125.0
|
FLIPPER_VERSION=0.182.0
|
||||||
|
|
||||||
# Use this property to specify which architecture you want to build.
|
# Use this property to specify which architecture you want to build.
|
||||||
# You can also override it from the CLI using
|
# You can also override it from the CLI using
|
||||||
@@ -38,3 +38,7 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
|||||||
# to write custom TurboModules/Fabric components OR use libraries that
|
# to write custom TurboModules/Fabric components OR use libraries that
|
||||||
# are providing them.
|
# are providing them.
|
||||||
newArchEnabled=false
|
newArchEnabled=false
|
||||||
|
|
||||||
|
# Use this property to enable or disable the Hermes JS engine.
|
||||||
|
# If set to false, you will be using JSC instead.
|
||||||
|
hermesEnabled=true
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-all.zip
|
||||||
|
networkTimeout=10000
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
18
Example/testHotUpdate/android/gradlew
vendored
18
Example/testHotUpdate/android/gradlew
vendored
@@ -55,7 +55,7 @@
|
|||||||
# Darwin, MinGW, and NonStop.
|
# Darwin, MinGW, and NonStop.
|
||||||
#
|
#
|
||||||
# (3) This script is generated from the Groovy template
|
# (3) This script is generated from the Groovy template
|
||||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||||
# within the Gradle project.
|
# within the Gradle project.
|
||||||
#
|
#
|
||||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||||
@@ -80,10 +80,10 @@ do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
# This is normally unused
|
||||||
|
# shellcheck disable=SC2034
|
||||||
APP_NAME="Gradle"
|
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
|
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
@@ -143,12 +143,16 @@ fi
|
|||||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
max*)
|
max*)
|
||||||
|
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC3045
|
||||||
MAX_FD=$( ulimit -H -n ) ||
|
MAX_FD=$( ulimit -H -n ) ||
|
||||||
warn "Could not query maximum file descriptor limit"
|
warn "Could not query maximum file descriptor limit"
|
||||||
esac
|
esac
|
||||||
case $MAX_FD in #(
|
case $MAX_FD in #(
|
||||||
'' | soft) :;; #(
|
'' | soft) :;; #(
|
||||||
*)
|
*)
|
||||||
|
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||||
|
# shellcheck disable=SC3045
|
||||||
ulimit -n "$MAX_FD" ||
|
ulimit -n "$MAX_FD" ||
|
||||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||||
esac
|
esac
|
||||||
@@ -205,6 +209,12 @@ set -- \
|
|||||||
org.gradle.wrapper.GradleWrapperMain \
|
org.gradle.wrapper.GradleWrapperMain \
|
||||||
"$@"
|
"$@"
|
||||||
|
|
||||||
|
# Stop when "xargs" is not available.
|
||||||
|
if ! command -v xargs >/dev/null 2>&1
|
||||||
|
then
|
||||||
|
die "xargs is not available"
|
||||||
|
fi
|
||||||
|
|
||||||
# Use "xargs" to parse quoted args.
|
# Use "xargs" to parse quoted args.
|
||||||
#
|
#
|
||||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||||
|
15
Example/testHotUpdate/android/gradlew.bat
vendored
15
Example/testHotUpdate/android/gradlew.bat
vendored
@@ -14,7 +14,7 @@
|
|||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
|
|
||||||
@if "%DEBUG%" == "" @echo off
|
@if "%DEBUG%"=="" @echo off
|
||||||
@rem ##########################################################################
|
@rem ##########################################################################
|
||||||
@rem
|
@rem
|
||||||
@rem Gradle startup script for Windows
|
@rem Gradle startup script for Windows
|
||||||
@@ -25,7 +25,8 @@
|
|||||||
if "%OS%"=="Windows_NT" setlocal
|
if "%OS%"=="Windows_NT" setlocal
|
||||||
|
|
||||||
set DIRNAME=%~dp0
|
set DIRNAME=%~dp0
|
||||||
if "%DIRNAME%" == "" set DIRNAME=.
|
if "%DIRNAME%"=="" set DIRNAME=.
|
||||||
|
@rem This is normally unused
|
||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
@@ -40,7 +41,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
|
|||||||
|
|
||||||
set JAVA_EXE=java.exe
|
set JAVA_EXE=java.exe
|
||||||
%JAVA_EXE% -version >NUL 2>&1
|
%JAVA_EXE% -version >NUL 2>&1
|
||||||
if "%ERRORLEVEL%" == "0" goto execute
|
if %ERRORLEVEL% equ 0 goto execute
|
||||||
|
|
||||||
echo.
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
@@ -75,13 +76,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
|||||||
|
|
||||||
:end
|
:end
|
||||||
@rem End local scope for the variables with windows NT shell
|
@rem End local scope for the variables with windows NT shell
|
||||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||||
|
|
||||||
:fail
|
:fail
|
||||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||||
rem the _cmd.exe /c_ return code!
|
rem the _cmd.exe /c_ return code!
|
||||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
set EXIT_CODE=%ERRORLEVEL%
|
||||||
exit /b 1
|
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||||
|
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||||
|
exit /b %EXIT_CODE%
|
||||||
|
|
||||||
:mainEnd
|
:mainEnd
|
||||||
if "%OS%"=="Windows_NT" endlocal
|
if "%OS%"=="Windows_NT" endlocal
|
||||||
|
@@ -1,11 +1,4 @@
|
|||||||
rootProject.name = 'AwesomeProject'
|
rootProject.name = 'AwesomeProject'
|
||||||
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
|
apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)
|
||||||
include ':app'
|
include ':app'
|
||||||
includeBuild('../node_modules/react-native-gradle-plugin')
|
includeBuild('../node_modules/@react-native/gradle-plugin')
|
||||||
|
|
||||||
if (settings.hasProperty("newArchEnabled") && settings.newArchEnabled == "true") {
|
|
||||||
include(":ReactAndroid")
|
|
||||||
project(":ReactAndroid").projectDir = file('../node_modules/react-native/ReactAndroid')
|
|
||||||
include(":ReactAndroid:hermes-engine")
|
|
||||||
project(":ReactAndroid:hermes-engine").projectDir = file('../node_modules/react-native/ReactAndroid/hermes-engine')
|
|
||||||
}
|
|
||||||
|
@@ -1,3 +1,8 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: ['module:metro-react-native-babel-preset'],
|
presets: ['module:metro-react-native-babel-preset'],
|
||||||
|
env: {
|
||||||
|
production: {
|
||||||
|
plugins: ['react-native-paper/babel'],
|
||||||
|
},
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
@@ -28,14 +28,7 @@ describe('测试Native模块的方法', () => {
|
|||||||
await element(by.id('done')).longPress();
|
await element(by.id('done')).longPress();
|
||||||
await expect(element(by.id('done'))).toBeNotVisible();
|
await expect(element(by.id('done'))).toBeNotVisible();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('setBlockUpdate', async () => {
|
|
||||||
await element(by.id('setBlockUpdate')).longPress();
|
|
||||||
await element(by.id('submit')).longPress();
|
|
||||||
await expect(element(by.text('done'))).toBeVisible();
|
|
||||||
await element(by.id('done')).longPress();
|
|
||||||
await expect(element(by.id('done'))).toBeNotVisible();
|
|
||||||
});
|
|
||||||
if (device.getPlatform() === 'android') {
|
if (device.getPlatform() === 'android') {
|
||||||
it('reloadUpdate', async () => {
|
it('reloadUpdate', async () => {
|
||||||
await element(by.id('reloadUpdate')).longPress();
|
await element(by.id('reloadUpdate')).longPress();
|
||||||
|
@@ -7,29 +7,14 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
00E356F31AD99517003FC87E /* AwesomeProjectTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* AwesomeProjectTests.m */; };
|
|
||||||
0C80B921A6F3F58F76C31292 /* libPods-AwesomeProject.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-AwesomeProject.a */; };
|
0C80B921A6F3F58F76C31292 /* libPods-AwesomeProject.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-AwesomeProject.a */; };
|
||||||
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
|
13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; };
|
||||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; };
|
||||||
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; };
|
||||||
7699B88040F8A987B510C191 /* libPods-AwesomeProject-AwesomeProjectTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-AwesomeProject-AwesomeProjectTests.a */; };
|
|
||||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
|
||||||
00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = {
|
|
||||||
isa = PBXContainerItemProxy;
|
|
||||||
containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */;
|
|
||||||
proxyType = 1;
|
|
||||||
remoteGlobalIDString = 13B07F861A680F5B00A75B9A;
|
|
||||||
remoteInfo = AwesomeProject;
|
|
||||||
};
|
|
||||||
/* End PBXContainerItemProxy section */
|
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
00E356EE1AD99517003FC87E /* AwesomeProjectTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AwesomeProjectTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
|
||||||
00E356F21AD99517003FC87E /* AwesomeProjectTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AwesomeProjectTests.m; sourceTree = "<group>"; };
|
|
||||||
13B07F961A680F5B00A75B9A /* AwesomeProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AwesomeProject.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
13B07F961A680F5B00A75B9A /* AwesomeProject.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AwesomeProject.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = AwesomeProject/AppDelegate.h; sourceTree = "<group>"; };
|
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = AwesomeProject/AppDelegate.h; sourceTree = "<group>"; };
|
||||||
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = AwesomeProject/AppDelegate.mm; sourceTree = "<group>"; };
|
13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = AwesomeProject/AppDelegate.mm; sourceTree = "<group>"; };
|
||||||
@@ -47,14 +32,6 @@
|
|||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
00E356EB1AD99517003FC87E /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
7699B88040F8A987B510C191 /* libPods-AwesomeProject-AwesomeProjectTests.a in Frameworks */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
|
13B07F8C1A680F5B00A75B9A /* Frameworks */ = {
|
||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -66,23 +43,6 @@
|
|||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
00E356EF1AD99517003FC87E /* AwesomeProjectTests */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
00E356F21AD99517003FC87E /* AwesomeProjectTests.m */,
|
|
||||||
00E356F01AD99517003FC87E /* Supporting Files */,
|
|
||||||
);
|
|
||||||
path = AwesomeProjectTests;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
00E356F01AD99517003FC87E /* Supporting Files */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
00E356F11AD99517003FC87E /* Info.plist */,
|
|
||||||
);
|
|
||||||
name = "Supporting Files";
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
13B07FAE1A68108700A75B9A /* AwesomeProject */ = {
|
13B07FAE1A68108700A75B9A /* AwesomeProject */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@@ -118,7 +78,6 @@
|
|||||||
children = (
|
children = (
|
||||||
13B07FAE1A68108700A75B9A /* AwesomeProject */,
|
13B07FAE1A68108700A75B9A /* AwesomeProject */,
|
||||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||||
00E356EF1AD99517003FC87E /* AwesomeProjectTests */,
|
|
||||||
83CBBA001A601CBA00E9B192 /* Products */,
|
83CBBA001A601CBA00E9B192 /* Products */,
|
||||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||||
BBD78D7AC51CEA395F1C20DB /* Pods */,
|
BBD78D7AC51CEA395F1C20DB /* Pods */,
|
||||||
@@ -132,7 +91,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
13B07F961A680F5B00A75B9A /* AwesomeProject.app */,
|
13B07F961A680F5B00A75B9A /* AwesomeProject.app */,
|
||||||
00E356EE1AD99517003FC87E /* AwesomeProjectTests.xctest */,
|
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -151,27 +109,6 @@
|
|||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
00E356ED1AD99517003FC87E /* AwesomeProjectTests */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "AwesomeProjectTests" */;
|
|
||||||
buildPhases = (
|
|
||||||
A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */,
|
|
||||||
00E356EA1AD99517003FC87E /* Sources */,
|
|
||||||
00E356EB1AD99517003FC87E /* Frameworks */,
|
|
||||||
00E356EC1AD99517003FC87E /* Resources */,
|
|
||||||
C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */,
|
|
||||||
F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
00E356F51AD99517003FC87E /* PBXTargetDependency */,
|
|
||||||
);
|
|
||||||
name = AwesomeProjectTests;
|
|
||||||
productName = AwesomeProjectTests;
|
|
||||||
productReference = 00E356EE1AD99517003FC87E /* AwesomeProjectTests.xctest */;
|
|
||||||
productType = "com.apple.product-type.bundle.unit-test";
|
|
||||||
};
|
|
||||||
13B07F861A680F5B00A75B9A /* AwesomeProject */ = {
|
13B07F861A680F5B00A75B9A /* AwesomeProject */ = {
|
||||||
isa = PBXNativeTarget;
|
isa = PBXNativeTarget;
|
||||||
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "AwesomeProject" */;
|
buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "AwesomeProject" */;
|
||||||
@@ -182,8 +119,8 @@
|
|||||||
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
13B07F8C1A680F5B00A75B9A /* Frameworks */,
|
||||||
13B07F8E1A680F5B00A75B9A /* Resources */,
|
13B07F8E1A680F5B00A75B9A /* Resources */,
|
||||||
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
|
||||||
00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */,
|
|
||||||
E235C05ADACE081382539298 /* [CP] Copy Pods Resources */,
|
E235C05ADACE081382539298 /* [CP] Copy Pods Resources */,
|
||||||
|
2177C9C260D54703D642190E /* [CP] Embed Pods Frameworks */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@@ -202,10 +139,6 @@
|
|||||||
attributes = {
|
attributes = {
|
||||||
LastUpgradeCheck = 1210;
|
LastUpgradeCheck = 1210;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
00E356ED1AD99517003FC87E = {
|
|
||||||
CreatedOnToolsVersion = 6.2;
|
|
||||||
TestTargetID = 13B07F861A680F5B00A75B9A;
|
|
||||||
};
|
|
||||||
13B07F861A680F5B00A75B9A = {
|
13B07F861A680F5B00A75B9A = {
|
||||||
LastSwiftMigration = 1120;
|
LastSwiftMigration = 1120;
|
||||||
};
|
};
|
||||||
@@ -225,19 +158,11 @@
|
|||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
13B07F861A680F5B00A75B9A /* AwesomeProject */,
|
13B07F861A680F5B00A75B9A /* AwesomeProject */,
|
||||||
00E356ED1AD99517003FC87E /* AwesomeProjectTests */,
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
|
||||||
/* Begin PBXResourcesBuildPhase section */
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
00E356EC1AD99517003FC87E /* Resources */ = {
|
|
||||||
isa = PBXResourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
13B07F8E1A680F5B00A75B9A /* Resources */ = {
|
13B07F8E1A680F5B00A75B9A /* Resources */ = {
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -266,7 +191,7 @@
|
|||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "set -e\n\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
|
shellScript = "set -e\n\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n";
|
||||||
};
|
};
|
||||||
00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = {
|
2177C9C260D54703D642190E /* [CP] Embed Pods Frameworks */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
@@ -283,28 +208,6 @@
|
|||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject-frameworks.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject-frameworks.sh\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
);
|
|
||||||
inputPaths = (
|
|
||||||
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
|
|
||||||
"${PODS_ROOT}/Manifest.lock",
|
|
||||||
);
|
|
||||||
name = "[CP] Check Pods Manifest.lock";
|
|
||||||
outputFileListPaths = (
|
|
||||||
);
|
|
||||||
outputPaths = (
|
|
||||||
"$(DERIVED_FILE_DIR)/Pods-AwesomeProject-AwesomeProjectTests-checkManifestLockResult.txt",
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = {
|
C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -327,23 +230,6 @@
|
|||||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests-frameworks-${CONFIGURATION}-input-files.xcfilelist",
|
|
||||||
);
|
|
||||||
name = "[CP] Embed Pods Frameworks";
|
|
||||||
outputFileListPaths = (
|
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests-frameworks-${CONFIGURATION}-output-files.xcfilelist",
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests-frameworks.sh\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = {
|
E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -361,23 +247,6 @@
|
|||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject-resources.sh\"\n";
|
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject-resources.sh\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */ = {
|
|
||||||
isa = PBXShellScriptBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
inputFileListPaths = (
|
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests-resources-${CONFIGURATION}-input-files.xcfilelist",
|
|
||||||
);
|
|
||||||
name = "[CP] Copy Pods Resources";
|
|
||||||
outputFileListPaths = (
|
|
||||||
"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests-resources-${CONFIGURATION}-output-files.xcfilelist",
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
shellPath = /bin/sh;
|
|
||||||
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests-resources.sh\"\n";
|
|
||||||
showEnvVarsInLog = 0;
|
|
||||||
};
|
|
||||||
FD10A7F022414F080027D42C /* Start Packager */ = {
|
FD10A7F022414F080027D42C /* Start Packager */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -400,14 +269,6 @@
|
|||||||
/* End PBXShellScriptBuildPhase section */
|
/* End PBXShellScriptBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
/* Begin PBXSourcesBuildPhase section */
|
||||||
00E356EA1AD99517003FC87E /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
00E356F31AD99517003FC87E /* AwesomeProjectTests.m in Sources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
13B07F871A680F5B00A75B9A /* Sources */ = {
|
13B07F871A680F5B00A75B9A /* Sources */ = {
|
||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@@ -419,66 +280,7 @@
|
|||||||
};
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXTargetDependency section */
|
|
||||||
00E356F51AD99517003FC87E /* PBXTargetDependency */ = {
|
|
||||||
isa = PBXTargetDependency;
|
|
||||||
target = 13B07F861A680F5B00A75B9A /* AwesomeProject */;
|
|
||||||
targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */;
|
|
||||||
};
|
|
||||||
/* End PBXTargetDependency section */
|
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
00E356F61AD99517003FC87E /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
baseConfigurationReference = 5B7EB9410499542E8C5724F5 /* Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig */;
|
|
||||||
buildSettings = {
|
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
|
||||||
"DEBUG=1",
|
|
||||||
"$(inherited)",
|
|
||||||
);
|
|
||||||
INFOPLIST_FILE = AwesomeProjectTests/Info.plist;
|
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
|
||||||
"$(inherited)",
|
|
||||||
"@executable_path/Frameworks",
|
|
||||||
"@loader_path/Frameworks",
|
|
||||||
);
|
|
||||||
OTHER_LDFLAGS = (
|
|
||||||
"-ObjC",
|
|
||||||
"-lc++",
|
|
||||||
"$(inherited)",
|
|
||||||
);
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AwesomeProject.app/AwesomeProject";
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
00E356F71AD99517003FC87E /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
baseConfigurationReference = 89C6BE57DB24E9ADA2F236DE /* Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig */;
|
|
||||||
buildSettings = {
|
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
|
||||||
COPY_PHASE_STRIP = NO;
|
|
||||||
INFOPLIST_FILE = AwesomeProjectTests/Info.plist;
|
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 12.4;
|
|
||||||
LD_RUNPATH_SEARCH_PATHS = (
|
|
||||||
"$(inherited)",
|
|
||||||
"@executable_path/Frameworks",
|
|
||||||
"@loader_path/Frameworks",
|
|
||||||
);
|
|
||||||
OTHER_LDFLAGS = (
|
|
||||||
"-ObjC",
|
|
||||||
"-lc++",
|
|
||||||
"$(inherited)",
|
|
||||||
);
|
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)";
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/AwesomeProject.app/AwesomeProject";
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
13B07F941A680F5B00A75B9A /* Debug */ = {
|
13B07F941A680F5B00A75B9A /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-AwesomeProject.debug.xcconfig */;
|
baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-AwesomeProject.debug.xcconfig */;
|
||||||
@@ -562,7 +364,7 @@
|
|||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
ENABLE_TESTABILITY = YES;
|
ENABLE_TESTABILITY = YES;
|
||||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
@@ -570,6 +372,7 @@
|
|||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
"DEBUG=1",
|
"DEBUG=1",
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION,
|
||||||
);
|
);
|
||||||
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
@@ -590,12 +393,20 @@
|
|||||||
);
|
);
|
||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
|
OTHER_CFLAGS = "$(inherited)";
|
||||||
OTHER_CPLUSPLUSFLAGS = (
|
OTHER_CPLUSPLUSFLAGS = (
|
||||||
"$(OTHER_CFLAGS)",
|
"$(OTHER_CFLAGS)",
|
||||||
"-DFOLLY_NO_CONFIG",
|
"-DFOLLY_NO_CONFIG",
|
||||||
"-DFOLLY_MOBILE=1",
|
"-DFOLLY_MOBILE=1",
|
||||||
"-DFOLLY_USE_LIBCPP=1",
|
"-DFOLLY_USE_LIBCPP=1",
|
||||||
);
|
);
|
||||||
|
OTHER_LDFLAGS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"-Wl",
|
||||||
|
"-ld_classic",
|
||||||
|
" ",
|
||||||
|
"-Wl -ld_classic ",
|
||||||
|
);
|
||||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
};
|
};
|
||||||
@@ -634,9 +445,13 @@
|
|||||||
COPY_PHASE_STRIP = YES;
|
COPY_PHASE_STRIP = YES;
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu99;
|
GCC_C_LANGUAGE_STANDARD = gnu99;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
|
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||||
|
"$(inherited)",
|
||||||
|
_LIBCPP_ENABLE_CXX17_REMOVED_UNARY_BINARY_FUNCTION,
|
||||||
|
);
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
||||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||||
@@ -654,12 +469,20 @@
|
|||||||
"\"$(inherited)\"",
|
"\"$(inherited)\"",
|
||||||
);
|
);
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
|
OTHER_CFLAGS = "$(inherited)";
|
||||||
OTHER_CPLUSPLUSFLAGS = (
|
OTHER_CPLUSPLUSFLAGS = (
|
||||||
"$(OTHER_CFLAGS)",
|
"$(OTHER_CFLAGS)",
|
||||||
"-DFOLLY_NO_CONFIG",
|
"-DFOLLY_NO_CONFIG",
|
||||||
"-DFOLLY_MOBILE=1",
|
"-DFOLLY_MOBILE=1",
|
||||||
"-DFOLLY_USE_LIBCPP=1",
|
"-DFOLLY_USE_LIBCPP=1",
|
||||||
);
|
);
|
||||||
|
OTHER_LDFLAGS = (
|
||||||
|
"$(inherited)",
|
||||||
|
"-Wl",
|
||||||
|
"-ld_classic",
|
||||||
|
" ",
|
||||||
|
"-Wl -ld_classic ",
|
||||||
|
);
|
||||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
@@ -669,15 +492,6 @@
|
|||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "AwesomeProjectTests" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
00E356F61AD99517003FC87E /* Debug */,
|
|
||||||
00E356F71AD99517003FC87E /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "AwesomeProject" */ = {
|
13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "AwesomeProject" */ = {
|
||||||
isa = XCConfigurationList;
|
isa = XCConfigurationList;
|
||||||
buildConfigurations = (
|
buildConfigurations = (
|
||||||
|
@@ -1,8 +1,6 @@
|
|||||||
#import <React/RCTBridgeDelegate.h>
|
#import <RCTAppDelegate.h>
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>
|
@interface AppDelegate : RCTAppDelegate
|
||||||
|
|
||||||
@property (nonatomic, strong) UIWindow *window;
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@@ -1,85 +1,18 @@
|
|||||||
#import "AppDelegate.h"
|
#import "AppDelegate.h"
|
||||||
#import "RCTPushy.h"
|
#import "RCTPushy.h"
|
||||||
#import <React/RCTBridge.h>
|
|
||||||
#import <React/RCTBundleURLProvider.h>
|
#import <React/RCTBundleURLProvider.h>
|
||||||
#import <React/RCTRootView.h>
|
|
||||||
|
|
||||||
#import <React/RCTAppSetupUtils.h>
|
|
||||||
|
|
||||||
#if RCT_NEW_ARCH_ENABLED
|
|
||||||
#import <React/CoreModulesPlugins.h>
|
|
||||||
#import <React/RCTCxxBridgeDelegate.h>
|
|
||||||
#import <React/RCTFabricSurfaceHostingProxyRootView.h>
|
|
||||||
#import <React/RCTSurfacePresenter.h>
|
|
||||||
#import <React/RCTSurfacePresenterBridgeAdapter.h>
|
|
||||||
#import <ReactCommon/RCTTurboModuleManager.h>
|
|
||||||
|
|
||||||
#import <react/config/ReactNativeConfig.h>
|
|
||||||
|
|
||||||
static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|
||||||
|
|
||||||
@interface AppDelegate () <RCTCxxBridgeDelegate, RCTTurboModuleManagerDelegate> {
|
|
||||||
RCTTurboModuleManager *_turboModuleManager;
|
|
||||||
RCTSurfacePresenterBridgeAdapter *_bridgeAdapter;
|
|
||||||
std::shared_ptr<const facebook::react::ReactNativeConfig> _reactNativeConfig;
|
|
||||||
facebook::react::ContextContainer::Shared _contextContainer;
|
|
||||||
}
|
|
||||||
@end
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||||
{
|
{
|
||||||
RCTAppSetupPrepareApp(application);
|
self.moduleName = @"AwesomeProject";
|
||||||
|
// You can add your custom initial props in the dictionary below.
|
||||||
|
// They will be passed down to the ViewController used by React Native.
|
||||||
|
self.initialProps = @{};
|
||||||
|
|
||||||
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions];
|
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
||||||
|
|
||||||
#if RCT_NEW_ARCH_ENABLED
|
|
||||||
_contextContainer = std::make_shared<facebook::react::ContextContainer const>();
|
|
||||||
_reactNativeConfig = std::make_shared<facebook::react::EmptyReactNativeConfig const>();
|
|
||||||
_contextContainer->insert("ReactNativeConfig", _reactNativeConfig);
|
|
||||||
_bridgeAdapter = [[RCTSurfacePresenterBridgeAdapter alloc] initWithBridge:bridge contextContainer:_contextContainer];
|
|
||||||
bridge.surfacePresenter = _bridgeAdapter.surfacePresenter;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
NSDictionary *initProps = [self prepareInitialProps];
|
|
||||||
UIView *rootView = RCTAppSetupDefaultRootView(bridge, @"AwesomeProject", initProps);
|
|
||||||
|
|
||||||
if (@available(iOS 13.0, *)) {
|
|
||||||
rootView.backgroundColor = [UIColor systemBackgroundColor];
|
|
||||||
} else {
|
|
||||||
rootView.backgroundColor = [UIColor whiteColor];
|
|
||||||
}
|
|
||||||
|
|
||||||
self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
|
|
||||||
UIViewController *rootViewController = [UIViewController new];
|
|
||||||
rootViewController.view = rootView;
|
|
||||||
self.window.rootViewController = rootViewController;
|
|
||||||
[self.window makeKeyAndVisible];
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off.
|
|
||||||
///
|
|
||||||
/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html
|
|
||||||
/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture).
|
|
||||||
/// @return: `true` if the `concurrentRoot` feture is enabled. Otherwise, it returns `false`.
|
|
||||||
- (BOOL)concurrentRootEnabled
|
|
||||||
{
|
|
||||||
// Switch this bool to turn on and off the concurrent root
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSDictionary *)prepareInitialProps
|
|
||||||
{
|
|
||||||
NSMutableDictionary *initProps = [NSMutableDictionary new];
|
|
||||||
|
|
||||||
#ifdef RCT_NEW_ARCH_ENABLED
|
|
||||||
initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return initProps;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
||||||
@@ -91,43 +24,4 @@ static NSString *const kRNConcurrentRoot = @"concurrentRoot";
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if RCT_NEW_ARCH_ENABLED
|
|
||||||
|
|
||||||
#pragma mark - RCTCxxBridgeDelegate
|
|
||||||
|
|
||||||
- (std::unique_ptr<facebook::react::JSExecutorFactory>)jsExecutorFactoryForBridge:(RCTBridge *)bridge
|
|
||||||
{
|
|
||||||
_turboModuleManager = [[RCTTurboModuleManager alloc] initWithBridge:bridge
|
|
||||||
delegate:self
|
|
||||||
jsInvoker:bridge.jsCallInvoker];
|
|
||||||
return RCTAppSetupDefaultJsExecutorFactory(bridge, _turboModuleManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark RCTTurboModuleManagerDelegate
|
|
||||||
|
|
||||||
- (Class)getModuleClassFromName:(const char *)name
|
|
||||||
{
|
|
||||||
return RCTCoreModulesClassProvider(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
|
|
||||||
jsInvoker:(std::shared_ptr<facebook::react::CallInvoker>)jsInvoker
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:(const std::string &)name
|
|
||||||
initParams:
|
|
||||||
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
||||||
{
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (id<RCTTurboModule>)getModuleInstanceFromClass:(Class)moduleClass
|
|
||||||
{
|
|
||||||
return RCTAppSetupDefaultModuleFromClass(moduleClass);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@@ -53,5 +53,10 @@
|
|||||||
</array>
|
</array>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
<false/>
|
<false/>
|
||||||
|
<key>NSCameraUsageDescription</key>
|
||||||
|
<string>For taking photos</string>
|
||||||
|
|
||||||
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
|
<string>For saving photos</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
@@ -1,66 +0,0 @@
|
|||||||
#import <UIKit/UIKit.h>
|
|
||||||
#import <XCTest/XCTest.h>
|
|
||||||
|
|
||||||
#import <React/RCTLog.h>
|
|
||||||
#import <React/RCTRootView.h>
|
|
||||||
|
|
||||||
#define TIMEOUT_SECONDS 600
|
|
||||||
#define TEXT_TO_LOOK_FOR @"Welcome to React"
|
|
||||||
|
|
||||||
@interface AwesomeProjectTests : XCTestCase
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation AwesomeProjectTests
|
|
||||||
|
|
||||||
- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test
|
|
||||||
{
|
|
||||||
if (test(view)) {
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
for (UIView *subview in [view subviews]) {
|
|
||||||
if ([self findSubviewInView:subview matching:test]) {
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testRendersWelcomeScreen
|
|
||||||
{
|
|
||||||
UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController];
|
|
||||||
NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS];
|
|
||||||
BOOL foundElement = NO;
|
|
||||||
|
|
||||||
__block NSString *redboxError = nil;
|
|
||||||
#ifdef DEBUG
|
|
||||||
RCTSetLogFunction(
|
|
||||||
^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
|
|
||||||
if (level >= RCTLogLevelError) {
|
|
||||||
redboxError = message;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
#endif
|
|
||||||
|
|
||||||
while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) {
|
|
||||||
[[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
|
||||||
[[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
|
|
||||||
|
|
||||||
foundElement = [self findSubviewInView:vc.view
|
|
||||||
matching:^BOOL(UIView *view) {
|
|
||||||
if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) {
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
return NO;
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
RCTSetLogFunction(RCTDefaultLogFunction);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
XCTAssertNil(redboxError, @"RedBox error: %@", redboxError);
|
|
||||||
XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
@@ -1,24 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
|
||||||
<string>en</string>
|
|
||||||
<key>CFBundleExecutable</key>
|
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
|
||||||
<key>CFBundleIdentifier</key>
|
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
|
||||||
<string>6.0</string>
|
|
||||||
<key>CFBundleName</key>
|
|
||||||
<string>$(PRODUCT_NAME)</string>
|
|
||||||
<key>CFBundlePackageType</key>
|
|
||||||
<string>BNDL</string>
|
|
||||||
<key>CFBundleShortVersionString</key>
|
|
||||||
<string>1.0</string>
|
|
||||||
<key>CFBundleSignature</key>
|
|
||||||
<string>????</string>
|
|
||||||
<key>CFBundleVersion</key>
|
|
||||||
<string>1</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
@@ -1,10 +1,29 @@
|
|||||||
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
# Resolve react_native_pods.rb with node to allow for hoisting
|
||||||
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
require Pod::Executable.execute_command('node', ['-p',
|
||||||
|
'require.resolve(
|
||||||
|
"react-native/scripts/react_native_pods.rb",
|
||||||
|
{paths: [process.argv[1]]},
|
||||||
|
)', __dir__]).strip
|
||||||
|
|
||||||
platform :ios, '12.4'
|
platform :ios, min_ios_version_supported
|
||||||
install! 'cocoapods', :deterministic_uuids => false
|
prepare_react_native_project!
|
||||||
|
|
||||||
production = ENV["PRODUCTION"] == "1"
|
# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
|
||||||
|
# because `react-native-flipper` depends on (FlipperKit,...) that will be excluded
|
||||||
|
#
|
||||||
|
# To fix this you can also exclude `react-native-flipper` using a `react-native.config.js`
|
||||||
|
# ```js
|
||||||
|
# module.exports = {
|
||||||
|
# dependencies: {
|
||||||
|
# ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}),
|
||||||
|
# ```
|
||||||
|
flipper_config = FlipperConfiguration.disabled
|
||||||
|
|
||||||
|
linkage = ENV['USE_FRAMEWORKS']
|
||||||
|
if linkage != nil
|
||||||
|
Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green
|
||||||
|
use_frameworks! :linkage => linkage.to_sym
|
||||||
|
end
|
||||||
|
|
||||||
target 'AwesomeProject' do
|
target 'AwesomeProject' do
|
||||||
config = use_native_modules!
|
config = use_native_modules!
|
||||||
@@ -14,22 +33,25 @@ target 'AwesomeProject' do
|
|||||||
|
|
||||||
use_react_native!(
|
use_react_native!(
|
||||||
:path => config[:reactNativePath],
|
:path => config[:reactNativePath],
|
||||||
# to enable hermes on iOS, change `false` to `true` and then install pods
|
# Hermes is now enabled by default. Disable by setting this flag to false.
|
||||||
:production => production,
|
|
||||||
:hermes_enabled => flags[:hermes_enabled],
|
:hermes_enabled => flags[:hermes_enabled],
|
||||||
:fabric_enabled => flags[:fabric_enabled],
|
:fabric_enabled => flags[:fabric_enabled],
|
||||||
:flipper_configuration => FlipperConfiguration.enabled,
|
# Enables Flipper.
|
||||||
|
#
|
||||||
|
# Note that if you have use_frameworks! enabled, Flipper will not work and
|
||||||
|
# you should disable the next line.
|
||||||
|
:flipper_configuration => flipper_config,
|
||||||
# An absolute path to your application root.
|
# An absolute path to your application root.
|
||||||
:app_path => "#{Pod::Config.instance.installation_root}/.."
|
:app_path => "#{Pod::Config.instance.installation_root}/.."
|
||||||
)
|
)
|
||||||
|
|
||||||
target 'AwesomeProjectTests' do
|
|
||||||
inherit! :complete
|
|
||||||
# Pods for testing
|
|
||||||
end
|
|
||||||
|
|
||||||
post_install do |installer|
|
post_install do |installer|
|
||||||
react_native_post_install(installer)
|
# https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
|
||||||
|
react_native_post_install(
|
||||||
|
installer,
|
||||||
|
config[:reactNativePath],
|
||||||
|
:mac_catalyst_enabled => false
|
||||||
|
)
|
||||||
__apply_Xcode_12_5_M1_post_install_workaround(installer)
|
__apply_Xcode_12_5_M1_post_install_workaround(installer)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,24 +1,11 @@
|
|||||||
const path = require('path');
|
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
||||||
|
|
||||||
const extraNodeModules = {
|
/**
|
||||||
react: path.resolve(__dirname, 'node_modules/react'),
|
* Metro configuration
|
||||||
'react-native': path.resolve(__dirname, 'node_modules/react-native'),
|
* https://facebook.github.io/metro/docs/configuration
|
||||||
'react-native-update': path.resolve(__dirname, '../..'),
|
*
|
||||||
'@babel/runtime': path.resolve(__dirname, 'node_modules/@babel/runtime'),
|
* @type {import('metro-config').MetroConfig}
|
||||||
};
|
*/
|
||||||
const watchFolders = [path.resolve(__dirname, '../..')];
|
const config = {};
|
||||||
|
|
||||||
module.exports = {
|
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
|
||||||
transformer: {
|
|
||||||
getTransformOptions: async () => ({
|
|
||||||
transform: {
|
|
||||||
experimentalImportSupport: false,
|
|
||||||
inlineRequires: true,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
resolver: {
|
|
||||||
extraNodeModules,
|
|
||||||
},
|
|
||||||
watchFolders,
|
|
||||||
};
|
|
||||||
|
@@ -9,30 +9,39 @@
|
|||||||
"test": "jest",
|
"test": "jest",
|
||||||
"test:e2e": "detox test --configuration android.emu.debug",
|
"test:e2e": "detox test --configuration android.emu.debug",
|
||||||
"lint": "eslint .",
|
"lint": "eslint .",
|
||||||
"postinstall": "patch-package"
|
"postinstall": "patch-package",
|
||||||
|
"apk": "cd android && ./gradlew assembleRelease"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"patch-package": "^6.5.1",
|
"patch-package": "^8.0.0",
|
||||||
"postinstall-postinstall": "^2.1.0",
|
"postinstall-postinstall": "^2.1.0",
|
||||||
"react": "18.0.0",
|
"react": "18.2.0",
|
||||||
"react-native": "0.69.8",
|
"react-native": "0.72.5",
|
||||||
"react-native-update": "link:../.."
|
"react-native-camera-kit": "^14.0.0-beta15",
|
||||||
|
"react-native-paper": "^5.12.5",
|
||||||
|
"react-native-safe-area-context": "^4.11.0",
|
||||||
|
"react-native-update": "^10.15.0",
|
||||||
|
"react-native-vector-icons": "^10.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.21.0",
|
"@babel/core": "^7.20.0",
|
||||||
"@babel/runtime": "^7.21.0",
|
"@babel/preset-env": "^7.20.0",
|
||||||
"@react-native-community/eslint-config": "^3.2.0",
|
"@babel/runtime": "^7.20.0",
|
||||||
"babel-jest": "^29.5.0",
|
"@react-native/eslint-config": "^0.72.2",
|
||||||
"detox": "^20.5.0",
|
"@react-native/metro-config": "^0.72.11",
|
||||||
"eslint": "^8.36.0",
|
"@tsconfig/react-native": "^3.0.0",
|
||||||
"jest": "^29.5.0",
|
"@types/react": "^18.0.24",
|
||||||
"metro-react-native-babel-preset": "^0.76.0",
|
"@types/react-test-renderer": "^18.0.0",
|
||||||
"pod-install": "^0.1.37",
|
"babel-jest": "^29.2.1",
|
||||||
"prettier": "^2.8.4",
|
"eslint": "^8.19.0",
|
||||||
|
"jest": "^29.2.1",
|
||||||
|
"metro-react-native-babel-preset": "0.76.8",
|
||||||
|
"prettier": "^2.4.1",
|
||||||
"react-test-renderer": "18.2.0",
|
"react-test-renderer": "18.2.0",
|
||||||
"ts-jest": "^29.0.5"
|
"typescript": "5.6.3"
|
||||||
},
|
},
|
||||||
"jest": {
|
"engines": {
|
||||||
"preset": "react-native"
|
"node": ">=16"
|
||||||
}
|
},
|
||||||
|
"packageManager": "yarn@3.6.4"
|
||||||
}
|
}
|
||||||
|
@@ -19,23 +19,25 @@ const UUID = '00000000-0000-0000-0000-000000000000';
|
|||||||
const DownloadUrl =
|
const DownloadUrl =
|
||||||
'http://cos.pgyer.com/697913e94d7441f20c686e2b0996a1aa.apk?sign=7a8f11b1df82cba45c8ac30b1acec88c&t=1680404102&response-content-disposition=attachment%3Bfilename%3DtestHotupdate_1.0.apk';
|
'http://cos.pgyer.com/697913e94d7441f20c686e2b0996a1aa.apk?sign=7a8f11b1df82cba45c8ac30b1acec88c&t=1680404102&response-content-disposition=attachment%3Bfilename%3DtestHotupdate_1.0.apk';
|
||||||
|
|
||||||
|
const CustomDialog = ({title, visible, onConfirm}) => {
|
||||||
|
if (!visible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
const CustomDialog = ({title, visible, onConfirm}) => {
|
return (
|
||||||
if (!visible) {
|
<View style={styles.overlay}>
|
||||||
return null;
|
<View style={styles.dialog}>
|
||||||
}
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
<TouchableOpacity
|
||||||
return (
|
testID="done"
|
||||||
<View style={styles.overlay}>
|
style={styles.button}
|
||||||
<View style={styles.dialog}>
|
onLongPress={onConfirm}>
|
||||||
<Text style={styles.title}>{title}</Text>
|
<Text style={styles.buttonText}>确认</Text>
|
||||||
<TouchableOpacity testID='done' style={styles.button} onLongPress={onConfirm}>
|
</TouchableOpacity>
|
||||||
<Text style={styles.buttonText}>确认</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
</View>
|
|
||||||
</View>
|
</View>
|
||||||
);
|
</View>
|
||||||
};
|
);
|
||||||
|
};
|
||||||
export default function TestConsole({visible}) {
|
export default function TestConsole({visible}) {
|
||||||
const [text, setText] = useState('');
|
const [text, setText] = useState('');
|
||||||
const [running, setRunning] = useState(false);
|
const [running, setRunning] = useState(false);
|
||||||
@@ -64,13 +66,6 @@ export default function TestConsole({visible}) {
|
|||||||
setText(`setUuid\n${UUID}`);
|
setText(`setUuid\n${UUID}`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'setBlockUpdate',
|
|
||||||
invoke: () => {
|
|
||||||
setText('setBlockUpdate');
|
|
||||||
setOptions({reason: 'application has been block', until: 1673082950});
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'reloadUpdate',
|
name: 'reloadUpdate',
|
||||||
invoke: () => {
|
invoke: () => {
|
||||||
@@ -132,9 +127,8 @@ export default function TestConsole({visible}) {
|
|||||||
testID={NativeTestMethod[i].name}
|
testID={NativeTestMethod[i].name}
|
||||||
onLongPress={() => {
|
onLongPress={() => {
|
||||||
NativeTestMethod[i].invoke();
|
NativeTestMethod[i].invoke();
|
||||||
}}
|
}}>
|
||||||
>
|
<Text>{NativeTestMethod[i].name}</Text>
|
||||||
<Text>{NativeTestMethod[i].name}</Text>
|
|
||||||
</TouchableOpacity>,
|
</TouchableOpacity>,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -164,8 +158,14 @@ export default function TestConsole({visible}) {
|
|||||||
/>
|
/>
|
||||||
{running && <ActivityIndicator />}
|
{running && <ActivityIndicator />}
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={{backgroundColor:'rgb(0,140,237)', justifyContent: 'center',
|
style={{
|
||||||
alignItems: 'center',paddingTop:10,paddingBottom:10,marginBottom:5}}
|
backgroundColor: 'rgb(0,140,237)',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingTop: 10,
|
||||||
|
paddingBottom: 10,
|
||||||
|
marginBottom: 5,
|
||||||
|
}}
|
||||||
testID="submit"
|
testID="submit"
|
||||||
onLongPress={async () => {
|
onLongPress={async () => {
|
||||||
setRunning(true);
|
setRunning(true);
|
||||||
@@ -195,17 +195,18 @@ export default function TestConsole({visible}) {
|
|||||||
setAlertMsg(e.message);
|
setAlertMsg(e.message);
|
||||||
}
|
}
|
||||||
setRunning(false);
|
setRunning(false);
|
||||||
}}
|
}}>
|
||||||
>
|
<Text style={{color: 'white'}}>执行</Text>
|
||||||
<Text style={{color:'white'}}>执行</Text>
|
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
<Button title="重置" onPress={() => setText('')} />
|
<Button title="重置" onPress={() => setText('')} />
|
||||||
{renderTestView()}
|
{renderTestView()}
|
||||||
<CustomDialog
|
<CustomDialog
|
||||||
title={alertMsg}
|
title={alertMsg}
|
||||||
visible={alertVisible}
|
visible={alertVisible}
|
||||||
onConfirm={()=>{setAlertVisible(false)}}
|
onConfirm={() => {
|
||||||
/>
|
setAlertVisible(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
@@ -1,206 +0,0 @@
|
|||||||
import React, {Component} from 'react';
|
|
||||||
import {
|
|
||||||
StyleSheet,
|
|
||||||
Platform,
|
|
||||||
Text,
|
|
||||||
View,
|
|
||||||
Alert,
|
|
||||||
TouchableOpacity,
|
|
||||||
Linking,
|
|
||||||
Image,
|
|
||||||
} from 'react-native';
|
|
||||||
|
|
||||||
import {
|
|
||||||
isFirstTime,
|
|
||||||
isRolledBack,
|
|
||||||
packageVersion,
|
|
||||||
currentVersion,
|
|
||||||
checkUpdate,
|
|
||||||
downloadUpdate,
|
|
||||||
switchVersion,
|
|
||||||
switchVersionLater,
|
|
||||||
markSuccess,
|
|
||||||
downloadAndInstallApk,
|
|
||||||
cInfo,
|
|
||||||
} from 'react-native-update';
|
|
||||||
|
|
||||||
import TestConsole from './TestConsole';
|
|
||||||
|
|
||||||
import _updateConfig from '../update.json';
|
|
||||||
const {appKey} = _updateConfig[Platform.OS];
|
|
||||||
export default class App extends Component {
|
|
||||||
state = {
|
|
||||||
received: 0,
|
|
||||||
total: 0,
|
|
||||||
showTestConsole: false,
|
|
||||||
};
|
|
||||||
componentDidMount() {
|
|
||||||
if (isRolledBack) {
|
|
||||||
Alert.alert('提示', '刚刚更新失败了,版本被回滚.');
|
|
||||||
} else if (isFirstTime) {
|
|
||||||
Alert.alert(
|
|
||||||
'提示',
|
|
||||||
'这是当前版本第一次启动,是否要模拟启动失败?将回滚到上一版本',
|
|
||||||
[
|
|
||||||
{
|
|
||||||
text: '是',
|
|
||||||
onPress: () => {
|
|
||||||
throw new Error('模拟启动失败,请重启应用');
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '否',
|
|
||||||
onPress: () => {
|
|
||||||
markSuccess();
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
doUpdate = async info => {
|
|
||||||
try {
|
|
||||||
const hash = await downloadUpdate(info, {
|
|
||||||
onDownloadProgress: ({received, total}) => {
|
|
||||||
this.setState({
|
|
||||||
received,
|
|
||||||
total,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
if (!hash) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Alert.alert('提示', '下载完毕,是否重启应用?', [
|
|
||||||
{
|
|
||||||
text: '是',
|
|
||||||
onPress: () => {
|
|
||||||
switchVersion(hash);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{text: '否'},
|
|
||||||
{
|
|
||||||
text: '下次启动时',
|
|
||||||
onPress: () => {
|
|
||||||
switchVersionLater(hash);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
} catch (err) {
|
|
||||||
Alert.alert('更新失败', err.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
checkUpdate = async () => {
|
|
||||||
let info;
|
|
||||||
try {
|
|
||||||
info = await checkUpdate(appKey);
|
|
||||||
} catch (err) {
|
|
||||||
Alert.alert('更新检查失败', err.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (info.expired) {
|
|
||||||
Alert.alert('提示', '您的应用版本已更新,点击确定下载安装新版本', [
|
|
||||||
{
|
|
||||||
text: '确定',
|
|
||||||
onPress: () => {
|
|
||||||
if (info.downloadUrl) {
|
|
||||||
// apk可直接下载安装
|
|
||||||
if (
|
|
||||||
Platform.OS === 'android' &&
|
|
||||||
info.downloadUrl.endsWith('.apk')
|
|
||||||
) {
|
|
||||||
downloadAndInstallApk({
|
|
||||||
url: info.downloadUrl,
|
|
||||||
onDownloadProgress: ({received, total}) => {
|
|
||||||
this.setState({
|
|
||||||
received,
|
|
||||||
total,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Linking.openURL(info.downloadUrl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
} else if (info.upToDate) {
|
|
||||||
Alert.alert('提示', '您的应用版本已是最新.');
|
|
||||||
} else {
|
|
||||||
Alert.alert(
|
|
||||||
'提示',
|
|
||||||
'检查到新的版本' + info.name + ',是否下载?\n' + info.description,
|
|
||||||
[
|
|
||||||
{
|
|
||||||
text: '是',
|
|
||||||
onPress: () => {
|
|
||||||
this.doUpdate(info);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{text: '否'},
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
const {received, total, showTestConsole} = this.state;
|
|
||||||
return (
|
|
||||||
<View style={styles.container}>
|
|
||||||
<Text style={styles.welcome}>欢迎使用热更新服务</Text>
|
|
||||||
<Image
|
|
||||||
resizeMode={'contain'}
|
|
||||||
source={require('./assets/shezhi.png')}
|
|
||||||
style={styles.image}
|
|
||||||
/>
|
|
||||||
<Text style={styles.instructions}>
|
|
||||||
这是版本一 {'\n'}
|
|
||||||
当前原生包版本号: {packageVersion}
|
|
||||||
{'\n'}
|
|
||||||
当前热更新版本Hash: {currentVersion || '(空)'}
|
|
||||||
{'\n'}
|
|
||||||
</Text>
|
|
||||||
<Text>
|
|
||||||
下载进度:{received} / {total}
|
|
||||||
</Text>
|
|
||||||
<TouchableOpacity onPress={this.checkUpdate}>
|
|
||||||
<Text style={styles.instructions}>点击这里检查更新</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
|
|
||||||
<TouchableOpacity
|
|
||||||
testID="testcase"
|
|
||||||
style={{marginTop: 15}}
|
|
||||||
onLongPress={() => {
|
|
||||||
this.setState({showTestConsole: true});
|
|
||||||
}}>
|
|
||||||
<Text style={styles.instructions}>
|
|
||||||
react-native-update版本:{cInfo.pushy}
|
|
||||||
</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
<TestConsole visible={showTestConsole} />
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
container: {
|
|
||||||
flex: 1,
|
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
backgroundColor: '#F5FCFF',
|
|
||||||
},
|
|
||||||
welcome: {
|
|
||||||
fontSize: 20,
|
|
||||||
textAlign: 'center',
|
|
||||||
margin: 10,
|
|
||||||
},
|
|
||||||
instructions: {
|
|
||||||
textAlign: 'center',
|
|
||||||
color: '#333333',
|
|
||||||
marginBottom: 5,
|
|
||||||
},
|
|
||||||
image: {},
|
|
||||||
});
|
|
205
Example/testHotUpdate/src/index.tsx
Normal file
205
Example/testHotUpdate/src/index.tsx
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
/* eslint-disable react/no-unstable-nested-components */
|
||||||
|
/* eslint-disable react-native/no-inline-styles */
|
||||||
|
import React, {useRef, useState} from 'react';
|
||||||
|
import {
|
||||||
|
StyleSheet,
|
||||||
|
Platform,
|
||||||
|
Text,
|
||||||
|
View,
|
||||||
|
TouchableOpacity,
|
||||||
|
Image,
|
||||||
|
Switch,
|
||||||
|
} from 'react-native';
|
||||||
|
import {
|
||||||
|
Icon,
|
||||||
|
PaperProvider,
|
||||||
|
Snackbar,
|
||||||
|
Banner,
|
||||||
|
Button,
|
||||||
|
Modal,
|
||||||
|
Portal,
|
||||||
|
} from 'react-native-paper';
|
||||||
|
import {Camera} from 'react-native-camera-kit';
|
||||||
|
|
||||||
|
import TestConsole from './TestConsole';
|
||||||
|
|
||||||
|
import _updateConfig from '../update.json';
|
||||||
|
import {PushyProvider, Pushy, usePushy} from 'react-native-update';
|
||||||
|
const {appKey} = _updateConfig[Platform.OS];
|
||||||
|
|
||||||
|
function App() {
|
||||||
|
const {
|
||||||
|
client,
|
||||||
|
checkUpdate,
|
||||||
|
downloadUpdate,
|
||||||
|
switchVersionLater,
|
||||||
|
switchVersion,
|
||||||
|
updateInfo,
|
||||||
|
packageVersion,
|
||||||
|
currentHash,
|
||||||
|
parseTestQrCode,
|
||||||
|
progress: {received, total} = {},
|
||||||
|
} = usePushy();
|
||||||
|
const [useDefaultAlert, setUseDefaultAlert] = useState(true);
|
||||||
|
const [showTestConsole, setShowTestConsole] = useState(false);
|
||||||
|
const [showUpdateBanner, setShowUpdateBanner] = useState(false);
|
||||||
|
const [showUpdateSnackbar, setShowUpdateSnackbar] = useState(false);
|
||||||
|
const snackbarVisible =
|
||||||
|
!useDefaultAlert && showUpdateSnackbar && updateInfo?.update;
|
||||||
|
const [showCamera, setShowCamera] = useState(false);
|
||||||
|
const lastParsedCode = useRef('');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.welcome}>欢迎使用Pushy热更新服务</Text>
|
||||||
|
<View style={{flexDirection: 'row'}}>
|
||||||
|
<Text>
|
||||||
|
{useDefaultAlert ? '当前使用' : '当前不使用'}默认的alert更新提示
|
||||||
|
</Text>
|
||||||
|
<Switch
|
||||||
|
value={useDefaultAlert}
|
||||||
|
onValueChange={v => {
|
||||||
|
setUseDefaultAlert(v);
|
||||||
|
client?.setOptions({
|
||||||
|
updateStrategy: v ? null : 'alwaysAlert',
|
||||||
|
});
|
||||||
|
setShowUpdateSnackbar(!v);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<Button onPress={() => setShowCamera(true)}>打开相机</Button>
|
||||||
|
<Portal>
|
||||||
|
<Modal visible={showCamera} onDismiss={() => setShowCamera(false)}>
|
||||||
|
<Camera
|
||||||
|
style={{minHeight: 320}}
|
||||||
|
scanBarcode={true}
|
||||||
|
onReadCode={({nativeEvent: {codeStringValue}}) => {
|
||||||
|
// 防止重复扫码
|
||||||
|
if (lastParsedCode.current === codeStringValue) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastParsedCode.current = codeStringValue;
|
||||||
|
setTimeout(() => {
|
||||||
|
lastParsedCode.current = '';
|
||||||
|
}, 1000);
|
||||||
|
setShowCamera(false);
|
||||||
|
parseTestQrCode(codeStringValue);
|
||||||
|
}} // optional
|
||||||
|
showFrame={true} // (default false) optional, show frame with transparent layer (qr code or barcode will be read on this area ONLY), start animation for scanner, that stops when a code has been found. Frame always at center of the screen
|
||||||
|
laserColor="red" // (default red) optional, color of laser in scanner frame
|
||||||
|
frameColor="white" // (default white) optional, color of border of scanner frame
|
||||||
|
/>
|
||||||
|
</Modal>
|
||||||
|
</Portal>
|
||||||
|
<Image
|
||||||
|
resizeMode={'contain'}
|
||||||
|
source={require('./assets/shezhi.png')}
|
||||||
|
style={styles.image}
|
||||||
|
/>
|
||||||
|
<Text style={styles.instructions}>
|
||||||
|
这是版本一 {'\n'}
|
||||||
|
当前原生包版本号: {packageVersion}
|
||||||
|
{'\n'}
|
||||||
|
当前热更新版本Hash: {currentHash || '(空)'}
|
||||||
|
{'\n'}
|
||||||
|
</Text>
|
||||||
|
<Text>
|
||||||
|
下载进度:{received} / {total}
|
||||||
|
</Text>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => {
|
||||||
|
checkUpdate();
|
||||||
|
setShowUpdateSnackbar(true);
|
||||||
|
}}>
|
||||||
|
<Text style={styles.instructions}>点击这里检查更新</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
testID="testcase"
|
||||||
|
style={{marginTop: 15}}
|
||||||
|
onLongPress={() => {
|
||||||
|
setShowTestConsole(true);
|
||||||
|
}}>
|
||||||
|
<Text style={styles.instructions}>
|
||||||
|
react-native-update版本:{client?.version}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TestConsole visible={showTestConsole} />
|
||||||
|
{snackbarVisible && (
|
||||||
|
<Snackbar
|
||||||
|
visible={snackbarVisible}
|
||||||
|
onDismiss={() => {
|
||||||
|
setShowUpdateSnackbar(false);
|
||||||
|
}}
|
||||||
|
action={{
|
||||||
|
label: '更新',
|
||||||
|
onPress: async () => {
|
||||||
|
setShowUpdateSnackbar(false);
|
||||||
|
await downloadUpdate();
|
||||||
|
setShowUpdateBanner(true);
|
||||||
|
},
|
||||||
|
}}>
|
||||||
|
<Text style={{color: 'white'}}>
|
||||||
|
有新版本({updateInfo.name})可用,是否更新?
|
||||||
|
</Text>
|
||||||
|
</Snackbar>
|
||||||
|
)}
|
||||||
|
<Banner
|
||||||
|
style={{width: '100%', position: 'absolute', top: 0}}
|
||||||
|
visible={showUpdateBanner}
|
||||||
|
actions={[
|
||||||
|
{
|
||||||
|
label: '立即重启',
|
||||||
|
onPress: switchVersion,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '下次再说',
|
||||||
|
onPress: () => {
|
||||||
|
switchVersionLater();
|
||||||
|
setShowUpdateBanner(false);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
icon={({size}) => (
|
||||||
|
<Icon name="checkcircleo" size={size} color="#00f" />
|
||||||
|
)}>
|
||||||
|
更新已完成,是否立即重启?
|
||||||
|
</Banner>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: '#F5FCFF',
|
||||||
|
},
|
||||||
|
welcome: {
|
||||||
|
fontSize: 20,
|
||||||
|
textAlign: 'center',
|
||||||
|
margin: 10,
|
||||||
|
},
|
||||||
|
instructions: {
|
||||||
|
textAlign: 'center',
|
||||||
|
color: '#333333',
|
||||||
|
marginBottom: 5,
|
||||||
|
},
|
||||||
|
image: {},
|
||||||
|
});
|
||||||
|
|
||||||
|
const pushyClient = new Pushy({
|
||||||
|
appKey,
|
||||||
|
debug: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function Root() {
|
||||||
|
return (
|
||||||
|
<PushyProvider client={pushyClient}>
|
||||||
|
<PaperProvider>
|
||||||
|
<App />
|
||||||
|
</PaperProvider>
|
||||||
|
</PushyProvider>
|
||||||
|
);
|
||||||
|
}
|
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"ios": {
|
"ios": {
|
||||||
"appId": 10976,
|
"appId": 24794,
|
||||||
"appKey": "p5q_rPYNOE0WdCf-i9ZCGPw-DUGUCMvY"
|
"appKey": "SqShg4Klnj2hG6LAFMW2PdcgSSuniz0T"
|
||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
"appId": 10977,
|
"appId": 27509,
|
||||||
"appKey": "bUYKfZgtC9VirZsQbAt8qken2Gq2uxe3"
|
"appKey": "aQz3Uc2pA7gt_prDaQ4rbWRY"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
### 优势
|
### 优势
|
||||||
|
|
||||||
1. 基于阿里云高速 CDN 分发,对比其他服务器在国外的热更新服务,分发更稳定,更新成功率极高。
|
1. 对中国用户使用阿里云高速 CDN 分发,对比其他服务器在国外的热更新服务,分发更稳定,更新成功率极高。对国外用户则智能分流至 cloudflare,同样享受高速更新服务。
|
||||||
2. 基于 bsdiff/hdiff 算法创建的**超小更新包**,通常版本迭代后在几十 KB 级别(其他全量热更新服务所需流量通常在几十 MB 级别)。
|
2. 基于 bsdiff/hdiff 算法创建的**超小更新包**,通常版本迭代后在几十 KB 级别(其他全量热更新服务所需流量通常在几十 MB 级别)。
|
||||||
3. 始终跟进 RN 最新正式版本,第一时间提供支持。支持 hermes 字节码格式。支持新架构。
|
3. 始终跟进 RN 最新正式版本,第一时间提供支持。支持 hermes 字节码格式。支持新架构。
|
||||||
4. 跨越多个版本进行更新时,只需要下载**一个更新包**,不需要逐版本依次更新。
|
4. 跨越多个版本进行更新时,只需要下载**一个更新包**,不需要逐版本依次更新。
|
||||||
@@ -33,4 +33,3 @@ $ yarn start
|
|||||||
本组件由[React Native 中文网](https://reactnative.cn/)独家发布,如有定制需求可以[联系我们](https://reactnative.cn/about.html#content)。
|
本组件由[React Native 中文网](https://reactnative.cn/)独家发布,如有定制需求可以[联系我们](https://reactnative.cn/about.html#content)。
|
||||||
|
|
||||||
关于此插件发现任何问题,可以前往[Issues](https://github.com/reactnativecn/react-native-pushy/issues)发帖提问。
|
关于此插件发现任何问题,可以前往[Issues](https://github.com/reactnativecn/react-native-pushy/issues)发帖提问。
|
||||||
|
|
||||||
|
@@ -9,6 +9,19 @@ def isNewArchitectureEnabled() {
|
|||||||
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
|
return project.hasProperty("newArchEnabled") && project.newArchEnabled == "true"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def supportsNamespace() {
|
||||||
|
def parsed = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')
|
||||||
|
def major = parsed[0].toInteger()
|
||||||
|
def minor = parsed[1].toInteger()
|
||||||
|
|
||||||
|
// Namespace support was added in 7.3.0
|
||||||
|
if (major == 7 && minor >= 3) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return major >= 8
|
||||||
|
}
|
||||||
|
|
||||||
apply plugin: 'com.android.library'
|
apply plugin: 'com.android.library'
|
||||||
if (isNewArchitectureEnabled()) {
|
if (isNewArchitectureEnabled()) {
|
||||||
apply plugin: 'com.facebook.react'
|
apply plugin: 'com.facebook.react'
|
||||||
@@ -16,6 +29,16 @@ if (isNewArchitectureEnabled()) {
|
|||||||
|
|
||||||
|
|
||||||
android {
|
android {
|
||||||
|
|
||||||
|
if (supportsNamespace()) {
|
||||||
|
namespace "cn.reactnative.modules.update"
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
manifest.srcFile "src/main/AndroidManifestNew.xml"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
compileSdkVersion safeExtGet('compileSdkVersion', 28)
|
compileSdkVersion safeExtGet('compileSdkVersion', 28)
|
||||||
buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3')
|
buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3')
|
||||||
|
|
||||||
|
@@ -1,4 +1,3 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="cn.reactnative.modules.update">
|
package="cn.reactnative.modules.update">
|
||||||
<application>
|
<application>
|
||||||
|
14
android/src/main/AndroidManifestNew.xml
Normal file
14
android/src/main/AndroidManifestNew.xml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<application>
|
||||||
|
<meta-data android:name="pushy_build_time" android:value="@string/pushy_build_time" />
|
||||||
|
<provider
|
||||||
|
android:name=".PushyFileProvider"
|
||||||
|
android:authorities="${applicationId}.pushy.fileprovider"
|
||||||
|
android:exported="false"
|
||||||
|
android:grantUriPermissions="true">
|
||||||
|
<meta-data
|
||||||
|
android:name="android.support.FILE_PROVIDER_PATHS"
|
||||||
|
android:resource="@xml/pushy_file_paths" />
|
||||||
|
</provider>
|
||||||
|
</application>
|
||||||
|
</manifest>
|
@@ -75,7 +75,7 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|||||||
.build();
|
.build();
|
||||||
Response response = client.newCall(request).execute();
|
Response response = client.newCall(request).execute();
|
||||||
if (response.code() > 299) {
|
if (response.code() > 299) {
|
||||||
throw new Error("Server return code " + response.code());
|
throw new Error("Server error:" + response.code() + " " + response.message());
|
||||||
}
|
}
|
||||||
ResponseBody body = response.body();
|
ResponseBody body = response.body();
|
||||||
long contentLength = body.contentLength();
|
long contentLength = body.contentLength();
|
||||||
@@ -237,19 +237,7 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|||||||
while (entries.hasMoreElements()) {
|
while (entries.hasMoreElements()) {
|
||||||
ZipEntry ze = entries.nextElement();
|
ZipEntry ze = entries.nextElement();
|
||||||
|
|
||||||
String fn = ze.getName();
|
zipFile.unzipToPath(ze, param.unzipDirectory);
|
||||||
File fmd = new File(param.unzipDirectory, fn);
|
|
||||||
|
|
||||||
if (UpdateContext.DEBUG) {
|
|
||||||
Log.d("RNUpdate", "Unzipping " + fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ze.isDirectory()) {
|
|
||||||
fmd.mkdirs();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
zipFile.unzipToFile(ze, fmd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
zipFile.close();
|
zipFile.close();
|
||||||
@@ -324,8 +312,15 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|||||||
} else {
|
} else {
|
||||||
target = copyList.get((from));
|
target = copyList.get((from));
|
||||||
}
|
}
|
||||||
target.add(new File(param.unzipDirectory, to));
|
File toFile = new File(param.unzipDirectory, to);
|
||||||
//copyFromResource(from, new File(param.unzipDirectory, to));
|
|
||||||
|
// Fixing a Zip Path Traversal Vulnerability
|
||||||
|
// https://support.google.com/faqs/answer/9294009
|
||||||
|
String canonicalPath = toFile.getCanonicalPath();
|
||||||
|
if (!canonicalPath.startsWith(param.unzipDirectory.getCanonicalPath() + File.separator)) {
|
||||||
|
throw new SecurityException("Illegal name: " + to);
|
||||||
|
}
|
||||||
|
target.add(toFile);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -339,18 +334,9 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|||||||
fout.close();
|
fout.close();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
File fmd = new File(param.unzipDirectory, fn);
|
|
||||||
|
|
||||||
if (UpdateContext.DEBUG) {
|
|
||||||
Log.d("RNUpdate", "Unzipping " + fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ze.isDirectory()) {
|
zipFile.unzipToPath(ze, param.unzipDirectory);
|
||||||
fmd.mkdirs();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
zipFile.unzipToFile(ze, fmd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
zipFile.close();
|
zipFile.close();
|
||||||
@@ -419,18 +405,8 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|||||||
fout.close();
|
fout.close();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
File fmd = new File(param.unzipDirectory, fn);
|
|
||||||
|
|
||||||
if (UpdateContext.DEBUG) {
|
zipFile.unzipToPath(ze, param.unzipDirectory);
|
||||||
Log.d("RNUpdate", "Unzipping " + fn);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ze.isDirectory()) {
|
|
||||||
fmd.mkdirs();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
zipFile.unzipToFile(ze, fmd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
zipFile.close();
|
zipFile.close();
|
||||||
@@ -454,6 +430,9 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|||||||
if (sub.getName().charAt(0) == '.') {
|
if (sub.getName().charAt(0) == '.') {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
if (isFileUpdatedWithinDays(sub, 7)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (sub.isFile()) {
|
if (sub.isFile()) {
|
||||||
sub.delete();
|
sub.delete();
|
||||||
} else {
|
} else {
|
||||||
@@ -465,6 +444,13 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isFileUpdatedWithinDays(File file, int days) {
|
||||||
|
long currentTime = System.currentTimeMillis();
|
||||||
|
long lastModified = file.lastModified();
|
||||||
|
long daysInMillis = days * 24 * 60 * 60 * 1000L;
|
||||||
|
return (currentTime - lastModified) < daysInMillis;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(DownloadTaskParams... params) {
|
protected Void doInBackground(DownloadTaskParams... params) {
|
||||||
int taskType = params[0].type;
|
int taskType = params[0].type;
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
package cn.reactnative.modules.update;
|
package cn.reactnative.modules.update;
|
||||||
|
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.BufferedOutputStream;
|
import java.io.BufferedOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -10,12 +12,15 @@ import java.util.Enumeration;
|
|||||||
import java.util.zip.ZipEntry;
|
import java.util.zip.ZipEntry;
|
||||||
import java.util.zip.ZipFile;
|
import java.util.zip.ZipFile;
|
||||||
|
|
||||||
|
|
||||||
public class SafeZipFile extends ZipFile {
|
public class SafeZipFile extends ZipFile {
|
||||||
|
|
||||||
public SafeZipFile(File file) throws IOException {
|
public SafeZipFile(File file) throws IOException {
|
||||||
super(file);
|
super(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final int BUFFER_SIZE = 8192;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Enumeration<? extends ZipEntry> entries() {
|
public Enumeration<? extends ZipEntry> entries() {
|
||||||
return new SafeZipEntryIterator(super.entries());
|
return new SafeZipEntryIterator(super.entries());
|
||||||
@@ -43,40 +48,46 @@ public class SafeZipFile extends ZipFile {
|
|||||||
* avoid ZipperDown
|
* avoid ZipperDown
|
||||||
*/
|
*/
|
||||||
if (null != name && (name.contains("../") || name.contains("..\\"))) {
|
if (null != name && (name.contains("../") || name.contains("..\\"))) {
|
||||||
throw new SecurityException("illegal entry: " + entry.getName());
|
throw new SecurityException("illegal entry: " + name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unzipToFile(ZipEntry entry, File output) throws IOException {
|
public void unzipToPath(ZipEntry ze, File targetPath) throws IOException {
|
||||||
InputStream inputStream = null;
|
String name = ze.getName();
|
||||||
try {
|
File target = new File(targetPath, name);
|
||||||
inputStream = getInputStream(entry);
|
|
||||||
writeOutInputStream(output, inputStream);
|
// Fixing a Zip Path Traversal Vulnerability
|
||||||
} finally {
|
// https://support.google.com/faqs/answer/9294009
|
||||||
if (inputStream != null) {
|
String canonicalPath = target.getCanonicalPath();
|
||||||
inputStream.close();
|
if (!canonicalPath.startsWith(targetPath.getCanonicalPath() + File.separator)) {
|
||||||
|
throw new SecurityException("Illegal name: " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (UpdateContext.DEBUG) {
|
||||||
|
Log.d("RNUpdate", "Unzipping " + name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ze.isDirectory()) {
|
||||||
|
target.mkdirs();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unzipToFile(ze, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void unzipToFile(ZipEntry ze, File target) throws IOException {
|
||||||
|
try (InputStream inputStream = getInputStream(ze)) {
|
||||||
|
try (BufferedOutputStream output = new BufferedOutputStream(new FileOutputStream(target));
|
||||||
|
BufferedInputStream input = new BufferedInputStream(inputStream)) {
|
||||||
|
byte[] buffer = new byte[BUFFER_SIZE];
|
||||||
|
int n;
|
||||||
|
while ((n = input.read(buffer, 0, BUFFER_SIZE)) >= 0) {
|
||||||
|
output.write(buffer, 0, n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeOutInputStream(File file, InputStream inputStream) throws IOException {
|
|
||||||
BufferedOutputStream output = null;
|
|
||||||
try {
|
|
||||||
output = new BufferedOutputStream(
|
|
||||||
new FileOutputStream(file));
|
|
||||||
BufferedInputStream input = new BufferedInputStream(inputStream);
|
|
||||||
byte b[] = new byte[8192];
|
|
||||||
int n;
|
|
||||||
while ((n = input.read(b, 0, 8192)) >= 0) {
|
|
||||||
output.write(b, 0, n);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
if (output != null) {
|
|
||||||
output.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
@@ -69,13 +69,6 @@ public class UpdateContext {
|
|||||||
return context.getString(R.string.pushy_build_time);
|
return context.getString(R.string.pushy_build_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map getBlockUpdate() {
|
|
||||||
return new HashMap<String, Object>() {{
|
|
||||||
put("until", sp.getInt("blockUntil", 0));
|
|
||||||
put("reason", sp.getString("blockReason", null));
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean getIsUsingBundleUrl() {
|
public boolean getIsUsingBundleUrl() {
|
||||||
return isUsingBundleUrl;
|
return isUsingBundleUrl;
|
||||||
}
|
}
|
||||||
@@ -166,13 +159,6 @@ public class UpdateContext {
|
|||||||
return sp.getString(key, null);
|
return sp.getString(key, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBlockUpdate(int until, String reason) {
|
|
||||||
SharedPreferences.Editor editor = sp.edit();
|
|
||||||
editor.putInt("blockUntil", until);
|
|
||||||
editor.putString("blockReason", reason);
|
|
||||||
editor.apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getCurrentVersion() {
|
public String getCurrentVersion() {
|
||||||
return sp.getString("currentVersion", null);
|
return sp.getString("currentVersion", null);
|
||||||
}
|
}
|
||||||
|
@@ -192,23 +192,6 @@ public class UpdateModuleImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setBlockUpdate(UpdateContext updateContext, ReadableMap options,Promise promise) {
|
|
||||||
try {
|
|
||||||
final int until = options.getInt("until");
|
|
||||||
final String reason = options.getString("reason");
|
|
||||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
updateContext.setBlockUpdate(until, reason);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
promise.resolve(true);
|
|
||||||
}catch (Exception e){
|
|
||||||
promise.reject("执行报错:"+e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void setUuid(UpdateContext updateContext, String uuid, Promise promise) {
|
public static void setUuid(UpdateContext updateContext, String uuid, Promise promise) {
|
||||||
try {
|
try {
|
||||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||||
|
@@ -14,7 +14,7 @@ import java.io.File;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public class UpdateModule extends NativeUpdateSpec {
|
public class UpdateModule extends NativePushySpec {
|
||||||
UpdateContext updateContext;
|
UpdateContext updateContext;
|
||||||
public static ReactApplicationContext mContext;
|
public static ReactApplicationContext mContext;
|
||||||
public UpdateModule(ReactApplicationContext reactContext, UpdateContext updateContext) {
|
public UpdateModule(ReactApplicationContext reactContext, UpdateContext updateContext) {
|
||||||
@@ -45,7 +45,6 @@ public class UpdateModule extends NativeUpdateSpec {
|
|||||||
if (rolledBackVersion != null) {
|
if (rolledBackVersion != null) {
|
||||||
updateContext.clearRollbackMark();
|
updateContext.clearRollbackMark();
|
||||||
}
|
}
|
||||||
constants.put("blockUpdate", updateContext.getBlockUpdate());
|
|
||||||
constants.put("uuid", updateContext.getKv("uuid"));
|
constants.put("uuid", updateContext.getKv("uuid"));
|
||||||
return constants;
|
return constants;
|
||||||
}
|
}
|
||||||
@@ -57,7 +56,7 @@ public class UpdateModule extends NativeUpdateSpec {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void downloadFullUpdate(ReadableMap options, final Promise promise) {
|
public void downloadFullUpdate(ReadableMap options, final Promise promise) {
|
||||||
UpdateModuleImpl.downloadFullUpdate(this.updateContext,options,promise);
|
UpdateModuleImpl.downloadFullUpdate(this.updateContext,options,promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -108,11 +107,6 @@ public class UpdateModule extends NativeUpdateSpec {
|
|||||||
UpdateModuleImpl.markSuccess(updateContext,promise);
|
UpdateModuleImpl.markSuccess(updateContext,promise);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setBlockUpdate(ReadableMap options,Promise promise) {
|
|
||||||
UpdateModuleImpl.setBlockUpdate(updateContext,options,promise);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUuid(final String uuid, Promise promise) {
|
public void setUuid(final String uuid, Promise promise) {
|
||||||
UpdateModuleImpl.setUuid(updateContext,uuid,promise);
|
UpdateModuleImpl.setUuid(updateContext,uuid,promise);
|
||||||
|
@@ -59,7 +59,6 @@ public class UpdateModule extends ReactContextBaseJavaModule {
|
|||||||
if (rolledBackVersion != null) {
|
if (rolledBackVersion != null) {
|
||||||
updateContext.clearRollbackMark();
|
updateContext.clearRollbackMark();
|
||||||
}
|
}
|
||||||
constants.put("blockUpdate", updateContext.getBlockUpdate());
|
|
||||||
constants.put("uuid", updateContext.getKv("uuid"));
|
constants.put("uuid", updateContext.getKv("uuid"));
|
||||||
return constants;
|
return constants;
|
||||||
}
|
}
|
||||||
@@ -239,18 +238,6 @@ public class UpdateModule extends ReactContextBaseJavaModule {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ReactMethod
|
|
||||||
public void setBlockUpdate(ReadableMap options) {
|
|
||||||
final int until = options.getInt("until");
|
|
||||||
final String reason = options.getString("reason");
|
|
||||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
updateContext.setBlockUpdate(until, reason);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@ReactMethod
|
@ReactMethod
|
||||||
public void setUuid(final String uuid) {
|
public void setUuid(final String uuid) {
|
||||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||||
|
3
babel.config.js
Normal file
3
babel.config.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: ['module:@react-native/babel-preset'],
|
||||||
|
};
|
@@ -1 +1 @@
|
|||||||
["update.react-native.cn", "update.reactnative.cn"]
|
["update.react-native.cn", "update.reactnative.cn", "p.reactnative.cn"]
|
||||||
|
@@ -1 +1 @@
|
|||||||
["https://update.react-native.cn/api", "https://update.reactnative.cn/api"]
|
["https://pushy-koa-qgbgqmcpis.cn-beijing.fcapp.run", "https://p.reactnative.cn/api"]
|
||||||
|
@@ -14,11 +14,8 @@
|
|||||||
249F2F64261C58C700A1E60E /* Lzma2Dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 249F2F60261C58C700A1E60E /* Lzma2Dec.c */; };
|
249F2F64261C58C700A1E60E /* Lzma2Dec.c in Sources */ = {isa = PBXBuildFile; fileRef = 249F2F60261C58C700A1E60E /* Lzma2Dec.c */; };
|
||||||
249F2F65261C58C700A1E60E /* LzmaDec.c in Sources */ = {isa = PBXBuildFile; fileRef = 249F2F62261C58C700A1E60E /* LzmaDec.c */; };
|
249F2F65261C58C700A1E60E /* LzmaDec.c in Sources */ = {isa = PBXBuildFile; fileRef = 249F2F62261C58C700A1E60E /* LzmaDec.c */; };
|
||||||
91C5F0031C76ECA90037E727 /* RCTPushy.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 91C5F0021C76ECA90037E727 /* RCTPushy.h */; };
|
91C5F0031C76ECA90037E727 /* RCTPushy.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 91C5F0021C76ECA90037E727 /* RCTPushy.h */; };
|
||||||
91C5F0051C76ECA90037E727 /* RCTPushy.m in Sources */ = {isa = PBXBuildFile; fileRef = 91C5F0041C76ECA90037E727 /* RCTPushy.m */; };
|
|
||||||
9F1BCB1D1CAE5937000EF2CB /* RCTPushyManager.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F1BCB1C1CAE5937000EF2CB /* RCTPushyManager.m */; };
|
|
||||||
9F1BCB461CAF6B3E000EF2CB /* bspatch.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F1BCB3B1CAF6B3E000EF2CB /* bspatch.c */; };
|
9F1BCB461CAF6B3E000EF2CB /* bspatch.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F1BCB3B1CAF6B3E000EF2CB /* bspatch.c */; };
|
||||||
9F1BCB4F1CAF6B68000EF2CB /* BSDiff.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F1BCB4E1CAF6B68000EF2CB /* BSDiff.m */; };
|
9F1BCB4F1CAF6B68000EF2CB /* BSDiff.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F1BCB4E1CAF6B68000EF2CB /* BSDiff.m */; };
|
||||||
9F292F7D1C7C44290095945D /* RCTPushyDownloader.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F292F7C1C7C44290095945D /* RCTPushyDownloader.m */; };
|
|
||||||
9F394D7D1C7C25DC00C794C0 /* aescrypt.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D5B1C7C25DC00C794C0 /* aescrypt.c */; };
|
9F394D7D1C7C25DC00C794C0 /* aescrypt.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D5B1C7C25DC00C794C0 /* aescrypt.c */; };
|
||||||
9F394D7E1C7C25DC00C794C0 /* aeskey.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D5C1C7C25DC00C794C0 /* aeskey.c */; };
|
9F394D7E1C7C25DC00C794C0 /* aeskey.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D5C1C7C25DC00C794C0 /* aeskey.c */; };
|
||||||
9F394D7F1C7C25DC00C794C0 /* aestab.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D5E1C7C25DC00C794C0 /* aestab.c */; };
|
9F394D7F1C7C25DC00C794C0 /* aestab.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D5E1C7C25DC00C794C0 /* aestab.c */; };
|
||||||
@@ -33,6 +30,9 @@
|
|||||||
9F394D881C7C25DC00C794C0 /* unzip.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D761C7C25DC00C794C0 /* unzip.c */; };
|
9F394D881C7C25DC00C794C0 /* unzip.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D761C7C25DC00C794C0 /* unzip.c */; };
|
||||||
9F394D891C7C25DC00C794C0 /* zip.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D781C7C25DC00C794C0 /* zip.c */; };
|
9F394D891C7C25DC00C794C0 /* zip.c in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D781C7C25DC00C794C0 /* zip.c */; };
|
||||||
9F394D8A1C7C25DC00C794C0 /* SSZipArchive.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D7B1C7C25DC00C794C0 /* SSZipArchive.m */; };
|
9F394D8A1C7C25DC00C794C0 /* SSZipArchive.m in Sources */ = {isa = PBXBuildFile; fileRef = 9F394D7B1C7C25DC00C794C0 /* SSZipArchive.m */; };
|
||||||
|
A3FD91A62C3C01600022D27F /* RCTPushy.mm in Sources */ = {isa = PBXBuildFile; fileRef = A3FD91A52C3C01600022D27F /* RCTPushy.mm */; };
|
||||||
|
A3FD91A82C3C01640022D27F /* RCTPushyDownloader.mm in Sources */ = {isa = PBXBuildFile; fileRef = A3FD91A72C3C01640022D27F /* RCTPushyDownloader.mm */; };
|
||||||
|
A3FD91AA2C3C01680022D27F /* RCTPushyManager.mm in Sources */ = {isa = PBXBuildFile; fileRef = A3FD91A92C3C01680022D27F /* RCTPushyManager.mm */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
@@ -64,15 +64,12 @@
|
|||||||
249F2F63261C58C700A1E60E /* LzmaDec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LzmaDec.h; path = ../../../android/jni/lzma/C/LzmaDec.h; sourceTree = "<group>"; };
|
249F2F63261C58C700A1E60E /* LzmaDec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = LzmaDec.h; path = ../../../android/jni/lzma/C/LzmaDec.h; sourceTree = "<group>"; };
|
||||||
91C5EFFF1C76ECA90037E727 /* libRCTPushy.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTPushy.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
91C5EFFF1C76ECA90037E727 /* libRCTPushy.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTPushy.a; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
91C5F0021C76ECA90037E727 /* RCTPushy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTPushy.h; sourceTree = "<group>"; };
|
91C5F0021C76ECA90037E727 /* RCTPushy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RCTPushy.h; sourceTree = "<group>"; };
|
||||||
91C5F0041C76ECA90037E727 /* RCTPushy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RCTPushy.m; sourceTree = "<group>"; };
|
|
||||||
9F1BCB1B1CAE5937000EF2CB /* RCTPushyManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPushyManager.h; sourceTree = "<group>"; };
|
9F1BCB1B1CAE5937000EF2CB /* RCTPushyManager.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPushyManager.h; sourceTree = "<group>"; };
|
||||||
9F1BCB1C1CAE5937000EF2CB /* RCTPushyManager.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPushyManager.m; sourceTree = "<group>"; };
|
|
||||||
9F1BCB3B1CAF6B3E000EF2CB /* bspatch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bspatch.c; sourceTree = "<group>"; };
|
9F1BCB3B1CAF6B3E000EF2CB /* bspatch.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = bspatch.c; sourceTree = "<group>"; };
|
||||||
9F1BCB3C1CAF6B3E000EF2CB /* bspatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bspatch.h; sourceTree = "<group>"; };
|
9F1BCB3C1CAF6B3E000EF2CB /* bspatch.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bspatch.h; sourceTree = "<group>"; };
|
||||||
9F1BCB4D1CAF6B68000EF2CB /* BSDiff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSDiff.h; sourceTree = "<group>"; };
|
9F1BCB4D1CAF6B68000EF2CB /* BSDiff.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BSDiff.h; sourceTree = "<group>"; };
|
||||||
9F1BCB4E1CAF6B68000EF2CB /* BSDiff.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSDiff.m; sourceTree = "<group>"; };
|
9F1BCB4E1CAF6B68000EF2CB /* BSDiff.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BSDiff.m; sourceTree = "<group>"; };
|
||||||
9F292F7B1C7C44290095945D /* RCTPushyDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPushyDownloader.h; sourceTree = "<group>"; };
|
9F292F7B1C7C44290095945D /* RCTPushyDownloader.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RCTPushyDownloader.h; sourceTree = "<group>"; };
|
||||||
9F292F7C1C7C44290095945D /* RCTPushyDownloader.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RCTPushyDownloader.m; sourceTree = "<group>"; };
|
|
||||||
9F394D591C7C25DC00C794C0 /* aes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aes.h; sourceTree = "<group>"; };
|
9F394D591C7C25DC00C794C0 /* aes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aes.h; sourceTree = "<group>"; };
|
||||||
9F394D5A1C7C25DC00C794C0 /* aes_via_ace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aes_via_ace.h; sourceTree = "<group>"; };
|
9F394D5A1C7C25DC00C794C0 /* aes_via_ace.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = aes_via_ace.h; sourceTree = "<group>"; };
|
||||||
9F394D5B1C7C25DC00C794C0 /* aescrypt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aescrypt.c; sourceTree = "<group>"; };
|
9F394D5B1C7C25DC00C794C0 /* aescrypt.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = aescrypt.c; sourceTree = "<group>"; };
|
||||||
@@ -108,6 +105,9 @@
|
|||||||
9F394D7A1C7C25DC00C794C0 /* SSZipArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSZipArchive.h; sourceTree = "<group>"; };
|
9F394D7A1C7C25DC00C794C0 /* SSZipArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSZipArchive.h; sourceTree = "<group>"; };
|
||||||
9F394D7B1C7C25DC00C794C0 /* SSZipArchive.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSZipArchive.m; sourceTree = "<group>"; };
|
9F394D7B1C7C25DC00C794C0 /* SSZipArchive.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSZipArchive.m; sourceTree = "<group>"; };
|
||||||
9F394D7C1C7C25DC00C794C0 /* ZipArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZipArchive.h; sourceTree = "<group>"; };
|
9F394D7C1C7C25DC00C794C0 /* ZipArchive.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ZipArchive.h; sourceTree = "<group>"; };
|
||||||
|
A3FD91A52C3C01600022D27F /* RCTPushy.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTPushy.mm; sourceTree = "<group>"; };
|
||||||
|
A3FD91A72C3C01640022D27F /* RCTPushyDownloader.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTPushyDownloader.mm; sourceTree = "<group>"; };
|
||||||
|
A3FD91A92C3C01680022D27F /* RCTPushyManager.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = RCTPushyManager.mm; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -172,11 +172,11 @@
|
|||||||
9F1BCB381CAF6B3E000EF2CB /* BSDiff */,
|
9F1BCB381CAF6B3E000EF2CB /* BSDiff */,
|
||||||
9F394D571C7C25DC00C794C0 /* SSZipArchive */,
|
9F394D571C7C25DC00C794C0 /* SSZipArchive */,
|
||||||
91C5F0021C76ECA90037E727 /* RCTPushy.h */,
|
91C5F0021C76ECA90037E727 /* RCTPushy.h */,
|
||||||
91C5F0041C76ECA90037E727 /* RCTPushy.m */,
|
|
||||||
9F1BCB1B1CAE5937000EF2CB /* RCTPushyManager.h */,
|
9F1BCB1B1CAE5937000EF2CB /* RCTPushyManager.h */,
|
||||||
9F1BCB1C1CAE5937000EF2CB /* RCTPushyManager.m */,
|
|
||||||
9F292F7B1C7C44290095945D /* RCTPushyDownloader.h */,
|
9F292F7B1C7C44290095945D /* RCTPushyDownloader.h */,
|
||||||
9F292F7C1C7C44290095945D /* RCTPushyDownloader.m */,
|
A3FD91A52C3C01600022D27F /* RCTPushy.mm */,
|
||||||
|
A3FD91A72C3C01640022D27F /* RCTPushyDownloader.mm */,
|
||||||
|
A3FD91A92C3C01680022D27F /* RCTPushyManager.mm */,
|
||||||
);
|
);
|
||||||
path = RCTPushy;
|
path = RCTPushy;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -321,25 +321,25 @@
|
|||||||
9F394D821C7C25DC00C794C0 /* hmac.c in Sources */,
|
9F394D821C7C25DC00C794C0 /* hmac.c in Sources */,
|
||||||
9F394D881C7C25DC00C794C0 /* unzip.c in Sources */,
|
9F394D881C7C25DC00C794C0 /* unzip.c in Sources */,
|
||||||
249F2F65261C58C700A1E60E /* LzmaDec.c in Sources */,
|
249F2F65261C58C700A1E60E /* LzmaDec.c in Sources */,
|
||||||
9F1BCB1D1CAE5937000EF2CB /* RCTPushyManager.m in Sources */,
|
|
||||||
249F2F5E261C589D00A1E60E /* patch.c in Sources */,
|
249F2F5E261C589D00A1E60E /* patch.c in Sources */,
|
||||||
9F1BCB4F1CAF6B68000EF2CB /* BSDiff.m in Sources */,
|
9F1BCB4F1CAF6B68000EF2CB /* BSDiff.m in Sources */,
|
||||||
9F394D7E1C7C25DC00C794C0 /* aeskey.c in Sources */,
|
9F394D7E1C7C25DC00C794C0 /* aeskey.c in Sources */,
|
||||||
9F394D7F1C7C25DC00C794C0 /* aestab.c in Sources */,
|
9F394D7F1C7C25DC00C794C0 /* aestab.c in Sources */,
|
||||||
9F394D7D1C7C25DC00C794C0 /* aescrypt.c in Sources */,
|
9F394D7D1C7C25DC00C794C0 /* aescrypt.c in Sources */,
|
||||||
9F394D801C7C25DC00C794C0 /* entropy.c in Sources */,
|
9F394D801C7C25DC00C794C0 /* entropy.c in Sources */,
|
||||||
9F292F7D1C7C44290095945D /* RCTPushyDownloader.m in Sources */,
|
|
||||||
249F2F64261C58C700A1E60E /* Lzma2Dec.c in Sources */,
|
249F2F64261C58C700A1E60E /* Lzma2Dec.c in Sources */,
|
||||||
249F2F55261C584900A1E60E /* hpatch.c in Sources */,
|
249F2F55261C584900A1E60E /* hpatch.c in Sources */,
|
||||||
9F394D831C7C25DC00C794C0 /* prng.c in Sources */,
|
9F394D831C7C25DC00C794C0 /* prng.c in Sources */,
|
||||||
9F394D861C7C25DC00C794C0 /* ioapi.c in Sources */,
|
9F394D861C7C25DC00C794C0 /* ioapi.c in Sources */,
|
||||||
|
A3FD91A82C3C01640022D27F /* RCTPushyDownloader.mm in Sources */,
|
||||||
9F1BCB461CAF6B3E000EF2CB /* bspatch.c in Sources */,
|
9F1BCB461CAF6B3E000EF2CB /* bspatch.c in Sources */,
|
||||||
|
A3FD91A62C3C01600022D27F /* RCTPushy.mm in Sources */,
|
||||||
9F394D8A1C7C25DC00C794C0 /* SSZipArchive.m in Sources */,
|
9F394D8A1C7C25DC00C794C0 /* SSZipArchive.m in Sources */,
|
||||||
249F2F50261C577300A1E60E /* HDiffPatch.m in Sources */,
|
249F2F50261C577300A1E60E /* HDiffPatch.m in Sources */,
|
||||||
|
A3FD91AA2C3C01680022D27F /* RCTPushyManager.mm in Sources */,
|
||||||
9F394D891C7C25DC00C794C0 /* zip.c in Sources */,
|
9F394D891C7C25DC00C794C0 /* zip.c in Sources */,
|
||||||
9F394D841C7C25DC00C794C0 /* pwd2key.c in Sources */,
|
9F394D841C7C25DC00C794C0 /* pwd2key.c in Sources */,
|
||||||
249F2F59261C586900A1E60E /* file_for_patch.c in Sources */,
|
249F2F59261C586900A1E60E /* file_for_patch.c in Sources */,
|
||||||
91C5F0051C76ECA90037E727 /* RCTPushy.m in Sources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@@ -16,7 +16,6 @@ static NSString *const paramLastVersion = @"lastVersion";
|
|||||||
static NSString *const paramCurrentVersion = @"currentVersion";
|
static NSString *const paramCurrentVersion = @"currentVersion";
|
||||||
static NSString *const paramIsFirstTime = @"isFirstTime";
|
static NSString *const paramIsFirstTime = @"isFirstTime";
|
||||||
static NSString *const paramIsFirstLoadOk = @"isFirstLoadOK";
|
static NSString *const paramIsFirstLoadOk = @"isFirstLoadOK";
|
||||||
static NSString *const keyBlockUpdate = @"REACTNATIVECN_PUSHY_BLOCKUPDATE";
|
|
||||||
static NSString *const keyUuid = @"REACTNATIVECN_PUSHY_UUID";
|
static NSString *const keyUuid = @"REACTNATIVECN_PUSHY_UUID";
|
||||||
static NSString *const keyHashInfo = @"REACTNATIVECN_PUSHY_HASH_";
|
static NSString *const keyHashInfo = @"REACTNATIVECN_PUSHY_HASH_";
|
||||||
static NSString *const keyFirstLoadMarked = @"REACTNATIVECN_PUSHY_FIRSTLOADMARKED_KEY";
|
static NSString *const keyFirstLoadMarked = @"REACTNATIVECN_PUSHY_FIRSTLOADMARKED_KEY";
|
||||||
@@ -76,7 +75,7 @@ RCT_EXPORT_MODULE(RCTPushy);
|
|||||||
if (needClearPushyInfo) {
|
if (needClearPushyInfo) {
|
||||||
[defaults setObject:nil forKey:keyPushyInfo];
|
[defaults setObject:nil forKey:keyPushyInfo];
|
||||||
[defaults setObject:@(YES) forKey:KeyPackageUpdatedMarked];
|
[defaults setObject:@(YES) forKey:KeyPackageUpdatedMarked];
|
||||||
[defaults synchronize];
|
|
||||||
// ...need clear files later
|
// ...need clear files later
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -98,7 +97,7 @@ RCT_EXPORT_MODULE(RCTPushy);
|
|||||||
newInfo[paramIsFirstTime] = @(NO);
|
newInfo[paramIsFirstTime] = @(NO);
|
||||||
[defaults setObject:newInfo forKey:keyPushyInfo];
|
[defaults setObject:newInfo forKey:keyPushyInfo];
|
||||||
[defaults setObject:@(YES) forKey:keyFirstLoadMarked];
|
[defaults setObject:@(YES) forKey:keyFirstLoadMarked];
|
||||||
[defaults synchronize];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NSString *downloadDir = [RCTPushy downloadDir];
|
NSString *downloadDir = [RCTPushy downloadDir];
|
||||||
@@ -138,7 +137,7 @@ RCT_EXPORT_MODULE(RCTPushy);
|
|||||||
[defaults setObject:nil forKey:keyPushyInfo];
|
[defaults setObject:nil forKey:keyPushyInfo];
|
||||||
}
|
}
|
||||||
[defaults setObject:curVersion forKey:keyRolledBackMarked];
|
[defaults setObject:curVersion forKey:keyRolledBackMarked];
|
||||||
[defaults synchronize];
|
|
||||||
return lastVersion;
|
return lastVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +156,6 @@ RCT_EXPORT_MODULE(RCTPushy);
|
|||||||
ret[@"buildTime"] = [RCTPushy buildTime];
|
ret[@"buildTime"] = [RCTPushy buildTime];
|
||||||
ret[@"rolledBackVersion"] = [defaults objectForKey:keyRolledBackMarked];
|
ret[@"rolledBackVersion"] = [defaults objectForKey:keyRolledBackMarked];
|
||||||
ret[@"isFirstTime"] = [defaults objectForKey:keyFirstLoadMarked];
|
ret[@"isFirstTime"] = [defaults objectForKey:keyFirstLoadMarked];
|
||||||
ret[@"blockUpdate"] = [defaults objectForKey:keyBlockUpdate];
|
|
||||||
ret[@"uuid"] = [defaults objectForKey:keyUuid];
|
ret[@"uuid"] = [defaults objectForKey:keyUuid];
|
||||||
NSDictionary *pushyInfo = [defaults dictionaryForKey:keyPushyInfo];
|
NSDictionary *pushyInfo = [defaults dictionaryForKey:keyPushyInfo];
|
||||||
ret[@"currentVersion"] = [pushyInfo objectForKey:paramCurrentVersion];
|
ret[@"currentVersion"] = [pushyInfo objectForKey:paramCurrentVersion];
|
||||||
@@ -178,7 +176,7 @@ RCT_EXPORT_MODULE(RCTPushy);
|
|||||||
[defaults setObject:nil forKey:KeyPackageUpdatedMarked];
|
[defaults setObject:nil forKey:KeyPackageUpdatedMarked];
|
||||||
[self clearInvalidFiles];
|
[self clearInvalidFiles];
|
||||||
}
|
}
|
||||||
[defaults synchronize];
|
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -192,31 +190,13 @@ RCT_EXPORT_MODULE(RCTPushy);
|
|||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(setBlockUpdate:(NSDictionary *)options
|
|
||||||
resolver:(RCTPromiseResolveBlock)resolve
|
|
||||||
rejecter:(RCTPromiseRejectBlock)reject)
|
|
||||||
{
|
|
||||||
// NSMutableDictionary *blockUpdateInfo = [NSMutableDictionary new];
|
|
||||||
// blockUpdateInfo[@"reason"] = options[@"reason"];
|
|
||||||
// blockUpdateInfo[@"until"] = options[@"until"];
|
|
||||||
@try {
|
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
|
||||||
[defaults setObject:options forKey:keyBlockUpdate];
|
|
||||||
[defaults synchronize];
|
|
||||||
resolve(@true);
|
|
||||||
}
|
|
||||||
@catch (NSException *exception) {
|
|
||||||
reject(@"执行报错", nil, nil);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(setUuid:(NSString *)uuid resolver:(RCTPromiseResolveBlock)resolve
|
RCT_EXPORT_METHOD(setUuid:(NSString *)uuid resolver:(RCTPromiseResolveBlock)resolve
|
||||||
rejecter:(RCTPromiseRejectBlock)reject)
|
rejecter:(RCTPromiseRejectBlock)reject)
|
||||||
{
|
{
|
||||||
@try {
|
@try {
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||||
[defaults setObject:uuid forKey:keyUuid];
|
[defaults setObject:uuid forKey:keyUuid];
|
||||||
[defaults synchronize];
|
|
||||||
resolve(@true);
|
resolve(@true);
|
||||||
}
|
}
|
||||||
@catch (NSException *exception) {
|
@catch (NSException *exception) {
|
||||||
@@ -234,7 +214,7 @@ RCT_EXPORT_METHOD(setLocalHashInfo:(NSString *)hash
|
|||||||
if (object && [object isKindOfClass:[NSDictionary class]]) {
|
if (object && [object isKindOfClass:[NSDictionary class]]) {
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||||
[defaults setObject:value forKey:[keyHashInfo stringByAppendingString:hash]];
|
[defaults setObject:value forKey:[keyHashInfo stringByAppendingString:hash]];
|
||||||
[defaults synchronize];
|
|
||||||
resolve(@true);
|
resolve(@true);
|
||||||
} else {
|
} else {
|
||||||
reject(@"json格式校验报错", nil, nil);
|
reject(@"json格式校验报错", nil, nil);
|
||||||
@@ -315,7 +295,7 @@ RCT_EXPORT_METHOD(setNeedUpdate:(NSDictionary *)options
|
|||||||
newInfo[paramPackageVersion] = [RCTPushy packageVersion];
|
newInfo[paramPackageVersion] = [RCTPushy packageVersion];
|
||||||
[defaults setObject:newInfo forKey:keyPushyInfo];
|
[defaults setObject:newInfo forKey:keyPushyInfo];
|
||||||
|
|
||||||
[defaults synchronize];
|
|
||||||
resolve(@true);
|
resolve(@true);
|
||||||
}else{
|
}else{
|
||||||
reject(@"执行报错", nil, nil);
|
reject(@"执行报错", nil, nil);
|
||||||
@@ -349,8 +329,7 @@ RCT_EXPORT_METHOD(reloadUpdate:(NSDictionary *)options
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RCT_EXPORT_METHOD(markSuccess:
|
RCT_EXPORT_METHOD(markSuccess:(RCTPromiseResolveBlock)resolve
|
||||||
resolver:(RCTPromiseResolveBlock)resolve
|
|
||||||
rejecter:(RCTPromiseRejectBlock)reject)
|
rejecter:(RCTPromiseRejectBlock)reject)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -367,7 +346,7 @@ RCT_EXPORT_METHOD(markSuccess:
|
|||||||
[pushyInfo removeObjectForKey:[keyHashInfo stringByAppendingString:lastVersion]];
|
[pushyInfo removeObjectForKey:[keyHashInfo stringByAppendingString:lastVersion]];
|
||||||
}
|
}
|
||||||
[defaults setObject:pushyInfo forKey:keyPushyInfo];
|
[defaults setObject:pushyInfo forKey:keyPushyInfo];
|
||||||
[defaults synchronize];
|
|
||||||
|
|
||||||
// clear other package dir
|
// clear other package dir
|
||||||
[self clearInvalidFiles];
|
[self clearInvalidFiles];
|
||||||
@@ -557,7 +536,15 @@ RCT_EXPORT_METHOD(markSuccess:
|
|||||||
|
|
||||||
for(NSString *fileName in list) {
|
for(NSString *fileName in list) {
|
||||||
if (![fileName isEqualToString:curVersion]) {
|
if (![fileName isEqualToString:curVersion]) {
|
||||||
[_fileManager removeFile:[downloadDir stringByAppendingPathComponent:fileName] completionHandler:nil];
|
NSString *filePath = [downloadDir stringByAppendingPathComponent:fileName];
|
||||||
|
NSDictionary *attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error];
|
||||||
|
if (error) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
NSDate *modificationDate = [attributes fileModificationDate];
|
||||||
|
if ([[NSDate date] timeIntervalSinceDate:modificationDate] > 7 * 24 * 60 * 60) {
|
||||||
|
[_fileManager removeFile:filePath completionHandler:nil];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -630,7 +617,7 @@ RCT_EXPORT_METHOD(markSuccess:
|
|||||||
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
||||||
(const facebook::react::ObjCTurboModule::InitParams &)params
|
(const facebook::react::ObjCTurboModule::InitParams &)params
|
||||||
{
|
{
|
||||||
return std::make_shared<facebook::react::NativeUpdateSpecJSI>(params);
|
return std::make_shared<facebook::react::NativePushySpecJSI>(params);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -1,53 +0,0 @@
|
|||||||
import { logger } from './utils';
|
|
||||||
|
|
||||||
let currentEndpoint = 'https://update.react-native.cn/api';
|
|
||||||
let backupEndpoints: string[] = ['https://update.reactnative.cn/api'];
|
|
||||||
let backupEndpointsQueryUrl: string | null = null;
|
|
||||||
|
|
||||||
export async function updateBackupEndpoints() {
|
|
||||||
if (backupEndpointsQueryUrl) {
|
|
||||||
try {
|
|
||||||
const resp = await fetch(backupEndpointsQueryUrl);
|
|
||||||
const remoteEndpoints = await resp.json();
|
|
||||||
if (Array.isArray(remoteEndpoints)) {
|
|
||||||
backupEndpoints = Array.from(
|
|
||||||
new Set([...backupEndpoints, ...remoteEndpoints]),
|
|
||||||
);
|
|
||||||
logger('fetch remote endpoints:', remoteEndpoints);
|
|
||||||
logger('merged backup endpoints:', backupEndpoints);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
logger('fetch remote endpoints failed');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return backupEndpoints;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getCheckUrl(APPKEY, endpoint = currentEndpoint) {
|
|
||||||
return `${endpoint}/checkUpdate/${APPKEY}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} main - The main api endpoint
|
|
||||||
* @param {string[]} [backups] - The back up endpoints.
|
|
||||||
* @param {string} [backupQueryUrl] - An url that return a json file containing an array of endpoint.
|
|
||||||
* like: ["https://backup.api/1", "https://backup.api/2"]
|
|
||||||
*/
|
|
||||||
export function setCustomEndpoints({
|
|
||||||
main,
|
|
||||||
backups,
|
|
||||||
backupQueryUrl,
|
|
||||||
}: {
|
|
||||||
main: string;
|
|
||||||
backups?: string[];
|
|
||||||
backupQueryUrl?: string;
|
|
||||||
}) {
|
|
||||||
currentEndpoint = main;
|
|
||||||
backupEndpointsQueryUrl = null;
|
|
||||||
if (Array.isArray(backups) && backups.length > 0) {
|
|
||||||
backupEndpoints = backups;
|
|
||||||
}
|
|
||||||
if (typeof backupQueryUrl === 'string') {
|
|
||||||
backupEndpointsQueryUrl = backupQueryUrl;
|
|
||||||
}
|
|
||||||
}
|
|
@@ -1,2 +0,0 @@
|
|||||||
export * from './main';
|
|
||||||
export * from './simpleUpdate';
|
|
@@ -1,18 +0,0 @@
|
|||||||
export const downloadRootDir = '';
|
|
||||||
export const packageVersion = '';
|
|
||||||
export const currentVersion = '';
|
|
||||||
export const isFirstTime = false;
|
|
||||||
export const isRolledBack = false;
|
|
||||||
|
|
||||||
const noop = () => {};
|
|
||||||
|
|
||||||
export const checkUpdate = noop;
|
|
||||||
export const downloadUpdate = noop;
|
|
||||||
export const switchVersion = noop;
|
|
||||||
export const switchVersionLater = noop;
|
|
||||||
export const markSuccess = noop;
|
|
||||||
export const downloadAndInstallApk = noop;
|
|
||||||
export const setCustomEndpoints = noop;
|
|
||||||
export const getCurrentVersionInfo = noop;
|
|
||||||
export const simpleUpdate = (app) => app;
|
|
||||||
export const onPushyEvents = noop;
|
|
398
lib/main.ts
398
lib/main.ts
@@ -1,398 +0,0 @@
|
|||||||
import {
|
|
||||||
updateBackupEndpoints,
|
|
||||||
getCheckUrl,
|
|
||||||
setCustomEndpoints,
|
|
||||||
} from './endpoint';
|
|
||||||
import {
|
|
||||||
NativeEventEmitter,
|
|
||||||
NativeModules,
|
|
||||||
Platform,
|
|
||||||
PermissionsAndroid,
|
|
||||||
} from 'react-native';
|
|
||||||
import {
|
|
||||||
CheckResult,
|
|
||||||
EventType,
|
|
||||||
ProgressData,
|
|
||||||
UpdateAvailableResult,
|
|
||||||
UpdateEventsListener,
|
|
||||||
} from './type';
|
|
||||||
import { assertRelease, logger } from './utils';
|
|
||||||
export { setCustomEndpoints };
|
|
||||||
const {
|
|
||||||
version: v,
|
|
||||||
} = require('react-native/Libraries/Core/ReactNativeVersion');
|
|
||||||
const RNVersion = `${v.major}.${v.minor}.${v.patch}`;
|
|
||||||
const isTurboModuleEnabled = global.__turboModuleProxy != null;
|
|
||||||
|
|
||||||
export const PushyModule = isTurboModuleEnabled
|
|
||||||
? require('./NativeUpdate').default
|
|
||||||
: NativeModules.Pushy;
|
|
||||||
|
|
||||||
if (!PushyModule) {
|
|
||||||
throw new Error('react-native-update模块无法加载,请对照安装文档检查配置。');
|
|
||||||
}
|
|
||||||
const PushyConstants = isTurboModuleEnabled
|
|
||||||
? PushyModule.getConstants()
|
|
||||||
: PushyModule;
|
|
||||||
|
|
||||||
export const downloadRootDir = PushyConstants.downloadRootDir;
|
|
||||||
export const packageVersion = PushyConstants.packageVersion;
|
|
||||||
export const currentVersion = PushyConstants.currentVersion;
|
|
||||||
export const isFirstTime = PushyConstants.isFirstTime;
|
|
||||||
const rolledBackVersion = PushyConstants.rolledBackVersion;
|
|
||||||
export const isRolledBack = typeof rolledBackVersion === 'string';
|
|
||||||
|
|
||||||
export const buildTime = PushyConstants.buildTime;
|
|
||||||
let blockUpdate = PushyConstants.blockUpdate;
|
|
||||||
let uuid = PushyConstants.uuid;
|
|
||||||
|
|
||||||
if (Platform.OS === 'android' && !PushyConstants.isUsingBundleUrl) {
|
|
||||||
throw new Error(
|
|
||||||
'react-native-update模块无法加载,请对照文档检查Bundle URL的配置',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function setLocalHashInfo(hash: string, info: Record<string, any>) {
|
|
||||||
PushyModule.setLocalHashInfo(hash, JSON.stringify(info));
|
|
||||||
}
|
|
||||||
|
|
||||||
async function getLocalHashInfo(hash: string) {
|
|
||||||
return JSON.parse(await PushyModule.getLocalHashInfo(hash));
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getCurrentVersionInfo(): Promise<{
|
|
||||||
name?: string;
|
|
||||||
description?: string;
|
|
||||||
metaInfo?: string;
|
|
||||||
}> {
|
|
||||||
return currentVersion ? (await getLocalHashInfo(currentVersion)) || {} : {};
|
|
||||||
}
|
|
||||||
|
|
||||||
const eventEmitter = new NativeEventEmitter(PushyModule);
|
|
||||||
|
|
||||||
if (!uuid) {
|
|
||||||
uuid = require('nanoid/non-secure').nanoid();
|
|
||||||
PushyModule.setUuid(uuid);
|
|
||||||
}
|
|
||||||
|
|
||||||
const noop = () => {};
|
|
||||||
let reporter: UpdateEventsListener = noop;
|
|
||||||
|
|
||||||
export function onPushyEvents(customReporter: UpdateEventsListener) {
|
|
||||||
reporter = customReporter;
|
|
||||||
if (isRolledBack) {
|
|
||||||
report({
|
|
||||||
type: 'rollback',
|
|
||||||
data: {
|
|
||||||
rolledBackVersion,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function report({
|
|
||||||
type,
|
|
||||||
message = '',
|
|
||||||
data = {},
|
|
||||||
}: {
|
|
||||||
type: EventType;
|
|
||||||
message?: string;
|
|
||||||
data?: Record<string, string | number>;
|
|
||||||
}) {
|
|
||||||
logger(type + ' ' + message);
|
|
||||||
reporter({
|
|
||||||
type,
|
|
||||||
data: {
|
|
||||||
currentVersion,
|
|
||||||
cInfo,
|
|
||||||
packageVersion,
|
|
||||||
buildTime,
|
|
||||||
message,
|
|
||||||
...data,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
logger('uuid: ' + uuid);
|
|
||||||
|
|
||||||
export const cInfo = {
|
|
||||||
pushy: require('../package.json').version,
|
|
||||||
rn: RNVersion,
|
|
||||||
os: Platform.OS + ' ' + Platform.Version,
|
|
||||||
uuid,
|
|
||||||
};
|
|
||||||
|
|
||||||
let lastChecking;
|
|
||||||
const empty = {};
|
|
||||||
let lastResult: CheckResult;
|
|
||||||
export async function checkUpdate(APPKEY: string) {
|
|
||||||
assertRelease();
|
|
||||||
const now = Date.now();
|
|
||||||
if (lastResult && lastChecking && now - lastChecking < 1000 * 60) {
|
|
||||||
// logger('repeated checking, ignored');
|
|
||||||
return lastResult;
|
|
||||||
}
|
|
||||||
lastChecking = now;
|
|
||||||
if (blockUpdate && blockUpdate.until > Date.now() / 1000) {
|
|
||||||
report({
|
|
||||||
type: 'errorChecking',
|
|
||||||
message: `热更新已暂停,原因:${blockUpdate.reason}。请在"${new Date(
|
|
||||||
blockUpdate.until * 1000,
|
|
||||||
).toLocaleString()}"之后重试。`,
|
|
||||||
});
|
|
||||||
return lastResult || empty;
|
|
||||||
}
|
|
||||||
report({ type: 'checking' });
|
|
||||||
const fetchPayload = {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
Accept: 'application/json',
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
packageVersion,
|
|
||||||
hash: currentVersion,
|
|
||||||
buildTime,
|
|
||||||
cInfo,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
let resp;
|
|
||||||
try {
|
|
||||||
resp = await fetch(getCheckUrl(APPKEY), fetchPayload);
|
|
||||||
} catch (e) {
|
|
||||||
report({
|
|
||||||
type: 'errorChecking',
|
|
||||||
message: '无法连接主更新服务器,尝试备用节点',
|
|
||||||
});
|
|
||||||
const backupEndpoints = await updateBackupEndpoints();
|
|
||||||
if (backupEndpoints) {
|
|
||||||
try {
|
|
||||||
resp = await Promise.race(
|
|
||||||
backupEndpoints.map((endpoint) =>
|
|
||||||
fetch(getCheckUrl(APPKEY, endpoint), fetchPayload),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} catch {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!resp) {
|
|
||||||
report({
|
|
||||||
type: 'errorChecking',
|
|
||||||
message: '无法连接更新服务器,请检查网络连接后重试',
|
|
||||||
});
|
|
||||||
return lastResult || empty;
|
|
||||||
}
|
|
||||||
const result: CheckResult = await resp.json();
|
|
||||||
|
|
||||||
lastResult = result;
|
|
||||||
// @ts-ignore
|
|
||||||
checkOperation(result.op);
|
|
||||||
|
|
||||||
if (resp.status !== 200) {
|
|
||||||
report({
|
|
||||||
type: 'errorChecking',
|
|
||||||
//@ts-ignore
|
|
||||||
message: result.message,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkOperation(
|
|
||||||
op: { type: string; reason: string; duration: number }[],
|
|
||||||
) {
|
|
||||||
if (!Array.isArray(op)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
op.forEach((action) => {
|
|
||||||
if (action.type === 'block') {
|
|
||||||
blockUpdate = {
|
|
||||||
reason: action.reason,
|
|
||||||
until: Math.round((Date.now() + action.duration) / 1000),
|
|
||||||
};
|
|
||||||
PushyModule.setBlockUpdate(blockUpdate);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
let downloadingThrottling = false;
|
|
||||||
let downloadedHash: string;
|
|
||||||
export async function downloadUpdate(
|
|
||||||
options: UpdateAvailableResult,
|
|
||||||
eventListeners?: {
|
|
||||||
onDownloadProgress?: (data: ProgressData) => void;
|
|
||||||
},
|
|
||||||
) {
|
|
||||||
assertRelease();
|
|
||||||
if (!options.update) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (rolledBackVersion === options.hash) {
|
|
||||||
logger(`rolledback hash ${rolledBackVersion}, ignored`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (downloadedHash === options.hash) {
|
|
||||||
logger(`duplicated downloaded hash ${downloadedHash}, ignored`);
|
|
||||||
return downloadedHash;
|
|
||||||
}
|
|
||||||
if (downloadingThrottling) {
|
|
||||||
logger('repeated downloading, ignored');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
downloadingThrottling = true;
|
|
||||||
setTimeout(() => {
|
|
||||||
downloadingThrottling = false;
|
|
||||||
}, 3000);
|
|
||||||
let progressHandler;
|
|
||||||
if (eventListeners) {
|
|
||||||
if (eventListeners.onDownloadProgress) {
|
|
||||||
const downloadCallback = eventListeners.onDownloadProgress;
|
|
||||||
progressHandler = eventEmitter.addListener(
|
|
||||||
'RCTPushyDownloadProgress',
|
|
||||||
(progressData) => {
|
|
||||||
if (progressData.hash === options.hash) {
|
|
||||||
downloadCallback(progressData);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let succeeded = false;
|
|
||||||
report({ type: 'downloading' });
|
|
||||||
if (options.diffUrl) {
|
|
||||||
logger('downloading diff');
|
|
||||||
try {
|
|
||||||
await PushyModule.downloadPatchFromPpk({
|
|
||||||
updateUrl: options.diffUrl,
|
|
||||||
hash: options.hash,
|
|
||||||
originHash: currentVersion,
|
|
||||||
});
|
|
||||||
succeeded = true;
|
|
||||||
} catch (e) {
|
|
||||||
logger(`diff error: ${e.message}, try pdiff`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!succeeded && options.pdiffUrl) {
|
|
||||||
logger('downloading pdiff');
|
|
||||||
try {
|
|
||||||
await PushyModule.downloadPatchFromPackage({
|
|
||||||
updateUrl: options.pdiffUrl,
|
|
||||||
hash: options.hash,
|
|
||||||
});
|
|
||||||
succeeded = true;
|
|
||||||
} catch (e) {
|
|
||||||
logger(`pdiff error: ${e.message}, try full patch`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!succeeded && options.updateUrl) {
|
|
||||||
logger('downloading full patch');
|
|
||||||
try {
|
|
||||||
await PushyModule.downloadFullUpdate({
|
|
||||||
updateUrl: options.updateUrl,
|
|
||||||
hash: options.hash,
|
|
||||||
});
|
|
||||||
succeeded = true;
|
|
||||||
} catch (e) {
|
|
||||||
logger(`full patch error: ${e.message}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
progressHandler && progressHandler.remove();
|
|
||||||
if (!succeeded) {
|
|
||||||
return report({ type: 'errorUpdate', data: { newVersion: options.hash } });
|
|
||||||
}
|
|
||||||
setLocalHashInfo(options.hash, {
|
|
||||||
name: options.name,
|
|
||||||
description: options.description,
|
|
||||||
metaInfo: options.metaInfo,
|
|
||||||
});
|
|
||||||
downloadedHash = options.hash;
|
|
||||||
return options.hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertHash(hash: string) {
|
|
||||||
if (!downloadedHash) {
|
|
||||||
logger(`no downloaded hash`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (hash !== downloadedHash) {
|
|
||||||
logger(`use downloaded hash ${downloadedHash} first`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
let applyingUpdate = false;
|
|
||||||
export function switchVersion(hash: string) {
|
|
||||||
assertRelease();
|
|
||||||
if (assertHash(hash) && !applyingUpdate) {
|
|
||||||
logger('switchVersion: ' + hash);
|
|
||||||
applyingUpdate = true;
|
|
||||||
PushyModule.reloadUpdate({ hash });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function switchVersionLater(hash: string) {
|
|
||||||
assertRelease();
|
|
||||||
if (assertHash(hash)) {
|
|
||||||
logger('switchVersionLater: ' + hash);
|
|
||||||
PushyModule.setNeedUpdate({ hash });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let marked = false;
|
|
||||||
export function markSuccess() {
|
|
||||||
assertRelease();
|
|
||||||
if (marked) {
|
|
||||||
logger('repeated markSuccess, ignored');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
marked = true;
|
|
||||||
PushyModule.markSuccess();
|
|
||||||
report({ type: 'markSuccess' });
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function downloadAndInstallApk({
|
|
||||||
url,
|
|
||||||
onDownloadProgress,
|
|
||||||
}: {
|
|
||||||
url: string;
|
|
||||||
onDownloadProgress?: (data: ProgressData) => void;
|
|
||||||
}) {
|
|
||||||
if (Platform.OS !== 'android') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
report({ type: 'downloadingApk' });
|
|
||||||
if (Platform.Version <= 23) {
|
|
||||||
try {
|
|
||||||
const granted = await PermissionsAndroid.request(
|
|
||||||
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
|
|
||||||
);
|
|
||||||
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
|
|
||||||
return report({ type: 'rejectStoragePermission' });
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
return report({ type: 'errorStoragePermission' });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let hash = Date.now().toString();
|
|
||||||
let progressHandler;
|
|
||||||
if (onDownloadProgress) {
|
|
||||||
progressHandler = eventEmitter.addListener(
|
|
||||||
'RCTPushyDownloadProgress',
|
|
||||||
(progressData: ProgressData) => {
|
|
||||||
if (progressData.hash === hash) {
|
|
||||||
onDownloadProgress(progressData);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
await PushyModule.downloadAndInstallApk({
|
|
||||||
url,
|
|
||||||
target: 'update.apk',
|
|
||||||
hash,
|
|
||||||
}).catch(() => {
|
|
||||||
report({ type: 'errowDownloadAndInstallApk' });
|
|
||||||
});
|
|
||||||
progressHandler && progressHandler.remove();
|
|
||||||
}
|
|
@@ -1,135 +0,0 @@
|
|||||||
import React, { PureComponent, ComponentType } from 'react';
|
|
||||||
import {
|
|
||||||
Platform,
|
|
||||||
Alert,
|
|
||||||
Linking,
|
|
||||||
AppState,
|
|
||||||
NativeEventSubscription,
|
|
||||||
} from 'react-native';
|
|
||||||
|
|
||||||
import {
|
|
||||||
isFirstTime,
|
|
||||||
isRolledBack,
|
|
||||||
checkUpdate,
|
|
||||||
downloadUpdate,
|
|
||||||
switchVersion,
|
|
||||||
switchVersionLater,
|
|
||||||
markSuccess,
|
|
||||||
downloadAndInstallApk,
|
|
||||||
onPushyEvents,
|
|
||||||
} from './main';
|
|
||||||
import { UpdateEventsListener } from './type';
|
|
||||||
|
|
||||||
export function simpleUpdate(
|
|
||||||
WrappedComponent: ComponentType,
|
|
||||||
options: { appKey?: string; onPushyEvents?: UpdateEventsListener } = {},
|
|
||||||
) {
|
|
||||||
const { appKey, onPushyEvents: eventListeners } = options;
|
|
||||||
if (!appKey) {
|
|
||||||
throw new Error('appKey is required for simpleUpdate()');
|
|
||||||
}
|
|
||||||
if (typeof eventListeners === 'function') {
|
|
||||||
onPushyEvents(eventListeners);
|
|
||||||
}
|
|
||||||
return __DEV__
|
|
||||||
? WrappedComponent
|
|
||||||
: class AppUpdate extends PureComponent {
|
|
||||||
stateListener: NativeEventSubscription;
|
|
||||||
componentDidMount() {
|
|
||||||
if (isRolledBack) {
|
|
||||||
Alert.alert('抱歉', '刚刚更新遭遇错误,已为您恢复到更新前版本');
|
|
||||||
} else if (isFirstTime) {
|
|
||||||
markSuccess();
|
|
||||||
}
|
|
||||||
this.stateListener = AppState.addEventListener(
|
|
||||||
'change',
|
|
||||||
(nextAppState) => {
|
|
||||||
if (nextAppState === 'active') {
|
|
||||||
this.checkUpdate();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
this.checkUpdate();
|
|
||||||
}
|
|
||||||
componentWillUnmount() {
|
|
||||||
this.stateListener && this.stateListener.remove();
|
|
||||||
}
|
|
||||||
doUpdate = async (info) => {
|
|
||||||
try {
|
|
||||||
const hash = await downloadUpdate(info);
|
|
||||||
if (!hash) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this.stateListener && this.stateListener.remove();
|
|
||||||
Alert.alert('提示', '下载完毕,是否立即更新?', [
|
|
||||||
{
|
|
||||||
text: '以后再说',
|
|
||||||
style: 'cancel',
|
|
||||||
onPress: () => {
|
|
||||||
switchVersionLater(hash);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '立即更新',
|
|
||||||
style: 'default',
|
|
||||||
onPress: () => {
|
|
||||||
switchVersion(hash);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
} catch (err) {
|
|
||||||
Alert.alert('更新失败', err.message);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
checkUpdate = async () => {
|
|
||||||
let info;
|
|
||||||
try {
|
|
||||||
info = await checkUpdate(appKey!);
|
|
||||||
} catch (err) {
|
|
||||||
Alert.alert('更新检查失败', err.message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (info.expired) {
|
|
||||||
Alert.alert('提示', '您的应用版本已更新,点击确定下载安装新版本', [
|
|
||||||
{
|
|
||||||
text: '确定',
|
|
||||||
onPress: () => {
|
|
||||||
if (info.downloadUrl) {
|
|
||||||
if (
|
|
||||||
Platform.OS === 'android' &&
|
|
||||||
info.downloadUrl.endsWith('.apk')
|
|
||||||
) {
|
|
||||||
downloadAndInstallApk({
|
|
||||||
url: info.downloadUrl,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Linking.openURL(info.downloadUrl);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
} else if (info.update) {
|
|
||||||
Alert.alert(
|
|
||||||
'提示',
|
|
||||||
'检查到新的版本' + info.name + ',是否下载?\n' + info.description,
|
|
||||||
[
|
|
||||||
{ text: '否', style: 'cancel' },
|
|
||||||
{
|
|
||||||
text: '是',
|
|
||||||
style: 'default',
|
|
||||||
onPress: () => {
|
|
||||||
this.doUpdate(info);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return <WrappedComponent {...this.props} />;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
71
lib/type.ts
71
lib/type.ts
@@ -1,71 +0,0 @@
|
|||||||
export interface ExpiredResult {
|
|
||||||
upToDate?: false;
|
|
||||||
expired: true;
|
|
||||||
downloadUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UpTodateResult {
|
|
||||||
expired?: false;
|
|
||||||
upToDate: true;
|
|
||||||
paused?: 'app' | 'package';
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface UpdateAvailableResult {
|
|
||||||
expired?: false;
|
|
||||||
upToDate?: false;
|
|
||||||
update: true;
|
|
||||||
name: string; // version name
|
|
||||||
hash: string;
|
|
||||||
description: string;
|
|
||||||
metaInfo: string;
|
|
||||||
pdiffUrl: string;
|
|
||||||
diffUrl?: string;
|
|
||||||
updateUrl?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type CheckResult =
|
|
||||||
| ExpiredResult
|
|
||||||
| UpTodateResult
|
|
||||||
| UpdateAvailableResult
|
|
||||||
| {};
|
|
||||||
|
|
||||||
export interface ProgressData {
|
|
||||||
hash: string;
|
|
||||||
received: number;
|
|
||||||
total: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type EventType =
|
|
||||||
| 'rollback'
|
|
||||||
| 'errorChecking'
|
|
||||||
| 'checking'
|
|
||||||
| 'downloading'
|
|
||||||
| 'errorUpdate'
|
|
||||||
| 'markSuccess'
|
|
||||||
| 'downloadingApk'
|
|
||||||
| 'rejectStoragePermission'
|
|
||||||
| 'errorStoragePermission'
|
|
||||||
| 'errowDownloadAndInstallApk';
|
|
||||||
|
|
||||||
export interface EventData {
|
|
||||||
currentVersion: string;
|
|
||||||
cInfo: {
|
|
||||||
pushy: string;
|
|
||||||
rn: string;
|
|
||||||
os: string;
|
|
||||||
uuid: string;
|
|
||||||
};
|
|
||||||
packageVersion: string;
|
|
||||||
buildTime: number;
|
|
||||||
message?: string;
|
|
||||||
rolledBackVersion?: string;
|
|
||||||
newVersion?: string;
|
|
||||||
[key: string]: any;
|
|
||||||
}
|
|
||||||
export type UpdateEventsListener = ({
|
|
||||||
type,
|
|
||||||
data,
|
|
||||||
}: {
|
|
||||||
type: EventType;
|
|
||||||
data: EventData;
|
|
||||||
}) => void;
|
|
@@ -1,9 +0,0 @@
|
|||||||
export function logger(...args: any[]) {
|
|
||||||
console.log('Pushy: ', ...args);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function assertRelease() {
|
|
||||||
if (__DEV__) {
|
|
||||||
throw new Error('react-native-update 只能在 RELEASE 版本中运行.');
|
|
||||||
}
|
|
||||||
}
|
|
42
package.json
42
package.json
@@ -1,10 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "react-native-update",
|
"name": "react-native-update",
|
||||||
"version": "9.1.4",
|
"version": "10.15.1",
|
||||||
"description": "react-native hot update",
|
"description": "react-native hot update",
|
||||||
"main": "lib/index.ts",
|
"main": "src/index",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepublish": "yarn submodule",
|
"prepack": "yarn submodule && yarn lint",
|
||||||
|
"lint": "eslint \"src/*.@(ts|tsx|js|jsx)\" && tsc --noEmit",
|
||||||
"submodule": "git submodule update --init --recursive",
|
"submodule": "git submodule update --init --recursive",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1",
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
"build-lib": "yarn submodule && $ANDROID_HOME/ndk/20.1.5948944/ndk-build NDK_PROJECT_PATH=android APP_BUILD_SCRIPT=android/jni/Android.mk NDK_APPLICATION_MK=android/jni/Application.mk NDK_LIBS_OUT=android/lib",
|
"build-lib": "yarn submodule && $ANDROID_HOME/ndk/20.1.5948944/ndk-build NDK_PROJECT_PATH=android APP_BUILD_SCRIPT=android/jni/Android.mk NDK_APPLICATION_MK=android/jni/Application.mk NDK_LIBS_OUT=android/lib",
|
||||||
@@ -39,33 +40,40 @@
|
|||||||
"url": "https://github.com/reactnativecn/react-native-pushy/issues"
|
"url": "https://github.com/reactnativecn/react-native-pushy/issues"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react-native": ">=0.57.0"
|
"react": ">=16.8.0",
|
||||||
|
"react-native": ">=0.59.0"
|
||||||
},
|
},
|
||||||
"homepage": "https://github.com/reactnativecn/react-native-pushy#readme",
|
"homepage": "https://github.com/reactnativecn/react-native-pushy#readme",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"nanoid": "^3.3.3"
|
"nanoid": "^3.3.3",
|
||||||
|
"react-native-url-polyfill": "^2.0.0"
|
||||||
},
|
},
|
||||||
"codegenConfig": {
|
"codegenConfig": {
|
||||||
"libraries": [
|
"name": "RCTPushySpec",
|
||||||
{
|
"type": "modules",
|
||||||
"name": "RCTPushySpec",
|
"jsSrcsDir": "src"
|
||||||
"type": "modules",
|
|
||||||
"jsSrcsDir": "lib"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.24.0",
|
||||||
|
"@react-native/babel-preset": "^0.73.21",
|
||||||
|
"@react-native/eslint-config": "^0.73.2",
|
||||||
|
"@react-native/typescript-config": "^0.74.0",
|
||||||
"@types/fs-extra": "^9.0.13",
|
"@types/fs-extra": "^9.0.13",
|
||||||
"@types/jest": "^29.2.1",
|
"@types/jest": "^29.2.1",
|
||||||
"@types/node": "^20.8.9",
|
"@types/node": "^20.8.9",
|
||||||
"@types/react": "^18.2.33",
|
"@types/react": "^18.2.46",
|
||||||
"detox": "^20.5.0",
|
"detox": "^20.5.0",
|
||||||
|
"eslint": "^8.57.0",
|
||||||
|
"eslint-plugin-ft-flow": "^3.0.7",
|
||||||
"firebase-tools": "^11.24.1",
|
"firebase-tools": "^11.24.1",
|
||||||
"fs-extra": "^9.1.0",
|
"fs-extra": "^9.1.0",
|
||||||
"jest": "^29.2.1",
|
"jest": "^29.7.0",
|
||||||
"pod-install": "^0.1.37",
|
"pod-install": "^0.1.37",
|
||||||
"react-native": "^0.72.6",
|
"prettier": "^2",
|
||||||
|
"react": "18.2.0",
|
||||||
|
"react-native": "0.73",
|
||||||
"ts-jest": "^29.0.3",
|
"ts-jest": "^29.0.3",
|
||||||
"typescript": "^5.2.2"
|
"typescript": "^5.3.3"
|
||||||
}
|
},
|
||||||
|
"packageManager": "yarn@4.5.1"
|
||||||
}
|
}
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
require 'json'
|
require 'json'
|
||||||
|
|
||||||
|
new_arch_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1'
|
||||||
|
|
||||||
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
|
||||||
|
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
@@ -40,5 +42,25 @@ Pod::Spec.new do |s|
|
|||||||
'android/jni/lzma/C/Lzma2Dec.{h,c}']
|
'android/jni/lzma/C/Lzma2Dec.{h,c}']
|
||||||
ss.private_header_files = 'ios/RCTPushy/HDiffPatch/**/*.h'
|
ss.private_header_files = 'ios/RCTPushy/HDiffPatch/**/*.h'
|
||||||
end
|
end
|
||||||
install_modules_dependencies(s)
|
|
||||||
|
if defined?(install_modules_dependencies()) != nil
|
||||||
|
install_modules_dependencies(s);
|
||||||
|
else
|
||||||
|
if new_arch_enabled
|
||||||
|
folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32'
|
||||||
|
|
||||||
|
s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
|
||||||
|
|
||||||
|
s.pod_target_xcconfig = {
|
||||||
|
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
|
||||||
|
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
|
||||||
|
}
|
||||||
|
|
||||||
|
s.dependency "React-Codegen"
|
||||||
|
s.dependency "RCT-Folly"
|
||||||
|
s.dependency "RCTRequired"
|
||||||
|
s.dependency "RCTTypeSafety"
|
||||||
|
s.dependency "ReactCommon/turbomodule/core"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@@ -1,5 +1,4 @@
|
|||||||
import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport';
|
import { TurboModule, TurboModuleRegistry } from 'react-native';
|
||||||
import { TurboModuleRegistry } from 'react-native';
|
|
||||||
|
|
||||||
export interface Spec extends TurboModule {
|
export interface Spec extends TurboModule {
|
||||||
getConstants: () => {
|
getConstants: () => {
|
||||||
@@ -9,14 +8,12 @@ export interface Spec extends TurboModule {
|
|||||||
isFirstTime: boolean;
|
isFirstTime: boolean;
|
||||||
rolledBackVersion: string;
|
rolledBackVersion: string;
|
||||||
buildTime: string;
|
buildTime: string;
|
||||||
blockUpdate: Object;
|
|
||||||
uuid: string;
|
uuid: string;
|
||||||
isUsingBundleUrl: boolean;
|
isUsingBundleUrl: boolean;
|
||||||
};
|
};
|
||||||
setLocalHashInfo(hash: string, info: string): Promise<void>;
|
setLocalHashInfo(hash: string, info: string): Promise<void>;
|
||||||
getLocalHashInfo(hash: string): Promise<string>;
|
getLocalHashInfo(hash: string): Promise<string>;
|
||||||
setUuid(uuid: string): Promise<void>;
|
setUuid(uuid: string): Promise<void>;
|
||||||
setBlockUpdate(options: { reason: string; until: number }): Promise<void>;
|
|
||||||
reloadUpdate(options: { hash: string }): Promise<void>;
|
reloadUpdate(options: { hash: string }): Promise<void>;
|
||||||
setNeedUpdate(options: { hash: string }): Promise<void>;
|
setNeedUpdate(options: { hash: string }): Promise<void>;
|
||||||
markSuccess(): Promise<void>;
|
markSuccess(): Promise<void>;
|
459
src/client.ts
Normal file
459
src/client.ts
Normal file
@@ -0,0 +1,459 @@
|
|||||||
|
import { CheckResult, PushyOptions, ProgressData, EventType } from './type';
|
||||||
|
import { joinUrls, log, testUrls } from './utils';
|
||||||
|
import { EmitterSubscription, Platform } from 'react-native';
|
||||||
|
import { PermissionsAndroid } from './permissions';
|
||||||
|
import {
|
||||||
|
PushyModule,
|
||||||
|
buildTime,
|
||||||
|
cInfo,
|
||||||
|
pushyNativeEventEmitter,
|
||||||
|
currentVersion,
|
||||||
|
packageVersion,
|
||||||
|
rolledBackVersion,
|
||||||
|
setLocalHashInfo,
|
||||||
|
isFirstTime,
|
||||||
|
isRolledBack,
|
||||||
|
} from './core';
|
||||||
|
|
||||||
|
const defaultServer = {
|
||||||
|
main: 'https://update.react-native.cn/api',
|
||||||
|
backups: ['https://update.reactnative.cn/api'],
|
||||||
|
queryUrls: [
|
||||||
|
'https://gitee.com/sunnylqm/react-native-pushy/raw/master/endpoints.json',
|
||||||
|
'https://cdn.jsdelivr.net/gh/reactnativecn/react-native-pushy@master/endpoints.json',
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const empty = {};
|
||||||
|
const noop = () => {};
|
||||||
|
|
||||||
|
if (Platform.OS === 'web') {
|
||||||
|
console.warn('react-native-update 不支持 web 端热更,不会执行操作');
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Pushy {
|
||||||
|
options: PushyOptions = {
|
||||||
|
appKey: '',
|
||||||
|
server: defaultServer,
|
||||||
|
autoMarkSuccess: true,
|
||||||
|
updateStrategy: __DEV__ ? 'alwaysAlert' : 'alertUpdateAndIgnoreError',
|
||||||
|
checkStrategy: 'both',
|
||||||
|
logger: noop,
|
||||||
|
debug: false,
|
||||||
|
throwError: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
lastChecking?: number;
|
||||||
|
lastRespJson?: Promise<any>;
|
||||||
|
|
||||||
|
progressHandlers: Record<string, EmitterSubscription> = {};
|
||||||
|
downloadedHash?: string;
|
||||||
|
|
||||||
|
marked = false;
|
||||||
|
applyingUpdate = false;
|
||||||
|
version = cInfo.pushy;
|
||||||
|
loggerPromise = (() => {
|
||||||
|
let resolve: (value?: unknown) => void = () => {};
|
||||||
|
const promise = new Promise(res => {
|
||||||
|
resolve = res;
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
promise,
|
||||||
|
resolve,
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
||||||
|
constructor(options: PushyOptions) {
|
||||||
|
if (Platform.OS === 'ios' || Platform.OS === 'android') {
|
||||||
|
if (!options.appKey) {
|
||||||
|
throw new Error('appKey is required');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.setOptions(options);
|
||||||
|
if (isRolledBack) {
|
||||||
|
this.report({
|
||||||
|
type: 'rollback',
|
||||||
|
data: {
|
||||||
|
rolledBackVersion,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setOptions = (options: Partial<PushyOptions>) => {
|
||||||
|
for (const [key, value] of Object.entries(options)) {
|
||||||
|
if (value !== undefined) {
|
||||||
|
(this.options as any)[key] = value;
|
||||||
|
if (key === 'logger') {
|
||||||
|
this.loggerPromise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
report = async ({
|
||||||
|
type,
|
||||||
|
message = '',
|
||||||
|
data = {},
|
||||||
|
}: {
|
||||||
|
type: EventType;
|
||||||
|
message?: string;
|
||||||
|
data?: Record<string, string | number>;
|
||||||
|
}) => {
|
||||||
|
log(type + ' ' + message);
|
||||||
|
await this.loggerPromise.promise;
|
||||||
|
const { logger = noop, appKey } = this.options;
|
||||||
|
logger({
|
||||||
|
type,
|
||||||
|
data: {
|
||||||
|
appKey,
|
||||||
|
currentVersion,
|
||||||
|
cInfo,
|
||||||
|
packageVersion,
|
||||||
|
buildTime,
|
||||||
|
message,
|
||||||
|
...data,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
getCheckUrl = (endpoint: string = this.options.server!.main) => {
|
||||||
|
return `${endpoint}/checkUpdate/${this.options.appKey}`;
|
||||||
|
};
|
||||||
|
assertHash = (hash: string) => {
|
||||||
|
if (!this.downloadedHash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (hash !== this.downloadedHash) {
|
||||||
|
log(`use downloaded hash ${this.downloadedHash} first`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
markSuccess = () => {
|
||||||
|
if (this.marked || __DEV__ || !isFirstTime) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.marked = true;
|
||||||
|
PushyModule.markSuccess();
|
||||||
|
this.report({ type: 'markSuccess' });
|
||||||
|
};
|
||||||
|
switchVersion = async (hash: string) => {
|
||||||
|
if (__DEV__) {
|
||||||
|
console.warn(
|
||||||
|
'您调用了switchVersion方法,但是当前是开发环境,不会进行任何操作。',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.assertHash(hash) && !this.applyingUpdate) {
|
||||||
|
log('switchVersion: ' + hash);
|
||||||
|
this.applyingUpdate = true;
|
||||||
|
return PushyModule.reloadUpdate({ hash });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
switchVersionLater = async (hash: string) => {
|
||||||
|
if (__DEV__) {
|
||||||
|
console.warn(
|
||||||
|
'您调用了switchVersionLater方法,但是当前是开发环境,不会进行任何操作。',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.assertHash(hash)) {
|
||||||
|
log('switchVersionLater: ' + hash);
|
||||||
|
return PushyModule.setNeedUpdate({ hash });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
checkUpdate = async (extra?: Record<string, any>) => {
|
||||||
|
if (__DEV__ && !this.options.debug) {
|
||||||
|
console.info(
|
||||||
|
'您当前处于开发环境且未启用 debug,不会进行热更检查。如需在开发环境中调试热更,请在 client 中设置 debug 为 true',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Platform.OS === 'web') {
|
||||||
|
console.warn('web 端不支持热更新检查');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
this.options.beforeCheckUpdate &&
|
||||||
|
(await this.options.beforeCheckUpdate()) === false
|
||||||
|
) {
|
||||||
|
log('beforeCheckUpdate 返回 false, 忽略检查');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const now = Date.now();
|
||||||
|
if (
|
||||||
|
this.lastRespJson &&
|
||||||
|
this.lastChecking &&
|
||||||
|
now - this.lastChecking < 1000 * 5
|
||||||
|
) {
|
||||||
|
return await this.lastRespJson;
|
||||||
|
}
|
||||||
|
this.lastChecking = now;
|
||||||
|
const fetchBody = {
|
||||||
|
packageVersion,
|
||||||
|
hash: currentVersion,
|
||||||
|
buildTime,
|
||||||
|
cInfo,
|
||||||
|
...extra,
|
||||||
|
};
|
||||||
|
if (__DEV__) {
|
||||||
|
// @ts-ignore
|
||||||
|
delete fetchBody.buildTime;
|
||||||
|
}
|
||||||
|
const body = JSON.stringify(fetchBody);
|
||||||
|
const fetchPayload = {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
Accept: 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
body,
|
||||||
|
};
|
||||||
|
let resp;
|
||||||
|
try {
|
||||||
|
this.report({
|
||||||
|
type: 'checking',
|
||||||
|
message: this.options.appKey + ': ' + body,
|
||||||
|
});
|
||||||
|
resp = await fetch(this.getCheckUrl(), fetchPayload);
|
||||||
|
} catch (e: any) {
|
||||||
|
this.report({
|
||||||
|
type: 'errorChecking',
|
||||||
|
message: 'Can not connect to update server. Trying backup endpoints.',
|
||||||
|
});
|
||||||
|
const backupEndpoints = await this.getBackupEndpoints();
|
||||||
|
if (backupEndpoints) {
|
||||||
|
try {
|
||||||
|
resp = await Promise.race(
|
||||||
|
backupEndpoints.map(endpoint =>
|
||||||
|
fetch(this.getCheckUrl(endpoint), fetchPayload),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!resp) {
|
||||||
|
this.report({
|
||||||
|
type: 'errorChecking',
|
||||||
|
message: 'Can not connect to update server. Please check your network.',
|
||||||
|
});
|
||||||
|
return this.lastRespJson ? await this.lastRespJson : empty;
|
||||||
|
}
|
||||||
|
this.lastRespJson = resp.json();
|
||||||
|
|
||||||
|
const result: CheckResult = await this.lastRespJson;
|
||||||
|
|
||||||
|
log('checking result:', result);
|
||||||
|
|
||||||
|
if (resp.status !== 200) {
|
||||||
|
this.report({
|
||||||
|
type: 'errorChecking',
|
||||||
|
message: result.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
getBackupEndpoints = async () => {
|
||||||
|
const { server } = this.options;
|
||||||
|
if (!server) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
if (server.queryUrls) {
|
||||||
|
try {
|
||||||
|
const resp = await Promise.race(
|
||||||
|
server.queryUrls.map(queryUrl => fetch(queryUrl)),
|
||||||
|
);
|
||||||
|
const remoteEndpoints = await resp.json();
|
||||||
|
log('fetch endpoints:', remoteEndpoints);
|
||||||
|
if (Array.isArray(remoteEndpoints)) {
|
||||||
|
server.backups = Array.from(
|
||||||
|
new Set([...(server.backups || []), ...remoteEndpoints]),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
log('failed to fetch endpoints from: ', server.queryUrls);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return server.backups;
|
||||||
|
};
|
||||||
|
downloadUpdate = async (
|
||||||
|
info: CheckResult,
|
||||||
|
onDownloadProgress?: (data: ProgressData) => void,
|
||||||
|
) => {
|
||||||
|
const {
|
||||||
|
hash,
|
||||||
|
diff,
|
||||||
|
pdiff,
|
||||||
|
full,
|
||||||
|
paths = [],
|
||||||
|
name,
|
||||||
|
description = '',
|
||||||
|
metaInfo,
|
||||||
|
} = info;
|
||||||
|
if (
|
||||||
|
this.options.beforeDownloadUpdate &&
|
||||||
|
(await this.options.beforeDownloadUpdate(info)) === false
|
||||||
|
) {
|
||||||
|
log('beforeDownloadUpdate 返回 false, 忽略下载');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!info.update || !hash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (rolledBackVersion === hash) {
|
||||||
|
log(`rolledback hash ${rolledBackVersion}, ignored`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (this.downloadedHash === hash) {
|
||||||
|
log(`duplicated downloaded hash ${this.downloadedHash}, ignored`);
|
||||||
|
return this.downloadedHash;
|
||||||
|
}
|
||||||
|
if (this.progressHandlers[hash]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (onDownloadProgress) {
|
||||||
|
this.progressHandlers[hash] = pushyNativeEventEmitter.addListener(
|
||||||
|
'RCTPushyDownloadProgress',
|
||||||
|
progressData => {
|
||||||
|
if (progressData.hash === hash) {
|
||||||
|
onDownloadProgress(progressData);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
let succeeded = '';
|
||||||
|
this.report({ type: 'downloading' });
|
||||||
|
let lastError: any;
|
||||||
|
const diffUrl = await testUrls(joinUrls(paths, diff));
|
||||||
|
if (diffUrl) {
|
||||||
|
log('downloading diff');
|
||||||
|
try {
|
||||||
|
await PushyModule.downloadPatchFromPpk({
|
||||||
|
updateUrl: diffUrl,
|
||||||
|
hash,
|
||||||
|
originHash: currentVersion,
|
||||||
|
});
|
||||||
|
succeeded = 'diff';
|
||||||
|
} catch (e: any) {
|
||||||
|
lastError = e;
|
||||||
|
if (__DEV__) {
|
||||||
|
succeeded = 'diff';
|
||||||
|
} else {
|
||||||
|
log(`diff error: ${e.message}, try pdiff`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const pdiffUrl = await testUrls(joinUrls(paths, pdiff));
|
||||||
|
if (!succeeded && pdiffUrl) {
|
||||||
|
log('downloading pdiff');
|
||||||
|
try {
|
||||||
|
await PushyModule.downloadPatchFromPackage({
|
||||||
|
updateUrl: pdiffUrl,
|
||||||
|
hash,
|
||||||
|
});
|
||||||
|
succeeded = 'pdiff';
|
||||||
|
} catch (e: any) {
|
||||||
|
lastError = e;
|
||||||
|
if (__DEV__) {
|
||||||
|
succeeded = 'pdiff';
|
||||||
|
} else {
|
||||||
|
log(`pdiff error: ${e.message}, try full patch`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const fullUrl = await testUrls(joinUrls(paths, full));
|
||||||
|
if (!succeeded && fullUrl) {
|
||||||
|
log('downloading full patch');
|
||||||
|
try {
|
||||||
|
await PushyModule.downloadFullUpdate({
|
||||||
|
updateUrl: fullUrl,
|
||||||
|
hash,
|
||||||
|
});
|
||||||
|
succeeded = 'full';
|
||||||
|
} catch (e: any) {
|
||||||
|
lastError = e;
|
||||||
|
if (__DEV__) {
|
||||||
|
succeeded = 'full';
|
||||||
|
} else {
|
||||||
|
log(`full patch error: ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.progressHandlers[hash]) {
|
||||||
|
this.progressHandlers[hash].remove();
|
||||||
|
delete this.progressHandlers[hash];
|
||||||
|
}
|
||||||
|
if (__DEV__) {
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
if (!succeeded) {
|
||||||
|
this.report({
|
||||||
|
type: 'errorUpdate',
|
||||||
|
data: { newVersion: hash },
|
||||||
|
});
|
||||||
|
if (lastError) {
|
||||||
|
throw lastError;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
this.report({
|
||||||
|
type: 'downloadSuccess',
|
||||||
|
data: { newVersion: hash, diff: succeeded },
|
||||||
|
});
|
||||||
|
}
|
||||||
|
log(`downloaded ${succeeded} hash:`, hash);
|
||||||
|
setLocalHashInfo(hash, {
|
||||||
|
name,
|
||||||
|
description,
|
||||||
|
metaInfo,
|
||||||
|
});
|
||||||
|
this.downloadedHash = hash;
|
||||||
|
return hash;
|
||||||
|
};
|
||||||
|
downloadAndInstallApk = async (
|
||||||
|
url: string,
|
||||||
|
onDownloadProgress?: (data: ProgressData) => void,
|
||||||
|
) => {
|
||||||
|
if (Platform.OS !== 'android') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.report({ type: 'downloadingApk' });
|
||||||
|
if (Platform.Version <= 23) {
|
||||||
|
try {
|
||||||
|
const granted = await PermissionsAndroid.request(
|
||||||
|
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
|
||||||
|
);
|
||||||
|
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
|
||||||
|
return this.report({ type: 'rejectStoragePermission' });
|
||||||
|
}
|
||||||
|
} catch (e: any) {
|
||||||
|
return this.report({ type: 'errorStoragePermission' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const progressKey = 'downloadingApk';
|
||||||
|
if (onDownloadProgress) {
|
||||||
|
this.progressHandlers[progressKey] = pushyNativeEventEmitter.addListener(
|
||||||
|
'RCTPushyDownloadProgress',
|
||||||
|
(progressData: ProgressData) => {
|
||||||
|
if (progressData.hash === progressKey) {
|
||||||
|
onDownloadProgress(progressData);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await PushyModule.downloadAndInstallApk({
|
||||||
|
url,
|
||||||
|
target: 'update.apk',
|
||||||
|
hash: progressKey,
|
||||||
|
}).catch(() => {
|
||||||
|
this.report({ type: 'errorDownloadAndInstallApk' });
|
||||||
|
});
|
||||||
|
if (this.progressHandlers[progressKey]) {
|
||||||
|
this.progressHandlers[progressKey].remove();
|
||||||
|
delete this.progressHandlers[progressKey];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
44
src/context.ts
Normal file
44
src/context.ts
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
import { createContext, useContext } from 'react';
|
||||||
|
import { CheckResult, ProgressData } from './type';
|
||||||
|
import { Pushy } from './client';
|
||||||
|
|
||||||
|
const noop = () => {};
|
||||||
|
const asyncNoop = () => Promise.resolve();
|
||||||
|
|
||||||
|
export const defaultContext = {
|
||||||
|
checkUpdate: asyncNoop,
|
||||||
|
switchVersion: asyncNoop,
|
||||||
|
switchVersionLater: asyncNoop,
|
||||||
|
markSuccess: noop,
|
||||||
|
dismissError: noop,
|
||||||
|
downloadUpdate: asyncNoop,
|
||||||
|
downloadAndInstallApk: asyncNoop,
|
||||||
|
getCurrentVersionInfo: () => Promise.resolve({}),
|
||||||
|
parseTestQrCode: () => false,
|
||||||
|
currentHash: '',
|
||||||
|
packageVersion: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const PushyContext = createContext<{
|
||||||
|
checkUpdate: () => Promise<void>;
|
||||||
|
switchVersion: () => Promise<void>;
|
||||||
|
switchVersionLater: () => Promise<void>;
|
||||||
|
markSuccess: () => void;
|
||||||
|
dismissError: () => void;
|
||||||
|
downloadUpdate: () => Promise<void>;
|
||||||
|
downloadAndInstallApk: (url: string) => Promise<void>;
|
||||||
|
getCurrentVersionInfo: () => Promise<{
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
metaInfo?: string;
|
||||||
|
}>;
|
||||||
|
parseTestQrCode: (code: string) => boolean;
|
||||||
|
currentHash: string;
|
||||||
|
packageVersion: string;
|
||||||
|
client?: Pushy;
|
||||||
|
progress?: ProgressData;
|
||||||
|
updateInfo?: CheckResult;
|
||||||
|
lastError?: Error;
|
||||||
|
}>(defaultContext);
|
||||||
|
|
||||||
|
export const usePushy = () => useContext(PushyContext);
|
70
src/core.ts
Normal file
70
src/core.ts
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
|
||||||
|
import { emptyModule, log } from './utils';
|
||||||
|
const {
|
||||||
|
version: v,
|
||||||
|
} = require('react-native/Libraries/Core/ReactNativeVersion');
|
||||||
|
const RNVersion = `${v.major}.${v.minor}.${v.patch}`;
|
||||||
|
const isTurboModuleEnabled = (global as any).__turboModuleProxy != null;
|
||||||
|
|
||||||
|
export const PushyModule =
|
||||||
|
Platform.OS === 'web'
|
||||||
|
? emptyModule
|
||||||
|
: isTurboModuleEnabled
|
||||||
|
? require('./NativePushy').default
|
||||||
|
: NativeModules.Pushy;
|
||||||
|
|
||||||
|
if (!PushyModule) {
|
||||||
|
throw new Error('react-native-update 模块无法加载,请对照安装文档检查配置。');
|
||||||
|
}
|
||||||
|
|
||||||
|
const PushyConstants = isTurboModuleEnabled
|
||||||
|
? PushyModule.getConstants()
|
||||||
|
: PushyModule;
|
||||||
|
|
||||||
|
export const downloadRootDir: string = PushyConstants.downloadRootDir;
|
||||||
|
export const packageVersion: string = PushyConstants.packageVersion;
|
||||||
|
export const currentVersion: string = PushyConstants.currentVersion;
|
||||||
|
export const isFirstTime: boolean = PushyConstants.isFirstTime;
|
||||||
|
export const rolledBackVersion: string = PushyConstants.rolledBackVersion;
|
||||||
|
export const isRolledBack: boolean = typeof rolledBackVersion === 'string';
|
||||||
|
|
||||||
|
export const buildTime: string = PushyConstants.buildTime;
|
||||||
|
let uuid = PushyConstants.uuid;
|
||||||
|
|
||||||
|
if (Platform.OS === 'android' && !PushyConstants.isUsingBundleUrl) {
|
||||||
|
throw new Error(
|
||||||
|
'react-native-update 模块无法加载,请对照文档检查 Bundle URL 的配置',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function setLocalHashInfo(hash: string, info: Record<string, any>) {
|
||||||
|
PushyModule.setLocalHashInfo(hash, JSON.stringify(info));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getLocalHashInfo(hash: string) {
|
||||||
|
return JSON.parse(await PushyModule.getLocalHashInfo(hash));
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getCurrentVersionInfo(): Promise<{
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
metaInfo?: string;
|
||||||
|
}> {
|
||||||
|
return currentVersion ? (await getLocalHashInfo(currentVersion)) || {} : {};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const pushyNativeEventEmitter = new NativeEventEmitter(PushyModule);
|
||||||
|
|
||||||
|
if (!uuid) {
|
||||||
|
uuid = require('nanoid/non-secure').nanoid();
|
||||||
|
PushyModule.setUuid(uuid);
|
||||||
|
}
|
||||||
|
|
||||||
|
log('uuid: ' + uuid);
|
||||||
|
|
||||||
|
export const cInfo = {
|
||||||
|
pushy: require('../package.json').version,
|
||||||
|
rn: RNVersion,
|
||||||
|
os: Platform.OS + ' ' + Platform.Version,
|
||||||
|
uuid,
|
||||||
|
};
|
3
src/index.ts
Normal file
3
src/index.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export { Pushy } from './client';
|
||||||
|
export { PushyContext, usePushy } from './context';
|
||||||
|
export { PushyProvider } from './provider';
|
77
src/isInRollout.ts
Normal file
77
src/isInRollout.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/* eslint-disable no-fallthrough */
|
||||||
|
|
||||||
|
import { cInfo } from './core';
|
||||||
|
|
||||||
|
/* eslint-disable no-bitwise */
|
||||||
|
function murmurhash3_32_gc(key: string, seed = 0) {
|
||||||
|
let remainder, bytes, h1, h1b, c1, c2, k1, i;
|
||||||
|
|
||||||
|
remainder = key.length & 3; // key.length % 4
|
||||||
|
bytes = key.length - remainder;
|
||||||
|
h1 = seed;
|
||||||
|
c1 = 0xcc9e2d51;
|
||||||
|
c2 = 0x1b873593;
|
||||||
|
i = 0;
|
||||||
|
|
||||||
|
while (i < bytes) {
|
||||||
|
k1 =
|
||||||
|
(key.charCodeAt(i) & 0xff) |
|
||||||
|
((key.charCodeAt(++i) & 0xff) << 8) |
|
||||||
|
((key.charCodeAt(++i) & 0xff) << 16) |
|
||||||
|
((key.charCodeAt(++i) & 0xff) << 24);
|
||||||
|
++i;
|
||||||
|
|
||||||
|
((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
|
||||||
|
k1 = (k1 << 15) | (k1 >>> 17);
|
||||||
|
k1 =
|
||||||
|
((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
|
||||||
|
|
||||||
|
h1 ^= k1;
|
||||||
|
h1 = (h1 << 13) | (h1 >>> 19);
|
||||||
|
h1b =
|
||||||
|
((h1 & 0xffff) * 5 + ((((h1 >>> 16) * 5) & 0xffff) << 16)) & 0xffffffff;
|
||||||
|
h1 = (h1b & 0xffff) + 0x6b64 + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
k1 = 0;
|
||||||
|
|
||||||
|
switch (remainder) {
|
||||||
|
case 3:
|
||||||
|
k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
|
||||||
|
case 2:
|
||||||
|
k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
|
||||||
|
case 1:
|
||||||
|
k1 ^= key.charCodeAt(i) & 0xff;
|
||||||
|
|
||||||
|
k1 =
|
||||||
|
((k1 & 0xffff) * c1 + ((((k1 >>> 16) * c1) & 0xffff) << 16)) &
|
||||||
|
0xffffffff;
|
||||||
|
k1 = (k1 << 15) | (k1 >>> 17);
|
||||||
|
k1 =
|
||||||
|
((k1 & 0xffff) * c2 + ((((k1 >>> 16) * c2) & 0xffff) << 16)) &
|
||||||
|
0xffffffff;
|
||||||
|
h1 ^= k1;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 ^= key.length;
|
||||||
|
|
||||||
|
h1 ^= h1 >>> 16;
|
||||||
|
h1 =
|
||||||
|
((h1 & 0xffff) * 0x85ebca6b +
|
||||||
|
((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) &
|
||||||
|
0xffffffff;
|
||||||
|
h1 ^= h1 >>> 13;
|
||||||
|
h1 =
|
||||||
|
((h1 & 0xffff) * 0xc2b2ae35 +
|
||||||
|
((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16)) &
|
||||||
|
0xffffffff;
|
||||||
|
h1 ^= h1 >>> 16;
|
||||||
|
|
||||||
|
return h1 >>> 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const intForUUID = murmurhash3_32_gc(cInfo.uuid);
|
||||||
|
|
||||||
|
export function isInRollout(rollout: number) {
|
||||||
|
return intForUUID % 100 < rollout;
|
||||||
|
}
|
1
src/permissions.native.ts
Normal file
1
src/permissions.native.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { PermissionsAndroid } from 'react-native';
|
4
src/permissions.ts
Normal file
4
src/permissions.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import type { PermissionsAndroidStatic } from 'react-native';
|
||||||
|
import { emptyModule } from './utils';
|
||||||
|
|
||||||
|
export const PermissionsAndroid = emptyModule as PermissionsAndroidStatic;
|
354
src/provider.tsx
Normal file
354
src/provider.tsx
Normal file
@@ -0,0 +1,354 @@
|
|||||||
|
import React, {
|
||||||
|
ReactNode,
|
||||||
|
useCallback,
|
||||||
|
useEffect,
|
||||||
|
useRef,
|
||||||
|
useState,
|
||||||
|
} from 'react';
|
||||||
|
import {
|
||||||
|
Alert,
|
||||||
|
NativeEventSubscription,
|
||||||
|
AppState,
|
||||||
|
Platform,
|
||||||
|
Linking,
|
||||||
|
} from 'react-native';
|
||||||
|
import { Pushy } from './client';
|
||||||
|
import { currentVersion, packageVersion, getCurrentVersionInfo } from './core';
|
||||||
|
import { CheckResult, ProgressData, PushyTestPayload } from './type';
|
||||||
|
import { PushyContext } from './context';
|
||||||
|
import { URL } from 'react-native-url-polyfill';
|
||||||
|
import { isInRollout } from './isInRollout';
|
||||||
|
import { log } from './utils';
|
||||||
|
|
||||||
|
export const PushyProvider = ({
|
||||||
|
client,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
client: Pushy;
|
||||||
|
children: ReactNode;
|
||||||
|
}) => {
|
||||||
|
const { options } = client;
|
||||||
|
const stateListener = useRef<NativeEventSubscription>();
|
||||||
|
const [updateInfo, setUpdateInfo] = useState<CheckResult>();
|
||||||
|
const updateInfoRef = useRef(updateInfo);
|
||||||
|
const [progress, setProgress] = useState<ProgressData>();
|
||||||
|
const [lastError, setLastError] = useState<Error>();
|
||||||
|
const lastChecking = useRef(0);
|
||||||
|
|
||||||
|
const throwErrorIfEnabled = useCallback(
|
||||||
|
(e: Error) => {
|
||||||
|
if (options.throwError) {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[options.throwError],
|
||||||
|
);
|
||||||
|
|
||||||
|
const dismissError = useCallback(() => {
|
||||||
|
setLastError(undefined);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const alertUpdate = useCallback(
|
||||||
|
(...args: Parameters<typeof Alert.alert>) => {
|
||||||
|
if (
|
||||||
|
options.updateStrategy === 'alwaysAlert' ||
|
||||||
|
options.updateStrategy === 'alertUpdateAndIgnoreError'
|
||||||
|
) {
|
||||||
|
Alert.alert(...args);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[options.updateStrategy],
|
||||||
|
);
|
||||||
|
|
||||||
|
const alertError = useCallback(
|
||||||
|
(...args: Parameters<typeof Alert.alert>) => {
|
||||||
|
if (options.updateStrategy === 'alwaysAlert') {
|
||||||
|
Alert.alert(...args);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[options.updateStrategy],
|
||||||
|
);
|
||||||
|
|
||||||
|
const switchVersion = useCallback(
|
||||||
|
async (info: CheckResult | undefined = updateInfoRef.current) => {
|
||||||
|
if (info && info.hash) {
|
||||||
|
return client.switchVersion(info.hash);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[client],
|
||||||
|
);
|
||||||
|
|
||||||
|
const switchVersionLater = useCallback(
|
||||||
|
async (info: CheckResult | undefined = updateInfoRef.current) => {
|
||||||
|
if (info && info.hash) {
|
||||||
|
return client.switchVersionLater(info.hash);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[client],
|
||||||
|
);
|
||||||
|
|
||||||
|
const downloadUpdate = useCallback(
|
||||||
|
async (info: CheckResult | undefined = updateInfoRef.current) => {
|
||||||
|
if (!info || !info.update) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const hash = await client.downloadUpdate(info, setProgress);
|
||||||
|
if (!hash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
stateListener.current && stateListener.current.remove();
|
||||||
|
if (options.updateStrategy === 'silentAndNow') {
|
||||||
|
return client.switchVersion(hash);
|
||||||
|
} else if (options.updateStrategy === 'silentAndLater') {
|
||||||
|
return client.switchVersionLater(hash);
|
||||||
|
}
|
||||||
|
alertUpdate('提示', '下载完毕,是否立即更新?', [
|
||||||
|
{
|
||||||
|
text: '下次再说',
|
||||||
|
style: 'cancel',
|
||||||
|
onPress: () => {
|
||||||
|
client.switchVersionLater(hash);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: '立即更新',
|
||||||
|
style: 'default',
|
||||||
|
onPress: () => {
|
||||||
|
client.switchVersion(hash);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
} catch (e: any) {
|
||||||
|
setLastError(e);
|
||||||
|
alertError('更新失败', e.message);
|
||||||
|
throwErrorIfEnabled(e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
client,
|
||||||
|
options.updateStrategy,
|
||||||
|
alertUpdate,
|
||||||
|
alertError,
|
||||||
|
throwErrorIfEnabled,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const downloadAndInstallApk = useCallback(
|
||||||
|
async (downloadUrl: string) => {
|
||||||
|
if (Platform.OS === 'android' && downloadUrl) {
|
||||||
|
await client.downloadAndInstallApk(downloadUrl, setProgress);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[client],
|
||||||
|
);
|
||||||
|
|
||||||
|
const checkUpdate = useCallback(
|
||||||
|
async ({ extra }: { extra?: Record<string, any> } | undefined = {}) => {
|
||||||
|
const now = Date.now();
|
||||||
|
if (lastChecking.current && now - lastChecking.current < 1000) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
lastChecking.current = now;
|
||||||
|
let info: CheckResult;
|
||||||
|
try {
|
||||||
|
info = await client.checkUpdate(extra);
|
||||||
|
} catch (e: any) {
|
||||||
|
setLastError(e);
|
||||||
|
alertError('更新检查失败', e.message);
|
||||||
|
throwErrorIfEnabled(e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!info) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const rollout = info.config?.rollout?.[packageVersion];
|
||||||
|
if (rollout) {
|
||||||
|
if (!isInRollout(rollout)) {
|
||||||
|
log(`not in ${rollout}% rollout, ignored`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
log(`in ${rollout}% rollout, continue`);
|
||||||
|
}
|
||||||
|
info.description = info.description ?? '';
|
||||||
|
updateInfoRef.current = info;
|
||||||
|
setUpdateInfo(info);
|
||||||
|
if (info.expired) {
|
||||||
|
const { downloadUrl } = info;
|
||||||
|
if (downloadUrl) {
|
||||||
|
if (options.updateStrategy === 'silentAndNow') {
|
||||||
|
if (Platform.OS === 'android' && downloadUrl.endsWith('.apk')) {
|
||||||
|
downloadAndInstallApk(downloadUrl);
|
||||||
|
} else {
|
||||||
|
Linking.openURL(downloadUrl);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
alertUpdate('提示', '您的应用版本已更新,点击更新下载安装新版本', [
|
||||||
|
{
|
||||||
|
text: '更新',
|
||||||
|
onPress: () => {
|
||||||
|
if (Platform.OS === 'android' && downloadUrl.endsWith('.apk')) {
|
||||||
|
downloadAndInstallApk(downloadUrl);
|
||||||
|
} else {
|
||||||
|
Linking.openURL(downloadUrl);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else if (info.update) {
|
||||||
|
if (
|
||||||
|
options.updateStrategy === 'silentAndNow' ||
|
||||||
|
options.updateStrategy === 'silentAndLater'
|
||||||
|
) {
|
||||||
|
return downloadUpdate(info);
|
||||||
|
}
|
||||||
|
alertUpdate(
|
||||||
|
'提示',
|
||||||
|
'检查到新的版本' + info.name + ',是否下载?\n' + info.description,
|
||||||
|
[
|
||||||
|
{ text: '取消', style: 'cancel' },
|
||||||
|
{
|
||||||
|
text: '确定',
|
||||||
|
style: 'default',
|
||||||
|
onPress: () => {
|
||||||
|
downloadUpdate();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[
|
||||||
|
client,
|
||||||
|
alertError,
|
||||||
|
throwErrorIfEnabled,
|
||||||
|
options.updateStrategy,
|
||||||
|
alertUpdate,
|
||||||
|
downloadAndInstallApk,
|
||||||
|
downloadUpdate,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
const markSuccess = client.markSuccess;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (__DEV__ && !options.debug) {
|
||||||
|
console.info(
|
||||||
|
'您当前处于开发环境且未启用debug,不会进行热更检查。如需在开发环境中调试热更,请在client中设置debug为true',
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { checkStrategy, dismissErrorAfter, autoMarkSuccess } = options;
|
||||||
|
if (autoMarkSuccess) {
|
||||||
|
markSuccess();
|
||||||
|
}
|
||||||
|
if (checkStrategy === 'both' || checkStrategy === 'onAppResume') {
|
||||||
|
stateListener.current = AppState.addEventListener(
|
||||||
|
'change',
|
||||||
|
nextAppState => {
|
||||||
|
if (nextAppState === 'active') {
|
||||||
|
checkUpdate();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (checkStrategy === 'both' || checkStrategy === 'onAppStart') {
|
||||||
|
checkUpdate();
|
||||||
|
}
|
||||||
|
let dismissErrorTimer: ReturnType<typeof setTimeout>;
|
||||||
|
if (typeof dismissErrorAfter === 'number' && dismissErrorAfter > 0) {
|
||||||
|
dismissErrorTimer = setTimeout(() => {
|
||||||
|
dismissError();
|
||||||
|
}, dismissErrorAfter);
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
stateListener.current && stateListener.current.remove();
|
||||||
|
clearTimeout(dismissErrorTimer);
|
||||||
|
};
|
||||||
|
}, [checkUpdate, options, dismissError, markSuccess]);
|
||||||
|
|
||||||
|
const parseTestPayload = useCallback(
|
||||||
|
(payload: PushyTestPayload) => {
|
||||||
|
if (payload && payload.type && payload.type.startsWith('__rnPushy')) {
|
||||||
|
const logger = options.logger || (() => {});
|
||||||
|
options.logger = ({ type, data }) => {
|
||||||
|
logger({ type, data });
|
||||||
|
Alert.alert(type, JSON.stringify(data));
|
||||||
|
};
|
||||||
|
if (payload.type === '__rnPushyVersionHash') {
|
||||||
|
checkUpdate({ extra: { toHash: payload.data } }).then(() => {
|
||||||
|
if (updateInfoRef.current && updateInfoRef.current.upToDate) {
|
||||||
|
Alert.alert(
|
||||||
|
'提示',
|
||||||
|
'当前尚未检测到更新版本,如果是首次扫码,请等待服务器端生成补丁包后再试(约10秒)',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
options.logger = logger;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
[checkUpdate, options],
|
||||||
|
);
|
||||||
|
|
||||||
|
const parseTestQrCode = useCallback(
|
||||||
|
(code: string | PushyTestPayload) => {
|
||||||
|
try {
|
||||||
|
const payload = typeof code === 'string' ? JSON.parse(code) : code;
|
||||||
|
return parseTestPayload(payload);
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[parseTestPayload],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const parseLinking = (url: string | null) => {
|
||||||
|
if (!url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const params = new URL(url).searchParams;
|
||||||
|
const payload = {
|
||||||
|
type: params.get('type'),
|
||||||
|
data: params.get('data'),
|
||||||
|
};
|
||||||
|
parseTestPayload(payload);
|
||||||
|
};
|
||||||
|
|
||||||
|
Linking.getInitialURL().then(parseLinking);
|
||||||
|
const linkingListener = Linking.addEventListener('url', ({ url }) =>
|
||||||
|
parseLinking(url),
|
||||||
|
);
|
||||||
|
return () => {
|
||||||
|
linkingListener.remove();
|
||||||
|
};
|
||||||
|
}, [parseTestPayload]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PushyContext.Provider
|
||||||
|
value={{
|
||||||
|
checkUpdate,
|
||||||
|
switchVersion,
|
||||||
|
switchVersionLater,
|
||||||
|
dismissError,
|
||||||
|
updateInfo,
|
||||||
|
lastError,
|
||||||
|
markSuccess,
|
||||||
|
client,
|
||||||
|
downloadUpdate,
|
||||||
|
packageVersion,
|
||||||
|
currentHash: currentVersion,
|
||||||
|
progress,
|
||||||
|
downloadAndInstallApk,
|
||||||
|
getCurrentVersionInfo,
|
||||||
|
parseTestQrCode,
|
||||||
|
}}>
|
||||||
|
{children}
|
||||||
|
</PushyContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
95
src/type.ts
Normal file
95
src/type.ts
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
export interface CheckResult {
|
||||||
|
upToDate?: true;
|
||||||
|
expired?: true;
|
||||||
|
downloadUrl?: string;
|
||||||
|
update?: true;
|
||||||
|
name?: string; // version name
|
||||||
|
hash?: string;
|
||||||
|
description?: string;
|
||||||
|
metaInfo?: string;
|
||||||
|
config?: {
|
||||||
|
rollout?: {
|
||||||
|
[packageVersion: string]: number;
|
||||||
|
};
|
||||||
|
[key: string]: any;
|
||||||
|
};
|
||||||
|
pdiff?: string;
|
||||||
|
diff?: string;
|
||||||
|
full?: string;
|
||||||
|
paths?: string[];
|
||||||
|
paused?: 'app' | 'package';
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ProgressData {
|
||||||
|
hash: string;
|
||||||
|
received: number;
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type EventType =
|
||||||
|
| 'rollback'
|
||||||
|
| 'errorChecking'
|
||||||
|
| 'checking'
|
||||||
|
| 'downloading'
|
||||||
|
| 'downloadSuccess'
|
||||||
|
| 'errorUpdate'
|
||||||
|
| 'markSuccess'
|
||||||
|
| 'downloadingApk'
|
||||||
|
| 'rejectStoragePermission'
|
||||||
|
| 'errorStoragePermission'
|
||||||
|
| 'errorDownloadAndInstallApk';
|
||||||
|
|
||||||
|
export interface EventData {
|
||||||
|
currentVersion: string;
|
||||||
|
cInfo: {
|
||||||
|
pushy: string;
|
||||||
|
rn: string;
|
||||||
|
os: string;
|
||||||
|
uuid: string;
|
||||||
|
};
|
||||||
|
packageVersion: string;
|
||||||
|
buildTime: string;
|
||||||
|
message?: string;
|
||||||
|
rolledBackVersion?: string;
|
||||||
|
newVersion?: string;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type UpdateEventsLogger = ({
|
||||||
|
type,
|
||||||
|
data,
|
||||||
|
}: {
|
||||||
|
type: EventType;
|
||||||
|
data: EventData;
|
||||||
|
}) => void;
|
||||||
|
|
||||||
|
export interface PushyServerConfig {
|
||||||
|
main: string;
|
||||||
|
backups?: string[];
|
||||||
|
queryUrls?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PushyOptions {
|
||||||
|
appKey: string;
|
||||||
|
server?: PushyServerConfig;
|
||||||
|
logger?: UpdateEventsLogger;
|
||||||
|
updateStrategy?:
|
||||||
|
| 'alwaysAlert'
|
||||||
|
| 'alertUpdateAndIgnoreError'
|
||||||
|
| 'silentAndNow'
|
||||||
|
| 'silentAndLater'
|
||||||
|
| null;
|
||||||
|
checkStrategy?: 'onAppStart' | 'onAppResume' | 'both' | null;
|
||||||
|
autoMarkSuccess?: boolean;
|
||||||
|
dismissErrorAfter?: number;
|
||||||
|
debug?: boolean;
|
||||||
|
throwError?: boolean;
|
||||||
|
beforeCheckUpdate?: () => Promise<boolean>;
|
||||||
|
beforeDownloadUpdate?: (info: CheckResult) => Promise<boolean>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PushyTestPayload {
|
||||||
|
type: '__rnPushyVersionHash' | string | null;
|
||||||
|
data: any;
|
||||||
|
}
|
48
src/utils.ts
Normal file
48
src/utils.ts
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { Platform } from 'react-native';
|
||||||
|
|
||||||
|
export function log(...args: any[]) {
|
||||||
|
console.log('pushy: ', ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
const noop = () => {};
|
||||||
|
class EmptyModule {
|
||||||
|
constructor() {
|
||||||
|
return new Proxy(this, {
|
||||||
|
get() {
|
||||||
|
return noop;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export const emptyModule = new EmptyModule();
|
||||||
|
|
||||||
|
const ping =
|
||||||
|
Platform.OS === 'web'
|
||||||
|
? Promise.resolve
|
||||||
|
: async (url: string) =>
|
||||||
|
Promise.race([
|
||||||
|
fetch(url, {
|
||||||
|
method: 'HEAD',
|
||||||
|
})
|
||||||
|
.then(({ status }) => (status === 200 ? url : null))
|
||||||
|
.catch(() => null),
|
||||||
|
new Promise(r => setTimeout(() => r(null), 2000)),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const canUseGoogle = ping('https://www.google.com');
|
||||||
|
|
||||||
|
export function joinUrls(paths: string[], fileName?: string) {
|
||||||
|
if (fileName) {
|
||||||
|
return paths.map(path => 'https://' + path + '/' + fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const testUrls = async (urls?: string[]) => {
|
||||||
|
if (!urls?.length) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (await canUseGoogle) {
|
||||||
|
return urls[0];
|
||||||
|
}
|
||||||
|
return Promise.race(urls.map(ping)).catch(() => null);
|
||||||
|
};
|
6
tea.yaml
Normal file
6
tea.yaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# https://tea.xyz/what-is-this-file
|
||||||
|
---
|
||||||
|
version: 1.0.0
|
||||||
|
codeOwners:
|
||||||
|
- '0x10D90dC0034E2e82F0AC55954B3ed4EC0550ECe7'
|
||||||
|
quorum: 1
|
4
tsconfig.json
Normal file
4
tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "@react-native/typescript-config/tsconfig.json",
|
||||||
|
"include": ["src/**/*"]
|
||||||
|
}
|
Reference in New Issue
Block a user