1
0
Code Issues Pull Requests Packages Projects Releases Wiki Activity GitHub Gitee
react-native-pushy/lib/index.js

315 lines
7.4 KiB
JavaScript
Raw Normal View History

2020-08-13 00:32:07 +08:00
import {
tryBackupEndpoints,
getCheckUrl,
setCustomEndpoints,
} from './endpoint';
2021-05-18 12:23:33 +08:00
import {
NativeEventEmitter,
NativeModules,
Platform,
PermissionsAndroid,
} from 'react-native';
2020-07-28 23:15:42 +08:00
export { setCustomEndpoints };
2020-08-31 11:47:08 +08:00
const {
version: v,
} = require('react-native/Libraries/Core/ReactNativeVersion');
const RNVersion = `${v.major}.${v.minor}.${v.patch}`;
2016-04-04 23:02:28 +08:00
2020-04-30 17:59:28 +08:00
let Pushy = NativeModules.Pushy;
if (!Pushy) {
throw new Error('react-native-update模块无法加载请对照安装文档检查配置。');
}
2016-04-04 23:02:28 +08:00
2019-11-16 00:31:30 +08:00
export const downloadRootDir = Pushy.downloadRootDir;
export const packageVersion = Pushy.packageVersion;
export const currentVersion = Pushy.currentVersion;
export const isFirstTime = Pushy.isFirstTime;
export const isRolledBack = Pushy.isRolledBack;
export const buildTime = Pushy.buildTime;
2020-08-31 01:17:28 +08:00
let blockUpdate = Pushy.blockUpdate;
2020-08-31 11:47:08 +08:00
let uuid = Pushy.uuid;
2016-04-04 23:02:28 +08:00
2020-08-13 00:32:07 +08:00
if (Platform.OS === 'android' && !Pushy.isUsingBundleUrl) {
throw new Error(
'react-native-update模块无法加载请对照文档检查Bundle URL的配置',
);
}
2021-10-04 23:19:22 +08:00
function setLocalHashInfo(hash, info) {
Pushy.setLocalHashInfo(hash, JSON.stringify(info));
}
async function getLocalHashInfo(hash) {
return JSON.parse(await Pushy.getLocalHashInfo(hash));
}
export async function getCurrentVersionInfo() {
return currentVersion ? getLocalHashInfo(currentVersion) : {};
}
2020-09-16 13:01:14 +08:00
const eventEmitter = new NativeEventEmitter(Pushy);
2020-08-31 11:47:08 +08:00
if (!uuid) {
2021-10-05 14:08:56 +08:00
uuid = require('nanoid/non-secure').nanoid();
2020-08-31 11:47:08 +08:00
Pushy.setUuid(uuid);
}
2020-09-16 13:01:14 +08:00
function logger(text) {
console.log(`Pushy: ${text}`);
}
logger('uuid: ' + uuid);
2020-08-31 11:47:08 +08:00
2016-04-04 23:02:28 +08:00
/*
Return json:
2020-08-31 01:17:28 +08:00
Package expired:
2016-04-04 23:02:28 +08:00
{
expired: true,
downloadUrl: 'http://appstore/downloadUrl',
}
Package is up to date:
{
upToDate: true,
}
There is available update:
{
update: true,
name: '1.0.3-rc',
hash: 'hash',
description: '添加聊天功能\n修复商城页面BUG',
metaInfo: '{"silent":true}',
pdiffUrl: 'http://update-packages.reactnative.cn/hash',
diffUrl: 'http://update-packages.reactnative.cn/hash',
}
*/
2019-10-04 22:27:33 +08:00
2021-04-08 23:46:14 +08:00
export const cInfo = {
pushy: require('../package.json').version,
rn: RNVersion,
os: Platform.OS + ' ' + Platform.Version,
uuid,
};
2019-10-04 22:27:33 +08:00
function assertRelease() {
if (__DEV__) {
throw new Error('react-native-update can only run on RELEASE version.');
}
}
2021-07-29 00:50:48 +08:00
let checkingThrottling = false;
2020-01-18 23:55:10 +08:00
export async function checkUpdate(APPKEY, isRetry) {
2019-10-04 22:27:33 +08:00
assertRelease();
2021-07-29 00:50:48 +08:00
if (checkingThrottling) {
logger('repeated checking, ignored');
return;
}
checkingThrottling = true;
setTimeout(() => {
checkingThrottling = false;
}, 3000);
2020-08-31 18:39:03 +08:00
if (blockUpdate && blockUpdate.until > Date.now() / 1000) {
throw new Error(
`热更新已暂停,原因:${blockUpdate.reason}。请在"${new Date(
blockUpdate.until * 1000,
).toLocaleString()}"之后重试`,
2020-08-31 01:17:28 +08:00
);
}
2020-09-16 13:01:14 +08:00
if (typeof APPKEY !== 'string') {
throw new Error('未检查到合法的APPKEY请查看update.json文件是否正确生成');
2020-09-16 13:01:14 +08:00
}
logger('checking update');
2020-01-18 23:55:10 +08:00
let resp;
try {
2020-07-28 23:15:42 +08:00
resp = await fetch(getCheckUrl(APPKEY), {
2020-01-18 23:55:10 +08:00
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
packageVersion,
hash: currentVersion,
buildTime,
2021-04-08 23:46:14 +08:00
cInfo,
2020-01-18 23:55:10 +08:00
}),
});
} catch (e) {
if (isRetry) {
throw new Error('Could not connect to pushy server');
}
2020-07-28 23:15:42 +08:00
await tryBackupEndpoints(APPKEY);
2020-01-18 23:55:10 +08:00
return checkUpdate(APPKEY, true);
}
2020-08-31 18:39:03 +08:00
const result = await resp.json();
checkOperation(result.op);
2020-08-31 01:17:28 +08:00
2016-04-04 23:02:28 +08:00
if (resp.status !== 200) {
2020-08-31 18:39:03 +08:00
throw new Error(result.message);
2016-04-04 23:02:28 +08:00
}
2020-08-31 18:39:03 +08:00
return result;
2016-04-04 23:02:28 +08:00
}
2020-08-31 18:39:03 +08:00
function checkOperation(op) {
if (!Array.isArray(op)) {
2020-08-31 01:17:28 +08:00
return;
}
2020-08-31 18:39:03 +08:00
op.forEach((action) => {
2020-08-31 01:17:28 +08:00
if (action.type === 'block') {
blockUpdate = {
reason: action.reason,
2021-06-24 15:13:03 +08:00
until: Math.round((Date.now() + action.duration) / 1000),
2020-08-31 01:17:28 +08:00
};
Pushy.setBlockUpdate(blockUpdate);
}
});
}
2021-07-29 00:50:48 +08:00
let downloadingThrottling = false;
let downloadedHash;
2020-09-16 13:01:14 +08:00
export async function downloadUpdate(options, eventListeners) {
2019-10-04 22:27:33 +08:00
assertRelease();
2016-04-04 23:02:28 +08:00
if (!options.update) {
return;
}
2021-07-29 00:50:48 +08:00
if (downloadedHash === options.hash) {
logger(`duplicated downloaded hash ${downloadedHash}, ignored`);
return;
}
2021-08-22 11:33:54 +08:00
if (readyHash) {
logger(`hash ${readyHash} applied. reboot first`);
return;
}
2021-07-29 00:50:48 +08:00
if (downloadingThrottling) {
logger('repeated downloading, ignored');
return;
}
downloadingThrottling = true;
setTimeout(() => {
downloadingThrottling = false;
}, 3000);
2020-09-27 22:16:27 +08:00
let progressHandler;
2020-09-16 13:01:14 +08:00
if (eventListeners) {
if (eventListeners.onDownloadProgress) {
const downloadCallback = eventListeners.onDownloadProgress;
2021-04-08 23:46:14 +08:00
progressHandler = eventEmitter.addListener(
'RCTPushyDownloadProgress',
(progressData) => {
if (progressData.hash === options.hash) {
downloadCallback(progressData);
}
},
);
2020-09-16 13:01:14 +08:00
}
}
2016-04-05 15:36:05 +08:00
if (options.diffUrl) {
2020-09-16 13:01:14 +08:00
logger('downloading diff');
2021-06-19 13:01:07 +08:00
try {
await Pushy.downloadPatchFromPpk({
updateUrl: options.diffUrl,
hash: options.hash,
originHash: currentVersion,
});
} catch (e) {
logger(e.message);
logger('diff error, try pdiff');
await Pushy.downloadPatchFromPackage({
updateUrl: options.pdiffUrl,
hash: options.hash,
});
}
2016-04-05 15:36:05 +08:00
} else if (options.pdiffUrl) {
2020-09-16 13:01:14 +08:00
logger('downloading pdiff');
2019-11-16 00:31:30 +08:00
await Pushy.downloadPatchFromPackage({
2016-04-04 23:02:28 +08:00
updateUrl: options.pdiffUrl,
hash: options.hash,
});
2016-04-04 23:02:28 +08:00
}
2021-10-04 23:19:22 +08:00
setLocalHashInfo(options.hash, {
name: options.name,
description: options.description,
metaInfo: options.metaInfo,
});
2020-09-27 22:16:27 +08:00
progressHandler && progressHandler.remove();
2021-07-29 00:50:48 +08:00
downloadedHash = options.hash;
2016-04-05 17:55:04 +08:00
return options.hash;
2016-04-04 23:02:28 +08:00
}
2021-08-22 11:33:54 +08:00
let readyHash;
function assertHash(hash) {
if (!downloadedHash) {
logger(`no downloaded hash`);
return;
}
if (hash !== downloadedHash) {
logger(`use downloaded hash ${downloadedHash} first`);
return;
}
if (readyHash === hash) {
logger(`hash ${readyHash} already applied. reboot first.`);
return;
}
readyHash = hash;
}
2019-10-04 22:27:33 +08:00
export function switchVersion(hash) {
assertRelease();
2021-08-22 11:33:54 +08:00
assertHash(hash);
logger('switchVersion: ' + hash);
2020-09-24 22:44:37 +08:00
Pushy.reloadUpdate({ hash });
2016-04-04 23:02:28 +08:00
}
2019-10-04 22:27:33 +08:00
export function switchVersionLater(hash) {
assertRelease();
2021-08-22 11:33:54 +08:00
assertHash(hash);
logger('switchVersionLater: ' + hash);
2020-09-24 22:44:37 +08:00
Pushy.setNeedUpdate({ hash });
2016-04-04 23:02:28 +08:00
}
2016-04-04 23:45:29 +08:00
2021-07-29 00:50:48 +08:00
let marked = false;
2016-04-05 17:00:03 +08:00
export function markSuccess() {
2019-10-04 22:27:33 +08:00
assertRelease();
2021-07-29 00:50:48 +08:00
if (marked) {
logger('repeated markSuccess, ignored');
return;
}
marked = true;
2020-09-16 13:01:14 +08:00
logger('markSuccess');
2019-11-16 00:31:30 +08:00
Pushy.markSuccess();
2016-04-04 23:45:29 +08:00
}
2020-09-27 22:16:27 +08:00
export async function downloadAndInstallApk({ url, onDownloadProgress }) {
logger('downloadAndInstallApk');
2021-05-18 12:23:33 +08:00
if (Platform.OS === 'android' && Platform.Version <= 23) {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
);
if (granted !== PermissionsAndroid.RESULTS.GRANTED) {
return;
}
} catch (err) {
console.warn(err);
}
}
2020-09-27 22:16:27 +08:00
let hash = Date.now().toString();
let progressHandler;
if (onDownloadProgress) {
2021-04-08 23:46:14 +08:00
progressHandler = eventEmitter.addListener(
'RCTPushyDownloadProgress',
(progressData) => {
if (progressData.hash === hash) {
onDownloadProgress(progressData);
}
},
);
2020-09-27 22:16:27 +08:00
}
await Pushy.downloadAndInstallApk({
url,
target: 'update.apk',
hash,
});
progressHandler && progressHandler.remove();
}