mirror of
https://gitcode.com/gh_mirrors/re/react-native-pushy.git
synced 2025-09-18 00:16:10 +08:00
Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
1614a21968 | ||
![]() |
d7879ac2fe | ||
![]() |
a564770597 |
29
.eslintrc
Normal file
29
.eslintrc
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"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
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +0,0 @@
|
|||||||
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-14-arm64
|
runs-on: macos-12
|
||||||
# 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@v4
|
- uses: actions/setup-node@v3
|
||||||
with:
|
with:
|
||||||
node-version: 18
|
node-version: 16
|
||||||
|
|
||||||
- name: Configure JDK 1.11
|
- name: Configure JDK 1.11
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
|
@@ -41,17 +41,14 @@ 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(
|
throw new functions.https.HttpsError('invalid-argument', 'Invalid test requested.');
|
||||||
'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: any) {
|
} catch (e) {
|
||||||
console.error(e);
|
console.error(e);
|
||||||
throw new functions.https.HttpsError(
|
throw new functions.https.HttpsError(
|
||||||
'invalid-argument',
|
'invalid-argument',
|
||||||
|
4
.gitignore
vendored
4
.gitignore
vendored
@@ -46,7 +46,3 @@ Example/**/.pushy
|
|||||||
Example/testHotUpdate/artifacts
|
Example/testHotUpdate/artifacts
|
||||||
|
|
||||||
yarn-error.log
|
yarn-error.log
|
||||||
Example/testHotUpdate/.yarn
|
|
||||||
android/bin
|
|
||||||
Example/testHotUpdate/harmony
|
|
||||||
Example/testHotUpdate/android/app/.cxx
|
|
||||||
|
@@ -47,6 +47,4 @@ Example
|
|||||||
yarn.lock
|
yarn.lock
|
||||||
|
|
||||||
domains.json
|
domains.json
|
||||||
endpoints.json
|
endpoints.json
|
||||||
|
|
||||||
tea.yaml
|
|
@@ -1,6 +1,4 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
arrowParens: 'avoid',
|
|
||||||
bracketSameLine: true,
|
|
||||||
singleQuote: true,
|
|
||||||
trailingComma: 'all',
|
trailingComma: 'all',
|
||||||
|
singleQuote: true,
|
||||||
};
|
};
|
||||||
|
@@ -1 +0,0 @@
|
|||||||
nodeLinker: node-modules
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
6
Example/testHotUpdate/.buckconfig
Normal file
6
Example/testHotUpdate/.buckconfig
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
[android]
|
||||||
|
target = Google Inc.:Google APIs:23
|
||||||
|
|
||||||
|
[maven_repositories]
|
||||||
|
central = https://repo1.maven.org/maven2
|
2
Example/testHotUpdate/.bundle/config
Normal file
2
Example/testHotUpdate/.bundle/config
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
BUNDLE_PATH: "vendor/bundle"
|
||||||
|
BUNDLE_FORCE_RUBY_PLATFORM: 1
|
@@ -1,4 +1,4 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
root: true,
|
root: true,
|
||||||
extends: '@react-native',
|
extends: '@react-native-community',
|
||||||
};
|
};
|
||||||
|
66
Example/testHotUpdate/.flowconfig
Normal file
66
Example/testHotUpdate/.flowconfig
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
[ignore]
|
||||||
|
; We fork some components by platform
|
||||||
|
.*/*[.]android.js
|
||||||
|
|
||||||
|
; Ignore "BUCK" generated dirs
|
||||||
|
<PROJECT_ROOT>/\.buckd/
|
||||||
|
|
||||||
|
; Ignore polyfills
|
||||||
|
node_modules/react-native/Libraries/polyfills/.*
|
||||||
|
|
||||||
|
; Flow doesn't support platforms
|
||||||
|
.*/Libraries/Utilities/LoadingView.js
|
||||||
|
|
||||||
|
.*/node_modules/resolve/test/resolver/malformed_package_json/package\.json$
|
||||||
|
|
||||||
|
[untyped]
|
||||||
|
.*/node_modules/@react-native-community/cli/.*/.*
|
||||||
|
|
||||||
|
[include]
|
||||||
|
|
||||||
|
[libs]
|
||||||
|
node_modules/react-native/interface.js
|
||||||
|
node_modules/react-native/flow/
|
||||||
|
|
||||||
|
[options]
|
||||||
|
emoji=true
|
||||||
|
|
||||||
|
exact_by_default=true
|
||||||
|
|
||||||
|
format.bracket_spacing=false
|
||||||
|
|
||||||
|
module.file_ext=.js
|
||||||
|
module.file_ext=.json
|
||||||
|
module.file_ext=.ios.js
|
||||||
|
|
||||||
|
munge_underscores=true
|
||||||
|
|
||||||
|
module.name_mapper='^react-native/\(.*\)$' -> '<PROJECT_ROOT>/node_modules/react-native/\1'
|
||||||
|
module.name_mapper='^@?[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> '<PROJECT_ROOT>/node_modules/react-native/Libraries/Image/RelativeImageStub'
|
||||||
|
|
||||||
|
suppress_type=$FlowIssue
|
||||||
|
suppress_type=$FlowFixMe
|
||||||
|
suppress_type=$FlowFixMeProps
|
||||||
|
suppress_type=$FlowFixMeState
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
sketchy-null-number=warn
|
||||||
|
sketchy-null-mixed=warn
|
||||||
|
sketchy-number=warn
|
||||||
|
untyped-type-import=warn
|
||||||
|
nonstrict-import=warn
|
||||||
|
deprecated-type=warn
|
||||||
|
unsafe-getters-setters=warn
|
||||||
|
unnecessary-invariant=warn
|
||||||
|
|
||||||
|
[strict]
|
||||||
|
deprecated-type
|
||||||
|
nonstrict-import
|
||||||
|
sketchy-null
|
||||||
|
unclear-type
|
||||||
|
unsafe-getters-setters
|
||||||
|
untyped-import
|
||||||
|
untyped-type-import
|
||||||
|
|
||||||
|
[version]
|
||||||
|
^0.176.3
|
1
Example/testHotUpdate/.ruby-version
Normal file
1
Example/testHotUpdate/.ruby-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
2.7.5
|
6
Example/testHotUpdate/Gemfile
Normal file
6
Example/testHotUpdate/Gemfile
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
|
# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version
|
||||||
|
ruby '3.1.1'
|
||||||
|
|
||||||
|
gem 'cocoapods', '~> 1.11', '>= 1.11.2'
|
98
Example/testHotUpdate/Gemfile.lock
Normal file
98
Example/testHotUpdate/Gemfile.lock
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
GEM
|
||||||
|
remote: https://rubygems.org/
|
||||||
|
specs:
|
||||||
|
CFPropertyList (3.0.6)
|
||||||
|
rexml
|
||||||
|
activesupport (7.0.4.3)
|
||||||
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
|
i18n (>= 1.6, < 2)
|
||||||
|
minitest (>= 5.1)
|
||||||
|
tzinfo (~> 2.0)
|
||||||
|
addressable (2.8.1)
|
||||||
|
public_suffix (>= 2.0.2, < 6.0)
|
||||||
|
algoliasearch (1.27.5)
|
||||||
|
httpclient (~> 2.8, >= 2.8.3)
|
||||||
|
json (>= 1.5.1)
|
||||||
|
atomos (0.1.3)
|
||||||
|
claide (1.1.0)
|
||||||
|
cocoapods (1.12.0)
|
||||||
|
addressable (~> 2.8)
|
||||||
|
claide (>= 1.0.2, < 2.0)
|
||||||
|
cocoapods-core (= 1.12.0)
|
||||||
|
cocoapods-deintegrate (>= 1.0.3, < 2.0)
|
||||||
|
cocoapods-downloader (>= 1.6.0, < 2.0)
|
||||||
|
cocoapods-plugins (>= 1.0.0, < 2.0)
|
||||||
|
cocoapods-search (>= 1.0.0, < 2.0)
|
||||||
|
cocoapods-trunk (>= 1.6.0, < 2.0)
|
||||||
|
cocoapods-try (>= 1.1.0, < 2.0)
|
||||||
|
colored2 (~> 3.1)
|
||||||
|
escape (~> 0.0.4)
|
||||||
|
fourflusher (>= 2.3.0, < 3.0)
|
||||||
|
gh_inspector (~> 1.0)
|
||||||
|
molinillo (~> 0.8.0)
|
||||||
|
nap (~> 1.0)
|
||||||
|
ruby-macho (>= 2.3.0, < 3.0)
|
||||||
|
xcodeproj (>= 1.21.0, < 2.0)
|
||||||
|
cocoapods-core (1.12.0)
|
||||||
|
activesupport (>= 5.0, < 8)
|
||||||
|
addressable (~> 2.8)
|
||||||
|
algoliasearch (~> 1.0)
|
||||||
|
concurrent-ruby (~> 1.1)
|
||||||
|
fuzzy_match (~> 2.0.4)
|
||||||
|
nap (~> 1.0)
|
||||||
|
netrc (~> 0.11)
|
||||||
|
public_suffix (~> 4.0)
|
||||||
|
typhoeus (~> 1.0)
|
||||||
|
cocoapods-deintegrate (1.0.5)
|
||||||
|
cocoapods-downloader (1.6.3)
|
||||||
|
cocoapods-plugins (1.0.0)
|
||||||
|
nap
|
||||||
|
cocoapods-search (1.0.1)
|
||||||
|
cocoapods-trunk (1.6.0)
|
||||||
|
nap (>= 0.8, < 2.0)
|
||||||
|
netrc (~> 0.11)
|
||||||
|
cocoapods-try (1.2.0)
|
||||||
|
colored2 (3.1.2)
|
||||||
|
concurrent-ruby (1.2.2)
|
||||||
|
escape (0.0.4)
|
||||||
|
ethon (0.16.0)
|
||||||
|
ffi (>= 1.15.0)
|
||||||
|
ffi (1.15.5)
|
||||||
|
fourflusher (2.3.1)
|
||||||
|
fuzzy_match (2.0.4)
|
||||||
|
gh_inspector (1.1.3)
|
||||||
|
httpclient (2.8.3)
|
||||||
|
i18n (1.12.0)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
json (2.6.3)
|
||||||
|
minitest (5.18.0)
|
||||||
|
molinillo (0.8.0)
|
||||||
|
nanaimo (0.3.0)
|
||||||
|
nap (1.1.0)
|
||||||
|
netrc (0.11.0)
|
||||||
|
public_suffix (4.0.7)
|
||||||
|
rexml (3.2.5)
|
||||||
|
ruby-macho (2.5.1)
|
||||||
|
typhoeus (1.4.0)
|
||||||
|
ethon (>= 0.9.0)
|
||||||
|
tzinfo (2.0.6)
|
||||||
|
concurrent-ruby (~> 1.0)
|
||||||
|
xcodeproj (1.22.0)
|
||||||
|
CFPropertyList (>= 2.3.3, < 4.0)
|
||||||
|
atomos (~> 0.1.3)
|
||||||
|
claide (>= 1.0.2, < 2.0)
|
||||||
|
colored2 (~> 3.1)
|
||||||
|
nanaimo (~> 0.3.0)
|
||||||
|
rexml (~> 3.2.4)
|
||||||
|
|
||||||
|
PLATFORMS
|
||||||
|
ruby
|
||||||
|
|
||||||
|
DEPENDENCIES
|
||||||
|
cocoapods (~> 1.11, >= 1.11.2)
|
||||||
|
|
||||||
|
RUBY VERSION
|
||||||
|
ruby 3.1.1p18
|
||||||
|
|
||||||
|
BUNDLED WITH
|
||||||
|
2.1.4
|
@@ -1,87 +1,226 @@
|
|||||||
apply plugin: "com.android.application"
|
plugins {
|
||||||
apply plugin: "org.jetbrains.kotlin.android"
|
id 'com.android.application'
|
||||||
apply plugin: "com.facebook.react"
|
id 'org.jetbrains.kotlin.android'
|
||||||
|
|
||||||
/**
|
|
||||||
* 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"]
|
|
||||||
autolinkLibrariesWithApp()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
import com.android.build.OutputFile
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set this to true to Run Proguard on Release builds to minify the Java bytecode.
|
* The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets
|
||||||
|
* 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 (JSC)
|
* The preferred build flavor of JavaScriptCore.
|
||||||
*
|
*
|
||||||
* 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 {
|
||||||
@@ -96,24 +235,94 @@ android {
|
|||||||
signingConfig signingConfigs.debug
|
signingConfig signingConfigs.debug
|
||||||
}
|
}
|
||||||
release {
|
release {
|
||||||
crunchPngs false
|
|
||||||
// Caution! In production, you need to generate your own keystore file.
|
// Caution! In production, you need to generate your own keystore file.
|
||||||
// see https://reactnative.dev/docs/signed-apk-android.
|
// see https://reactnative.dev/docs/signed-apk-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 {
|
||||||
// The version of react-native is set by the React Native Gradle Plugin
|
androidTestImplementation('com.wix:detox:+')
|
||||||
implementation("com.facebook.react:react-android")
|
implementation 'androidx.appcompat:appcompat:1.1.0'
|
||||||
|
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||||
|
|
||||||
if (hermesEnabled.toBoolean()) {
|
//noinspection GradleDynamicVersion
|
||||||
implementation("com.facebook.react:hermes-android")
|
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-network-plugin:${FLIPPER_VERSION}") {
|
||||||
|
exclude group:'com.facebook.flipper'
|
||||||
|
exclude group:'com.squareup.okhttp3', module:'okhttp'
|
||||||
|
}
|
||||||
|
|
||||||
|
debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") {
|
||||||
|
exclude group:'com.facebook.flipper'
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
@@ -0,0 +1,73 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
||||||
|
*
|
||||||
|
* <p>This source code is licensed under the MIT license found in the LICENSE file in the root
|
||||||
|
* directory of this source tree.
|
||||||
|
*/
|
||||||
|
package com.awesomeproject;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import com.facebook.flipper.android.AndroidFlipperClient;
|
||||||
|
import com.facebook.flipper.android.utils.FlipperUtils;
|
||||||
|
import com.facebook.flipper.core.FlipperClient;
|
||||||
|
import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin;
|
||||||
|
import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin;
|
||||||
|
import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin;
|
||||||
|
import com.facebook.flipper.plugins.inspector.DescriptorMapping;
|
||||||
|
import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin;
|
||||||
|
import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor;
|
||||||
|
import com.facebook.flipper.plugins.network.NetworkFlipperPlugin;
|
||||||
|
import com.facebook.flipper.plugins.react.ReactFlipperPlugin;
|
||||||
|
import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin;
|
||||||
|
import com.facebook.react.ReactInstanceEventListener;
|
||||||
|
import com.facebook.react.ReactInstanceManager;
|
||||||
|
import com.facebook.react.bridge.ReactContext;
|
||||||
|
import com.facebook.react.modules.network.NetworkingModule;
|
||||||
|
import okhttp3.OkHttpClient;
|
||||||
|
|
||||||
|
public class ReactNativeFlipper {
|
||||||
|
public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) {
|
||||||
|
if (FlipperUtils.shouldEnableFlipper(context)) {
|
||||||
|
final FlipperClient client = AndroidFlipperClient.getInstance(context);
|
||||||
|
|
||||||
|
client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults()));
|
||||||
|
client.addPlugin(new ReactFlipperPlugin());
|
||||||
|
client.addPlugin(new DatabasesFlipperPlugin(context));
|
||||||
|
client.addPlugin(new SharedPreferencesFlipperPlugin(context));
|
||||||
|
client.addPlugin(CrashReporterPlugin.getInstance());
|
||||||
|
|
||||||
|
NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin();
|
||||||
|
NetworkingModule.setCustomClientBuilder(
|
||||||
|
new NetworkingModule.CustomClientBuilder() {
|
||||||
|
@Override
|
||||||
|
public void apply(OkHttpClient.Builder builder) {
|
||||||
|
builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
client.addPlugin(networkFlipperPlugin);
|
||||||
|
client.start();
|
||||||
|
|
||||||
|
// Fresco Plugin needs to ensure that ImagePipelineFactory is initialized
|
||||||
|
// Hence we run if after all native modules have been initialized
|
||||||
|
ReactContext reactContext = reactInstanceManager.getCurrentReactContext();
|
||||||
|
if (reactContext == null) {
|
||||||
|
reactInstanceManager.addReactInstanceEventListener(
|
||||||
|
new ReactInstanceEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onReactContextInitialized(ReactContext reactContext) {
|
||||||
|
reactInstanceManager.removeReactInstanceEventListener(this);
|
||||||
|
reactContext.runOnNativeModulesQueueThread(
|
||||||
|
new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
client.addPlugin(new FrescoFlipperPlugin());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
client.addPlugin(new FrescoFlipperPlugin());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@@ -2,8 +2,6 @@
|
|||||||
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"
|
||||||
@@ -21,14 +19,8 @@
|
|||||||
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,8 +2,7 @@ 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.defaults.DefaultNewArchitectureEntryPoint;
|
import com.facebook.react.ReactRootView;
|
||||||
import com.facebook.react.defaults.DefaultReactActivityDelegate;
|
|
||||||
|
|
||||||
public class MainActivity extends ReactActivity {
|
public class MainActivity extends ReactActivity {
|
||||||
|
|
||||||
@@ -17,16 +16,33 @@ public class MainActivity extends ReactActivity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link
|
* Returns the instance of the {@link ReactActivityDelegate}. There the RootView is created and
|
||||||
* DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React
|
* you can specify the renderer you wish to use - the new renderer (Fabric) or the old renderer
|
||||||
* (aka React 18) with two boolean flags.
|
* (Paper).
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected ReactActivityDelegate createReactActivityDelegate() {
|
protected ReactActivityDelegate createReactActivityDelegate() {
|
||||||
return new DefaultReactActivityDelegate(
|
return new MainActivityDelegate(this, getMainComponentName());
|
||||||
this,
|
}
|
||||||
getMainComponentName(),
|
|
||||||
// If you opted-in for the New Architecture, we enable the Fabric Renderer.
|
public static class MainActivityDelegate extends ReactActivityDelegate {
|
||||||
DefaultNewArchitectureEntryPoint.getFabricEnabled());
|
public MainActivityDelegate(ReactActivity activity, String mainComponentName) {
|
||||||
|
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,69 +1,104 @@
|
|||||||
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.defaults.DefaultNewArchitectureEntryPoint;
|
import com.facebook.react.config.ReactFeatureFlags;
|
||||||
import com.facebook.react.defaults.DefaultReactNativeHost;
|
import com.facebook.react.shell.MainReactPackage;
|
||||||
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 DefaultReactNativeHost(this) {
|
new ReactNativeHost(this) {
|
||||||
@Override
|
@Override
|
||||||
public boolean getUseDeveloperSupport() {
|
public boolean getUseDeveloperSupport() {
|
||||||
return BuildConfig.DEBUG;
|
return BuildConfig.DEBUG;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Nullable
|
||||||
protected String getJSBundleFile() {
|
@Override
|
||||||
return UpdateContext.getBundleUrl(MainApplication.this);
|
protected String getJSBundleFile() {
|
||||||
}
|
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 boolean isNewArchEnabled() {
|
protected String getJSMainModuleName() {
|
||||||
return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
|
return "index";
|
||||||
}
|
}
|
||||||
|
};
|
||||||
@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() {
|
||||||
return mReactNativeHost;
|
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
||||||
|
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);
|
||||||
if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
|
initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
|
||||||
// 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,116 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,36 @@
|
|||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@@ -0,0 +1,48 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
48
Example/testHotUpdate/android/app/src/main/jni/Android.mk
Normal file
48
Example/testHotUpdate/android/app/src/main/jni/Android.mk
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
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)
|
@@ -0,0 +1,24 @@
|
|||||||
|
#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
|
@@ -0,0 +1,16 @@
|
|||||||
|
#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
|
@@ -0,0 +1,45 @@
|
|||||||
|
#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
|
@@ -0,0 +1,38 @@
|
|||||||
|
#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
|
@@ -0,0 +1,61 @@
|
|||||||
|
#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
|
@@ -0,0 +1,32 @@
|
|||||||
|
#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
|
11
Example/testHotUpdate/android/app/src/main/jni/OnLoad.cpp
Normal file
11
Example/testHotUpdate/android/app/src/main/jni/OnLoad.cpp
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
#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,21 +1,58 @@
|
|||||||
|
import org.apache.tools.ant.taskdefs.condition.Os
|
||||||
|
|
||||||
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
|
|
||||||
buildscript {
|
buildscript {
|
||||||
ext {
|
ext {
|
||||||
buildToolsVersion = "35.0.0"
|
buildToolsVersion = "31.0.0"
|
||||||
minSdkVersion = 24
|
minSdkVersion = 21
|
||||||
compileSdkVersion = 35
|
compileSdkVersion = 31
|
||||||
targetSdkVersion = 34
|
targetSdkVersion = 31
|
||||||
ndkVersion = "26.1.10909125"
|
kotlinVersion = '1.6.10'
|
||||||
kotlinVersion = "1.9.24"
|
|
||||||
|
if (System.properties['os.arch'] == "aarch64") {
|
||||||
|
// For M1 Users we need to use the NDK 24 which added support for aarch64
|
||||||
|
ndkVersion = "24.0.8215888"
|
||||||
|
} else {
|
||||||
|
// 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")
|
classpath("com.android.tools.build:gradle:7.1.1")
|
||||||
classpath("com.facebook.react:react-native-gradle-plugin")
|
classpath("com.facebook.react:react-native-gradle-plugin")
|
||||||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin")
|
classpath("de.undercouch:gradle-download-task:5.0.1")
|
||||||
|
// 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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
apply plugin: "com.facebook.react.rootproject"
|
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' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -21,6 +21,11 @@ org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m
|
|||||||
# Android operating system, and which are packaged with your app's APK
|
# Android operating system, and which are packaged with your app's APK
|
||||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
|
# Automatically convert third-party libraries to use AndroidX
|
||||||
|
android.enableJetifier=true
|
||||||
|
|
||||||
|
# Version of flipper SDK to use with React Native
|
||||||
|
FLIPPER_VERSION=0.125.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
|
||||||
@@ -32,8 +37,4 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
|
|||||||
# your application. You should enable this flag either if you want
|
# your application. You should enable this flag either if you want
|
||||||
# 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=true
|
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,6 +1,5 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-all.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
|
||||||
networkTimeout=10000
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
44
Example/testHotUpdate/android/gradlew
vendored
44
Example/testHotUpdate/android/gradlew
vendored
@@ -15,8 +15,6 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
|
||||||
#
|
|
||||||
|
|
||||||
##############################################################################
|
##############################################################################
|
||||||
#
|
#
|
||||||
@@ -57,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/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
# https://github.com/gradle/gradle/blob/master/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/.
|
||||||
@@ -82,12 +80,13 @@ do
|
|||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
|
|
||||||
# This is normally unused
|
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||||
# shellcheck disable=SC2034
|
|
||||||
|
APP_NAME="Gradle"
|
||||||
APP_BASE_NAME=${0##*/}
|
APP_BASE_NAME=${0##*/}
|
||||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
|
||||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
|
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
' "$PWD" ) || exit
|
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||||
|
|
||||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||||
MAX_FD=maximum
|
MAX_FD=maximum
|
||||||
@@ -134,29 +133,22 @@ location of your Java installation."
|
|||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
JAVACMD=java
|
JAVACMD=java
|
||||||
if ! command -v java >/dev/null 2>&1
|
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
then
|
|
||||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
|
||||||
|
|
||||||
Please set the JAVA_HOME variable in your environment to match the
|
Please set the JAVA_HOME variable in your environment to match the
|
||||||
location of your Java installation."
|
location of your Java installation."
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Increase the maximum file descriptors if we can.
|
# Increase the maximum file descriptors if we can.
|
||||||
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=SC2039,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=SC2039,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
|
||||||
@@ -201,15 +193,11 @@ if "$cygwin" || "$msys" ; then
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Collect all arguments for the java command;
|
||||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
# shell script including quotes and variable substitutions, so put them in
|
||||||
|
# double quotes to make sure that they get re-expanded; and
|
||||||
# Collect all arguments for the java command:
|
# * put everything else in single quotes, so that it's not re-expanded.
|
||||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
|
|
||||||
# and any embedded shellness will be escaped.
|
|
||||||
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
|
|
||||||
# treated as '${Hostname}' itself on the command line.
|
|
||||||
|
|
||||||
set -- \
|
set -- \
|
||||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||||
@@ -217,12 +205,6 @@ 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.
|
||||||
|
37
Example/testHotUpdate/android/gradlew.bat
vendored
37
Example/testHotUpdate/android/gradlew.bat
vendored
@@ -13,10 +13,8 @@
|
|||||||
@rem See the License for the specific language governing permissions and
|
@rem See the License for the specific language governing permissions and
|
||||||
@rem limitations under the License.
|
@rem limitations under the License.
|
||||||
@rem
|
@rem
|
||||||
@rem SPDX-License-Identifier: Apache-2.0
|
|
||||||
@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
|
||||||
@@ -27,8 +25,7 @@
|
|||||||
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%
|
||||||
|
|
||||||
@@ -43,13 +40,13 @@ 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% equ 0 goto execute
|
if "%ERRORLEVEL%" == "0" goto execute
|
||||||
|
|
||||||
echo. 1>&2
|
echo.
|
||||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||||
echo. 1>&2
|
echo.
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
echo location of your Java installation. 1>&2
|
echo location of your Java installation.
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -59,11 +56,11 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
|||||||
|
|
||||||
if exist "%JAVA_EXE%" goto execute
|
if exist "%JAVA_EXE%" goto execute
|
||||||
|
|
||||||
echo. 1>&2
|
echo.
|
||||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||||
echo. 1>&2
|
echo.
|
||||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
echo Please set the JAVA_HOME variable in your environment to match the
|
||||||
echo location of your Java installation. 1>&2
|
echo location of your Java installation.
|
||||||
|
|
||||||
goto fail
|
goto fail
|
||||||
|
|
||||||
@@ -78,15 +75,13 @@ 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% equ 0 goto mainEnd
|
if "%ERRORLEVEL%"=="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!
|
||||||
set EXIT_CODE=%ERRORLEVEL%
|
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
exit /b 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,7 +1,11 @@
|
|||||||
pluginManagement { includeBuild("../node_modules/@react-native/gradle-plugin") }
|
|
||||||
plugins { id("com.facebook.react.settings") }
|
|
||||||
extensions.configure(com.facebook.react.ReactSettingsExtension){ ex -> ex.autolinkLibrariesFromCommand() }
|
|
||||||
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,8 +1,3 @@
|
|||||||
module.exports = {
|
module.exports = {
|
||||||
presets: ['module:@react-native/babel-preset'],
|
presets: ['module:metro-react-native-babel-preset'],
|
||||||
env: {
|
|
||||||
production: {
|
|
||||||
plugins: ['react-native-paper/babel'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
Binary file not shown.
@@ -28,7 +28,14 @@ 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,15 +7,29 @@
|
|||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
0C06D3B1D2C63EC04B024612 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = 77A7A2D881E69AE3DCCE6BFE /* PrivacyInfo.xcprivacy */; };
|
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>"; };
|
||||||
@@ -27,13 +41,20 @@
|
|||||||
5709B34CF0A7D63546082F79 /* Pods-AwesomeProject.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AwesomeProject.release.xcconfig"; path = "Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject.release.xcconfig"; sourceTree = "<group>"; };
|
5709B34CF0A7D63546082F79 /* Pods-AwesomeProject.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AwesomeProject.release.xcconfig"; path = "Target Support Files/Pods-AwesomeProject/Pods-AwesomeProject.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
5B7EB9410499542E8C5724F5 /* Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig"; path = "Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig"; sourceTree = "<group>"; };
|
5B7EB9410499542E8C5724F5 /* Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig"; path = "Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
5DCACB8F33CDC322A6C60F78 /* libPods-AwesomeProject.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AwesomeProject.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
5DCACB8F33CDC322A6C60F78 /* libPods-AwesomeProject.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-AwesomeProject.a"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
77A7A2D881E69AE3DCCE6BFE /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; name = PrivacyInfo.xcprivacy; path = AwesomeProject/PrivacyInfo.xcprivacy; sourceTree = "<group>"; };
|
|
||||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = AwesomeProject/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = AwesomeProject/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
89C6BE57DB24E9ADA2F236DE /* Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig"; path = "Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig"; sourceTree = "<group>"; };
|
89C6BE57DB24E9ADA2F236DE /* Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig"; path = "Target Support Files/Pods-AwesomeProject-AwesomeProjectTests/Pods-AwesomeProject-AwesomeProjectTests.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
|
||||||
/* 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;
|
||||||
@@ -45,6 +66,23 @@
|
|||||||
/* 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 = (
|
||||||
@@ -54,7 +92,6 @@
|
|||||||
13B07FB61A68108700A75B9A /* Info.plist */,
|
13B07FB61A68108700A75B9A /* Info.plist */,
|
||||||
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
|
81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */,
|
||||||
13B07FB71A68108700A75B9A /* main.m */,
|
13B07FB71A68108700A75B9A /* main.m */,
|
||||||
77A7A2D881E69AE3DCCE6BFE /* PrivacyInfo.xcprivacy */,
|
|
||||||
);
|
);
|
||||||
name = AwesomeProject;
|
name = AwesomeProject;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -81,6 +118,7 @@
|
|||||||
children = (
|
children = (
|
||||||
13B07FAE1A68108700A75B9A /* AwesomeProject */,
|
13B07FAE1A68108700A75B9A /* AwesomeProject */,
|
||||||
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
832341AE1AAA6A7D00B99B32 /* Libraries */,
|
||||||
|
00E356EF1AD99517003FC87E /* AwesomeProjectTests */,
|
||||||
83CBBA001A601CBA00E9B192 /* Products */,
|
83CBBA001A601CBA00E9B192 /* Products */,
|
||||||
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
2D16E6871FA4F8E400B85C8A /* Frameworks */,
|
||||||
BBD78D7AC51CEA395F1C20DB /* Pods */,
|
BBD78D7AC51CEA395F1C20DB /* Pods */,
|
||||||
@@ -94,6 +132,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
13B07F961A680F5B00A75B9A /* AwesomeProject.app */,
|
13B07F961A680F5B00A75B9A /* AwesomeProject.app */,
|
||||||
|
00E356EE1AD99517003FC87E /* AwesomeProjectTests.xctest */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -112,6 +151,27 @@
|
|||||||
/* 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" */;
|
||||||
@@ -122,8 +182,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 = (
|
||||||
);
|
);
|
||||||
@@ -142,6 +202,10 @@
|
|||||||
attributes = {
|
attributes = {
|
||||||
LastUpgradeCheck = 1210;
|
LastUpgradeCheck = 1210;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
|
00E356ED1AD99517003FC87E = {
|
||||||
|
CreatedOnToolsVersion = 6.2;
|
||||||
|
TestTargetID = 13B07F861A680F5B00A75B9A;
|
||||||
|
};
|
||||||
13B07F861A680F5B00A75B9A = {
|
13B07F861A680F5B00A75B9A = {
|
||||||
LastSwiftMigration = 1120;
|
LastSwiftMigration = 1120;
|
||||||
};
|
};
|
||||||
@@ -161,18 +225,25 @@
|
|||||||
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;
|
||||||
files = (
|
files = (
|
||||||
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
|
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
|
||||||
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */,
|
||||||
0C06D3B1D2C63EC04B024612 /* PrivacyInfo.xcprivacy in Resources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -195,7 +266,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";
|
||||||
};
|
};
|
||||||
2177C9C260D54703D642190E /* [CP] Embed Pods Frameworks */ = {
|
00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
@@ -212,6 +283,28 @@
|
|||||||
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;
|
||||||
@@ -234,6 +327,23 @@
|
|||||||
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;
|
||||||
@@ -251,6 +361,23 @@
|
|||||||
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;
|
||||||
@@ -273,6 +400,14 @@
|
|||||||
/* 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;
|
||||||
@@ -284,7 +419,66 @@
|
|||||||
};
|
};
|
||||||
/* 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 */;
|
||||||
@@ -340,7 +534,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
@@ -368,7 +562,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*]" = i386;
|
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
||||||
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;
|
||||||
@@ -376,7 +570,6 @@
|
|||||||
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;
|
||||||
@@ -397,18 +590,14 @@
|
|||||||
);
|
);
|
||||||
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)";
|
|
||||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) DEBUG";
|
|
||||||
USE_HERMES = true;
|
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
};
|
};
|
||||||
@@ -417,7 +606,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "c++20";
|
CLANG_CXX_LANGUAGE_STANDARD = "c++17";
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
CLANG_CXX_LIBRARY = "libc++";
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
CLANG_ENABLE_OBJC_ARC = YES;
|
||||||
@@ -445,13 +634,9 @@
|
|||||||
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*]" = i386;
|
"EXCLUDED_ARCHS[sdk=iphonesimulator*]" = "";
|
||||||
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;
|
||||||
@@ -469,17 +654,14 @@
|
|||||||
"\"$(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)";
|
|
||||||
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
USE_HERMES = true;
|
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
@@ -487,6 +669,15 @@
|
|||||||
/* 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,6 +1,8 @@
|
|||||||
#import <RCTAppDelegate.h>
|
#import <React/RCTBridgeDelegate.h>
|
||||||
#import <UIKit/UIKit.h>
|
#import <UIKit/UIKit.h>
|
||||||
|
|
||||||
@interface AppDelegate : RCTAppDelegate
|
@interface AppDelegate : UIResponder <UIApplicationDelegate, RCTBridgeDelegate>
|
||||||
|
|
||||||
|
@property (nonatomic, strong) UIWindow *window;
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@@ -1,28 +1,133 @@
|
|||||||
#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
|
||||||
{
|
{
|
||||||
self.moduleName = @"AwesomeProject";
|
RCTAppSetupPrepareApp(application);
|
||||||
// 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 = @{};
|
|
||||||
|
|
||||||
return [super application:application didFinishLaunchingWithOptions:launchOptions];
|
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions: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;
|
||||||
}
|
}
|
||||||
|
|
||||||
- (NSURL *)bundleURL
|
/// 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
|
||||||
{
|
{
|
||||||
#if DEBUG
|
// Switch this bool to turn on and off the concurrent root
|
||||||
// 原先DEBUG这里的写法不作修改(所以DEBUG模式下不可热更新)
|
return true;
|
||||||
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
|
|
||||||
#else
|
|
||||||
return [RCTPushy bundleURL]; // <-- 把这里非DEBUG的情况替换为热更新bundle
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)prepareInitialProps
|
||||||
|
{
|
||||||
|
NSMutableDictionary *initProps = [NSMutableDictionary new];
|
||||||
|
|
||||||
|
#ifdef RCT_NEW_ARCH_ENABLED
|
||||||
|
initProps[kRNConcurrentRoot] = @([self concurrentRootEnabled]);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return initProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
|
||||||
|
#else
|
||||||
|
return [RCTPushy bundleURL];
|
||||||
|
#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,10 +53,5 @@
|
|||||||
</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,37 +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>NSPrivacyAccessedAPITypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>NSPrivacyAccessedAPIType</key>
|
|
||||||
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
|
||||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
||||||
<array>
|
|
||||||
<string>C617.1</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>NSPrivacyAccessedAPIType</key>
|
|
||||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
|
||||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
||||||
<array>
|
|
||||||
<string>CA92.1</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
|
||||||
<key>NSPrivacyAccessedAPIType</key>
|
|
||||||
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
|
||||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
||||||
<array>
|
|
||||||
<string>35F9.1</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>NSPrivacyCollectedDataTypes</key>
|
|
||||||
<array/>
|
|
||||||
<key>NSPrivacyTracking</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
@@ -0,0 +1,66 @@
|
|||||||
|
#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
|
24
Example/testHotUpdate/ios/AwesomeProjectTests/Info.plist
Normal file
24
Example/testHotUpdate/ios/AwesomeProjectTests/Info.plist
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
<?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,36 +1,35 @@
|
|||||||
# Resolve react_native_pods.rb with node to allow for hoisting
|
require_relative '../node_modules/react-native/scripts/react_native_pods'
|
||||||
require Pod::Executable.execute_command('node', ['-p',
|
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
|
||||||
'require.resolve(
|
|
||||||
"react-native/scripts/react_native_pods.rb",
|
|
||||||
{paths: [process.argv[1]]},
|
|
||||||
)', __dir__]).strip
|
|
||||||
|
|
||||||
platform :ios, min_ios_version_supported
|
platform :ios, '12.4'
|
||||||
prepare_react_native_project!
|
install! 'cocoapods', :deterministic_uuids => false
|
||||||
|
|
||||||
|
production = ENV["PRODUCTION"] == "1"
|
||||||
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!
|
||||||
|
|
||||||
|
# Flags change depending on the env values.
|
||||||
|
flags = get_default_flags()
|
||||||
|
|
||||||
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
|
||||||
|
:production => production,
|
||||||
|
:hermes_enabled => flags[:hermes_enabled],
|
||||||
|
:fabric_enabled => flags[:fabric_enabled],
|
||||||
|
:flipper_configuration => FlipperConfiguration.enabled,
|
||||||
# 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|
|
||||||
# https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202
|
react_native_post_install(installer)
|
||||||
react_native_post_install(
|
__apply_Xcode_12_5_M1_post_install_workaround(installer)
|
||||||
installer,
|
|
||||||
config[:reactNativePath],
|
|
||||||
:mac_catalyst_enabled => false,
|
|
||||||
# :ccache_enabled => true
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
File diff suppressed because it is too large
Load Diff
@@ -1,11 +1,24 @@
|
|||||||
const {getDefaultConfig, mergeConfig} = require('@react-native/metro-config');
|
const path = require('path');
|
||||||
|
|
||||||
/**
|
const extraNodeModules = {
|
||||||
* Metro configuration
|
react: path.resolve(__dirname, 'node_modules/react'),
|
||||||
* https://reactnative.dev/docs/metro
|
'react-native': path.resolve(__dirname, 'node_modules/react-native'),
|
||||||
*
|
'react-native-update': path.resolve(__dirname, '../..'),
|
||||||
* @type {import('metro-config').MetroConfig}
|
'@babel/runtime': path.resolve(__dirname, 'node_modules/@babel/runtime'),
|
||||||
*/
|
};
|
||||||
const config = {};
|
const watchFolders = [path.resolve(__dirname, '../..')];
|
||||||
|
|
||||||
module.exports = mergeConfig(getDefaultConfig(__dirname), config);
|
module.exports = {
|
||||||
|
transformer: {
|
||||||
|
getTransformOptions: async () => ({
|
||||||
|
transform: {
|
||||||
|
experimentalImportSupport: false,
|
||||||
|
inlineRequires: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
resolver: {
|
||||||
|
extraNodeModules,
|
||||||
|
},
|
||||||
|
watchFolders,
|
||||||
|
};
|
||||||
|
@@ -9,46 +9,30 @@
|
|||||||
"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",
|
|
||||||
"dev:harmony": "react-native bundle-harmony --dev"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"form-data": "^4.0.1",
|
"patch-package": "^6.5.1",
|
||||||
"patch-package": "^8.0.0",
|
|
||||||
"postinstall-postinstall": "^2.1.0",
|
"postinstall-postinstall": "^2.1.0",
|
||||||
"react": "18.3.1",
|
"react": "18.0.0",
|
||||||
"react-native": "0.76.3",
|
"react-native": "0.69.8",
|
||||||
"react-native-camera-kit": "^14.1.0",
|
"react-native-update": "link:../.."
|
||||||
"react-native-paper": "^5.12.5",
|
|
||||||
"react-native-safe-area-context": "^4.14.0",
|
|
||||||
"react-native-update": "^10.17.1",
|
|
||||||
"react-native-vector-icons": "^10.2.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.26.0",
|
"@babel/core": "^7.21.0",
|
||||||
"@babel/preset-env": "^7.26.0",
|
"@babel/runtime": "^7.21.0",
|
||||||
"@babel/runtime": "^7.26.0",
|
"@react-native-community/eslint-config": "^3.2.0",
|
||||||
"@react-native-community/cli": "15.0.0-alpha.2",
|
"babel-jest": "^29.5.0",
|
||||||
"@react-native-community/cli-platform-android": "15.0.0-alpha.2",
|
"detox": "^20.5.0",
|
||||||
"@react-native-community/cli-platform-ios": "15.0.0-alpha.2",
|
"eslint": "^8.36.0",
|
||||||
"@react-native/babel-preset": "0.76.3",
|
"jest": "^29.5.0",
|
||||||
"@react-native/eslint-config": "0.76.3",
|
"metro-react-native-babel-preset": "^0.76.0",
|
||||||
"@react-native/metro-config": "0.76.3",
|
"pod-install": "^0.1.37",
|
||||||
"@react-native/typescript-config": "0.76.3",
|
"prettier": "^2.8.4",
|
||||||
"@types/react": "^18.2.6",
|
"react-test-renderer": "18.2.0",
|
||||||
"@types/react-test-renderer": "^18.0.0",
|
"ts-jest": "^29.0.5"
|
||||||
"babel-jest": "^29.6.3",
|
|
||||||
"eslint": "^8.19.0",
|
|
||||||
"jest": "^29.6.3",
|
|
||||||
"prettier": "2.8.8",
|
|
||||||
"react-test-renderer": "18.3.1",
|
|
||||||
"typescript": "5.7.2"
|
|
||||||
},
|
},
|
||||||
"engines": {
|
"jest": {
|
||||||
"node": ">=16"
|
"preset": "react-native"
|
||||||
},
|
}
|
||||||
"trustedDependencies": [
|
|
||||||
"postinstall-postinstall"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
13
Example/testHotUpdate/patches/react-native+0.69.8.patch
Normal file
13
Example/testHotUpdate/patches/react-native+0.69.8.patch
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
diff --git a/node_modules/react-native/scripts/react_native_pods.rb b/node_modules/react-native/scripts/react_native_pods.rb
|
||||||
|
index e7c59ad..1461d45 100644
|
||||||
|
--- a/node_modules/react-native/scripts/react_native_pods.rb
|
||||||
|
+++ b/node_modules/react-native/scripts/react_native_pods.rb
|
||||||
|
@@ -420,7 +420,7 @@ def get_react_codegen_spec(options={})
|
||||||
|
'source' => { :git => '' },
|
||||||
|
'header_mappings_dir' => './',
|
||||||
|
'platforms' => {
|
||||||
|
- 'ios' => '11.0',
|
||||||
|
+ 'ios' => '12.0',
|
||||||
|
},
|
||||||
|
'source_files' => "**/*.{h,mm,cpp}",
|
||||||
|
'pod_target_xcconfig' => { "HEADER_SEARCH_PATHS" =>
|
@@ -1,8 +1,7 @@
|
|||||||
/* eslint-disable react-native/no-inline-styles */
|
/* eslint-disable react-native/no-inline-styles */
|
||||||
/* eslint-disable react/react-in-jsx-scope */
|
/* eslint-disable react/react-in-jsx-scope */
|
||||||
import {useState} from 'react';
|
import {useCallback, useMemo, useState} from 'react';
|
||||||
import {
|
import {
|
||||||
Alert,
|
|
||||||
ActivityIndicator,
|
ActivityIndicator,
|
||||||
Modal,
|
Modal,
|
||||||
TextInput,
|
TextInput,
|
||||||
@@ -14,127 +13,138 @@ import {
|
|||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
|
|
||||||
import {PushyModule} from 'react-native-update/src/core';
|
import {PushyModule} from 'react-native-update';
|
||||||
const Hash = '9D5CE6EBA420717BE7E7D308B11F8207681B066C951D68F3994D19828F342474';
|
const Hash = '9D5CE6EBA420717BE7E7D308B11F8207681B066C951D68F3994D19828F342474';
|
||||||
const UUID = '00000000-0000-0000-0000-000000000000';
|
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';
|
||||||
|
|
||||||
export default function TestConsole({visible, onClose}) {
|
|
||||||
|
const CustomDialog = ({title, visible, onConfirm}) => {
|
||||||
|
if (!visible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.overlay}>
|
||||||
|
<View style={styles.dialog}>
|
||||||
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
<TouchableOpacity testID='done' style={styles.button} onLongPress={onConfirm}>
|
||||||
|
<Text style={styles.buttonText}>确认</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default function TestConsole({visible}) {
|
||||||
const [text, setText] = useState('');
|
const [text, setText] = useState('');
|
||||||
const [running, setRunning] = useState(false);
|
const [running, setRunning] = useState(false);
|
||||||
const convertCommands = (cmd, params) => {
|
const [options, setOptions] = useState();
|
||||||
if (typeof params === 'string') {
|
const [alertVisible, setAlertVisible] = useState(false);
|
||||||
return `${cmd}\n${params}`;
|
const [alertMsg, setAlertMsg] = useState('');
|
||||||
}
|
const NativeTestMethod = useMemo(() => {
|
||||||
let paramText = '';
|
return [
|
||||||
for (const [k, v] of Object.entries(params)) {
|
|
||||||
paramText += `\n${k}\n${v}`;
|
|
||||||
}
|
|
||||||
return `${cmd}${paramText}`;
|
|
||||||
};
|
|
||||||
const shortCuts = [
|
|
||||||
{
|
{
|
||||||
name: 'setLocalHashInfo',
|
name: 'setLocalHashInfo',
|
||||||
invoke: () => {
|
invoke: () => {
|
||||||
setText(
|
setText(
|
||||||
convertCommands('setLocalHashInfo', {
|
`setLocalHashInfo\n${Hash}\n{\"version\":\"1.0.0\",\"size\":\"19M\"}`,
|
||||||
version: '1.0.0',
|
|
||||||
size: '19M',
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'getLocalHashInfo',
|
name: 'getLocalHashInfo',
|
||||||
invoke: () => {
|
invoke: () => {
|
||||||
setText(convertCommands('getLocalHashInfo', Hash));
|
setText(`getLocalHashInfo\n${Hash}`);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'setUuid',
|
name: 'setUuid',
|
||||||
invoke: () => {
|
invoke: () => {
|
||||||
setText(convertCommands('setUuid', UUID));
|
setText(`setUuid\n${UUID}`);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'setBlockUpdate',
|
||||||
|
invoke: () => {
|
||||||
|
setText('setBlockUpdate');
|
||||||
|
setOptions({reason: 'application has been block', until: 1673082950});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'reloadUpdate',
|
name: 'reloadUpdate',
|
||||||
invoke: () => {
|
invoke: () => {
|
||||||
setText(convertCommands('reloadUpdate', {hash: Hash}));
|
setText('reloadUpdate');
|
||||||
|
setOptions({hash: Hash});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'setNeedUpdate',
|
name: 'setNeedUpdate',
|
||||||
invoke: () => {
|
invoke: () => {
|
||||||
setText(convertCommands('setNeedUpdate', {hash: Hash}));
|
setText('setNeedUpdate');
|
||||||
|
setOptions({hash: Hash});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'markSuccess',
|
name: 'markSuccess',
|
||||||
invoke: () => {
|
invoke: () => {
|
||||||
setText(convertCommands('markSuccess'));
|
setText('markSuccess');
|
||||||
|
setOptions(undefined);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'downloadPatchFromPpk',
|
name: 'downloadPatchFromPpk',
|
||||||
invoke: () => {
|
invoke: () => {
|
||||||
setText(
|
setText('downloadPatchFromPpk');
|
||||||
convertCommands('downloadPatchFromPpk', {
|
setOptions({updateUrl: DownloadUrl, hash: Hash, originHash: Hash});
|
||||||
updateUrl: DownloadUrl,
|
|
||||||
hash: Hash,
|
|
||||||
originHash: Hash,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'downloadPatchFromPackage',
|
name: 'downloadPatchFromPackage',
|
||||||
invoke: () => {
|
invoke: () => {
|
||||||
setText(
|
setText('downloadPatchFromPackage');
|
||||||
convertCommands('downloadPatchFromPackage', {
|
setOptions({updateUrl: DownloadUrl, hash: Hash});
|
||||||
updateUrl: DownloadUrl,
|
|
||||||
hash: Hash,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'downloadFullUpdate',
|
name: 'downloadFullUpdate',
|
||||||
invoke: () => {
|
invoke: () => {
|
||||||
setText(
|
setText('downloadFullUpdate');
|
||||||
convertCommands('downloadFullUpdate', {
|
setOptions({updateUrl: DownloadUrl, hash: Hash});
|
||||||
updateUrl: DownloadUrl,
|
|
||||||
hash: Hash,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'downloadAndInstallApk',
|
name: 'downloadAndInstallApk',
|
||||||
invoke: () => {
|
invoke: () => {
|
||||||
setText(
|
setText('downloadAndInstallApk');
|
||||||
convertCommands('downloadAndInstallApk', {
|
setOptions({url: DownloadUrl, target: Hash, hash: Hash});
|
||||||
url: DownloadUrl,
|
|
||||||
target: Hash,
|
|
||||||
hash: Hash,
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const renderTestView = useCallback(() => {
|
||||||
|
const views = [];
|
||||||
|
for (let i = 0; i < NativeTestMethod.length; i++) {
|
||||||
|
views.push(
|
||||||
|
<TouchableOpacity
|
||||||
|
key={i}
|
||||||
|
testID={NativeTestMethod[i].name}
|
||||||
|
onLongPress={() => {
|
||||||
|
NativeTestMethod[i].invoke();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text>{NativeTestMethod[i].name}</Text>
|
||||||
|
</TouchableOpacity>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <View>{views}</View>;
|
||||||
|
}, [NativeTestMethod]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal visible={visible}>
|
<Modal visible={visible}>
|
||||||
<SafeAreaView style={{flex: 1, padding: 10}}>
|
<SafeAreaView style={{flex: 1, padding: 10}}>
|
||||||
<View
|
<Text>调试Pushy方法(方法名,参数,值换行)</Text>
|
||||||
style={{
|
|
||||||
flexDirection: 'row',
|
|
||||||
justifyContent: 'space-between',
|
|
||||||
alignItems: 'center',
|
|
||||||
}}>
|
|
||||||
<Text>调试Pushy方法(方法名,参数,值换行)</Text>
|
|
||||||
<Button title="Close" onPress={onClose} />
|
|
||||||
</View>
|
|
||||||
<TextInput
|
<TextInput
|
||||||
autoCorrect={false}
|
autoCorrect={false}
|
||||||
autoCapitalize="none"
|
autoCapitalize="none"
|
||||||
@@ -154,58 +164,48 @@ export default function TestConsole({visible, onClose}) {
|
|||||||
/>
|
/>
|
||||||
{running && <ActivityIndicator />}
|
{running && <ActivityIndicator />}
|
||||||
<TouchableOpacity
|
<TouchableOpacity
|
||||||
style={{
|
style={{backgroundColor:'rgb(0,140,237)', justifyContent: 'center',
|
||||||
backgroundColor: 'rgb(0,140,237)',
|
alignItems: 'center',paddingTop:10,paddingBottom:10,marginBottom:5}}
|
||||||
justifyContent: 'center',
|
|
||||||
alignItems: 'center',
|
|
||||||
paddingTop: 10,
|
|
||||||
paddingBottom: 10,
|
|
||||||
marginBottom: 5,
|
|
||||||
}}
|
|
||||||
testID="submit"
|
testID="submit"
|
||||||
onPress={async () => {
|
onLongPress={async () => {
|
||||||
setRunning(true);
|
setRunning(true);
|
||||||
try {
|
try {
|
||||||
const inputs = text.split('\n');
|
const inputs = text.split('\n');
|
||||||
const methodName = inputs[0];
|
const methodName = inputs[0];
|
||||||
let params;
|
let params = [];
|
||||||
if (inputs.length === 1) {
|
if (inputs.length === 1) {
|
||||||
await PushyModule[methodName]();
|
if (options) {
|
||||||
|
await PushyModule[methodName](options);
|
||||||
|
} else {
|
||||||
|
await PushyModule[methodName]();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
if (inputs.length === 2) {
|
if (inputs.length === 2) {
|
||||||
params = inputs[1];
|
params = [inputs[1]];
|
||||||
} else {
|
} else {
|
||||||
params = {};
|
params = [inputs[1], inputs[2]];
|
||||||
for (let i = 1; i < inputs.length; i += 2) {
|
|
||||||
params[inputs[i]] = inputs[i + 1];
|
|
||||||
}
|
|
||||||
console.log({inputs, params});
|
console.log({inputs, params});
|
||||||
}
|
}
|
||||||
await PushyModule[methodName](params);
|
await PushyModule[methodName](...params);
|
||||||
}
|
}
|
||||||
Alert.alert('done');
|
setAlertVisible(true);
|
||||||
|
setAlertMsg('done');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Alert.alert(e.message);
|
setAlertVisible(true);
|
||||||
|
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()}
|
||||||
<View>
|
<CustomDialog
|
||||||
{shortCuts.map(({name, invoke}, i) => (
|
title={alertMsg}
|
||||||
<TouchableOpacity
|
visible={alertVisible}
|
||||||
key={i}
|
onConfirm={()=>{setAlertVisible(false)}}
|
||||||
testID={name}
|
/>
|
||||||
onPress={() => {
|
|
||||||
invoke();
|
|
||||||
}}>
|
|
||||||
<Text>{name}</Text>
|
|
||||||
</TouchableOpacity>
|
|
||||||
))}
|
|
||||||
</View>
|
|
||||||
}
|
|
||||||
</SafeAreaView>
|
</SafeAreaView>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
206
Example/testHotUpdate/src/index.js
Normal file
206
Example/testHotUpdate/src/index.js
Normal file
@@ -0,0 +1,206 @@
|
|||||||
|
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: {},
|
||||||
|
});
|
@@ -1,208 +0,0 @@
|
|||||||
/* 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}
|
|
||||||
onClose={() => setShowTestConsole(false)}
|
|
||||||
/>
|
|
||||||
{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": 24794,
|
"appId": 10976,
|
||||||
"appKey": "SqShg4Klnj2hG6LAFMW2PdcgSSuniz0T"
|
"appKey": "p5q_rPYNOE0WdCf-i9ZCGPw-DUGUCMvY"
|
||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
"appId": 27509,
|
"appId": 10977,
|
||||||
"appKey": "aQz3Uc2pA7gt_prDaQ4rbWRY"
|
"appKey": "bUYKfZgtC9VirZsQbAt8qken2Gq2uxe3"
|
||||||
}
|
}
|
||||||
}
|
}
|
File diff suppressed because it is too large
Load Diff
@@ -8,9 +8,9 @@
|
|||||||
|
|
||||||
### 优势
|
### 优势
|
||||||
|
|
||||||
1. 对中国用户使用阿里云高速 CDN 分发,对比其他服务器在国外的热更新服务,分发更稳定,更新成功率极高。对国外用户则智能分流至 cloudflare,同样享受高速更新服务。
|
1. 基于阿里云高速 CDN 分发,对比其他服务器在国外的热更新服务,分发更稳定,更新成功率极高。
|
||||||
2. 基于 bsdiff/hdiff 算法创建的**超小更新包**,通常版本迭代后在几十 KB 级别(其他全量热更新服务所需流量通常在几十 MB 级别)。
|
2. 基于 bsdiff/hdiff 算法创建的**超小更新包**,通常版本迭代后在几十 KB 级别(其他全量热更新服务所需流量通常在几十 MB 级别)。
|
||||||
3. 始终跟进 RN 最新正式版本,第一时间提供支持。支持 hermes 字节码格式。支持新架构(注:安卓 0.73.0 ~ 0.76.0 的新架构因官方 bug 不支持,0.73 以下或 0.76.1 以上的新架构可用)。
|
3. 始终跟进 RN 最新正式版本,第一时间提供支持。支持 hermes 字节码格式。支持新架构。
|
||||||
4. 跨越多个版本进行更新时,只需要下载**一个更新包**,不需要逐版本依次更新。
|
4. 跨越多个版本进行更新时,只需要下载**一个更新包**,不需要逐版本依次更新。
|
||||||
5. 命令行工具 & 网页双端管理,版本发布过程简单便捷,完全可以集成 CI。
|
5. 命令行工具 & 网页双端管理,版本发布过程简单便捷,完全可以集成 CI。
|
||||||
6. 支持崩溃回滚,安全可靠。
|
6. 支持崩溃回滚,安全可靠。
|
||||||
@@ -33,3 +33,4 @@ $ 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)发帖提问。
|
||||||
|
|
||||||
|
@@ -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 error:" + response.code() + " " + response.message());
|
throw new Error("Server return code " + response.code());
|
||||||
}
|
}
|
||||||
ResponseBody body = response.body();
|
ResponseBody body = response.body();
|
||||||
long contentLength = body.contentLength();
|
long contentLength = body.contentLength();
|
||||||
@@ -430,9 +430,6 @@ 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 {
|
||||||
@@ -444,13 +441,6 @@ 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;
|
||||||
|
@@ -69,6 +69,13 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -159,6 +166,13 @@ 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);
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package cn.reactnative.modules.update;
|
package cn.reactnative.modules.update;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.app.Application;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import com.facebook.react.ReactApplication;
|
import com.facebook.react.ReactApplication;
|
||||||
import com.facebook.react.ReactInstanceManager;
|
import com.facebook.react.ReactInstanceManager;
|
||||||
@@ -117,7 +117,8 @@ public class UpdateModuleImpl {
|
|||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
updateContext.switchVersion(hash);
|
updateContext.switchVersion(hash);
|
||||||
final Context application = mContext.getApplicationContext();
|
Activity activity = mContext.getCurrentActivity();
|
||||||
|
Application application = activity.getApplication();
|
||||||
ReactInstanceManager instanceManager = updateContext.getCustomReactInstanceManager();
|
ReactInstanceManager instanceManager = updateContext.getCustomReactInstanceManager();
|
||||||
|
|
||||||
if (instanceManager == null) {
|
if (instanceManager == null) {
|
||||||
@@ -141,10 +142,7 @@ public class UpdateModuleImpl {
|
|||||||
promise.resolve(true);
|
promise.resolve(true);
|
||||||
} catch (Throwable err) {
|
} catch (Throwable err) {
|
||||||
promise.reject("pushy:"+err.getMessage());
|
promise.reject("pushy:"+err.getMessage());
|
||||||
final Activity activity = mContext.getCurrentActivity();
|
activity.recreate();
|
||||||
if (activity != null) {
|
|
||||||
activity.recreate();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Throwable err) {
|
} catch (Throwable err) {
|
||||||
@@ -194,6 +192,23 @@ 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 NativePushySpec {
|
public class UpdateModule extends NativeUpdateSpec {
|
||||||
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,6 +45,7 @@ public class UpdateModule extends NativePushySpec {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
@@ -107,6 +108,11 @@ public class UpdateModule extends NativePushySpec {
|
|||||||
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);
|
||||||
|
@@ -1,7 +1,7 @@
|
|||||||
package cn.reactnative.modules.update;
|
package cn.reactnative.modules.update;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.app.Application;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
@@ -59,6 +59,7 @@ 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;
|
||||||
}
|
}
|
||||||
@@ -176,7 +177,8 @@ public class UpdateModule extends ReactContextBaseJavaModule {
|
|||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
updateContext.switchVersion(hash);
|
updateContext.switchVersion(hash);
|
||||||
final Context application = getReactApplicationContext().getApplicationContext();
|
Activity activity = getCurrentActivity();
|
||||||
|
Application application = activity.getApplication();
|
||||||
ReactInstanceManager instanceManager = updateContext.getCustomReactInstanceManager();
|
ReactInstanceManager instanceManager = updateContext.getCustomReactInstanceManager();
|
||||||
|
|
||||||
if (instanceManager == null) {
|
if (instanceManager == null) {
|
||||||
@@ -199,10 +201,7 @@ public class UpdateModule extends ReactContextBaseJavaModule {
|
|||||||
instanceManager.recreateReactContextInBackground();
|
instanceManager.recreateReactContextInBackground();
|
||||||
promise.resolve(null);
|
promise.resolve(null);
|
||||||
} catch (Throwable err) {
|
} catch (Throwable err) {
|
||||||
final Activity activity = getCurrentActivity();
|
activity.recreate();
|
||||||
if (activity != null) {
|
|
||||||
activity.recreate();
|
|
||||||
}
|
|
||||||
promise.reject(err);
|
promise.reject(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -240,6 +239,18 @@ 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() {
|
||||||
|
@@ -1,3 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
presets: ['module:@react-native/babel-preset'],
|
|
||||||
};
|
|
@@ -1 +1 @@
|
|||||||
["update.react-native.cn", "update.reactnative.cn", "p.reactnative.cn"]
|
["update.react-native.cn", "update.reactnative.cn"]
|
||||||
|
@@ -1 +1 @@
|
|||||||
["https://pushy-koa-qgbgqmcpis.cn-beijing.fcapp.run", "https://p.reactnative.cn/api"]
|
["https://update.react-native.cn/api", "https://update.reactnative.cn/api"]
|
||||||
|
@@ -14,8 +14,11 @@
|
|||||||
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 */; };
|
||||||
@@ -30,9 +33,6 @@
|
|||||||
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,12 +64,15 @@
|
|||||||
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>"; };
|
||||||
@@ -105,9 +108,6 @@
|
|||||||
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 */,
|
||||||
A3FD91A52C3C01600022D27F /* RCTPushy.mm */,
|
9F292F7C1C7C44290095945D /* RCTPushyDownloader.m */,
|
||||||
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;
|
||||||
};
|
};
|
||||||
|
@@ -1,10 +1,6 @@
|
|||||||
#import "RCTPushy.h"
|
#import "RCTPushy.h"
|
||||||
#import "RCTPushyDownloader.h"
|
#import "RCTPushyDownloader.h"
|
||||||
#import "RCTPushyManager.h"
|
#import "RCTPushyManager.h"
|
||||||
|
|
||||||
#if __has_include("RCTReloadCommand.h")
|
|
||||||
#import "RCTReloadCommand.h"
|
|
||||||
#endif
|
|
||||||
// Thanks to this guard, we won't import this header when we build for the old architecture.
|
// Thanks to this guard, we won't import this header when we build for the old architecture.
|
||||||
#ifdef RCT_NEW_ARCH_ENABLED
|
#ifdef RCT_NEW_ARCH_ENABLED
|
||||||
#import "RCTPushySpec.h"
|
#import "RCTPushySpec.h"
|
||||||
@@ -20,6 +16,7 @@ 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";
|
||||||
@@ -79,7 +76,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 {
|
||||||
@@ -101,7 +98,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];
|
||||||
@@ -141,7 +138,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,6 +157,7 @@ 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];
|
||||||
@@ -180,7 +178,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;
|
||||||
}
|
}
|
||||||
@@ -194,13 +192,31 @@ 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) {
|
||||||
@@ -218,7 +234,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);
|
||||||
@@ -299,7 +315,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);
|
||||||
@@ -315,17 +331,14 @@ RCT_EXPORT_METHOD(reloadUpdate:(NSDictionary *)options
|
|||||||
if (hash.length) {
|
if (hash.length) {
|
||||||
[self setNeedUpdate:options resolver:resolve rejecter:reject];
|
[self setNeedUpdate:options resolver:resolve rejecter:reject];
|
||||||
|
|
||||||
#if __has_include("RCTReloadCommand.h")
|
// reload 0.62+
|
||||||
// reload 0.62+
|
// RCTReloadCommandSetBundleURL([[self class] bundleURL]);
|
||||||
RCTReloadCommandSetBundleURL([[self class] bundleURL]);
|
// RCTTriggerReloadCommandListeners(@"pushy reload");
|
||||||
RCTTriggerReloadCommandListeners(@"pushy reload");
|
|
||||||
#else
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
// reload in earlier version
|
[self.bridge setValue:[[self class] bundleURL] forKey:@"bundleURL"];
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
[self.bridge reload];
|
||||||
[self.bridge setValue:[[self class] bundleURL] forKey:@"bundleURL"];
|
});
|
||||||
[self.bridge reload];
|
|
||||||
});
|
|
||||||
#endif
|
|
||||||
resolve(@true);
|
resolve(@true);
|
||||||
}else{
|
}else{
|
||||||
reject(@"执行报错", nil, nil);
|
reject(@"执行报错", nil, nil);
|
||||||
@@ -353,7 +366,7 @@ RCT_EXPORT_METHOD(markSuccess:(RCTPromiseResolveBlock)resolve
|
|||||||
[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];
|
||||||
@@ -543,15 +556,7 @@ RCT_EXPORT_METHOD(markSuccess:(RCTPromiseResolveBlock)resolve
|
|||||||
|
|
||||||
for(NSString *fileName in list) {
|
for(NSString *fileName in list) {
|
||||||
if (![fileName isEqualToString:curVersion]) {
|
if (![fileName isEqualToString:curVersion]) {
|
||||||
NSString *filePath = [downloadDir stringByAppendingPathComponent:fileName];
|
[_fileManager removeFile:[downloadDir stringByAppendingPathComponent:fileName] completionHandler:nil];
|
||||||
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];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -624,7 +629,7 @@ RCT_EXPORT_METHOD(markSuccess:(RCTPromiseResolveBlock)resolve
|
|||||||
- (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::NativePushySpecJSI>(params);
|
return std::make_shared<facebook::react::NativeUpdateSpecJSI>(params);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@@ -1,4 +1,5 @@
|
|||||||
import { TurboModule, TurboModuleRegistry } from 'react-native';
|
import type { TurboModule } from 'react-native/Libraries/TurboModule/RCTExport';
|
||||||
|
import { TurboModuleRegistry } from 'react-native';
|
||||||
|
|
||||||
export interface Spec extends TurboModule {
|
export interface Spec extends TurboModule {
|
||||||
getConstants: () => {
|
getConstants: () => {
|
||||||
@@ -8,12 +9,14 @@ 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>;
|
60
lib/endpoint.ts
Normal file
60
lib/endpoint.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { logger, promiseAny } from './utils';
|
||||||
|
|
||||||
|
let currentEndpoint = 'https://update.react-native.cn/api';
|
||||||
|
let backupEndpoints: string[] = [
|
||||||
|
'https://pushy-koa-qgbgqmcpis.cn-beijing.fcapp.run',
|
||||||
|
'https://update.reactnative.cn/api',
|
||||||
|
];
|
||||||
|
let backupEndpointsQueryUrls = [
|
||||||
|
'https://gitee.com/sunnylqm/react-native-pushy/raw/master/endpoints.json',
|
||||||
|
'https://cdn.jsdelivr.net/gh/reactnativecn/react-native-pushy@master/endpoints.json',
|
||||||
|
];
|
||||||
|
|
||||||
|
export async function updateBackupEndpoints() {
|
||||||
|
if (backupEndpointsQueryUrls) {
|
||||||
|
try {
|
||||||
|
const resp = await promiseAny(
|
||||||
|
backupEndpointsQueryUrls.map(queryUrl => fetch(queryUrl)),
|
||||||
|
);
|
||||||
|
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,
|
||||||
|
backupQueryUrls,
|
||||||
|
}: {
|
||||||
|
main: string;
|
||||||
|
backups?: string[];
|
||||||
|
backupQueryUrls?: string[];
|
||||||
|
}) {
|
||||||
|
currentEndpoint = main;
|
||||||
|
if (Array.isArray(backups) && backups.length > 0) {
|
||||||
|
backupEndpoints = backups;
|
||||||
|
}
|
||||||
|
if (Array.isArray(backupQueryUrls) && backupQueryUrls.length > 0) {
|
||||||
|
backupEndpointsQueryUrls = backupQueryUrls;
|
||||||
|
}
|
||||||
|
}
|
2
lib/index.ts
Normal file
2
lib/index.ts
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export * from './main';
|
||||||
|
export * from './simpleUpdate';
|
18
lib/index.web.js
Normal file
18
lib/index.web.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
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;
|
403
lib/main.ts
Normal file
403
lib/main.ts
Normal file
@@ -0,0 +1,403 @@
|
|||||||
|
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, promiseAny, testUrls } 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 promiseAny(
|
||||||
|
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' });
|
||||||
|
const diffUrl = (await testUrls(options.diffUrls)) || options.diffUrl;
|
||||||
|
if (diffUrl) {
|
||||||
|
logger('downloading diff');
|
||||||
|
try {
|
||||||
|
await PushyModule.downloadPatchFromPpk({
|
||||||
|
updateUrl: diffUrl,
|
||||||
|
hash: options.hash,
|
||||||
|
originHash: currentVersion,
|
||||||
|
});
|
||||||
|
succeeded = true;
|
||||||
|
} catch (e) {
|
||||||
|
logger(`diff error: ${e.message}, try pdiff`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!succeeded) {
|
||||||
|
const pdiffUrl = (await testUrls(options.pdiffUrls)) || options.pdiffUrl;
|
||||||
|
if (pdiffUrl) {
|
||||||
|
logger('downloading pdiff');
|
||||||
|
try {
|
||||||
|
await PushyModule.downloadPatchFromPackage({
|
||||||
|
updateUrl: pdiffUrl,
|
||||||
|
hash: options.hash,
|
||||||
|
});
|
||||||
|
succeeded = true;
|
||||||
|
} catch (e) {
|
||||||
|
logger(`pdiff error: ${e.message}, try full patch`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!succeeded) {
|
||||||
|
const updateUrl = (await testUrls(options.updateUrls)) || options.updateUrl;
|
||||||
|
if (updateUrl) {
|
||||||
|
logger('downloading full patch');
|
||||||
|
try {
|
||||||
|
await PushyModule.downloadFullUpdate({
|
||||||
|
updateUrl: 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function switchVersion(hash: string) {
|
||||||
|
assertRelease();
|
||||||
|
if (assertHash(hash)) {
|
||||||
|
logger('switchVersion: ' + hash);
|
||||||
|
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();
|
||||||
|
}
|
135
lib/simpleUpdate.tsx
Normal file
135
lib/simpleUpdate.tsx
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
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} />;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
74
lib/type.ts
Normal file
74
lib/type.ts
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
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;
|
||||||
|
pdiffUrls?: string[];
|
||||||
|
diffUrl?: string;
|
||||||
|
diffUrls?: string[];
|
||||||
|
updateUrl?: string;
|
||||||
|
updateUrls?: 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,7 +1,7 @@
|
|||||||
import { Platform } from 'react-native';
|
import { Platform } from 'react-native';
|
||||||
|
|
||||||
export function log(...args: any[]) {
|
export function logger(...args: any[]) {
|
||||||
console.log('pushy: ', ...args);
|
console.log('Pushy: ', ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function promiseAny<T>(promises: Promise<T>[]) {
|
export function promiseAny<T>(promises: Promise<T>[]) {
|
||||||
@@ -21,18 +21,11 @@ export function promiseAny<T>(promises: Promise<T>[]) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export const emptyObj = {};
|
export function assertRelease() {
|
||||||
export const noop = () => {};
|
if (__DEV__) {
|
||||||
class EmptyModule {
|
throw new Error('react-native-update 只能在 RELEASE 版本中运行.');
|
||||||
constructor() {
|
|
||||||
return new Proxy(this, {
|
|
||||||
get() {
|
|
||||||
return noop;
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const emptyModule = new EmptyModule();
|
|
||||||
|
|
||||||
const ping =
|
const ping =
|
||||||
Platform.OS === 'web'
|
Platform.OS === 'web'
|
||||||
@@ -41,18 +34,10 @@ const ping =
|
|||||||
Promise.race([
|
Promise.race([
|
||||||
fetch(url, {
|
fetch(url, {
|
||||||
method: 'HEAD',
|
method: 'HEAD',
|
||||||
})
|
}).then(({ status }) => (status === 200 ? url : null)),
|
||||||
.then(({ status }) => (status === 200 ? url : null))
|
|
||||||
.catch(() => null),
|
|
||||||
new Promise(r => setTimeout(() => r(null), 2000)),
|
new Promise(r => setTimeout(() => r(null), 2000)),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
export function joinUrls(paths: string[], fileName?: string) {
|
|
||||||
if (fileName) {
|
|
||||||
return paths.map(path => 'https://' + path + '/' + fileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const testUrls = async (urls?: string[]) => {
|
export const testUrls = async (urls?: string[]) => {
|
||||||
if (!urls?.length) {
|
if (!urls?.length) {
|
||||||
return null;
|
return null;
|
55
package.json
55
package.json
@@ -1,11 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "react-native-update",
|
"name": "react-native-update",
|
||||||
"version": "10.19.1",
|
"version": "9.2.2",
|
||||||
"description": "react-native hot update",
|
"description": "react-native hot update",
|
||||||
"main": "src/index",
|
"main": "lib/index.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepack": "yarn submodule && yarn lint",
|
"prepublish": "yarn submodule",
|
||||||
"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",
|
||||||
@@ -40,40 +39,34 @@
|
|||||||
"url": "https://github.com/reactnativecn/react-native-pushy/issues"
|
"url": "https://github.com/reactnativecn/react-native-pushy/issues"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"react": ">=16.8.0",
|
"react-native": ">=0.57.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": {
|
||||||
"name": "RCTPushySpec",
|
"libraries": [
|
||||||
"type": "modules",
|
{
|
||||||
"jsSrcsDir": "src"
|
"name": "RCTPushySpec",
|
||||||
|
"type": "modules",
|
||||||
|
"jsSrcsDir": "lib"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.25.8",
|
"@types/fs-extra": "^9.0.13",
|
||||||
"@react-native/babel-preset": "^0.73.21",
|
"@types/jest": "^29.2.1",
|
||||||
"@react-native/eslint-config": "^0.73.2",
|
"@types/node": "^20.8.9",
|
||||||
"@react-native/typescript-config": "^0.74.0",
|
"@types/react": "^18.2.33",
|
||||||
"@types/fs-extra": "^11.0.4",
|
"detox": "^20.5.0",
|
||||||
"@types/jest": "^29.5.13",
|
"firebase-tools": "^11.24.1",
|
||||||
"@types/node": "^22.7.6",
|
"fs-extra": "^9.1.0",
|
||||||
"@types/react": "^18.3.11",
|
"jest": "^29.2.1",
|
||||||
"detox": "^20.27.3",
|
"pod-install": "^0.1.37",
|
||||||
"eslint": "^8.57.0",
|
"react-native": "^0.72.6",
|
||||||
"eslint-plugin-ft-flow": "^3.0.7",
|
"ts-jest": "^29.0.3",
|
||||||
"firebase-tools": "^13.22.1",
|
"typescript": "^5.2.2"
|
||||||
"fs-extra": "^11.2.0",
|
|
||||||
"jest": "^29.7.0",
|
|
||||||
"pod-install": "^0.2.2",
|
|
||||||
"prettier": "^2",
|
|
||||||
"react": "18.2.0",
|
|
||||||
"react-native": "0.73",
|
|
||||||
"ts-jest": "^29.2.5",
|
|
||||||
"typescript": "^5.6.3"
|
|
||||||
},
|
},
|
||||||
"packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
|
"packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,5 @@
|
|||||||
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|
|
||||||
@@ -42,25 +40,5 @@ 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
|
||||||
|
487
src/client.ts
487
src/client.ts
@@ -1,487 +0,0 @@
|
|||||||
import { CheckResult, PushyOptions, ProgressData, EventType } from './type';
|
|
||||||
import { emptyObj, joinUrls, log, noop, promiseAny, 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',
|
|
||||||
],
|
|
||||||
};
|
|
||||||
|
|
||||||
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>;
|
|
||||||
|
|
||||||
static progressHandlers: Record<string, EmitterSubscription> = {};
|
|
||||||
static downloadedHash?: string;
|
|
||||||
|
|
||||||
static apkStatus: 'downloading' | 'downloaded' | null = null;
|
|
||||||
|
|
||||||
static marked = false;
|
|
||||||
static 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,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
throwIfEnabled = (e: Error) => {
|
|
||||||
if (this.options.throwError) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
getCheckUrl = (endpoint: string = this.options.server!.main) => {
|
|
||||||
return `${endpoint}/checkUpdate/${this.options.appKey}`;
|
|
||||||
};
|
|
||||||
static assertHash = (hash: string) => {
|
|
||||||
if (!Pushy.downloadedHash) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (hash !== Pushy.downloadedHash) {
|
|
||||||
log(`use downloaded hash ${Pushy.downloadedHash} first`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
markSuccess = () => {
|
|
||||||
if (Pushy.marked || __DEV__ || !isFirstTime) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Pushy.marked = true;
|
|
||||||
PushyModule.markSuccess();
|
|
||||||
this.report({ type: 'markSuccess' });
|
|
||||||
};
|
|
||||||
switchVersion = async (hash: string) => {
|
|
||||||
if (__DEV__) {
|
|
||||||
console.warn(
|
|
||||||
'您调用了switchVersion方法,但是当前是开发环境,不会进行任何操作。',
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Pushy.assertHash(hash) && !Pushy.applyingUpdate) {
|
|
||||||
log('switchVersion: ' + hash);
|
|
||||||
Pushy.applyingUpdate = true;
|
|
||||||
return PushyModule.reloadUpdate({ hash });
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
switchVersionLater = async (hash: string) => {
|
|
||||||
if (__DEV__) {
|
|
||||||
console.warn(
|
|
||||||
'您调用了switchVersionLater方法,但是当前是开发环境,不会进行任何操作。',
|
|
||||||
);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Pushy.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 promiseAny(
|
|
||||||
backupEndpoints.map(endpoint =>
|
|
||||||
fetch(this.getCheckUrl(endpoint), fetchPayload),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} catch (err: any) {
|
|
||||||
this.throwIfEnabled(new Error('errorCheckingUseBackup'));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.throwIfEnabled(new Error('errorCheckingGetBackup'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!resp) {
|
|
||||||
this.report({
|
|
||||||
type: 'errorChecking',
|
|
||||||
message: 'Can not connect to update server. Please check your network.',
|
|
||||||
});
|
|
||||||
this.throwIfEnabled(new Error('errorChecking'));
|
|
||||||
return this.lastRespJson ? await this.lastRespJson : emptyObj;
|
|
||||||
}
|
|
||||||
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,
|
|
||||||
});
|
|
||||||
this.throwIfEnabled(new Error(result.message));
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
};
|
|
||||||
getBackupEndpoints = async () => {
|
|
||||||
const { server } = this.options;
|
|
||||||
if (!server) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
if (server.queryUrls) {
|
|
||||||
try {
|
|
||||||
const resp = await promiseAny(
|
|
||||||
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 (Pushy.downloadedHash === hash) {
|
|
||||||
log(`duplicated downloaded hash ${Pushy.downloadedHash}, ignored`);
|
|
||||||
return Pushy.downloadedHash;
|
|
||||||
}
|
|
||||||
if (Pushy.progressHandlers[hash]) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (onDownloadProgress) {
|
|
||||||
Pushy.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 (Pushy.progressHandlers[hash]) {
|
|
||||||
Pushy.progressHandlers[hash].remove();
|
|
||||||
delete Pushy.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,
|
|
||||||
});
|
|
||||||
Pushy.downloadedHash = hash;
|
|
||||||
return hash;
|
|
||||||
};
|
|
||||||
downloadAndInstallApk = async (
|
|
||||||
url: string,
|
|
||||||
onDownloadProgress?: (data: ProgressData) => void,
|
|
||||||
) => {
|
|
||||||
if (Platform.OS !== 'android') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Pushy.apkStatus === 'downloading') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Pushy.apkStatus === 'downloaded') {
|
|
||||||
this.report({ type: 'errorInstallApk' });
|
|
||||||
this.throwIfEnabled(new Error('errorInstallApk'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (Platform.Version <= 23) {
|
|
||||||
try {
|
|
||||||
const granted = await PermissionsAndroid.request(
|
|
||||||
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
|
|
||||||
);
|
|
||||||
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
|
|
||||||
this.report({ type: 'rejectStoragePermission' });
|
|
||||||
this.throwIfEnabled(new Error('rejectStoragePermission'));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (e: any) {
|
|
||||||
this.report({ type: 'errorStoragePermission' });
|
|
||||||
this.throwIfEnabled(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Pushy.apkStatus = 'downloading';
|
|
||||||
this.report({ type: 'downloadingApk' });
|
|
||||||
const progressKey = 'downloadingApk';
|
|
||||||
if (onDownloadProgress) {
|
|
||||||
if (Pushy.progressHandlers[progressKey]) {
|
|
||||||
Pushy.progressHandlers[progressKey].remove();
|
|
||||||
}
|
|
||||||
Pushy.progressHandlers[progressKey] = pushyNativeEventEmitter.addListener(
|
|
||||||
'RCTPushyDownloadProgress',
|
|
||||||
(progressData: ProgressData) => {
|
|
||||||
if (progressData.hash === progressKey) {
|
|
||||||
onDownloadProgress(progressData);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
await PushyModule.downloadAndInstallApk({
|
|
||||||
url,
|
|
||||||
target: 'update.apk',
|
|
||||||
hash: progressKey,
|
|
||||||
}).catch(() => {
|
|
||||||
Pushy.apkStatus = null;
|
|
||||||
this.report({ type: 'errorDownloadAndInstallApk' });
|
|
||||||
this.throwIfEnabled(new Error('errorDownloadAndInstallApk'));
|
|
||||||
});
|
|
||||||
Pushy.apkStatus = 'downloaded';
|
|
||||||
if (Pushy.progressHandlers[progressKey]) {
|
|
||||||
Pushy.progressHandlers[progressKey].remove();
|
|
||||||
delete Pushy.progressHandlers[progressKey];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
@@ -1,44 +0,0 @@
|
|||||||
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<boolean | 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
70
src/core.ts
@@ -1,70 +0,0 @@
|
|||||||
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,
|
|
||||||
};
|
|
@@ -1,3 +0,0 @@
|
|||||||
export { Pushy } from './client';
|
|
||||||
export { PushyContext, usePushy } from './context';
|
|
||||||
export { PushyProvider } from './provider';
|
|
@@ -1,77 +0,0 @@
|
|||||||
/* 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 +0,0 @@
|
|||||||
export { PermissionsAndroid } from 'react-native';
|
|
@@ -1,4 +0,0 @@
|
|||||||
import type { PermissionsAndroidStatic } from 'react-native';
|
|
||||||
import { emptyModule } from './utils';
|
|
||||||
|
|
||||||
export const PermissionsAndroid = emptyModule as PermissionsAndroidStatic;
|
|
359
src/provider.tsx
359
src/provider.tsx
@@ -1,359 +0,0 @@
|
|||||||
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 false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const hash = await client.downloadUpdate(info, setProgress);
|
|
||||||
if (!hash) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
stateListener.current && stateListener.current.remove();
|
|
||||||
if (options.updateStrategy === 'silentAndNow') {
|
|
||||||
client.switchVersion(hash);
|
|
||||||
return true;
|
|
||||||
} else if (options.updateStrategy === 'silentAndLater') {
|
|
||||||
client.switchVersionLater(hash);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
alertUpdate('提示', '下载完毕,是否立即更新?', [
|
|
||||||
{
|
|
||||||
text: '下次再说',
|
|
||||||
style: 'cancel',
|
|
||||||
onPress: () => {
|
|
||||||
client.switchVersionLater(hash);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
text: '立即更新',
|
|
||||||
style: 'default',
|
|
||||||
onPress: () => {
|
|
||||||
client.switchVersion(hash);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
return true;
|
|
||||||
} catch (e: any) {
|
|
||||||
setLastError(e);
|
|
||||||
alertError('更新失败', e.message);
|
|
||||||
throwErrorIfEnabled(e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[
|
|
||||||
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'
|
|
||||||
) {
|
|
||||||
downloadUpdate(info);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
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>
|
|
||||||
);
|
|
||||||
};
|
|
96
src/type.ts
96
src/type.ts
@@ -1,96 +0,0 @@
|
|||||||
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'
|
|
||||||
| 'errorInstallApk';
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
6
tea.yaml
6
tea.yaml
@@ -1,6 +0,0 @@
|
|||||||
# https://tea.xyz/what-is-this-file
|
|
||||||
---
|
|
||||||
version: 1.0.0
|
|
||||||
codeOwners:
|
|
||||||
- '0x10D90dC0034E2e82F0AC55954B3ed4EC0550ECe7'
|
|
||||||
quorum: 1
|
|
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "@react-native/typescript-config/tsconfig.json",
|
|
||||||
"include": ["src/**/*"]
|
|
||||||
}
|
|
Reference in New Issue
Block a user