mirror of
https://gitcode.com/gh_mirrors/re/react-native-pushy.git
synced 2025-09-16 13:01:38 +08:00
Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
d5194a1ad1 | ||
![]() |
ebc9b97e70 | ||
![]() |
40742e16d8 | ||
![]() |
598ae1a506 | ||
![]() |
e5424591d1 | ||
![]() |
2cf7336b6a | ||
![]() |
7eac48ab5d | ||
![]() |
18d9b75545 |
@@ -36,10 +36,6 @@
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
name: 'pushy',
|
||||
srcPath: '../node_modules/react-native-update/harmony',
|
||||
}
|
||||
]
|
||||
}
|
@@ -7,7 +7,7 @@
|
||||
"license": "",
|
||||
"dependencies": {
|
||||
"@rnoh/react-native-openharmony": "0.72.38",
|
||||
"pushy": "file:../../node_modules/react-native-update/harmony"
|
||||
"pushy": "file:../../node_modules/react-native-update/harmony/pushy.har",
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -18,7 +18,7 @@
|
||||
"@react-native-oh/react-native-harmony": "^0.72.59",
|
||||
"react": "18.2.0",
|
||||
"react-native": "0.72.5",
|
||||
"react-native-update": "^10.26.4"
|
||||
"react-native-update": "latest"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.20.0",
|
||||
|
4
Example/testHotUpdate/.gitignore
vendored
4
Example/testHotUpdate/.gitignore
vendored
@@ -62,3 +62,7 @@ buck-out/
|
||||
# Ruby / CocoaPods
|
||||
/ios/Pods/
|
||||
/vendor/bundle/
|
||||
|
||||
# react-native-update
|
||||
.update
|
||||
.pushy
|
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -17,33 +17,33 @@
|
||||
"form-data": "^4.0.2",
|
||||
"patch-package": "^8.0.0",
|
||||
"react": "19.0.0",
|
||||
"react-native": "0.78.0",
|
||||
"react-native-camera-kit": "^14.2.0",
|
||||
"react-native-paper": "^5.13.1",
|
||||
"react-native-safe-area-context": "^5.3.0",
|
||||
"react-native-svg": "^15.11.2",
|
||||
"react-native-update": "^10.26.4",
|
||||
"react-native": "0.79.2",
|
||||
"react-native-camera-kit": "^15.0.1",
|
||||
"react-native-paper": "^5.14.5",
|
||||
"react-native-safe-area-context": "^5.4.1",
|
||||
"react-native-svg": "^15.12.0",
|
||||
"react-native-update": "^10.29.0",
|
||||
"react-native-vector-icons": "^10.2.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.26.0",
|
||||
"@babel/preset-env": "^7.26.0",
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@react-native-community/cli": "15.0.1",
|
||||
"@react-native-community/cli-platform-android": "15.0.1",
|
||||
"@react-native-community/cli-platform-ios": "15.0.1",
|
||||
"@react-native/babel-preset": "0.78.0",
|
||||
"@react-native/eslint-config": "0.78.0",
|
||||
"@react-native/metro-config": "0.78.0",
|
||||
"@react-native/typescript-config": "0.78.0",
|
||||
"@babel/core": "^7.27.3",
|
||||
"@babel/preset-env": "^7.27.2",
|
||||
"@babel/runtime": "^7.27.3",
|
||||
"@react-native-community/cli": "18.0.0",
|
||||
"@react-native-community/cli-platform-android": "18.0.0",
|
||||
"@react-native-community/cli-platform-ios": "18.0.0",
|
||||
"@react-native/babel-preset": "0.79.2",
|
||||
"@react-native/eslint-config": "0.79.2",
|
||||
"@react-native/metro-config": "0.79.2",
|
||||
"@react-native/typescript-config": "0.79.2",
|
||||
"@types/react": "^19.0.0",
|
||||
"@types/react-test-renderer": "^19.0.0",
|
||||
"detox": "^20.32.0",
|
||||
"detox": "^20.39.0",
|
||||
"eslint": "^8.19.0",
|
||||
"jest": "^29.6.3",
|
||||
"prettier": "2.8.8",
|
||||
"react-test-renderer": "19.0.0",
|
||||
"typescript": "5.8.2"
|
||||
"typescript": "5.8.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
@@ -52,4 +52,4 @@
|
||||
"detox",
|
||||
"dtrace-provider"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
36
android/proguard.pro
vendored
36
android/proguard.pro
vendored
@@ -1,3 +1,37 @@
|
||||
# Keep our update module classes
|
||||
-keepnames class cn.reactnative.modules.update.DownloadTask { *; }
|
||||
-keepnames class cn.reactnative.modules.update.UpdateModuleImpl { *; }
|
||||
-keepnames class com.facebook.react.ReactInstanceManager { *; }
|
||||
-keepnames class cn.reactnative.modules.update.** { *; }
|
||||
|
||||
# Keep React Native classes
|
||||
-keepnames class com.facebook.react.ReactInstanceManager { *; }
|
||||
-keepnames class com.facebook.react.** { *; }
|
||||
-keepnames class com.facebook.react.bridge.** { *; }
|
||||
-keepnames class com.facebook.react.devsupport.** { *; }
|
||||
|
||||
# Keep fields used in reflection
|
||||
-keepclassmembers class com.facebook.react.ReactInstanceManager {
|
||||
private JSBundleLoader mBundleLoader;
|
||||
private String mJSBundleFile;
|
||||
}
|
||||
|
||||
-keepclassmembers class com.facebook.react.ReactDelegate {
|
||||
private ReactHost mReactHost;
|
||||
}
|
||||
|
||||
-keepclassmembers class com.facebook.react.ReactHost {
|
||||
private boolean mUseDevSupport;
|
||||
private ReactHostDelegate mReactHostDelegate;
|
||||
}
|
||||
|
||||
# Keep Expo related classes
|
||||
-keepnames class expo.modules.ExpoReactHostFactory$ExpoReactHostDelegate { *; }
|
||||
|
||||
# Keep methods used in reflection
|
||||
-keepclassmembers class com.facebook.react.ReactActivity {
|
||||
public ReactDelegate getReactDelegate();
|
||||
}
|
||||
|
||||
-keepclassmembers class com.facebook.react.ReactHost {
|
||||
public void reload(java.lang.String);
|
||||
}
|
@@ -169,17 +169,19 @@ public class UpdateContext {
|
||||
}
|
||||
|
||||
public void markSuccess() {
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putBoolean("firstTimeOk", true);
|
||||
String lastVersion = sp.getString("lastVersion", null);
|
||||
String curVersion = sp.getString("currentVersion", null);
|
||||
if (lastVersion != null && !lastVersion.equals(curVersion)) {
|
||||
editor.remove("lastVersion");
|
||||
editor.remove("hash_" + lastVersion);
|
||||
}
|
||||
editor.apply();
|
||||
if (!BuildConfig.DEBUG) {
|
||||
SharedPreferences.Editor editor = sp.edit();
|
||||
editor.putBoolean("firstTimeOk", true);
|
||||
String lastVersion = sp.getString("lastVersion", null);
|
||||
String curVersion = sp.getString("currentVersion", null);
|
||||
if (lastVersion != null && !lastVersion.equals(curVersion)) {
|
||||
editor.remove("lastVersion");
|
||||
editor.remove("hash_" + lastVersion);
|
||||
}
|
||||
editor.apply();
|
||||
|
||||
this.cleanUp();
|
||||
this.cleanUp();
|
||||
}
|
||||
}
|
||||
|
||||
public void clearFirstTime() {
|
||||
|
@@ -147,13 +147,22 @@ public class UpdateModuleImpl {
|
||||
reactHostField.setAccessible(true);
|
||||
Object reactHost = reactHostField.get(reactDelegate);
|
||||
|
||||
Field devSupport = reactHost.getClass().getDeclaredField("mUseDevSupport");
|
||||
devSupport.setAccessible(true);
|
||||
devSupport.set(reactHost, false);
|
||||
|
||||
// Access the mReactHostDelegate field
|
||||
Field reactHostDelegateField = reactHost.getClass().getDeclaredField("mReactHostDelegate");
|
||||
reactHostDelegateField.setAccessible(true);
|
||||
Object reactHostDelegate = reactHostDelegateField.get(reactHost);
|
||||
|
||||
String bundleFieldName = "jsBundleLoader";
|
||||
if (reactHostDelegate.getClass().getCanonicalName().equals("expo.modules.ExpoReactHostFactory.ExpoReactHostDelegate")) {
|
||||
bundleFieldName = "_jsBundleLoader";
|
||||
}
|
||||
|
||||
// Modify the jsBundleLoader field
|
||||
Field jsBundleLoaderField = reactHostDelegate.getClass().getDeclaredField("jsBundleLoader");
|
||||
Field jsBundleLoaderField = reactHostDelegate.getClass().getDeclaredField(bundleFieldName);
|
||||
jsBundleLoaderField.setAccessible(true);
|
||||
jsBundleLoaderField.set(reactHostDelegate, loader);
|
||||
|
||||
|
@@ -4,6 +4,8 @@ import static androidx.core.content.FileProvider.getUriForFile;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import com.facebook.react.bridge.Promise;
|
||||
import com.facebook.react.bridge.ReactApplicationContext;
|
||||
import com.facebook.react.bridge.ReactContext;
|
||||
@@ -17,6 +19,7 @@ import java.util.Map;
|
||||
public class UpdateModule extends NativePushySpec {
|
||||
UpdateContext updateContext;
|
||||
public static ReactApplicationContext mContext;
|
||||
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||
public UpdateModule(ReactApplicationContext reactContext, UpdateContext updateContext) {
|
||||
super(reactContext);
|
||||
this.updateContext = updateContext;
|
||||
@@ -38,12 +41,22 @@ public class UpdateModule extends NativePushySpec {
|
||||
boolean isFirstTime = updateContext.isFirstTime();
|
||||
constants.put("isFirstTime", isFirstTime);
|
||||
if (isFirstTime) {
|
||||
updateContext.clearFirstTime();
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateContext.clearFirstTime();
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
String rolledBackVersion = updateContext.rolledBackVersion();
|
||||
constants.put("rolledBackVersion", rolledBackVersion);
|
||||
if (rolledBackVersion != null) {
|
||||
updateContext.clearRollbackMark();
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
updateContext.clearRollbackMark();
|
||||
}
|
||||
}, 2000);
|
||||
}
|
||||
constants.put("uuid", updateContext.getKv("uuid"));
|
||||
return constants;
|
||||
|
@@ -1 +0,0 @@
|
||||
../../../../../harmony/oh_modules/.ohpm/@rnoh+react-native-openharmony@0.72.38/oh_modules/@rnoh/react-native-openharmony
|
BIN
harmony/pushy.har
Normal file
BIN
harmony/pushy.har
Normal file
Binary file not shown.
Submodule harmony/src/main/cpp/HDiffPatch deleted from eb959d671c
Submodule harmony/src/main/cpp/lzma deleted from c80bb80bbb
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "react-native-update",
|
||||
"version": "10.28.10",
|
||||
"version": "10.29.2",
|
||||
"description": "react-native hot update",
|
||||
"main": "src/index",
|
||||
"scripts": {
|
||||
|
132
src/client.ts
132
src/client.ts
@@ -1,33 +1,33 @@
|
||||
import { CheckResult, ClientOptions, ProgressData, EventType } from './type';
|
||||
import {
|
||||
assertDev,
|
||||
DeviceEventEmitter,
|
||||
EmitterSubscription,
|
||||
Platform,
|
||||
} from 'react-native';
|
||||
import {
|
||||
PushyModule,
|
||||
buildTime,
|
||||
cInfo,
|
||||
currentVersion,
|
||||
getCurrentVersionInfo,
|
||||
isFirstTime,
|
||||
isRolledBack,
|
||||
packageVersion,
|
||||
pushyNativeEventEmitter,
|
||||
rolledBackVersion,
|
||||
setLocalHashInfo,
|
||||
} from './core';
|
||||
import { PermissionsAndroid } from './permissions';
|
||||
import { CheckResult, ClientOptions, EventType, ProgressData } from './type';
|
||||
import {
|
||||
assertWeb,
|
||||
emptyObj,
|
||||
enhancedFetch,
|
||||
joinUrls,
|
||||
log,
|
||||
noop,
|
||||
promiseAny,
|
||||
testUrls,
|
||||
} from './utils';
|
||||
import {
|
||||
EmitterSubscription,
|
||||
Platform,
|
||||
DeviceEventEmitter,
|
||||
} from 'react-native';
|
||||
import { PermissionsAndroid } from './permissions';
|
||||
import {
|
||||
PushyModule,
|
||||
buildTime,
|
||||
cInfo,
|
||||
pushyNativeEventEmitter,
|
||||
currentVersion,
|
||||
packageVersion,
|
||||
rolledBackVersion,
|
||||
setLocalHashInfo,
|
||||
isFirstTime,
|
||||
isRolledBack,
|
||||
getCurrentVersionInfo,
|
||||
} from './core';
|
||||
|
||||
const SERVER_PRESETS = {
|
||||
// cn
|
||||
@@ -170,10 +170,12 @@ export class Pushy {
|
||||
getCheckUrl = (endpoint: string = this.options.server!.main) => {
|
||||
return `${endpoint}/checkUpdate/${this.options.appKey}`;
|
||||
};
|
||||
assertDebug = () => {
|
||||
assertDebug = (matter: string) => {
|
||||
if (__DEV__ && !this.options.debug) {
|
||||
console.info(
|
||||
'You are currently in the development environment and have not enabled debug mode. The hot update check will not be performed. If you need to debug hot updates in the development environment, please set debug to true in the client.',
|
||||
`You are currently in the development environment and have not enabled debug mode.
|
||||
${matter} will not be performed.
|
||||
If you need to debug ${matter} in the development environment, please set debug to true in the client.`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -188,7 +190,7 @@ export class Pushy {
|
||||
this.report({ type: 'markSuccess' });
|
||||
};
|
||||
switchVersion = async (hash: string) => {
|
||||
if (!assertDev('switchVersion()')) {
|
||||
if (!this.assertDebug('switchVersion()')) {
|
||||
return;
|
||||
}
|
||||
if (assertHash(hash) && !sharedState.applyingUpdate) {
|
||||
@@ -199,7 +201,7 @@ export class Pushy {
|
||||
};
|
||||
|
||||
switchVersionLater = async (hash: string) => {
|
||||
if (!assertDev('switchVersionLater()')) {
|
||||
if (!this.assertDebug('switchVersionLater()')) {
|
||||
return;
|
||||
}
|
||||
if (assertHash(hash)) {
|
||||
@@ -208,7 +210,7 @@ export class Pushy {
|
||||
}
|
||||
};
|
||||
checkUpdate = async (extra?: Record<string, any>) => {
|
||||
if (!this.assertDebug()) {
|
||||
if (!this.assertDebug('checkUpdate()')) {
|
||||
return;
|
||||
}
|
||||
if (!assertWeb()) {
|
||||
@@ -261,7 +263,7 @@ export class Pushy {
|
||||
type: 'checking',
|
||||
message: this.options.appKey + ': ' + stringifyBody,
|
||||
});
|
||||
resp = await fetch(this.getCheckUrl(), fetchPayload);
|
||||
resp = await enhancedFetch(this.getCheckUrl(), fetchPayload);
|
||||
} catch (e: any) {
|
||||
this.report({
|
||||
type: 'errorChecking',
|
||||
@@ -272,7 +274,7 @@ export class Pushy {
|
||||
try {
|
||||
resp = await promiseAny(
|
||||
backupEndpoints.map(endpoint =>
|
||||
fetch(this.getCheckUrl(endpoint), fetchPayload),
|
||||
enhancedFetch(this.getCheckUrl(endpoint), fetchPayload),
|
||||
),
|
||||
);
|
||||
} catch (err: any) {
|
||||
@@ -393,7 +395,7 @@ export class Pushy {
|
||||
let lastError: any;
|
||||
let errorMessages: string[] = [];
|
||||
const diffUrl = await testUrls(joinUrls(paths, diff));
|
||||
if (diffUrl) {
|
||||
if (diffUrl && !__DEV__) {
|
||||
log('downloading diff');
|
||||
try {
|
||||
await PushyModule.downloadPatchFromPpk({
|
||||
@@ -406,60 +408,56 @@ export class Pushy {
|
||||
const errorMessage = `diff error: ${e.message}`;
|
||||
errorMessages.push(errorMessage);
|
||||
lastError = new Error(errorMessage);
|
||||
if (__DEV__) {
|
||||
succeeded = 'diff';
|
||||
} else {
|
||||
log(errorMessage);
|
||||
}
|
||||
log(errorMessage);
|
||||
}
|
||||
}
|
||||
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) {
|
||||
const errorMessage = `pdiff error: ${e.message}`;
|
||||
errorMessages.push(errorMessage);
|
||||
lastError = new Error(errorMessage);
|
||||
if (__DEV__) {
|
||||
if (!succeeded) {
|
||||
const pdiffUrl = await testUrls(joinUrls(paths, pdiff));
|
||||
if (pdiffUrl && !__DEV__) {
|
||||
log('downloading pdiff');
|
||||
try {
|
||||
await PushyModule.downloadPatchFromPackage({
|
||||
updateUrl: pdiffUrl,
|
||||
hash,
|
||||
});
|
||||
succeeded = 'pdiff';
|
||||
} else {
|
||||
} catch (e: any) {
|
||||
const errorMessage = `pdiff error: ${e.message}`;
|
||||
errorMessages.push(errorMessage);
|
||||
lastError = new Error(errorMessage);
|
||||
log(errorMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
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) {
|
||||
const errorMessage = `full patch error: ${e.message}`;
|
||||
errorMessages.push(errorMessage);
|
||||
lastError = new Error(errorMessage);
|
||||
if (__DEV__) {
|
||||
if (!succeeded) {
|
||||
const fullUrl = await testUrls(joinUrls(paths, full));
|
||||
if (fullUrl) {
|
||||
log('downloading full patch');
|
||||
try {
|
||||
await PushyModule.downloadFullUpdate({
|
||||
updateUrl: fullUrl,
|
||||
hash,
|
||||
});
|
||||
succeeded = 'full';
|
||||
} else {
|
||||
} catch (e: any) {
|
||||
const errorMessage = `full patch error: ${e.message}`;
|
||||
errorMessages.push(errorMessage);
|
||||
lastError = new Error(errorMessage);
|
||||
log(errorMessage);
|
||||
}
|
||||
} else if (__DEV__) {
|
||||
log(
|
||||
`当前是开发环境,无法执行增量式热更新,重启不会生效。
|
||||
如果需要在开发环境中测试可生效的全量热更新(但也会在再次重启后重新连接 metro),
|
||||
请打开“忽略时间戳”开关再重试。`,
|
||||
);
|
||||
succeeded = 'full';
|
||||
}
|
||||
}
|
||||
if (sharedState.progressHandlers[hash]) {
|
||||
sharedState.progressHandlers[hash].remove();
|
||||
delete sharedState.progressHandlers[hash];
|
||||
}
|
||||
if (__DEV__) {
|
||||
return hash;
|
||||
}
|
||||
if (!succeeded) {
|
||||
this.report({
|
||||
type: 'errorUpdate',
|
||||
|
@@ -251,7 +251,7 @@ export const UpdateProvider = ({
|
||||
const markSuccess = client.markSuccess;
|
||||
|
||||
useEffect(() => {
|
||||
if (!client.assertDebug()) {
|
||||
if (!client.assertDebug('checkUpdate()')) {
|
||||
return;
|
||||
}
|
||||
const { checkStrategy, dismissErrorAfter, autoMarkSuccess } = options;
|
||||
|
31
src/utils.ts
31
src/utils.ts
@@ -40,13 +40,13 @@ const ping =
|
||||
: async (url: string) => {
|
||||
let pingFinished = false;
|
||||
return Promise.race([
|
||||
fetch(url, {
|
||||
enhancedFetch(url, {
|
||||
method: 'HEAD',
|
||||
})
|
||||
.then(({ status, statusText }) => {
|
||||
.then(({ status, statusText, url: finalUrl }) => {
|
||||
pingFinished = true;
|
||||
if (status === 200) {
|
||||
return url;
|
||||
return finalUrl;
|
||||
}
|
||||
log('ping failed', url, status, statusText);
|
||||
throw new Error('Ping failed');
|
||||
@@ -69,7 +69,7 @@ const ping =
|
||||
|
||||
export function joinUrls(paths: string[], fileName?: string) {
|
||||
if (fileName) {
|
||||
return paths.map(path => 'https://' + path + '/' + fileName);
|
||||
return paths.map(path => `https://${path}/${fileName}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,12 +99,19 @@ export const assertWeb = () => {
|
||||
return true;
|
||||
};
|
||||
|
||||
export const assertDev = (matter: string) => {
|
||||
if (__DEV__) {
|
||||
console.warn(
|
||||
`${matter} is not supported in development environment; no action taken.`,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
// export const isAndroid70AndBelow = () => {
|
||||
// // android 7.0 and below devices do not support letsencrypt cert
|
||||
// // https://letsencrypt.org/2023/07/10/cross-sign-expiration/
|
||||
// return Platform.OS === 'android' && Platform.Version <= 24;
|
||||
// };
|
||||
|
||||
export const enhancedFetch = async (
|
||||
url: string,
|
||||
params: Parameters<typeof fetch>[1],
|
||||
) => {
|
||||
return fetch(url, params).catch(e => {
|
||||
log('fetch error', url, e);
|
||||
log('trying fallback to http');
|
||||
return fetch(url.replace('https', 'http'), params);
|
||||
});
|
||||
};
|
||||
|
Reference in New Issue
Block a user