Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02517a9eb0 | ||
|
|
f7309f699f | ||
|
|
a224113998 | ||
|
|
f2ede92ea1 | ||
|
|
a913e8c10e | ||
|
|
c5f458291a | ||
|
|
9699632a43 | ||
|
|
80e42f5dba | ||
|
|
9b718b8f75 | ||
|
|
99e3431844 | ||
|
|
d7b5562ab7 | ||
|
|
6a0a5b2d49 | ||
|
|
7023ff57ca | ||
|
|
17e21d79cf | ||
|
|
1cab582bd0 | ||
|
|
7da5a165fd | ||
|
|
d5194a1ad1 | ||
|
|
ebc9b97e70 | ||
|
|
40742e16d8 | ||
|
|
598ae1a506 | ||
|
|
e5424591d1 | ||
|
|
2cf7336b6a | ||
|
|
7eac48ab5d | ||
|
|
18d9b75545 | ||
|
|
e8ec85c65f | ||
|
|
48a776d506 | ||
|
|
8ad526148f | ||
|
|
cea39f1746 | ||
|
|
aa56c2ec0f | ||
|
|
00a989d567 | ||
|
|
83ca3a6c05 | ||
|
|
257f2697e0 | ||
|
|
77aa345f11 | ||
|
|
66332d007a | ||
|
|
37849b1730 | ||
|
|
c771672fcd | ||
|
|
2978454298 | ||
|
|
905413e424 | ||
|
|
ee36fd9334 |
1
.gitignore
vendored
@@ -51,3 +51,4 @@ android/bin
|
|||||||
Example/testHotUpdate/harmony
|
Example/testHotUpdate/harmony
|
||||||
Example/testHotUpdate/android/app/.cxx
|
Example/testHotUpdate/android/app/.cxx
|
||||||
Example/harmony_use_pushy/libs
|
Example/harmony_use_pushy/libs
|
||||||
|
.cursor/mcp.json
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
Example
|
Example
|
||||||
android/build
|
android/build
|
||||||
.vscode
|
.vscode
|
||||||
|
.github/
|
||||||
|
|
||||||
# OSX
|
# OSX
|
||||||
#
|
#
|
||||||
@@ -52,3 +53,5 @@ endpoints.json
|
|||||||
endpoints_cresc.json
|
endpoints_cresc.json
|
||||||
|
|
||||||
tea.yaml
|
tea.yaml
|
||||||
|
|
||||||
|
e2e/
|
||||||
@@ -1 +0,0 @@
|
|||||||
nodeLinker: node-modules
|
|
||||||
36
Example/expoUsePushy/.gitignore
vendored
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Expo
|
||||||
|
.expo/
|
||||||
|
dist/
|
||||||
|
web-build/
|
||||||
|
expo-env.d.ts
|
||||||
|
|
||||||
|
# Native
|
||||||
|
*.orig.*
|
||||||
|
*.jks
|
||||||
|
*.p8
|
||||||
|
*.p12
|
||||||
|
*.key
|
||||||
|
*.mobileprovision
|
||||||
|
|
||||||
|
# Metro
|
||||||
|
.metro-health-check*
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.*
|
||||||
|
yarn-debug.*
|
||||||
|
yarn-error.*
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env*.local
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
221
Example/expoUsePushy/App.tsx
Normal file
@@ -0,0 +1,221 @@
|
|||||||
|
/* eslint-disable react/no-unstable-nested-components */
|
||||||
|
/* eslint-disable react-native/no-inline-styles */
|
||||||
|
import React, {useState} from 'react';
|
||||||
|
import {StyleSheet, Text, View, TouchableOpacity, Image} from 'react-native';
|
||||||
|
|
||||||
|
import TestConsole from './TestConsole';
|
||||||
|
|
||||||
|
import _updateConfig from './update.json';
|
||||||
|
import {PushyProvider, Pushy, usePushy} from 'react-native-update';
|
||||||
|
const {appKey} = _updateConfig.android;
|
||||||
|
|
||||||
|
function Home() {
|
||||||
|
const {
|
||||||
|
client,
|
||||||
|
checkUpdate,
|
||||||
|
downloadUpdate,
|
||||||
|
switchVersionLater,
|
||||||
|
switchVersion,
|
||||||
|
updateInfo,
|
||||||
|
packageVersion,
|
||||||
|
currentHash,
|
||||||
|
progress: {received, total} = {},
|
||||||
|
} = usePushy();
|
||||||
|
const [useDefaultAlert, setUseDefaultAlert] = useState(false);
|
||||||
|
const [showTestConsole, setShowTestConsole] = useState(false);
|
||||||
|
const [showUpdateBanner, setShowUpdateBanner] = useState(false);
|
||||||
|
const [showUpdateSnackbar, setShowUpdateSnackbar] = useState(false);
|
||||||
|
// if (updateInfo) {
|
||||||
|
// updateInfo!.name = 'name';
|
||||||
|
// updateInfo!.update = true;
|
||||||
|
// }
|
||||||
|
const snackbarVisible =
|
||||||
|
!useDefaultAlert && showUpdateSnackbar && updateInfo?.update;
|
||||||
|
|
||||||
|
if (showTestConsole) {
|
||||||
|
return (
|
||||||
|
<TestConsole visible={true} onClose={() => setShowTestConsole(false)} />
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.welcome}>欢迎使用Pushy热更新服务</Text>
|
||||||
|
{/* <Text style={styles.welcome}>😁hdiffFromAPP更新成功!!!</Text> */}
|
||||||
|
{/* <Text style={styles.welcome}>😁hdiffFromPPk更新成功!!!</Text> */}
|
||||||
|
<View style={{flexDirection: 'row'}}>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => {
|
||||||
|
client?.setOptions({
|
||||||
|
updateStrategy: !useDefaultAlert ? null : 'alwaysAlert',
|
||||||
|
});
|
||||||
|
setShowUpdateSnackbar(useDefaultAlert);
|
||||||
|
setUseDefaultAlert(!useDefaultAlert);
|
||||||
|
}}
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: '#999',
|
||||||
|
backgroundColor: useDefaultAlert ? 'blue' : 'white',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}>
|
||||||
|
{useDefaultAlert && <Text style={{color: 'white'}}>✓</Text>}
|
||||||
|
</View>
|
||||||
|
<Text style={{marginLeft: 8}}>
|
||||||
|
{' '}
|
||||||
|
{useDefaultAlert ? '当前使用' : '当前不使用'}默认的alert更新提示
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
<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}}
|
||||||
|
onPress={() => {
|
||||||
|
setShowTestConsole(true);
|
||||||
|
}}>
|
||||||
|
<Text style={styles.instructions}>
|
||||||
|
react-native-update版本:{client?.version}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
{snackbarVisible && (
|
||||||
|
<View style={styles.overlay}>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
backgroundColor: '#333',
|
||||||
|
padding: 16,
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}>
|
||||||
|
<Text style={{color: 'white'}}>
|
||||||
|
有新版本({updateInfo.name})可用,是否更新?
|
||||||
|
</Text>
|
||||||
|
<View style={{flexDirection: 'row'}}>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => setShowUpdateSnackbar(false)}
|
||||||
|
style={{marginRight: 10}}>
|
||||||
|
<Text style={{color: 'white'}}>取消</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={async () => {
|
||||||
|
setShowUpdateSnackbar(false);
|
||||||
|
await downloadUpdate();
|
||||||
|
setShowUpdateBanner(true);
|
||||||
|
}}>
|
||||||
|
<Text style={{color: '#2196F3'}}>更新</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{showUpdateBanner && (
|
||||||
|
<View style={styles.overlay}>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
padding: 16,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: '#eee',
|
||||||
|
}}>
|
||||||
|
<View style={{flexDirection: 'row', alignItems: 'center'}}>
|
||||||
|
<Text>更新已完成,是否立即重启?</Text>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
marginTop: 10,
|
||||||
|
}}>
|
||||||
|
<TouchableOpacity
|
||||||
|
onPress={() => {
|
||||||
|
switchVersionLater();
|
||||||
|
setShowUpdateBanner(false);
|
||||||
|
}}
|
||||||
|
style={{marginRight: 20}}>
|
||||||
|
<Text style={{color: '#2196F3'}}>下次再说</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<TouchableOpacity onPress={switchVersion}>
|
||||||
|
<Text style={{color: '#2196F3'}}>立即重启</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
overlay: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
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 HomeScreen() {
|
||||||
|
return (
|
||||||
|
<PushyProvider client={pushyClient}>
|
||||||
|
<Home />
|
||||||
|
</PushyProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
274
Example/expoUsePushy/TestConsole.js
Normal file
@@ -0,0 +1,274 @@
|
|||||||
|
/* eslint-disable react-native/no-inline-styles */
|
||||||
|
/* eslint-disable react/react-in-jsx-scope */
|
||||||
|
import {useCallback, useMemo, useState} from 'react';
|
||||||
|
import {
|
||||||
|
ActivityIndicator,
|
||||||
|
TextInput,
|
||||||
|
Button,
|
||||||
|
StyleSheet,
|
||||||
|
SafeAreaView,
|
||||||
|
Text,
|
||||||
|
View,
|
||||||
|
TouchableOpacity,
|
||||||
|
} from 'react-native';
|
||||||
|
|
||||||
|
import {PushyModule} from 'react-native-update';
|
||||||
|
const Hash = '9D5CE6EBA420717BE7E7D308B11F8207681B066C951D68F3994D19828F342474';
|
||||||
|
const UUID = '00000000-0000-0000-0000-000000000000';
|
||||||
|
const DownloadUrl = 'https://localhost:3000/diff.ppk-patch';
|
||||||
|
const AppPatchDownloadUrl = 'https://github.com/bozaigao/test_pushy_server/raw/refs/heads/main/hdiff.app-patch';
|
||||||
|
const AppPatchHash = 'f5ba92c7c04250d4b8a446c8267ef459';
|
||||||
|
const PPKDownloadUrl = 'https://github.com/bozaigao/test_pushy_server/raw/refs/heads/main/hdiff.ppk-patch';
|
||||||
|
const PPKPatchHash = '6b3d26b7d868d1f67aedadb7f0b342d9';
|
||||||
|
const OriginHash = 'f5ba92c7c04250d4b8a446c8267ef459';
|
||||||
|
|
||||||
|
|
||||||
|
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}
|
||||||
|
onPress={onConfirm}>
|
||||||
|
<Text style={styles.buttonText}>确认</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default function TestConsole({visible, onClose}) {
|
||||||
|
const [text, setText] = useState('');
|
||||||
|
const [running, setRunning] = useState(false);
|
||||||
|
const [options, setOptions] = useState();
|
||||||
|
const [alertVisible, setAlertVisible] = useState(false);
|
||||||
|
const [alertMsg, setAlertMsg] = useState('');
|
||||||
|
const NativeTestMethod = useMemo(() => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
name: 'setLocalHashInfo',
|
||||||
|
invoke: () => {
|
||||||
|
setText(
|
||||||
|
`setLocalHashInfo\n${Hash}\n{\"version\":\"1.0.0\",\"size\":\"19M\"}`,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'getLocalHashInfo',
|
||||||
|
invoke: () => {
|
||||||
|
setText(`getLocalHashInfo\n${Hash}`);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'setUuid',
|
||||||
|
invoke: () => {
|
||||||
|
setText(`setUuid\n${UUID}`);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'reloadUpdate',
|
||||||
|
invoke: () => {
|
||||||
|
setText('reloadUpdate');
|
||||||
|
setOptions({hash: Hash});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'setNeedUpdateForApp',
|
||||||
|
invoke: () => {
|
||||||
|
setText('setNeedUpdate');
|
||||||
|
setOptions({hash: AppPatchHash});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'setNeedUpdateForPPK',
|
||||||
|
invoke: () => {
|
||||||
|
setText('setNeedUpdate');
|
||||||
|
setOptions({hash: PPKPatchHash});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'markSuccess',
|
||||||
|
invoke: () => {
|
||||||
|
setText('markSuccess');
|
||||||
|
setOptions(undefined);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'downloadPatchFromPpk',
|
||||||
|
invoke: () => {
|
||||||
|
setText('downloadPatchFromPpk');
|
||||||
|
setOptions({updateUrl: PPKDownloadUrl, hash: PPKPatchHash, originHash: OriginHash});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'downloadPatchFromPackage',
|
||||||
|
invoke: () => {
|
||||||
|
setText('downloadPatchFromPackage');
|
||||||
|
setOptions({updateUrl: AppPatchDownloadUrl, hash: AppPatchHash});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'downloadFullUpdate',
|
||||||
|
invoke: () => {
|
||||||
|
setText('downloadFullUpdate');
|
||||||
|
setOptions({updateUrl: DownloadUrl, hash: Hash});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'downloadAndInstallApk',
|
||||||
|
invoke: () => {
|
||||||
|
setText('downloadAndInstallApk');
|
||||||
|
setOptions({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}
|
||||||
|
onPress={() => {
|
||||||
|
NativeTestMethod[i].invoke();
|
||||||
|
}}>
|
||||||
|
<Text>{NativeTestMethod[i].name}</Text>
|
||||||
|
</TouchableOpacity>,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return <View>{views}</View>;
|
||||||
|
}, [NativeTestMethod]);
|
||||||
|
if (!visible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SafeAreaView style={{flex: 1, padding: 10}}>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
padding: 10,
|
||||||
|
}}>
|
||||||
|
<Text>调试Pushy方法(方法名,参数,值换行)</Text>
|
||||||
|
<Button title="关闭" onPress={() => onClose()} />
|
||||||
|
</View>
|
||||||
|
<TextInput
|
||||||
|
autoCorrect={false}
|
||||||
|
autoCapitalize="none"
|
||||||
|
style={{
|
||||||
|
borderWidth: StyleSheet.hairlineWidth * 4,
|
||||||
|
borderColor: 'black',
|
||||||
|
height: '30%',
|
||||||
|
marginTop: 20,
|
||||||
|
marginBottom: 20,
|
||||||
|
padding: 10,
|
||||||
|
fontSize: 20,
|
||||||
|
}}
|
||||||
|
textAlignVertical="top"
|
||||||
|
multiline={true}
|
||||||
|
value={text}
|
||||||
|
onChangeText={setText}
|
||||||
|
/>
|
||||||
|
{running && <ActivityIndicator />}
|
||||||
|
<TouchableOpacity
|
||||||
|
style={{
|
||||||
|
backgroundColor: 'rgb(0,140,237)',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingTop: 10,
|
||||||
|
paddingBottom: 10,
|
||||||
|
marginBottom: 5,
|
||||||
|
}}
|
||||||
|
testID="submit"
|
||||||
|
onPress={async () => {
|
||||||
|
setRunning(true);
|
||||||
|
try {
|
||||||
|
const inputs = text.split('\n');
|
||||||
|
const methodName = inputs[0];
|
||||||
|
let params = [];
|
||||||
|
if (inputs.length === 1) {
|
||||||
|
if (options) {
|
||||||
|
await PushyModule[methodName](options);
|
||||||
|
} else {
|
||||||
|
await PushyModule[methodName]();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (inputs.length === 2) {
|
||||||
|
params = [inputs[1]];
|
||||||
|
} else {
|
||||||
|
params = [inputs[1], inputs[2]];
|
||||||
|
console.log({inputs, params});
|
||||||
|
}
|
||||||
|
await PushyModule[methodName](...params);
|
||||||
|
}
|
||||||
|
setAlertVisible(true);
|
||||||
|
setAlertMsg('done');
|
||||||
|
} catch (e) {
|
||||||
|
setAlertVisible(true);
|
||||||
|
setAlertMsg(e.message);
|
||||||
|
}
|
||||||
|
setRunning(false);
|
||||||
|
}}>
|
||||||
|
<Text style={{color: 'white'}}>执行</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
<Button title="重置" onPress={() => setText('')} />
|
||||||
|
{renderTestView()}
|
||||||
|
<CustomDialog
|
||||||
|
title={alertMsg}
|
||||||
|
visible={alertVisible}
|
||||||
|
onConfirm={() => {
|
||||||
|
setAlertVisible(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</SafeAreaView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
overlay: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.5)',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
dialog: {
|
||||||
|
backgroundColor: 'white',
|
||||||
|
borderRadius: 10,
|
||||||
|
padding: 20,
|
||||||
|
width: '80%',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
marginBottom: 20,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
backgroundColor: '#2196F3',
|
||||||
|
borderRadius: 5,
|
||||||
|
paddingVertical: 10,
|
||||||
|
paddingHorizontal: 20,
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
color: 'white',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
});
|
||||||
29
Example/expoUsePushy/app.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"expo": {
|
||||||
|
"name": "expoUsePushy",
|
||||||
|
"slug": "expoUsePushy",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"orientation": "portrait",
|
||||||
|
"icon": "./assets/icon.png",
|
||||||
|
"userInterfaceStyle": "light",
|
||||||
|
"newArchEnabled": true,
|
||||||
|
"splash": {
|
||||||
|
"image": "./assets/splash-icon.png",
|
||||||
|
"resizeMode": "contain",
|
||||||
|
"backgroundColor": "#ffffff"
|
||||||
|
},
|
||||||
|
"ios": {
|
||||||
|
"supportsTablet": true,
|
||||||
|
"bundleIdentifier": "com.anonymous.expoUsePushy"
|
||||||
|
},
|
||||||
|
"android": {
|
||||||
|
"adaptiveIcon": {
|
||||||
|
"foregroundImage": "./assets/adaptive-icon.png",
|
||||||
|
"backgroundColor": "#ffffff"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"web": {
|
||||||
|
"favicon": "./assets/favicon.png"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Example/expoUsePushy/assets/adaptive-icon.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
Example/expoUsePushy/assets/favicon.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
Example/expoUsePushy/assets/icon.png
Normal file
|
After Width: | Height: | Size: 22 KiB |
BIN
Example/expoUsePushy/assets/shezhi.png
Normal file
|
After Width: | Height: | Size: 696 B |
BIN
Example/expoUsePushy/assets/shezhi@2x.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
Example/expoUsePushy/assets/shezhi@3x.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
Example/expoUsePushy/assets/splash-icon.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
1765
Example/expoUsePushy/bun.lock
Normal file
8
Example/expoUsePushy/index.js
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
import { registerRootComponent } from 'expo';
|
||||||
|
|
||||||
|
import App from './App';
|
||||||
|
|
||||||
|
// registerRootComponent calls AppRegistry.registerComponent('main', () => App);
|
||||||
|
// It also ensures that whether you load the app in Expo Go or in a native build,
|
||||||
|
// the environment is set up appropriately
|
||||||
|
registerRootComponent(App);
|
||||||
24
Example/expoUsePushy/package.json
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"name": "expousepushy",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "expo start",
|
||||||
|
"android": "expo run:android",
|
||||||
|
"ios": "expo run:ios",
|
||||||
|
"web": "expo start --web"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"expo": "~52.0.46",
|
||||||
|
"expo-status-bar": "~2.0.1",
|
||||||
|
"react": "18.3.1",
|
||||||
|
"react-native": "0.76.9",
|
||||||
|
"react-native-update": "^10.28.7"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.25.2",
|
||||||
|
"@types/react": "~18.3.12",
|
||||||
|
"typescript": "^5.3.3"
|
||||||
|
},
|
||||||
|
"private": true
|
||||||
|
}
|
||||||
6
Example/expoUsePushy/tsconfig.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"extends": "expo/tsconfig.base",
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true
|
||||||
|
}
|
||||||
|
}
|
||||||
14
Example/expoUsePushy/update.json
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"ios": {
|
||||||
|
"appId": 29439,
|
||||||
|
"appKey": "jNA71vpFHTDpEqeZd9yx87zj"
|
||||||
|
},
|
||||||
|
"android": {
|
||||||
|
"appId": 29413,
|
||||||
|
"appKey": "vdZWPXU6eyaPE6Avk96-YvwK"
|
||||||
|
},
|
||||||
|
"harmony": {
|
||||||
|
"appId": 29140,
|
||||||
|
"appKey": "JLklGflGIRbY-cMebjQwm1J1"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"": {
|
"": {
|
||||||
"name": "harmony_use_pushy",
|
"name": "harmony_use_pushy",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-native-oh/react-native-harmony": "^0.72.43",
|
"@react-native-oh/react-native-harmony": "^0.72.59",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-native": "0.72.5",
|
"react-native": "0.72.5",
|
||||||
"react-native-update": "^10.26.4",
|
"react-native-update": "^10.26.4",
|
||||||
|
|||||||
@@ -36,10 +36,6 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'pushy',
|
|
||||||
srcPath: '../node_modules/react-native-update/harmony',
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
"license": "",
|
"license": "",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rnoh/react-native-openharmony": "0.72.38",
|
"@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",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { FileJSBundleProvider } from 'pushy/src/main/ets/FileJSBundleProvider';
|
import { PushyFileJSBundleProvider } from 'pushy/src/main/ets/PushyFileJSBundleProvider';
|
||||||
import { ComponentBuilderContext, RNOHCoreContext,RNAbility,
|
import { ComponentBuilderContext, RNOHCoreContext,RNAbility,
|
||||||
MetroJSBundleProvider } from '@rnoh/react-native-openharmony';
|
MetroJSBundleProvider } from '@rnoh/react-native-openharmony';
|
||||||
import {
|
import {
|
||||||
@@ -65,7 +65,7 @@ struct Index {
|
|||||||
// local debug mode
|
// local debug mode
|
||||||
new MetroJSBundleProvider(),
|
new MetroJSBundleProvider(),
|
||||||
// release mode
|
// release mode
|
||||||
new FileJSBundleProvider(this.rnohCoreContext.uiAbilityContext),
|
new PushyFileJSBundleProvider(this.rnohCoreContext.uiAbilityContext),
|
||||||
new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'bundle.harmony.js')
|
new ResourceJSBundleProvider(this.rnohCoreContext.uiAbilityContext.resourceManager, 'bundle.harmony.js')
|
||||||
]),
|
]),
|
||||||
this.rnohCoreContext.logger),
|
this.rnohCoreContext.logger),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
{
|
{
|
||||||
"pushy_build_time": "2025-04-12T11:12:43.423Z",
|
"pushy_build_time": "2025-04-30T02:46:33.340Z",
|
||||||
"versionName": "1.0.0"
|
"versionName": "1.0.0"
|
||||||
}
|
}
|
||||||
@@ -15,10 +15,10 @@
|
|||||||
"hash": "pushy hash /Users/yanbo.he/Desktop/HarmonyOS/react-native-pushy/Example/harmony_use_pushy/.pushy/output/harmony.1735048297258.ppk"
|
"hash": "pushy hash /Users/yanbo.he/Desktop/HarmonyOS/react-native-pushy/Example/harmony_use_pushy/.pushy/output/harmony.1735048297258.ppk"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@react-native-oh/react-native-harmony": "^0.72.43",
|
"@react-native-oh/react-native-harmony": "^0.72.59",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-native": "0.72.5",
|
"react-native": "0.72.5",
|
||||||
"react-native-update": "^10.26.4"
|
"react-native-update": "latest"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.0",
|
"@babel/core": "^7.20.0",
|
||||||
|
|||||||
4
Example/testHotUpdate/.gitignore
vendored
@@ -62,3 +62,7 @@ buck-out/
|
|||||||
# Ruby / CocoaPods
|
# Ruby / CocoaPods
|
||||||
/ios/Pods/
|
/ios/Pods/
|
||||||
/vendor/bundle/
|
/vendor/bundle/
|
||||||
|
|
||||||
|
# react-native-update
|
||||||
|
.update
|
||||||
|
.pushy
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
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
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
@@ -2,12 +2,14 @@
|
|||||||
#import "RCTPushy.h"
|
#import "RCTPushy.h"
|
||||||
|
|
||||||
#import <React/RCTBundleURLProvider.h>
|
#import <React/RCTBundleURLProvider.h>
|
||||||
|
#import <ReactAppDependencyProvider/RCTAppDependencyProvider.h>
|
||||||
|
|
||||||
@implementation AppDelegate
|
@implementation AppDelegate
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
|
||||||
{
|
{
|
||||||
self.moduleName = @"AwesomeProject";
|
self.moduleName = @"AwesomeProject";
|
||||||
|
self.dependencyProvider = [RCTAppDependencyProvider new];
|
||||||
// You can add your custom initial props in the dictionary below.
|
// You can add your custom initial props in the dictionary below.
|
||||||
// They will be passed down to the ViewController used by React Native.
|
// They will be passed down to the ViewController used by React Native.
|
||||||
self.initialProps = @{};
|
self.initialProps = @{};
|
||||||
|
|||||||
@@ -14,36 +14,36 @@
|
|||||||
"dev:harmony": "react-native bundle-harmony --dev"
|
"dev:harmony": "react-native bundle-harmony --dev"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"form-data": "^4.0.2",
|
"form-data": "^4.0.3",
|
||||||
"patch-package": "^8.0.0",
|
"patch-package": "^8.0.0",
|
||||||
"react": "19.0.0",
|
"react": "19.0.0",
|
||||||
"react-native": "0.78.0",
|
"react-native": "0.79.2",
|
||||||
"react-native-camera-kit": "^14.2.0",
|
"react-native-camera-kit": "^15.1.0",
|
||||||
"react-native-paper": "^5.13.1",
|
"react-native-paper": "^5.14.5",
|
||||||
"react-native-safe-area-context": "^5.3.0",
|
"react-native-safe-area-context": "^5.5.0",
|
||||||
"react-native-svg": "^15.11.2",
|
"react-native-svg": "^15.12.0",
|
||||||
"react-native-update": "^10.26.4",
|
"react-native-update": "^10.29.4",
|
||||||
"react-native-vector-icons": "^10.2.0"
|
"react-native-vector-icons": "^10.2.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.26.0",
|
"@babel/core": "^7.27.3",
|
||||||
"@babel/preset-env": "^7.26.0",
|
"@babel/preset-env": "^7.27.2",
|
||||||
"@babel/runtime": "^7.26.0",
|
"@babel/runtime": "^7.27.3",
|
||||||
"@react-native-community/cli": "15.0.1",
|
"@react-native-community/cli": "18.0.0",
|
||||||
"@react-native-community/cli-platform-android": "15.0.1",
|
"@react-native-community/cli-platform-android": "18.0.0",
|
||||||
"@react-native-community/cli-platform-ios": "15.0.1",
|
"@react-native-community/cli-platform-ios": "18.0.0",
|
||||||
"@react-native/babel-preset": "0.78.0",
|
"@react-native/babel-preset": "0.79.2",
|
||||||
"@react-native/eslint-config": "0.78.0",
|
"@react-native/eslint-config": "0.79.2",
|
||||||
"@react-native/metro-config": "0.78.0",
|
"@react-native/metro-config": "0.79.2",
|
||||||
"@react-native/typescript-config": "0.78.0",
|
"@react-native/typescript-config": "0.79.2",
|
||||||
"@types/react": "^19.0.0",
|
"@types/react": "^19.0.0",
|
||||||
"@types/react-test-renderer": "^19.0.0",
|
"@types/react-test-renderer": "^19.0.0",
|
||||||
"detox": "^20.32.0",
|
"detox": "^20.39.0",
|
||||||
"eslint": "^8.19.0",
|
"eslint": "^8.19.0",
|
||||||
"jest": "^29.6.3",
|
"jest": "^29.6.3",
|
||||||
"prettier": "2.8.8",
|
"prettier": "2.8.8",
|
||||||
"react-test-renderer": "19.0.0",
|
"react-test-renderer": "19.0.0",
|
||||||
"typescript": "5.8.2"
|
"typescript": "5.8.3"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=18"
|
"node": ">=18"
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ function App() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<Text style={styles.welcome}>欢迎使用Pushy热更新服务</Text>
|
<Text style={styles.welcome}>欢迎xxx使用Pushy热更新服务</Text>
|
||||||
<View style={{flexDirection: 'row'}}>
|
<View style={{flexDirection: 'row'}}>
|
||||||
<Text>
|
<Text>
|
||||||
{useDefaultAlert ? '当前使用' : '当前不使用'}默认的alert更新提示
|
{useDefaultAlert ? '当前使用' : '当前不使用'}默认的alert更新提示
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"ios": {
|
"ios": {
|
||||||
"appId": 24794,
|
"appId": 28943,
|
||||||
"appKey": "SqShg4Klnj2hG6LAFMW2PdcgSSuniz0T"
|
"appKey": "d-OmPxIBivPrDfKhLHjxN-HS"
|
||||||
},
|
},
|
||||||
"android": {
|
"android": {
|
||||||
"appId": 27509,
|
"appId": 27509,
|
||||||
|
|||||||
@@ -22,21 +22,49 @@ def supportsNamespace() {
|
|||||||
return major >= 8
|
return major >= 8
|
||||||
}
|
}
|
||||||
|
|
||||||
def isExpoProject() {
|
def checkProjectInfo() {
|
||||||
def hasExpoModulesCore = rootProject.subprojects.any { it.name == 'expo-modules-core' }
|
def hasExpoModulesCore = rootProject.subprojects.any { it.name == 'expo-modules-core' }
|
||||||
|
|
||||||
def packageJsonFile = new File(rootProject.projectDir.parentFile, 'package.json')
|
def packageJsonFile = new File(rootProject.projectDir.parentFile, 'package.json')
|
||||||
|
|
||||||
def hasExpoDependency = false
|
def hasExpoDependency = false
|
||||||
|
def projectVersion = '1.0.0' // Default version
|
||||||
|
|
||||||
if (packageJsonFile.exists()) {
|
if (packageJsonFile.exists()) {
|
||||||
def packageJson = new groovy.json.JsonSlurper().parseText(packageJsonFile.text)
|
def packageJson = new groovy.json.JsonSlurper().parseText(packageJsonFile.text)
|
||||||
hasExpoDependency = (packageJson.dependencies?.expo != null) ||
|
projectVersion = packageJson.version ?: '1.0.0' // Get project version
|
||||||
(packageJson.devDependencies?.expo != null)
|
|
||||||
|
// Check for expo dependency and version >= 50
|
||||||
|
String expoVersionString = packageJson.dependencies?.expo ?: packageJson.devDependencies?.expo
|
||||||
|
boolean expoVersionIsHighEnough = false
|
||||||
|
if (expoVersionString) {
|
||||||
|
try {
|
||||||
|
// Extract the first number sequence as the major version
|
||||||
|
def matcher = (expoVersionString =~ /(\d+)/)
|
||||||
|
if (matcher.find()) {
|
||||||
|
int majorVersion = matcher[0][0].toInteger()
|
||||||
|
if (majorVersion >= 50) {
|
||||||
|
expoVersionIsHighEnough = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
// Handle error if version parsing fails, maybe log a warning
|
||||||
|
println "Warning: Could not parse Expo version string: ${expoVersionString}"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hasExpoDependency = expoVersionIsHighEnough // Update based on version check
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasExpoModulesCore || hasExpoDependency
|
def isExpo = hasExpoModulesCore && hasExpoDependency
|
||||||
|
|
||||||
|
// Return a map containing both pieces of information
|
||||||
|
return [isExpo: isExpo, version: projectVersion]
|
||||||
}
|
}
|
||||||
|
|
||||||
def expoProject = isExpoProject()
|
// Get project info map
|
||||||
|
def projectInfo = checkProjectInfo()
|
||||||
|
// Extract info into variables
|
||||||
|
def projectVersion = projectInfo.version
|
||||||
|
def expoProject = projectInfo.isExpo
|
||||||
|
|
||||||
apply plugin: 'com.android.library'
|
apply plugin: 'com.android.library'
|
||||||
if (isNewArchitectureEnabled()) {
|
if (isNewArchitectureEnabled()) {
|
||||||
@@ -45,16 +73,16 @@ if (isNewArchitectureEnabled()) {
|
|||||||
|
|
||||||
if (expoProject) {
|
if (expoProject) {
|
||||||
group = 'expo.modules.pushy'
|
group = 'expo.modules.pushy'
|
||||||
version = '1.0.0'
|
version = projectVersion
|
||||||
|
|
||||||
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
|
||||||
apply from: expoModulesCorePlugin
|
apply from: expoModulesCorePlugin
|
||||||
applyKotlinExpoModulesCorePlugin()
|
applyKotlinExpoModulesCorePlugin()
|
||||||
|
// useExpoPublishing()
|
||||||
useCoreDependencies()
|
useCoreDependencies()
|
||||||
useExpoPublishing()
|
|
||||||
} else {
|
} else {
|
||||||
group = 'cn.reactnative.modules.update'
|
group = 'cn.reactnative.modules.update'
|
||||||
version = '1.0.0'
|
version = projectVersion
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -120,7 +148,6 @@ repositories {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation 'com.facebook.react:react-native:+'
|
implementation 'com.facebook.react:react-native:+'
|
||||||
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0'
|
implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0'
|
||||||
implementation 'com.jakewharton:process-phoenix:3.0.0'
|
|
||||||
}
|
}
|
||||||
if (isNewArchitectureEnabled()) {
|
if (isNewArchitectureEnabled()) {
|
||||||
react {
|
react {
|
||||||
|
|||||||
34
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.DownloadTask { *; }
|
||||||
-keepnames class cn.reactnative.modules.update.UpdateModuleImpl { *; }
|
-keepnames class cn.reactnative.modules.update.UpdateModuleImpl { *; }
|
||||||
|
-keepnames class cn.reactnative.modules.update.** { *; }
|
||||||
|
|
||||||
|
# Keep React Native classes
|
||||||
-keepnames class com.facebook.react.ReactInstanceManager { *; }
|
-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);
|
||||||
|
}
|
||||||
@@ -120,14 +120,13 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onProgressUpdate(long[]... values) {
|
protected void onProgressUpdate(final long[]... values) {
|
||||||
super.onProgressUpdate(values);
|
super.onProgressUpdate(values);
|
||||||
WritableMap params = Arguments.createMap();
|
WritableMap params = Arguments.createMap();
|
||||||
params.putDouble("received", (values[0][0]));
|
params.putDouble("received", (values[0][0]));
|
||||||
params.putDouble("total", (values[0][1]));
|
params.putDouble("total", (values[0][1]));
|
||||||
params.putString("hash", this.hash);
|
params.putString("hash", this.hash);
|
||||||
sendEvent("RCTPushyDownloadProgress", params);
|
sendEvent("RCTPushyDownloadProgress", params);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] buffer = new byte[1024*4];
|
byte[] buffer = new byte[1024*4];
|
||||||
@@ -452,7 +451,7 @@ class DownloadTask extends AsyncTask<DownloadTaskParams, long[], Void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(DownloadTaskParams... params) {
|
protected Void doInBackground(final DownloadTaskParams... params) {
|
||||||
int taskType = params[0].type;
|
int taskType = params[0].type;
|
||||||
try {
|
try {
|
||||||
switch (taskType) {
|
switch (taskType) {
|
||||||
|
|||||||
@@ -36,10 +36,18 @@ public class UpdateContext {
|
|||||||
this.sp = context.getSharedPreferences("update", Context.MODE_PRIVATE);
|
this.sp = context.getSharedPreferences("update", Context.MODE_PRIVATE);
|
||||||
|
|
||||||
String packageVersion = getPackageVersion();
|
String packageVersion = getPackageVersion();
|
||||||
if (!packageVersion.equals(this.sp.getString("packageVersion", null))) {
|
String buildTime = getBuildTime();
|
||||||
|
String storedPackageVersion = this.sp.getString("packageVersion", null);
|
||||||
|
String storedBuildTime = this.sp.getString("buildTime", null);
|
||||||
|
|
||||||
|
boolean packageVersionChanged = !packageVersion.equals(storedPackageVersion);
|
||||||
|
boolean buildTimeChanged = !buildTime.equals(storedBuildTime);
|
||||||
|
|
||||||
|
if (packageVersionChanged || buildTimeChanged) {
|
||||||
SharedPreferences.Editor editor = sp.edit();
|
SharedPreferences.Editor editor = sp.edit();
|
||||||
editor.clear();
|
editor.clear();
|
||||||
editor.putString("packageVersion", packageVersion);
|
editor.putString("packageVersion", packageVersion);
|
||||||
|
editor.putString("buildTime", buildTime);
|
||||||
editor.apply();
|
editor.apply();
|
||||||
|
|
||||||
this.cleanUp();
|
this.cleanUp();
|
||||||
@@ -169,6 +177,7 @@ public class UpdateContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void markSuccess() {
|
public void markSuccess() {
|
||||||
|
if (!BuildConfig.DEBUG) {
|
||||||
SharedPreferences.Editor editor = sp.edit();
|
SharedPreferences.Editor editor = sp.edit();
|
||||||
editor.putBoolean("firstTimeOk", true);
|
editor.putBoolean("firstTimeOk", true);
|
||||||
String lastVersion = sp.getString("lastVersion", null);
|
String lastVersion = sp.getString("lastVersion", null);
|
||||||
@@ -181,6 +190,7 @@ public class UpdateContext {
|
|||||||
|
|
||||||
this.cleanUp();
|
this.cleanUp();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void clearFirstTime() {
|
public void clearFirstTime() {
|
||||||
SharedPreferences.Editor editor = sp.edit();
|
SharedPreferences.Editor editor = sp.edit();
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ public class UpdateModuleImpl {
|
|||||||
|
|
||||||
public static final String NAME = "Pushy";
|
public static final String NAME = "Pushy";
|
||||||
|
|
||||||
public static void downloadFullUpdate(UpdateContext updateContext, ReadableMap options, Promise promise) {
|
public static void downloadFullUpdate(UpdateContext updateContext, final ReadableMap options, final Promise promise) {
|
||||||
String url = options.getString("updateUrl");
|
String url = options.getString("updateUrl");
|
||||||
String hash = options.getString("hash");
|
String hash = options.getString("hash");
|
||||||
updateContext.downloadFullUpdate(url, hash, new UpdateContext.DownloadFileListener() {
|
updateContext.downloadFullUpdate(url, hash, new UpdateContext.DownloadFileListener() {
|
||||||
@@ -40,7 +40,7 @@ public class UpdateModuleImpl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void downloadAndInstallApk(UpdateContext updateContext, ReadableMap options, Promise promise) {
|
public static void downloadAndInstallApk(UpdateContext updateContext, final ReadableMap options, final Promise promise) {
|
||||||
String url = options.getString("url");
|
String url = options.getString("url");
|
||||||
String hash = options.getString("hash");
|
String hash = options.getString("hash");
|
||||||
String target = options.getString("target");
|
String target = options.getString("target");
|
||||||
@@ -63,7 +63,7 @@ public class UpdateModuleImpl {
|
|||||||
UpdateModule.installApk(toInstall);
|
UpdateModule.installApk(toInstall);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void downloadPatchFromPackage(UpdateContext updateContext, ReadableMap options, Promise promise) {
|
public static void downloadPatchFromPackage(UpdateContext updateContext, final ReadableMap options, final Promise promise) {
|
||||||
String url = options.getString("updateUrl");
|
String url = options.getString("updateUrl");
|
||||||
String hash = options.getString("hash");
|
String hash = options.getString("hash");
|
||||||
updateContext.downloadPatchFromApk(url, hash, new UpdateContext.DownloadFileListener() {
|
updateContext.downloadPatchFromApk(url, hash, new UpdateContext.DownloadFileListener() {
|
||||||
@@ -79,7 +79,7 @@ public class UpdateModuleImpl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void downloadPatchFromPpk(UpdateContext updateContext, ReadableMap options, Promise promise) {
|
public static void downloadPatchFromPpk(UpdateContext updateContext, final ReadableMap options, final Promise promise) {
|
||||||
try {
|
try {
|
||||||
String url = options.getString("updateUrl");
|
String url = options.getString("updateUrl");
|
||||||
String hash = options.getString("hash");
|
String hash = options.getString("hash");
|
||||||
@@ -102,7 +102,7 @@ public class UpdateModuleImpl {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void reloadUpdate(UpdateContext updateContext, ReactApplicationContext mContext, ReadableMap options, Promise promise) {
|
public static void reloadUpdate(final UpdateContext updateContext,final ReactApplicationContext mContext, final ReadableMap options, final Promise promise) {
|
||||||
final String hash = options.getString("hash");
|
final String hash = options.getString("hash");
|
||||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@@ -147,13 +147,22 @@ public class UpdateModuleImpl {
|
|||||||
reactHostField.setAccessible(true);
|
reactHostField.setAccessible(true);
|
||||||
Object reactHost = reactHostField.get(reactDelegate);
|
Object reactHost = reactHostField.get(reactDelegate);
|
||||||
|
|
||||||
|
Field devSupport = reactHost.getClass().getDeclaredField("mUseDevSupport");
|
||||||
|
devSupport.setAccessible(true);
|
||||||
|
devSupport.set(reactHost, false);
|
||||||
|
|
||||||
// Access the mReactHostDelegate field
|
// Access the mReactHostDelegate field
|
||||||
Field reactHostDelegateField = reactHost.getClass().getDeclaredField("mReactHostDelegate");
|
Field reactHostDelegateField = reactHost.getClass().getDeclaredField("mReactHostDelegate");
|
||||||
reactHostDelegateField.setAccessible(true);
|
reactHostDelegateField.setAccessible(true);
|
||||||
Object reactHostDelegate = reactHostDelegateField.get(reactHost);
|
Object reactHostDelegate = reactHostDelegateField.get(reactHost);
|
||||||
|
|
||||||
|
String bundleFieldName = "jsBundleLoader";
|
||||||
|
if (reactHostDelegate.getClass().getCanonicalName().equals("expo.modules.ExpoReactHostFactory.ExpoReactHostDelegate")) {
|
||||||
|
bundleFieldName = "_jsBundleLoader";
|
||||||
|
}
|
||||||
|
|
||||||
// Modify the jsBundleLoader field
|
// Modify the jsBundleLoader field
|
||||||
Field jsBundleLoaderField = reactHostDelegate.getClass().getDeclaredField("jsBundleLoader");
|
Field jsBundleLoaderField = reactHostDelegate.getClass().getDeclaredField(bundleFieldName);
|
||||||
jsBundleLoaderField.setAccessible(true);
|
jsBundleLoaderField.setAccessible(true);
|
||||||
jsBundleLoaderField.set(reactHostDelegate, loader);
|
jsBundleLoaderField.set(reactHostDelegate, loader);
|
||||||
|
|
||||||
@@ -176,7 +185,7 @@ public class UpdateModuleImpl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void restartApp(final ReactApplicationContext mContext, Promise promise) {
|
public static void restartApp(final ReactApplicationContext mContext, final Promise promise) {
|
||||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -206,7 +215,7 @@ public class UpdateModuleImpl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setNeedUpdate(UpdateContext updateContext, ReadableMap options, Promise promise) {
|
public static void setNeedUpdate(final UpdateContext updateContext, final ReadableMap options, final Promise promise) {
|
||||||
final String hash = options.getString("hash");
|
final String hash = options.getString("hash");
|
||||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@@ -222,7 +231,7 @@ public class UpdateModuleImpl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void markSuccess(UpdateContext updateContext, Promise promise) {
|
public static void markSuccess(final UpdateContext updateContext, final Promise promise) {
|
||||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -232,7 +241,7 @@ public class UpdateModuleImpl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setUuid(UpdateContext updateContext, String uuid, Promise promise) {
|
public static void setUuid(final UpdateContext updateContext, final String uuid, final Promise promise) {
|
||||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -253,7 +262,7 @@ public class UpdateModuleImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void setLocalHashInfo(UpdateContext updateContext, final String hash, final String info, Promise promise) {
|
public static void setLocalHashInfo(final UpdateContext updateContext, final String hash, final String info, final Promise promise) {
|
||||||
UiThreadUtil.runOnUiThread(new Runnable() {
|
UiThreadUtil.runOnUiThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
@@ -268,7 +277,7 @@ public class UpdateModuleImpl {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void getLocalHashInfo(UpdateContext updateContext, final String hash, Promise promise) {
|
public static void getLocalHashInfo(UpdateContext updateContext, final String hash, final Promise promise) {
|
||||||
String value = updateContext.getKv("hash_" + hash);
|
String value = updateContext.getKv("hash_" + hash);
|
||||||
if (check(value)) {
|
if (check(value)) {
|
||||||
promise.resolve(value);
|
promise.resolve(value);
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ import static androidx.core.content.FileProvider.getUriForFile;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import com.facebook.react.bridge.Promise;
|
import com.facebook.react.bridge.Promise;
|
||||||
import com.facebook.react.bridge.ReactApplicationContext;
|
import com.facebook.react.bridge.ReactApplicationContext;
|
||||||
import com.facebook.react.bridge.ReactContext;
|
import com.facebook.react.bridge.ReactContext;
|
||||||
@@ -17,6 +19,7 @@ import java.util.Map;
|
|||||||
public class UpdateModule extends NativePushySpec {
|
public class UpdateModule extends NativePushySpec {
|
||||||
UpdateContext updateContext;
|
UpdateContext updateContext;
|
||||||
public static ReactApplicationContext mContext;
|
public static ReactApplicationContext mContext;
|
||||||
|
private final Handler handler = new Handler(Looper.getMainLooper());
|
||||||
public UpdateModule(ReactApplicationContext reactContext, UpdateContext updateContext) {
|
public UpdateModule(ReactApplicationContext reactContext, UpdateContext updateContext) {
|
||||||
super(reactContext);
|
super(reactContext);
|
||||||
this.updateContext = updateContext;
|
this.updateContext = updateContext;
|
||||||
@@ -38,13 +41,23 @@ public class UpdateModule extends NativePushySpec {
|
|||||||
boolean isFirstTime = updateContext.isFirstTime();
|
boolean isFirstTime = updateContext.isFirstTime();
|
||||||
constants.put("isFirstTime", isFirstTime);
|
constants.put("isFirstTime", isFirstTime);
|
||||||
if (isFirstTime) {
|
if (isFirstTime) {
|
||||||
|
handler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
updateContext.clearFirstTime();
|
updateContext.clearFirstTime();
|
||||||
}
|
}
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
String rolledBackVersion = updateContext.rolledBackVersion();
|
String rolledBackVersion = updateContext.rolledBackVersion();
|
||||||
constants.put("rolledBackVersion", rolledBackVersion);
|
constants.put("rolledBackVersion", rolledBackVersion);
|
||||||
if (rolledBackVersion != null) {
|
if (rolledBackVersion != null) {
|
||||||
|
handler.postDelayed(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
updateContext.clearRollbackMark();
|
updateContext.clearRollbackMark();
|
||||||
}
|
}
|
||||||
|
}, 2000);
|
||||||
|
}
|
||||||
constants.put("uuid", updateContext.getKv("uuid"));
|
constants.put("uuid", updateContext.getKv("uuid"));
|
||||||
return constants;
|
return constants;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import com.facebook.react.bridge.UiThreadUtil;
|
|||||||
import com.facebook.react.bridge.JSBundleLoader;
|
import com.facebook.react.bridge.JSBundleLoader;
|
||||||
import com.facebook.react.bridge.WritableMap;
|
import com.facebook.react.bridge.WritableMap;
|
||||||
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
import com.facebook.react.modules.core.DeviceEventManagerModule;
|
||||||
import com.jakewharton.processphoenix.ProcessPhoenix;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
["https://pushy-koa-qgbgqmcpis.cn-beijing.fcapp.run", "https://p.reactnative.cn/api"]
|
["https://p.reactnative.cn/api"]
|
||||||
|
|||||||
@@ -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
@@ -73,7 +73,7 @@ static jsi::Value _hostFunction_PushyTurboModule_downloadPatchFromPpk(
|
|||||||
const jsi::Value* args,
|
const jsi::Value* args,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"downloadPatchFromPpk", args, count));
|
return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).callAsync(rt,"downloadPatchFromPpk", args, count));
|
||||||
}
|
}
|
||||||
|
|
||||||
static jsi::Value _hostFunction_PushyTurboModule_downloadPatchFromPackage(
|
static jsi::Value _hostFunction_PushyTurboModule_downloadPatchFromPackage(
|
||||||
@@ -82,7 +82,7 @@ static jsi::Value _hostFunction_PushyTurboModule_downloadPatchFromPackage(
|
|||||||
const jsi::Value* args,
|
const jsi::Value* args,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"downloadPatchFromPackage", args, count));
|
return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).callAsync(rt,"downloadPatchFromPackage", args, count));
|
||||||
}
|
}
|
||||||
|
|
||||||
static jsi::Value _hostFunction_PushyTurboModule_downloadFullUpdate(
|
static jsi::Value _hostFunction_PushyTurboModule_downloadFullUpdate(
|
||||||
@@ -91,7 +91,7 @@ static jsi::Value _hostFunction_PushyTurboModule_downloadFullUpdate(
|
|||||||
const jsi::Value* args,
|
const jsi::Value* args,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"downloadFullUpdate", args, count));
|
return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).callAsync(rt,"downloadFullUpdate", args, count));
|
||||||
}
|
}
|
||||||
|
|
||||||
static jsi::Value _hostFunction_PushyTurboModule_downloadAndInstallApk(
|
static jsi::Value _hostFunction_PushyTurboModule_downloadAndInstallApk(
|
||||||
@@ -100,7 +100,7 @@ static jsi::Value _hostFunction_PushyTurboModule_downloadAndInstallApk(
|
|||||||
const jsi::Value* args,
|
const jsi::Value* args,
|
||||||
size_t count)
|
size_t count)
|
||||||
{
|
{
|
||||||
return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).call(rt,"downloadAndInstallApk", args, count));
|
return jsi::Value(static_cast<ArkTSTurboModule &> (turboModule).callAsync(rt,"downloadAndInstallApk", args, count));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -3,7 +3,7 @@ import fileIo from '@ohos.file.fs';
|
|||||||
import common from '@ohos.app.ability.common';
|
import common from '@ohos.app.ability.common';
|
||||||
import { UpdateContext } from './UpdateContext';
|
import { UpdateContext } from './UpdateContext';
|
||||||
|
|
||||||
export class FileJSBundleProvider extends JSBundleProvider {
|
export class PushyFileJSBundleProvider extends JSBundleProvider {
|
||||||
private updateContext: UpdateContext;
|
private updateContext: UpdateContext;
|
||||||
private filePath: string = ''
|
private filePath: string = ''
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@ export class FileJSBundleProvider extends JSBundleProvider {
|
|||||||
this.updateContext = new UpdateContext(context);
|
this.updateContext = new UpdateContext(context);
|
||||||
}
|
}
|
||||||
getURL(): string {
|
getURL(): string {
|
||||||
return this.updateContext.getBundleUrl();
|
return this.updateContext.getBundleUrl().substring(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
async getBundle(): Promise<ArrayBuffer> {
|
async getBundle(): Promise<ArrayBuffer> {
|
||||||
2
ios/ImportReact.h
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
@import React;
|
||||||
|
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
static NSString *const keyPushyInfo = @"REACTNATIVECN_PUSHY_INFO_KEY";
|
static NSString *const keyPushyInfo = @"REACTNATIVECN_PUSHY_INFO_KEY";
|
||||||
static NSString *const paramPackageVersion = @"packageVersion";
|
static NSString *const paramPackageVersion = @"packageVersion";
|
||||||
|
static NSString *const paramBuildTime = @"buildTime";
|
||||||
static NSString *const paramLastVersion = @"lastVersion";
|
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";
|
||||||
@@ -70,20 +71,28 @@ RCT_EXPORT_MODULE(RCTPushy);
|
|||||||
{
|
{
|
||||||
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
|
||||||
|
|
||||||
NSDictionary *pushyInfo = [defaults dictionaryForKey:keyPushyInfo];
|
// Check for version changes first
|
||||||
if (pushyInfo) {
|
|
||||||
NSString *curPackageVersion = [RCTPushy packageVersion];
|
NSString *curPackageVersion = [RCTPushy packageVersion];
|
||||||
NSString *packageVersion = [pushyInfo objectForKey:paramPackageVersion];
|
NSString *curBuildTime = [RCTPushy buildTime];
|
||||||
|
NSString *storedPackageVersion = [defaults stringForKey:paramPackageVersion];
|
||||||
|
NSString *storedBuildTime = [defaults stringForKey:paramBuildTime];
|
||||||
|
|
||||||
BOOL needClearPushyInfo = ![curPackageVersion isEqualToString:packageVersion];
|
BOOL packageVersionChanged = ![curPackageVersion isEqualToString:storedPackageVersion];
|
||||||
if (needClearPushyInfo) {
|
BOOL buildTimeChanged = ![curBuildTime isEqualToString:storedBuildTime];
|
||||||
|
|
||||||
|
if (packageVersionChanged || buildTimeChanged) {
|
||||||
|
// Clear all update data and store new versions
|
||||||
[defaults setObject:nil forKey:keyPushyInfo];
|
[defaults setObject:nil forKey:keyPushyInfo];
|
||||||
[defaults setObject:nil forKey:keyHashInfo];
|
[defaults setObject:nil forKey:keyHashInfo];
|
||||||
[defaults setObject:@(YES) forKey:KeyPackageUpdatedMarked];
|
[defaults setObject:@(YES) forKey:KeyPackageUpdatedMarked];
|
||||||
|
[defaults setObject:curPackageVersion forKey:paramPackageVersion];
|
||||||
|
[defaults setObject:curBuildTime forKey:paramBuildTime];
|
||||||
|
|
||||||
// ...need clear files later
|
// ...need clear files later
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
|
NSDictionary *pushyInfo = [defaults dictionaryForKey:keyPushyInfo];
|
||||||
|
if (pushyInfo) {
|
||||||
NSString *curVersion = pushyInfo[paramCurrentVersion];
|
NSString *curVersion = pushyInfo[paramCurrentVersion];
|
||||||
|
|
||||||
BOOL isFirstTime = [pushyInfo[paramIsFirstTime] boolValue];
|
BOOL isFirstTime = [pushyInfo[paramIsFirstTime] boolValue];
|
||||||
@@ -93,8 +102,7 @@ RCT_EXPORT_MODULE(RCTPushy);
|
|||||||
BOOL needRollback = (!ignoreRollback && isFirstTime == NO && isFirstLoadOK == NO) || loadVersion.length<=0;
|
BOOL needRollback = (!ignoreRollback && isFirstTime == NO && isFirstLoadOK == NO) || loadVersion.length<=0;
|
||||||
if (needRollback) {
|
if (needRollback) {
|
||||||
loadVersion = [self rollback];
|
loadVersion = [self rollback];
|
||||||
}
|
} else if (isFirstTime && !ignoreRollback){
|
||||||
else if (isFirstTime && !ignoreRollback){
|
|
||||||
// bundleURL may be called many times, ignore rollbacks before process restarted again.
|
// bundleURL may be called many times, ignore rollbacks before process restarted again.
|
||||||
ignoreRollback = true;
|
ignoreRollback = true;
|
||||||
|
|
||||||
@@ -128,13 +136,11 @@ RCT_EXPORT_MODULE(RCTPushy);
|
|||||||
NSDictionary *pushyInfo = [defaults dictionaryForKey:keyPushyInfo];
|
NSDictionary *pushyInfo = [defaults dictionaryForKey:keyPushyInfo];
|
||||||
NSString *lastVersion = pushyInfo[paramLastVersion];
|
NSString *lastVersion = pushyInfo[paramLastVersion];
|
||||||
NSString *curVersion = pushyInfo[paramCurrentVersion];
|
NSString *curVersion = pushyInfo[paramCurrentVersion];
|
||||||
NSString *curPackageVersion = [RCTPushy packageVersion];
|
|
||||||
if (lastVersion.length) {
|
if (lastVersion.length) {
|
||||||
// roll back to last version
|
// roll back to last version
|
||||||
[defaults setObject:@{paramCurrentVersion:lastVersion,
|
[defaults setObject:@{paramCurrentVersion:lastVersion,
|
||||||
paramIsFirstTime:@(NO),
|
paramIsFirstTime:@(NO),
|
||||||
paramIsFirstLoadOk:@(YES),
|
paramIsFirstLoadOk:@(YES)}
|
||||||
paramPackageVersion:curPackageVersion}
|
|
||||||
forKey:keyPushyInfo];
|
forKey:keyPushyInfo];
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -297,12 +303,11 @@ RCT_EXPORT_METHOD(setNeedUpdate:(NSDictionary *)options
|
|||||||
newInfo[paramLastVersion] = lastVersion;
|
newInfo[paramLastVersion] = lastVersion;
|
||||||
newInfo[paramIsFirstTime] = @(YES);
|
newInfo[paramIsFirstTime] = @(YES);
|
||||||
newInfo[paramIsFirstLoadOk] = @(NO);
|
newInfo[paramIsFirstLoadOk] = @(NO);
|
||||||
newInfo[paramPackageVersion] = [RCTPushy packageVersion];
|
|
||||||
[defaults setObject:newInfo forKey:keyPushyInfo];
|
[defaults setObject:newInfo forKey:keyPushyInfo];
|
||||||
|
|
||||||
|
|
||||||
resolve(@true);
|
resolve(@true);
|
||||||
}else{
|
} else {
|
||||||
reject(@"执行报错", nil, nil);
|
reject(@"执行报错", nil, nil);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -314,22 +319,22 @@ RCT_EXPORT_METHOD(reloadUpdate:(NSDictionary *)options
|
|||||||
@try {
|
@try {
|
||||||
NSString *hash = options[@"hash"];
|
NSString *hash = options[@"hash"];
|
||||||
if (hash.length) {
|
if (hash.length) {
|
||||||
[self setNeedUpdate:options resolver:resolve rejecter:reject];
|
// 只在 setNeedUpdate 成功后 resolve
|
||||||
|
[self setNeedUpdate:options resolver:^(id result) {
|
||||||
// reload in earlier version
|
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self.bridge setValue:[[self class] bundleURL] forKey:@"bundleURL"];
|
|
||||||
[self.bridge reload];
|
|
||||||
});
|
|
||||||
|
|
||||||
#if __has_include("RCTReloadCommand.h")
|
#if __has_include("RCTReloadCommand.h")
|
||||||
// reload 0.62+
|
// reload 0.62+
|
||||||
RCTReloadCommandSetBundleURL([[self class] bundleURL]);
|
RCTReloadCommandSetBundleURL([[self class] bundleURL]);
|
||||||
RCTTriggerReloadCommandListeners(@"pushy reload");
|
RCTTriggerReloadCommandListeners(@"pushy reloadUpdate");
|
||||||
|
#else
|
||||||
|
[self.bridge reload];
|
||||||
#endif
|
#endif
|
||||||
|
});
|
||||||
resolve(@true);
|
resolve(@true);
|
||||||
}else{
|
} rejecter:^(NSString *code, NSString *message, NSError *error) {
|
||||||
|
reject(code, message, error);
|
||||||
|
}];
|
||||||
|
} else {
|
||||||
reject(@"执行报错", nil, nil);
|
reject(@"执行报错", nil, nil);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -343,13 +348,14 @@ RCT_EXPORT_METHOD(restartApp:(RCTPromiseResolveBlock)resolve
|
|||||||
{
|
{
|
||||||
@try {
|
@try {
|
||||||
dispatch_async(dispatch_get_main_queue(), ^{
|
dispatch_async(dispatch_get_main_queue(), ^{
|
||||||
[self.bridge reload];
|
|
||||||
});
|
|
||||||
#if __has_include("RCTReloadCommand.h")
|
#if __has_include("RCTReloadCommand.h")
|
||||||
// reload 0.62+
|
// reload 0.62+
|
||||||
RCTReloadCommandSetBundleURL([[self class] bundleURL]);
|
RCTReloadCommandSetBundleURL([[self class] bundleURL]);
|
||||||
RCTTriggerReloadCommandListeners(@"pushy restartApp");
|
RCTTriggerReloadCommandListeners(@"pushy restartApp");
|
||||||
|
#else
|
||||||
|
[self.bridge reload];
|
||||||
#endif
|
#endif
|
||||||
|
});
|
||||||
|
|
||||||
resolve(@true);
|
resolve(@true);
|
||||||
}
|
}
|
||||||
@@ -361,6 +367,9 @@ RCT_EXPORT_METHOD(restartApp:(RCTPromiseResolveBlock)resolve
|
|||||||
RCT_EXPORT_METHOD(markSuccess:(RCTPromiseResolveBlock)resolve
|
RCT_EXPORT_METHOD(markSuccess:(RCTPromiseResolveBlock)resolve
|
||||||
rejecter:(RCTPromiseRejectBlock)reject)
|
rejecter:(RCTPromiseRejectBlock)reject)
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
|
resolve(@true);
|
||||||
|
#else
|
||||||
|
|
||||||
@try {
|
@try {
|
||||||
// up package info
|
// up package info
|
||||||
@@ -384,6 +393,7 @@ RCT_EXPORT_METHOD(markSuccess:(RCTPromiseResolveBlock)resolve
|
|||||||
@catch (NSException *exception) {
|
@catch (NSException *exception) {
|
||||||
reject(@"执行报错", nil, nil);
|
reject(@"执行报错", nil, nil);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -542,7 +552,15 @@ RCT_EXPORT_METHOD(markSuccess:(RCTPromiseResolveBlock)resolve
|
|||||||
callback([self errorWithMessage:ERROR_HDIFFPATCH]);
|
callback([self errorWithMessage:ERROR_HDIFFPATCH]);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@try {
|
||||||
[_fileManager hdiffFileAtPath:bundlePatch fromOrigin:bundleOrigin toDestination:destination completionHandler:completionHandler];
|
[_fileManager hdiffFileAtPath:bundlePatch fromOrigin:bundleOrigin toDestination:destination completionHandler:completionHandler];
|
||||||
|
}
|
||||||
|
@catch (NSException *exception) {
|
||||||
|
NSLog(@"Pushy _dopatch error: exception occurred during hdiffFileAtPath: %@, reason: %@",
|
||||||
|
exception.name, exception.reason);
|
||||||
|
callback([self errorWithMessage:ERROR_HDIFFPATCH]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)patch:(NSString *)hash fromBundle:(NSString *)bundleOrigin source:(NSString *)sourceOrigin callback:(void (^)(NSError *error))callback
|
- (void)patch:(NSString *)hash fromBundle:(NSString *)bundleOrigin source:(NSString *)sourceOrigin callback:(void (^)(NSError *error))callback
|
||||||
|
|||||||
49
package.json
@@ -1,28 +1,29 @@
|
|||||||
{
|
{
|
||||||
"name": "react-native-update",
|
"name": "react-native-update",
|
||||||
"version": "10.28.3",
|
"version": "10.30.1",
|
||||||
"description": "react-native hot update",
|
"description": "react-native hot update",
|
||||||
"main": "src/index",
|
"main": "src/index",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepack": "yarn submodule && yarn lint",
|
"postinstall": "node scripts/check-expo-version.js",
|
||||||
|
"prepack": "bun submodule && bun lint",
|
||||||
"lint": "eslint \"src/*.@(ts|tsx|js|jsx)\" && tsc --noEmit",
|
"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": "bun 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:ios-debug": "cd Example/testHotUpdate && yarn && detox build --configuration ios.sim.debug",
|
"build:ios-debug": "cd Example/testHotUpdate && bun && detox build --configuration ios.sim.debug",
|
||||||
"build:ios-release": "cd Example/testHotUpdate && yarn && detox build --configuration ios.sim.release",
|
"build:ios-release": "cd Example/testHotUpdate && bun && detox build --configuration ios.sim.release",
|
||||||
"test:ios-debug": "cd Example/testHotUpdate && detox test --configuration ios.sim.debug",
|
"test:ios-debug": "cd Example/testHotUpdate && detox test --configuration ios.sim.debug",
|
||||||
"test:ios-release": "cd Example/testHotUpdate && yarn detox test --configuration ios.sim.release",
|
"test:ios-release": "cd Example/testHotUpdate && bun detox test --configuration ios.sim.release",
|
||||||
"build:android-debug": "cd Example/testHotUpdate && yarn && detox build --configuration android.emu.debug",
|
"build:android-debug": "cd Example/testHotUpdate && bun && detox build --configuration android.emu.debug",
|
||||||
"build:android-release": "cd Example/testHotUpdate && yarn && detox build --configuration android.emu.release",
|
"build:android-release": "cd Example/testHotUpdate && bun && detox build --configuration android.emu.release",
|
||||||
"test:android-release": "cd Example/testHotUpdate && yarn detox test --configuration android.emu.release --headless --record-logs all",
|
"test:android-release": "cd Example/testHotUpdate && bun detox test --configuration android.emu.release --headless --record-logs all",
|
||||||
"test:android-debug": "cd Example/testHotUpdate && detox test --configuration android.emu.debug --headless --record-logs all",
|
"test:android-debug": "cd Example/testHotUpdate && detox test --configuration android.emu.debug --headless --record-logs all",
|
||||||
"e2e:ios": "npm run build:ios-release && npm run test:ios-release",
|
"e2e:ios": "bun build:ios-release && bun test:ios-release",
|
||||||
"e2e:android": "npm run build:android-release && npm run test:android-release",
|
"e2e:android": "bun build:android-release && bun test:android-release",
|
||||||
"tests:emulator:prepare": "cd .github/workflows/scripts/functions && yarn && yarn build",
|
"tests:emulator:prepare": "cd .github/workflows/scripts/functions && bun && bun build",
|
||||||
"tests:emulator:start-ci": "yarn tests:emulator:prepare && cd ./.github/workflows/scripts && ./start-firebase-emulator.sh",
|
"tests:emulator:start-ci": "bun tests:emulator:prepare && cd ./.github/workflows/scripts && ./start-firebase-emulator.sh",
|
||||||
"tests:packager:jet-ci": "cd Example/testHotUpdate && cross-env TMPDIR=$HOME/.metro REACT_DEBUGGER=\"echo nope\" node_modules/.bin/react-native start --no-interactive",
|
"tests:packager:jet-ci": "cd Example/testHotUpdate && cross-env TMPDIR=$HOME/.metro REACT_DEBUGGER=\"echo nope\" node_modules/.bin/react-native start --no-interactive",
|
||||||
"tests:ios:pod:install": "cd Example/testHotUpdate && yarn && yarn pod-install"
|
"tests:ios:pod:install": "cd Example/testHotUpdate && bun && bun pod-install"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
@@ -56,24 +57,20 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.25.8",
|
"@babel/core": "^7.25.8",
|
||||||
"@react-native/babel-preset": "^0.73.21",
|
"@react-native/babel-preset": "^0.73.21",
|
||||||
"@react-native/eslint-config": "^0.73.2",
|
"@react-native/eslint-config": "0.79.1",
|
||||||
"@react-native/typescript-config": "^0.74.0",
|
"@react-native/typescript-config": "0.79.1",
|
||||||
"@types/fs-extra": "^11.0.4",
|
"@types/jest": "^29.5.14",
|
||||||
"@types/jest": "^29.5.13",
|
"@types/node": "^22.15.2",
|
||||||
"@types/node": "^22.7.6",
|
|
||||||
"@types/react": "^18.3.11",
|
"@types/react": "^18.3.11",
|
||||||
"detox": "^20.27.3",
|
"detox": "^20.37.0",
|
||||||
"eslint": "^8.57.0",
|
"eslint": "^8.57.0",
|
||||||
"eslint-plugin-ft-flow": "^3.0.7",
|
|
||||||
"firebase-tools": "^13.22.1",
|
"firebase-tools": "^13.22.1",
|
||||||
"fs-extra": "^11.2.0",
|
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"pod-install": "^0.2.2",
|
"pod-install": "^0.3.7",
|
||||||
"prettier": "^2",
|
"prettier": "^2",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"react-native": "0.73",
|
"react-native": "0.73",
|
||||||
"ts-jest": "^29.2.5",
|
"ts-jest": "^29.3.2",
|
||||||
"typescript": "^5.6.3"
|
"typescript": "^5.6.3"
|
||||||
},
|
}
|
||||||
"packageManager": "yarn@1.22.21+sha1.1959a18351b811cdeedbd484a8f86c3cc3bbaf72"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,83 @@
|
|||||||
require 'json'
|
require 'json'
|
||||||
require 'rubygems' # Required for version comparison
|
require 'rubygems' # Required for version comparison
|
||||||
|
|
||||||
|
|
||||||
new_arch_enabled = ENV['RCT_NEW_ARCH_ENABLED'] == '1'
|
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')))
|
||||||
|
|
||||||
podspec_dir = File.dirname(__FILE__)
|
podspec_dir = File.dirname(__FILE__)
|
||||||
|
|
||||||
Pod::Spec.new do |s|
|
Pod::Spec.new do |s|
|
||||||
|
|
||||||
|
is_expo_in_podfile = false
|
||||||
|
begin
|
||||||
|
# Check Podfile for use_expo_modules!
|
||||||
|
podfile_path = File.join(Pod::Config.instance.installation_root, 'Podfile')
|
||||||
|
if File.exist?(podfile_path)
|
||||||
|
podfile_content = File.read(podfile_path)
|
||||||
|
is_expo_in_podfile = podfile_content.include?('use_expo_modules!')
|
||||||
|
end
|
||||||
|
rescue => e
|
||||||
|
# Silently ignore errors during check
|
||||||
|
end
|
||||||
|
|
||||||
|
# Determine final validity by checking Podfile presence AND Expo version
|
||||||
|
valid_expo_project = false # Default
|
||||||
|
if is_expo_in_podfile
|
||||||
|
# Only check expo version if use_expo_modules! is present
|
||||||
|
is_version_sufficient = false
|
||||||
|
begin
|
||||||
|
expo_version_str = `node --print \"require('expo/package.json').version\"`.strip
|
||||||
|
if expo_version_str && !expo_version_str.empty?
|
||||||
|
match = expo_version_str.match(/^\d+/)
|
||||||
|
if match
|
||||||
|
major_version = match[0].to_i
|
||||||
|
is_version_sufficient = major_version >= 50
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
# Node command failed, version remains insufficient
|
||||||
|
end
|
||||||
|
|
||||||
|
# Final check
|
||||||
|
valid_expo_project = is_version_sufficient
|
||||||
|
end
|
||||||
|
|
||||||
|
# Set platform based on whether it's a valid Expo project and if we can parse its target
|
||||||
|
final_ios_deployment_target = '11.0' # Default target
|
||||||
|
|
||||||
|
if valid_expo_project
|
||||||
|
# --- Try to find and parse ExpoModulesCore.podspec only if it's an Expo project ---
|
||||||
|
parsed_expo_ios_target = nil
|
||||||
|
expo_modules_core_podspec_path = begin
|
||||||
|
package_json_path = `node -p "require.resolve('expo-modules-core/package.json')"`.strip
|
||||||
|
File.join(File.dirname(package_json_path), 'ExpoModulesCore.podspec') if $?.success? && package_json_path && !package_json_path.empty?
|
||||||
|
rescue
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
if expo_modules_core_podspec_path && File.exist?(expo_modules_core_podspec_path)
|
||||||
|
begin
|
||||||
|
content = File.read(expo_modules_core_podspec_path)
|
||||||
|
match = content.match(/s\.platforms\s*=\s*\{[\s\S]*?:ios\s*=>\s*'([^\']+)'/) # Match within s.platforms hash
|
||||||
|
if match && match[1]
|
||||||
|
parsed_expo_ios_target = match[1]
|
||||||
|
else
|
||||||
|
match = content.match(/s\.platform\s*=\s*:ios,\s*'([^\']+)'/) # Fallback to s.platform = :ios, 'version'
|
||||||
|
if match && match[1]
|
||||||
|
parsed_expo_ios_target = match[1]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue => e
|
||||||
|
# Pod::UI.warn "Failed to read or parse ExpoModulesCore.podspec content: #{e.message}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if parsed_expo_ios_target
|
||||||
|
final_ios_deployment_target = parsed_expo_ios_target
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
s.platforms = { :ios => final_ios_deployment_target }
|
||||||
|
|
||||||
s.name = package['name']
|
s.name = package['name']
|
||||||
s.version = package['version']
|
s.version = package['version']
|
||||||
s.summary = package['description']
|
s.summary = package['description']
|
||||||
@@ -17,10 +87,9 @@ Pod::Spec.new do |s|
|
|||||||
s.homepage = package['homepage']
|
s.homepage = package['homepage']
|
||||||
|
|
||||||
s.cocoapods_version = '>= 1.6.0'
|
s.cocoapods_version = '>= 1.6.0'
|
||||||
s.platform = :ios, "8.0"
|
|
||||||
s.platforms = { :ios => "11.0" }
|
|
||||||
s.source = { :git => 'https://github.com/reactnativecn/react-native-update.git', :tag => '#{s.version}' }
|
s.source = { :git => 'https://github.com/reactnativecn/react-native-update.git', :tag => '#{s.version}' }
|
||||||
s.source_files = Dir.glob("ios/**/*.{h,m,mm,swift}").reject { |f| f.start_with?("ios/Expo/") }
|
|
||||||
s.libraries = 'bz2', 'z'
|
s.libraries = 'bz2', 'z'
|
||||||
s.vendored_libraries = 'RCTPushy/libRCTPushy.a'
|
s.vendored_libraries = 'RCTPushy/libRCTPushy.a'
|
||||||
s.pod_target_xcconfig = {
|
s.pod_target_xcconfig = {
|
||||||
@@ -34,64 +103,47 @@ Pod::Spec.new do |s|
|
|||||||
s.dependency "React-Core"
|
s.dependency "React-Core"
|
||||||
s.dependency 'SSZipArchive'
|
s.dependency 'SSZipArchive'
|
||||||
|
|
||||||
is_expo_project = false
|
# Conditionally add Expo dependency
|
||||||
expo_dependency_added = false
|
if valid_expo_project
|
||||||
supports_bundle_url_final = false
|
s.public_header_files = ['ios/ImportReact.h']
|
||||||
|
|
||||||
# Use CocoaPods mechanism to find Podfile
|
|
||||||
begin
|
|
||||||
podfile_path = File.join(Pod::Config.instance.installation_root, 'Podfile')
|
|
||||||
if File.exist?(podfile_path)
|
|
||||||
podfile_content = File.read(podfile_path)
|
|
||||||
is_expo_project = podfile_content.include?('use_expo_modules!')
|
|
||||||
end
|
|
||||||
rescue
|
|
||||||
# Silently skip if CocoaPods config is not available
|
|
||||||
end
|
|
||||||
|
|
||||||
if is_expo_project
|
|
||||||
s.dependency 'ExpoModulesCore'
|
s.dependency 'ExpoModulesCore'
|
||||||
expo_dependency_added = true
|
|
||||||
|
|
||||||
# Current directory is in node_modules/react-native-update, so parent is node_modules
|
|
||||||
expo_core_package_json_path = File.join(__dir__, '..', 'expo-modules-core', 'package.json')
|
|
||||||
|
|
||||||
if File.exist?(expo_core_package_json_path)
|
|
||||||
begin
|
|
||||||
core_package_json = JSON.parse(File.read(expo_core_package_json_path))
|
|
||||||
installed_version_str = core_package_json['version']
|
|
||||||
|
|
||||||
if installed_version_str
|
|
||||||
begin
|
|
||||||
installed_version = Gem::Version.new(installed_version_str)
|
|
||||||
target_version = Gem::Version.new('1.12.0')
|
|
||||||
supports_bundle_url_final = installed_version >= target_version
|
|
||||||
rescue ArgumentError
|
|
||||||
# Silently skip version parsing errors
|
|
||||||
end
|
|
||||||
end
|
|
||||||
rescue JSON::ParserError, StandardError
|
|
||||||
# Silently skip file reading and parsing errors
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
s.subspec 'RCTPushy' do |ss|
|
s.subspec 'RCTPushy' do |ss|
|
||||||
ss.source_files = 'ios/RCTPushy/*.{h,m,mm,swift}'
|
ss.source_files = ['ios/RCTPushy/**/*.{h,m,mm,c}',
|
||||||
ss.public_header_files = ['ios/RCTPushy/RCTPushy.h']
|
|
||||||
end
|
|
||||||
|
|
||||||
s.subspec 'HDiffPatch' do |ss|
|
|
||||||
ss.source_files = ['ios/RCTPushy/HDiffPatch/**/*.{h,m,c}',
|
|
||||||
'android/jni/hpatch.{h,c}',
|
'android/jni/hpatch.{h,c}',
|
||||||
'android/jni/HDiffPatch/libHDiffPatch/HPatch/*.{h,c}',
|
'android/jni/HDiffPatch/libHDiffPatch/HPatch/*.{h,c}',
|
||||||
'android/jni/HDiffPatch/file_for_patch.{h,c}',
|
'android/jni/HDiffPatch/file_for_patch.{h,c}',
|
||||||
'android/jni/lzma/C/LzmaDec.{h,c}',
|
'android/jni/lzma/C/LzmaDec.{h,c}',
|
||||||
'android/jni/lzma/C/Lzma2Dec.{h,c}']
|
'android/jni/lzma/C/Lzma2Dec.{h,c}']
|
||||||
ss.public_header_files = 'ios/RCTPushy/HDiffPatch/**/*.h'
|
ss.public_header_files = ['ios/RCTPushy/**/*.h']
|
||||||
|
end
|
||||||
|
|
||||||
|
# Conditionally add Expo subspec and check ExpoModulesCore version
|
||||||
|
if valid_expo_project
|
||||||
|
supports_bundle_url_final = false # Default
|
||||||
|
|
||||||
|
# 1. Try executing node to get the version string
|
||||||
|
expo_modules_core_version_str = begin
|
||||||
|
# Use node to directly require expo-modules-core/package.json and get its version
|
||||||
|
`node --print \"require('expo-modules-core/package.json').version\"` # Execute, keep raw output
|
||||||
|
rescue
|
||||||
|
# Node command failed (e.g., node not found, package not found). Return empty string.
|
||||||
|
''
|
||||||
|
end
|
||||||
|
|
||||||
|
# 2. Process the obtained version string (if not empty)
|
||||||
|
if expo_modules_core_version_str && !expo_modules_core_version_str.empty?
|
||||||
|
begin
|
||||||
|
# Compare versions using Gem::Version (handles trailing newline)
|
||||||
|
installed_version = Gem::Version.new(expo_modules_core_version_str)
|
||||||
|
target_version = Gem::Version.new('1.12.0')
|
||||||
|
supports_bundle_url_final = installed_version >= target_version
|
||||||
|
rescue ArgumentError
|
||||||
|
# If Gem::Version fails parsing, supports_bundle_url_final remains false.
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
if expo_dependency_added
|
|
||||||
s.subspec 'Expo' do |ss|
|
s.subspec 'Expo' do |ss|
|
||||||
ss.source_files = 'ios/Expo/**/*.{h,m,mm,swift}'
|
ss.source_files = 'ios/Expo/**/*.{h,m,mm,swift}'
|
||||||
if supports_bundle_url_final
|
if supports_bundle_url_final
|
||||||
|
|||||||
92
scripts/check-expo-version.js
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
const ownPackageJson = require('../package.json');
|
||||||
|
|
||||||
|
if (process.env.npm_package_name === ownPackageJson.name) {
|
||||||
|
console.log('Skipping postinstall during local development.');
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const projectRoot = path.resolve(__dirname, '..'); // react-native-update module root
|
||||||
|
const expoConfigPath = path.resolve(projectRoot, 'expo-module.config.json');
|
||||||
|
|
||||||
|
function getExpoMajorVersion() {
|
||||||
|
let resolvedExpoPackagePath;
|
||||||
|
try {
|
||||||
|
// Use require.resolve to find expo's package.json from the host project's perspective
|
||||||
|
resolvedExpoPackagePath = require.resolve('expo/package.json', {
|
||||||
|
paths: [path.resolve(projectRoot, '..', '..')],
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
console.log(
|
||||||
|
'Expo not found in project node_modules (via require.resolve).',
|
||||||
|
);
|
||||||
|
return null; // Expo not found or resolvable
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the resolved path actually exists (belt-and-suspenders)
|
||||||
|
if (!fs.existsSync(resolvedExpoPackagePath)) {
|
||||||
|
console.log(
|
||||||
|
`Expo package.json path resolved to ${resolvedExpoPackagePath}, but file does not exist.`,
|
||||||
|
);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const packageJson = JSON.parse(
|
||||||
|
fs.readFileSync(resolvedExpoPackagePath, 'utf8'),
|
||||||
|
);
|
||||||
|
const version = packageJson.version;
|
||||||
|
if (!version) {
|
||||||
|
console.log('Expo package.json does not contain a version.');
|
||||||
|
return null; // Version not found
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract the first number sequence as the major version
|
||||||
|
const match = version.match(/\d+/);
|
||||||
|
if (!match) {
|
||||||
|
console.log(
|
||||||
|
`Could not parse major version from Expo version string: ${version}`,
|
||||||
|
);
|
||||||
|
return null; // Cannot parse version
|
||||||
|
}
|
||||||
|
|
||||||
|
return parseInt(match[0], 10);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error reading or parsing Expo package.json:', error);
|
||||||
|
return null; // Error during processing
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkAndCleanExpoConfig() {
|
||||||
|
const majorVersion = getExpoMajorVersion();
|
||||||
|
|
||||||
|
// Condition: Expo not found OR major version is less than 50
|
||||||
|
if (majorVersion === null || majorVersion < 50) {
|
||||||
|
if (fs.existsSync(expoConfigPath)) {
|
||||||
|
try {
|
||||||
|
fs.unlinkSync(expoConfigPath);
|
||||||
|
console.log(
|
||||||
|
`Expo version (${
|
||||||
|
majorVersion !== null ? majorVersion : 'not found'
|
||||||
|
}) is < 50 or Expo not found. Deleted ${expoConfigPath}`,
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Failed to delete ${expoConfigPath}:`, error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
`Expo version (${
|
||||||
|
majorVersion !== null ? majorVersion : 'not found'
|
||||||
|
}) is < 50 or Expo not found. ${expoConfigPath} does not exist, no action needed.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log(
|
||||||
|
`Expo version (${majorVersion}) is >= 50. Kept ${expoConfigPath}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkAndCleanExpoConfig();
|
||||||
@@ -1,33 +1,33 @@
|
|||||||
import { CheckResult, ClientOptions, ProgressData, EventType } from './type';
|
|
||||||
import {
|
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,
|
assertWeb,
|
||||||
emptyObj,
|
emptyObj,
|
||||||
|
enhancedFetch,
|
||||||
joinUrls,
|
joinUrls,
|
||||||
log,
|
log,
|
||||||
noop,
|
noop,
|
||||||
promiseAny,
|
promiseAny,
|
||||||
testUrls,
|
testUrls,
|
||||||
} from './utils';
|
} 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 = {
|
const SERVER_PRESETS = {
|
||||||
// cn
|
// cn
|
||||||
@@ -148,6 +148,7 @@ export class Pushy {
|
|||||||
await this.loggerPromise.promise;
|
await this.loggerPromise.promise;
|
||||||
const { logger = noop, appKey } = this.options;
|
const { logger = noop, appKey } = this.options;
|
||||||
const info = await getCurrentVersionInfo();
|
const info = await getCurrentVersionInfo();
|
||||||
|
const overridePackageVersion = this.options.overridePackageVersion;
|
||||||
logger({
|
logger({
|
||||||
type,
|
type,
|
||||||
data: {
|
data: {
|
||||||
@@ -155,6 +156,7 @@ export class Pushy {
|
|||||||
currentVersion,
|
currentVersion,
|
||||||
cInfo,
|
cInfo,
|
||||||
packageVersion,
|
packageVersion,
|
||||||
|
overridePackageVersion,
|
||||||
buildTime,
|
buildTime,
|
||||||
message,
|
message,
|
||||||
...info,
|
...info,
|
||||||
@@ -170,10 +172,12 @@ export class Pushy {
|
|||||||
getCheckUrl = (endpoint: string = this.options.server!.main) => {
|
getCheckUrl = (endpoint: string = this.options.server!.main) => {
|
||||||
return `${endpoint}/checkUpdate/${this.options.appKey}`;
|
return `${endpoint}/checkUpdate/${this.options.appKey}`;
|
||||||
};
|
};
|
||||||
assertDebug = () => {
|
assertDebug = (matter: string) => {
|
||||||
if (__DEV__ && !this.options.debug) {
|
if (__DEV__ && !this.options.debug) {
|
||||||
console.info(
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -188,7 +192,7 @@ export class Pushy {
|
|||||||
this.report({ type: 'markSuccess' });
|
this.report({ type: 'markSuccess' });
|
||||||
};
|
};
|
||||||
switchVersion = async (hash: string) => {
|
switchVersion = async (hash: string) => {
|
||||||
if (!assertDev('switchVersion()')) {
|
if (!this.assertDebug('switchVersion()')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (assertHash(hash) && !sharedState.applyingUpdate) {
|
if (assertHash(hash) && !sharedState.applyingUpdate) {
|
||||||
@@ -199,7 +203,7 @@ export class Pushy {
|
|||||||
};
|
};
|
||||||
|
|
||||||
switchVersionLater = async (hash: string) => {
|
switchVersionLater = async (hash: string) => {
|
||||||
if (!assertDev('switchVersionLater()')) {
|
if (!this.assertDebug('switchVersionLater()')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (assertHash(hash)) {
|
if (assertHash(hash)) {
|
||||||
@@ -208,7 +212,7 @@ export class Pushy {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
checkUpdate = async (extra?: Record<string, any>) => {
|
checkUpdate = async (extra?: Record<string, any>) => {
|
||||||
if (!this.assertDebug()) {
|
if (!this.assertDebug('checkUpdate()')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!assertWeb()) {
|
if (!assertWeb()) {
|
||||||
@@ -231,7 +235,7 @@ export class Pushy {
|
|||||||
}
|
}
|
||||||
this.lastChecking = now;
|
this.lastChecking = now;
|
||||||
const fetchBody = {
|
const fetchBody = {
|
||||||
packageVersion,
|
packageVersion: this.options.overridePackageVersion || packageVersion,
|
||||||
hash: currentVersion,
|
hash: currentVersion,
|
||||||
buildTime,
|
buildTime,
|
||||||
cInfo,
|
cInfo,
|
||||||
@@ -261,7 +265,7 @@ export class Pushy {
|
|||||||
type: 'checking',
|
type: 'checking',
|
||||||
message: this.options.appKey + ': ' + stringifyBody,
|
message: this.options.appKey + ': ' + stringifyBody,
|
||||||
});
|
});
|
||||||
resp = await fetch(this.getCheckUrl(), fetchPayload);
|
resp = await enhancedFetch(this.getCheckUrl(), fetchPayload);
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
this.report({
|
this.report({
|
||||||
type: 'errorChecking',
|
type: 'errorChecking',
|
||||||
@@ -272,7 +276,7 @@ export class Pushy {
|
|||||||
try {
|
try {
|
||||||
resp = await promiseAny(
|
resp = await promiseAny(
|
||||||
backupEndpoints.map(endpoint =>
|
backupEndpoints.map(endpoint =>
|
||||||
fetch(this.getCheckUrl(endpoint), fetchPayload),
|
enhancedFetch(this.getCheckUrl(endpoint), fetchPayload),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
@@ -389,11 +393,16 @@ export class Pushy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let succeeded = '';
|
let succeeded = '';
|
||||||
this.report({ type: 'downloading' });
|
this.report({
|
||||||
|
type: 'downloading',
|
||||||
|
data: {
|
||||||
|
newVersion: hash,
|
||||||
|
},
|
||||||
|
});
|
||||||
let lastError: any;
|
let lastError: any;
|
||||||
let errorMessages: string[] = [];
|
let errorMessages: string[] = [];
|
||||||
const diffUrl = await testUrls(joinUrls(paths, diff));
|
const diffUrl = await testUrls(joinUrls(paths, diff));
|
||||||
if (diffUrl) {
|
if (diffUrl && !__DEV__) {
|
||||||
log('downloading diff');
|
log('downloading diff');
|
||||||
try {
|
try {
|
||||||
await PushyModule.downloadPatchFromPpk({
|
await PushyModule.downloadPatchFromPpk({
|
||||||
@@ -406,15 +415,12 @@ export class Pushy {
|
|||||||
const errorMessage = `diff error: ${e.message}`;
|
const errorMessage = `diff error: ${e.message}`;
|
||||||
errorMessages.push(errorMessage);
|
errorMessages.push(errorMessage);
|
||||||
lastError = new Error(errorMessage);
|
lastError = new Error(errorMessage);
|
||||||
if (__DEV__) {
|
|
||||||
succeeded = 'diff';
|
|
||||||
} else {
|
|
||||||
log(errorMessage);
|
log(errorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
if (!succeeded) {
|
||||||
const pdiffUrl = await testUrls(joinUrls(paths, pdiff));
|
const pdiffUrl = await testUrls(joinUrls(paths, pdiff));
|
||||||
if (!succeeded && pdiffUrl) {
|
if (pdiffUrl && !__DEV__) {
|
||||||
log('downloading pdiff');
|
log('downloading pdiff');
|
||||||
try {
|
try {
|
||||||
await PushyModule.downloadPatchFromPackage({
|
await PushyModule.downloadPatchFromPackage({
|
||||||
@@ -426,15 +432,13 @@ export class Pushy {
|
|||||||
const errorMessage = `pdiff error: ${e.message}`;
|
const errorMessage = `pdiff error: ${e.message}`;
|
||||||
errorMessages.push(errorMessage);
|
errorMessages.push(errorMessage);
|
||||||
lastError = new Error(errorMessage);
|
lastError = new Error(errorMessage);
|
||||||
if (__DEV__) {
|
|
||||||
succeeded = 'pdiff';
|
|
||||||
} else {
|
|
||||||
log(errorMessage);
|
log(errorMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!succeeded) {
|
||||||
const fullUrl = await testUrls(joinUrls(paths, full));
|
const fullUrl = await testUrls(joinUrls(paths, full));
|
||||||
if (!succeeded && fullUrl) {
|
if (fullUrl) {
|
||||||
log('downloading full patch');
|
log('downloading full patch');
|
||||||
try {
|
try {
|
||||||
await PushyModule.downloadFullUpdate({
|
await PushyModule.downloadFullUpdate({
|
||||||
@@ -446,20 +450,21 @@ export class Pushy {
|
|||||||
const errorMessage = `full patch error: ${e.message}`;
|
const errorMessage = `full patch error: ${e.message}`;
|
||||||
errorMessages.push(errorMessage);
|
errorMessages.push(errorMessage);
|
||||||
lastError = new Error(errorMessage);
|
lastError = new Error(errorMessage);
|
||||||
if (__DEV__) {
|
|
||||||
succeeded = 'full';
|
|
||||||
} else {
|
|
||||||
log(errorMessage);
|
log(errorMessage);
|
||||||
}
|
}
|
||||||
|
} else if (__DEV__) {
|
||||||
|
log(
|
||||||
|
`当前是开发环境,无法执行增量式热更新,重启不会生效。
|
||||||
|
如果需要在开发环境中测试可生效的全量热更新(但也会在再次重启后重新连接 metro),
|
||||||
|
请打开“忽略时间戳”开关再重试。`,
|
||||||
|
);
|
||||||
|
succeeded = 'full';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (sharedState.progressHandlers[hash]) {
|
if (sharedState.progressHandlers[hash]) {
|
||||||
sharedState.progressHandlers[hash].remove();
|
sharedState.progressHandlers[hash].remove();
|
||||||
delete sharedState.progressHandlers[hash];
|
delete sharedState.progressHandlers[hash];
|
||||||
}
|
}
|
||||||
if (__DEV__) {
|
|
||||||
return hash;
|
|
||||||
}
|
|
||||||
if (!succeeded) {
|
if (!succeeded) {
|
||||||
this.report({
|
this.report({
|
||||||
type: 'errorUpdate',
|
type: 'errorUpdate',
|
||||||
|
|||||||
@@ -251,7 +251,7 @@ export const UpdateProvider = ({
|
|||||||
const markSuccess = client.markSuccess;
|
const markSuccess = client.markSuccess;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!client.assertDebug()) {
|
if (!client.assertDebug('checkUpdate()')) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const { checkStrategy, dismissErrorAfter, autoMarkSuccess } = options;
|
const { checkStrategy, dismissErrorAfter, autoMarkSuccess } = options;
|
||||||
|
|||||||
@@ -93,6 +93,7 @@ export interface ClientOptions {
|
|||||||
beforeDownloadUpdate?: (info: CheckResult) => Promise<boolean>;
|
beforeDownloadUpdate?: (info: CheckResult) => Promise<boolean>;
|
||||||
afterDownloadUpdate?: (info: CheckResult) => Promise<boolean>;
|
afterDownloadUpdate?: (info: CheckResult) => Promise<boolean>;
|
||||||
onPackageExpired?: (info: CheckResult) => Promise<boolean>;
|
onPackageExpired?: (info: CheckResult) => Promise<boolean>;
|
||||||
|
overridePackageVersion?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateTestPayload {
|
export interface UpdateTestPayload {
|
||||||
|
|||||||
36
src/utils.ts
@@ -40,13 +40,13 @@ const ping =
|
|||||||
: async (url: string) => {
|
: async (url: string) => {
|
||||||
let pingFinished = false;
|
let pingFinished = false;
|
||||||
return Promise.race([
|
return Promise.race([
|
||||||
fetch(url, {
|
enhancedFetch(url, {
|
||||||
method: 'HEAD',
|
method: 'HEAD',
|
||||||
})
|
})
|
||||||
.then(({ status, statusText }) => {
|
.then(({ status, statusText, url: finalUrl }) => {
|
||||||
pingFinished = true;
|
pingFinished = true;
|
||||||
if (status === 200) {
|
if (status === 200) {
|
||||||
return url;
|
return finalUrl;
|
||||||
}
|
}
|
||||||
log('ping failed', url, status, statusText);
|
log('ping failed', url, status, statusText);
|
||||||
throw new Error('Ping failed');
|
throw new Error('Ping failed');
|
||||||
@@ -69,7 +69,7 @@ const ping =
|
|||||||
|
|
||||||
export function joinUrls(paths: string[], fileName?: string) {
|
export function joinUrls(paths: string[], fileName?: string) {
|
||||||
if (fileName) {
|
if (fileName) {
|
||||||
return paths.map(path => 'https://' + path + '/' + fileName);
|
return paths.map(path => `https://${path}/${fileName}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -99,12 +99,26 @@ export const assertWeb = () => {
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const assertDev = (matter: string) => {
|
// export const isAndroid70AndBelow = () => {
|
||||||
if (__DEV__) {
|
// // android 7.0 and below devices do not support letsencrypt cert
|
||||||
console.warn(
|
// // https://letsencrypt.org/2023/07/10/cross-sign-expiration/
|
||||||
`${matter} is not supported in development environment; no action taken.`,
|
// return Platform.OS === 'android' && Platform.Version <= 24;
|
||||||
);
|
// };
|
||||||
return false;
|
|
||||||
|
export const enhancedFetch = async (
|
||||||
|
url: string,
|
||||||
|
params: Parameters<typeof fetch>[1],
|
||||||
|
) => {
|
||||||
|
return fetch(url, params)
|
||||||
|
.then(r => {
|
||||||
|
if (r.ok) {
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
return true;
|
throw new Error(`${r.status} ${r.statusText}`);
|
||||||
|
})
|
||||||
|
.catch(e => {
|
||||||
|
log('fetch error', url, e);
|
||||||
|
log('trying fallback to http');
|
||||||
|
return fetch(url.replace('https', 'http'), params);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|||||||