react-native-update 8.2.0 → 8.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/src/main/AndroidManifest.xml +0 -3
- package/lib/endpoint.ts +53 -0
- package/lib/index.web.js +2 -1
- package/lib/{main.js → main.ts} +118 -80
- package/lib/{simpleUpdate.js → simpleUpdate.tsx} +21 -6
- package/lib/type.ts +71 -0
- package/lib/utils.ts +9 -0
- package/package.json +9 -3
- package/lib/endpoint.js +0 -93
- package/lib/index.d.ts +0 -94
- /package/lib/{index.js → index.ts} +0 -0
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="utf-8"?>
|
|
2
2
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
3
3
|
package="cn.reactnative.modules.update">
|
|
4
|
-
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
|
5
|
-
|
|
6
|
-
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
|
7
4
|
<application>
|
|
8
5
|
<meta-data android:name="pushy_build_time" android:value="@string/pushy_build_time" />
|
|
9
6
|
<provider
|
package/lib/endpoint.ts
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { logger } from './utils';
|
|
2
|
+
|
|
3
|
+
let currentEndpoint = 'https://update.react-native.cn/api';
|
|
4
|
+
let backupEndpoints: string[] = ['https://update.reactnative.cn/api'];
|
|
5
|
+
let backupEndpointsQueryUrl: string | null = null;
|
|
6
|
+
|
|
7
|
+
export async function updateBackupEndpoints() {
|
|
8
|
+
if (backupEndpointsQueryUrl) {
|
|
9
|
+
try {
|
|
10
|
+
const resp = await fetch(backupEndpointsQueryUrl);
|
|
11
|
+
const remoteEndpoints = await resp.json();
|
|
12
|
+
if (Array.isArray(remoteEndpoints)) {
|
|
13
|
+
backupEndpoints = Array.from(
|
|
14
|
+
new Set([...backupEndpoints, ...remoteEndpoints]),
|
|
15
|
+
);
|
|
16
|
+
logger('fetch remote endpoints:', remoteEndpoints);
|
|
17
|
+
logger('merged backup endpoints:', backupEndpoints);
|
|
18
|
+
}
|
|
19
|
+
} catch (e) {
|
|
20
|
+
logger('fetch remote endpoints failed');
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return backupEndpoints;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getCheckUrl(APPKEY, endpoint = currentEndpoint) {
|
|
27
|
+
return `${endpoint}/checkUpdate/${APPKEY}`;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* @param {string} main - The main api endpoint
|
|
32
|
+
* @param {string[]} [backups] - The back up endpoints.
|
|
33
|
+
* @param {string} [backupQueryUrl] - An url that return a json file containing an array of endpoint.
|
|
34
|
+
* like: ["https://backup.api/1", "https://backup.api/2"]
|
|
35
|
+
*/
|
|
36
|
+
export function setCustomEndpoints({
|
|
37
|
+
main,
|
|
38
|
+
backups,
|
|
39
|
+
backupQueryUrl,
|
|
40
|
+
}: {
|
|
41
|
+
main: string;
|
|
42
|
+
backups?: string[];
|
|
43
|
+
backupQueryUrl?: string;
|
|
44
|
+
}) {
|
|
45
|
+
currentEndpoint = main;
|
|
46
|
+
backupEndpointsQueryUrl = null;
|
|
47
|
+
if (Array.isArray(backups) && backups.length > 0) {
|
|
48
|
+
backupEndpoints = backups;
|
|
49
|
+
}
|
|
50
|
+
if (typeof backupQueryUrl === 'string') {
|
|
51
|
+
backupEndpointsQueryUrl = backupQueryUrl;
|
|
52
|
+
}
|
|
53
|
+
}
|
package/lib/index.web.js
CHANGED
|
@@ -14,4 +14,5 @@ export const markSuccess = noop;
|
|
|
14
14
|
export const downloadAndInstallApk = noop;
|
|
15
15
|
export const setCustomEndpoints = noop;
|
|
16
16
|
export const getCurrentVersionInfo = noop;
|
|
17
|
-
export const simpleUpdate =
|
|
17
|
+
export const simpleUpdate = (app) => app;
|
|
18
|
+
export const onPushyEvents = noop;
|
package/lib/{main.js → main.ts}
RENAMED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
updateBackupEndpoints,
|
|
3
3
|
getCheckUrl,
|
|
4
4
|
setCustomEndpoints,
|
|
5
|
-
getReportUrl,
|
|
6
5
|
} from './endpoint';
|
|
7
6
|
import {
|
|
8
7
|
NativeEventEmitter,
|
|
@@ -10,62 +9,71 @@ import {
|
|
|
10
9
|
Platform,
|
|
11
10
|
PermissionsAndroid,
|
|
12
11
|
} from 'react-native';
|
|
12
|
+
import {
|
|
13
|
+
CheckResult,
|
|
14
|
+
EventType,
|
|
15
|
+
ProgressData,
|
|
16
|
+
UpdateAvailableResult,
|
|
17
|
+
UpdateEventsListener,
|
|
18
|
+
} from './type';
|
|
19
|
+
import { assertRelease, logger } from './utils';
|
|
13
20
|
export { setCustomEndpoints };
|
|
14
21
|
const {
|
|
15
22
|
version: v,
|
|
16
23
|
} = require('react-native/Libraries/Core/ReactNativeVersion');
|
|
17
24
|
const RNVersion = `${v.major}.${v.minor}.${v.patch}`;
|
|
18
25
|
|
|
19
|
-
|
|
26
|
+
export const PushyModule = NativeModules.Pushy;
|
|
20
27
|
|
|
21
|
-
if (!
|
|
28
|
+
if (!PushyModule) {
|
|
22
29
|
throw new Error('react-native-update模块无法加载,请对照安装文档检查配置。');
|
|
23
30
|
}
|
|
31
|
+
const PushyConstants = PushyModule;
|
|
24
32
|
|
|
25
|
-
export const downloadRootDir =
|
|
26
|
-
export const packageVersion =
|
|
27
|
-
export const currentVersion =
|
|
28
|
-
export const isFirstTime =
|
|
29
|
-
const rolledBackVersion =
|
|
33
|
+
export const downloadRootDir = PushyConstants.downloadRootDir;
|
|
34
|
+
export const packageVersion = PushyConstants.packageVersion;
|
|
35
|
+
export const currentVersion = PushyConstants.currentVersion;
|
|
36
|
+
export const isFirstTime = PushyConstants.isFirstTime;
|
|
37
|
+
const rolledBackVersion = PushyConstants.rolledBackVersion;
|
|
30
38
|
export const isRolledBack = typeof rolledBackVersion === 'string';
|
|
31
39
|
|
|
32
|
-
export const buildTime =
|
|
33
|
-
let blockUpdate =
|
|
34
|
-
let uuid =
|
|
40
|
+
export const buildTime = PushyConstants.buildTime;
|
|
41
|
+
let blockUpdate = PushyConstants.blockUpdate;
|
|
42
|
+
let uuid = PushyConstants.uuid;
|
|
35
43
|
|
|
36
|
-
if (Platform.OS === 'android' && !
|
|
44
|
+
if (Platform.OS === 'android' && !PushyConstants.isUsingBundleUrl) {
|
|
37
45
|
throw new Error(
|
|
38
46
|
'react-native-update模块无法加载,请对照文档检查Bundle URL的配置',
|
|
39
47
|
);
|
|
40
48
|
}
|
|
41
49
|
|
|
42
|
-
function setLocalHashInfo(hash, info) {
|
|
43
|
-
|
|
50
|
+
function setLocalHashInfo(hash: string, info: Record<string, any>) {
|
|
51
|
+
PushyModule.setLocalHashInfo(hash, JSON.stringify(info));
|
|
44
52
|
}
|
|
45
53
|
|
|
46
|
-
async function getLocalHashInfo(hash) {
|
|
47
|
-
return JSON.parse(await
|
|
54
|
+
async function getLocalHashInfo(hash: string) {
|
|
55
|
+
return JSON.parse(await PushyModule.getLocalHashInfo(hash));
|
|
48
56
|
}
|
|
49
57
|
|
|
50
|
-
export async function getCurrentVersionInfo() {
|
|
58
|
+
export async function getCurrentVersionInfo(): Promise<{
|
|
59
|
+
name?: string;
|
|
60
|
+
description?: string;
|
|
61
|
+
metaInfo?: string;
|
|
62
|
+
}> {
|
|
51
63
|
return currentVersion ? (await getLocalHashInfo(currentVersion)) || {} : {};
|
|
52
64
|
}
|
|
53
65
|
|
|
54
|
-
const eventEmitter = new NativeEventEmitter(
|
|
66
|
+
const eventEmitter = new NativeEventEmitter(PushyModule);
|
|
55
67
|
|
|
56
68
|
if (!uuid) {
|
|
57
69
|
uuid = require('nanoid/non-secure').nanoid();
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function logger(...args) {
|
|
62
|
-
console.log('Pushy: ', ...args);
|
|
70
|
+
PushyModule.setUuid(uuid);
|
|
63
71
|
}
|
|
64
72
|
|
|
65
73
|
const noop = () => {};
|
|
66
|
-
let reporter = noop;
|
|
74
|
+
let reporter: UpdateEventsListener = noop;
|
|
67
75
|
|
|
68
|
-
export function
|
|
76
|
+
export function onPushyEvents(customReporter: UpdateEventsListener) {
|
|
69
77
|
reporter = customReporter;
|
|
70
78
|
if (isRolledBack) {
|
|
71
79
|
report({
|
|
@@ -77,7 +85,15 @@ export function onEvents(customReporter) {
|
|
|
77
85
|
}
|
|
78
86
|
}
|
|
79
87
|
|
|
80
|
-
function report({
|
|
88
|
+
function report({
|
|
89
|
+
type,
|
|
90
|
+
message = '',
|
|
91
|
+
data = {},
|
|
92
|
+
}: {
|
|
93
|
+
type: EventType;
|
|
94
|
+
message?: string;
|
|
95
|
+
data?: Record<string, string | number>;
|
|
96
|
+
}) {
|
|
81
97
|
logger(type + ' ' + message);
|
|
82
98
|
reporter({
|
|
83
99
|
type,
|
|
@@ -101,19 +117,10 @@ export const cInfo = {
|
|
|
101
117
|
uuid,
|
|
102
118
|
};
|
|
103
119
|
|
|
104
|
-
function assertRelease() {
|
|
105
|
-
if (__DEV__) {
|
|
106
|
-
throw new Error('react-native-update 只能在 RELEASE 版本中运行.');
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
120
|
let lastChecking;
|
|
111
121
|
const empty = {};
|
|
112
|
-
let lastResult;
|
|
113
|
-
export async function checkUpdate(APPKEY
|
|
114
|
-
if (typeof APPKEY !== 'string') {
|
|
115
|
-
throw new Error('未检查到合法的APPKEY,请查看update.json文件是否正确生成');
|
|
116
|
-
}
|
|
122
|
+
let lastResult: CheckResult;
|
|
123
|
+
export async function checkUpdate(APPKEY: string) {
|
|
117
124
|
assertRelease();
|
|
118
125
|
const now = Date.now();
|
|
119
126
|
if (lastResult && lastChecking && now - lastChecking < 1000 * 60) {
|
|
@@ -131,40 +138,55 @@ export async function checkUpdate(APPKEY, isRetry) {
|
|
|
131
138
|
return lastResult || empty;
|
|
132
139
|
}
|
|
133
140
|
report({ type: 'checking' });
|
|
141
|
+
const fetchPayload = {
|
|
142
|
+
method: 'POST',
|
|
143
|
+
headers: {
|
|
144
|
+
Accept: 'application/json',
|
|
145
|
+
'Content-Type': 'application/json',
|
|
146
|
+
},
|
|
147
|
+
body: JSON.stringify({
|
|
148
|
+
packageVersion,
|
|
149
|
+
hash: currentVersion,
|
|
150
|
+
buildTime,
|
|
151
|
+
cInfo,
|
|
152
|
+
}),
|
|
153
|
+
};
|
|
134
154
|
let resp;
|
|
135
155
|
try {
|
|
136
|
-
resp = await fetch(getCheckUrl(APPKEY),
|
|
137
|
-
method: 'POST',
|
|
138
|
-
headers: {
|
|
139
|
-
Accept: 'application/json',
|
|
140
|
-
'Content-Type': 'application/json',
|
|
141
|
-
},
|
|
142
|
-
body: JSON.stringify({
|
|
143
|
-
packageVersion,
|
|
144
|
-
hash: currentVersion,
|
|
145
|
-
buildTime,
|
|
146
|
-
cInfo,
|
|
147
|
-
}),
|
|
148
|
-
});
|
|
156
|
+
resp = await fetch(getCheckUrl(APPKEY), fetchPayload);
|
|
149
157
|
} catch (e) {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
158
|
+
report({
|
|
159
|
+
type: 'errorChecking',
|
|
160
|
+
message: '无法连接主更新服务器,尝试备用节点',
|
|
161
|
+
});
|
|
162
|
+
const backupEndpoints = await updateBackupEndpoints();
|
|
163
|
+
if (backupEndpoints) {
|
|
164
|
+
try {
|
|
165
|
+
resp = await Promise.race(
|
|
166
|
+
backupEndpoints.map((endpoint) =>
|
|
167
|
+
fetch(getCheckUrl(APPKEY, endpoint), fetchPayload),
|
|
168
|
+
),
|
|
169
|
+
);
|
|
170
|
+
} catch {}
|
|
156
171
|
}
|
|
157
|
-
await tryBackupEndpoints();
|
|
158
|
-
return checkUpdate(APPKEY, true);
|
|
159
172
|
}
|
|
160
|
-
|
|
161
|
-
|
|
173
|
+
if (!resp) {
|
|
174
|
+
report({
|
|
175
|
+
type: 'errorChecking',
|
|
176
|
+
message: '无法连接更新服务器,请检查网络连接后重试',
|
|
177
|
+
});
|
|
178
|
+
return lastResult || empty;
|
|
179
|
+
}
|
|
180
|
+
const result: CheckResult = await resp.json();
|
|
162
181
|
|
|
182
|
+
lastResult = result;
|
|
183
|
+
// @ts-ignore
|
|
163
184
|
checkOperation(result.op);
|
|
164
185
|
|
|
165
186
|
if (resp.status !== 200) {
|
|
166
187
|
report({
|
|
167
188
|
type: 'errorChecking',
|
|
189
|
+
//@ts-ignore
|
|
168
190
|
message: result.message,
|
|
169
191
|
});
|
|
170
192
|
}
|
|
@@ -172,7 +194,9 @@ export async function checkUpdate(APPKEY, isRetry) {
|
|
|
172
194
|
return result;
|
|
173
195
|
}
|
|
174
196
|
|
|
175
|
-
function checkOperation(
|
|
197
|
+
function checkOperation(
|
|
198
|
+
op: { type: string; reason: string; duration: number }[],
|
|
199
|
+
) {
|
|
176
200
|
if (!Array.isArray(op)) {
|
|
177
201
|
return;
|
|
178
202
|
}
|
|
@@ -182,14 +206,19 @@ function checkOperation(op) {
|
|
|
182
206
|
reason: action.reason,
|
|
183
207
|
until: Math.round((Date.now() + action.duration) / 1000),
|
|
184
208
|
};
|
|
185
|
-
|
|
209
|
+
PushyModule.setBlockUpdate(blockUpdate);
|
|
186
210
|
}
|
|
187
211
|
});
|
|
188
212
|
}
|
|
189
213
|
|
|
190
214
|
let downloadingThrottling = false;
|
|
191
|
-
let downloadedHash;
|
|
192
|
-
export async function downloadUpdate(
|
|
215
|
+
let downloadedHash: string;
|
|
216
|
+
export async function downloadUpdate(
|
|
217
|
+
options: UpdateAvailableResult,
|
|
218
|
+
eventListeners?: {
|
|
219
|
+
onDownloadProgress?: (data: ProgressData) => void;
|
|
220
|
+
},
|
|
221
|
+
) {
|
|
193
222
|
assertRelease();
|
|
194
223
|
if (!options.update) {
|
|
195
224
|
return;
|
|
@@ -229,7 +258,7 @@ export async function downloadUpdate(options, eventListeners) {
|
|
|
229
258
|
if (options.diffUrl) {
|
|
230
259
|
logger('downloading diff');
|
|
231
260
|
try {
|
|
232
|
-
await
|
|
261
|
+
await PushyModule.downloadPatchFromPpk({
|
|
233
262
|
updateUrl: options.diffUrl,
|
|
234
263
|
hash: options.hash,
|
|
235
264
|
originHash: currentVersion,
|
|
@@ -242,7 +271,7 @@ export async function downloadUpdate(options, eventListeners) {
|
|
|
242
271
|
if (!succeeded && options.pdiffUrl) {
|
|
243
272
|
logger('downloading pdiff');
|
|
244
273
|
try {
|
|
245
|
-
await
|
|
274
|
+
await PushyModule.downloadPatchFromPackage({
|
|
246
275
|
updateUrl: options.pdiffUrl,
|
|
247
276
|
hash: options.hash,
|
|
248
277
|
});
|
|
@@ -254,7 +283,7 @@ export async function downloadUpdate(options, eventListeners) {
|
|
|
254
283
|
if (!succeeded && options.updateUrl) {
|
|
255
284
|
logger('downloading full patch');
|
|
256
285
|
try {
|
|
257
|
-
await
|
|
286
|
+
await PushyModule.downloadFullUpdate({
|
|
258
287
|
updateUrl: options.updateUrl,
|
|
259
288
|
hash: options.hash,
|
|
260
289
|
});
|
|
@@ -276,7 +305,7 @@ export async function downloadUpdate(options, eventListeners) {
|
|
|
276
305
|
return options.hash;
|
|
277
306
|
}
|
|
278
307
|
|
|
279
|
-
function assertHash(hash) {
|
|
308
|
+
function assertHash(hash: string) {
|
|
280
309
|
if (!downloadedHash) {
|
|
281
310
|
logger(`no downloaded hash`);
|
|
282
311
|
return;
|
|
@@ -288,19 +317,19 @@ function assertHash(hash) {
|
|
|
288
317
|
return true;
|
|
289
318
|
}
|
|
290
319
|
|
|
291
|
-
export function switchVersion(hash) {
|
|
320
|
+
export function switchVersion(hash: string) {
|
|
292
321
|
assertRelease();
|
|
293
322
|
if (assertHash(hash)) {
|
|
294
323
|
logger('switchVersion: ' + hash);
|
|
295
|
-
|
|
324
|
+
PushyModule.reloadUpdate({ hash });
|
|
296
325
|
}
|
|
297
326
|
}
|
|
298
327
|
|
|
299
|
-
export function switchVersionLater(hash) {
|
|
328
|
+
export function switchVersionLater(hash: string) {
|
|
300
329
|
assertRelease();
|
|
301
330
|
if (assertHash(hash)) {
|
|
302
331
|
logger('switchVersionLater: ' + hash);
|
|
303
|
-
|
|
332
|
+
PushyModule.setNeedUpdate({ hash });
|
|
304
333
|
}
|
|
305
334
|
}
|
|
306
335
|
|
|
@@ -312,13 +341,22 @@ export function markSuccess() {
|
|
|
312
341
|
return;
|
|
313
342
|
}
|
|
314
343
|
marked = true;
|
|
315
|
-
|
|
316
|
-
report(
|
|
344
|
+
PushyModule.markSuccess();
|
|
345
|
+
report({ type: 'markSuccess' });
|
|
317
346
|
}
|
|
318
347
|
|
|
319
|
-
export async function downloadAndInstallApk({
|
|
320
|
-
|
|
321
|
-
|
|
348
|
+
export async function downloadAndInstallApk({
|
|
349
|
+
url,
|
|
350
|
+
onDownloadProgress,
|
|
351
|
+
}: {
|
|
352
|
+
url: string;
|
|
353
|
+
onDownloadProgress?: (data: ProgressData) => void;
|
|
354
|
+
}) {
|
|
355
|
+
if (Platform.OS !== 'android') {
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
report({ type: 'downloadingApk' });
|
|
359
|
+
if (Platform.Version <= 23) {
|
|
322
360
|
try {
|
|
323
361
|
const granted = await PermissionsAndroid.request(
|
|
324
362
|
PermissionsAndroid.PERMISSIONS.WRITE_EXTERNAL_STORAGE,
|
|
@@ -335,14 +373,14 @@ export async function downloadAndInstallApk({ url, onDownloadProgress }) {
|
|
|
335
373
|
if (onDownloadProgress) {
|
|
336
374
|
progressHandler = eventEmitter.addListener(
|
|
337
375
|
'RCTPushyDownloadProgress',
|
|
338
|
-
(progressData) => {
|
|
376
|
+
(progressData: ProgressData) => {
|
|
339
377
|
if (progressData.hash === hash) {
|
|
340
378
|
onDownloadProgress(progressData);
|
|
341
379
|
}
|
|
342
380
|
},
|
|
343
381
|
);
|
|
344
382
|
}
|
|
345
|
-
await
|
|
383
|
+
await PushyModule.downloadAndInstallApk({
|
|
346
384
|
url,
|
|
347
385
|
target: 'update.apk',
|
|
348
386
|
hash,
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import React, {
|
|
2
|
-
import {
|
|
1
|
+
import React, { PureComponent, ComponentType } from 'react';
|
|
2
|
+
import {
|
|
3
|
+
Platform,
|
|
4
|
+
Alert,
|
|
5
|
+
Linking,
|
|
6
|
+
AppState,
|
|
7
|
+
NativeEventSubscription,
|
|
8
|
+
} from 'react-native';
|
|
3
9
|
|
|
4
10
|
import {
|
|
5
11
|
isFirstTime,
|
|
@@ -10,16 +16,25 @@ import {
|
|
|
10
16
|
switchVersionLater,
|
|
11
17
|
markSuccess,
|
|
12
18
|
downloadAndInstallApk,
|
|
19
|
+
onPushyEvents,
|
|
13
20
|
} from './main';
|
|
21
|
+
import { UpdateEventsListener } from './type';
|
|
14
22
|
|
|
15
|
-
export function simpleUpdate(
|
|
16
|
-
|
|
23
|
+
export function simpleUpdate(
|
|
24
|
+
WrappedComponent: ComponentType,
|
|
25
|
+
options: { appKey?: string; onPushyEvents?: UpdateEventsListener } = {},
|
|
26
|
+
) {
|
|
27
|
+
const { appKey, onPushyEvents: eventListeners } = options;
|
|
17
28
|
if (!appKey) {
|
|
18
29
|
throw new Error('appKey is required for simpleUpdate()');
|
|
19
30
|
}
|
|
31
|
+
if (typeof eventListeners === 'function') {
|
|
32
|
+
onPushyEvents(eventListeners);
|
|
33
|
+
}
|
|
20
34
|
return __DEV__
|
|
21
35
|
? WrappedComponent
|
|
22
|
-
: class AppUpdate extends
|
|
36
|
+
: class AppUpdate extends PureComponent {
|
|
37
|
+
stateListener: NativeEventSubscription;
|
|
23
38
|
componentDidMount() {
|
|
24
39
|
if (isRolledBack) {
|
|
25
40
|
Alert.alert('抱歉', '刚刚更新遭遇错误,已为您恢复到更新前版本');
|
|
@@ -70,7 +85,7 @@ export function simpleUpdate(WrappedComponent, options = {}) {
|
|
|
70
85
|
checkUpdate = async () => {
|
|
71
86
|
let info;
|
|
72
87
|
try {
|
|
73
|
-
info = await checkUpdate(appKey);
|
|
88
|
+
info = await checkUpdate(appKey!);
|
|
74
89
|
} catch (err) {
|
|
75
90
|
Alert.alert('更新检查失败', err.message);
|
|
76
91
|
return;
|
package/lib/type.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
export interface ExpiredResult {
|
|
2
|
+
upToDate?: false;
|
|
3
|
+
expired: true;
|
|
4
|
+
downloadUrl: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface UpTodateResult {
|
|
8
|
+
expired?: false;
|
|
9
|
+
upToDate: true;
|
|
10
|
+
paused?: 'app' | 'package';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface UpdateAvailableResult {
|
|
14
|
+
expired?: false;
|
|
15
|
+
upToDate?: false;
|
|
16
|
+
update: true;
|
|
17
|
+
name: string; // version name
|
|
18
|
+
hash: string;
|
|
19
|
+
description: string;
|
|
20
|
+
metaInfo: string;
|
|
21
|
+
pdiffUrl: string;
|
|
22
|
+
diffUrl?: string;
|
|
23
|
+
updateUrl?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type CheckResult =
|
|
27
|
+
| ExpiredResult
|
|
28
|
+
| UpTodateResult
|
|
29
|
+
| UpdateAvailableResult
|
|
30
|
+
| {};
|
|
31
|
+
|
|
32
|
+
export interface ProgressData {
|
|
33
|
+
hash: string;
|
|
34
|
+
received: number;
|
|
35
|
+
total: number;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export type EventType =
|
|
39
|
+
| 'rollback'
|
|
40
|
+
| 'errorChecking'
|
|
41
|
+
| 'checking'
|
|
42
|
+
| 'downloading'
|
|
43
|
+
| 'errorUpdate'
|
|
44
|
+
| 'markSuccess'
|
|
45
|
+
| 'downloadingApk'
|
|
46
|
+
| 'rejectStoragePermission'
|
|
47
|
+
| 'errorStoragePermission'
|
|
48
|
+
| 'errowDownloadAndInstallApk';
|
|
49
|
+
|
|
50
|
+
export interface EventData {
|
|
51
|
+
currentVersion: string;
|
|
52
|
+
cInfo: {
|
|
53
|
+
pushy: string;
|
|
54
|
+
rn: string;
|
|
55
|
+
os: string;
|
|
56
|
+
uuid: string;
|
|
57
|
+
};
|
|
58
|
+
packageVersion: string;
|
|
59
|
+
buildTime: number;
|
|
60
|
+
message?: string;
|
|
61
|
+
rolledBackVersion?: string;
|
|
62
|
+
newVersion?: string;
|
|
63
|
+
[key: string]: any;
|
|
64
|
+
}
|
|
65
|
+
export type UpdateEventsListener = ({
|
|
66
|
+
type,
|
|
67
|
+
data,
|
|
68
|
+
}: {
|
|
69
|
+
type: EventType;
|
|
70
|
+
data: EventData;
|
|
71
|
+
}) => void;
|
package/lib/utils.ts
ADDED
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-update",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.4.0",
|
|
4
4
|
"description": "react-native hot update",
|
|
5
|
-
"main": "lib/index.
|
|
5
|
+
"main": "lib/index.ts",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"prepublish": "yarn submodule",
|
|
8
8
|
"submodule": "git submodule update --init --recursive",
|
|
@@ -25,10 +25,16 @@
|
|
|
25
25
|
"url": "https://github.com/reactnativecn/react-native-pushy/issues"
|
|
26
26
|
},
|
|
27
27
|
"peerDependencies": {
|
|
28
|
-
"react-native": ">=0.
|
|
28
|
+
"react-native": ">=0.57.0"
|
|
29
29
|
},
|
|
30
30
|
"homepage": "https://github.com/reactnativecn/react-native-pushy#readme",
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"nanoid": "^3.3.3"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^20.8.9",
|
|
36
|
+
"@types/react": "^18.2.33",
|
|
37
|
+
"react-native": "^0.72.6",
|
|
38
|
+
"typescript": "^5.2.2"
|
|
33
39
|
}
|
|
34
40
|
}
|
package/lib/endpoint.js
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
let currentEndpoint = 'https://update.react-native.cn/api';
|
|
2
|
-
|
|
3
|
-
function ping(url, rejectImmediate) {
|
|
4
|
-
return new Promise((resolve, reject) => {
|
|
5
|
-
const xhr = new XMLHttpRequest();
|
|
6
|
-
xhr.onreadystatechange = (e) => {
|
|
7
|
-
if (xhr.readyState !== 4) {
|
|
8
|
-
return;
|
|
9
|
-
}
|
|
10
|
-
if (xhr.status === 200) {
|
|
11
|
-
resolve(url);
|
|
12
|
-
} else {
|
|
13
|
-
rejectImmediate ? reject() : setTimeout(reject, 5000);
|
|
14
|
-
}
|
|
15
|
-
};
|
|
16
|
-
xhr.open('HEAD', url);
|
|
17
|
-
xhr.send();
|
|
18
|
-
xhr.timeout = 5000;
|
|
19
|
-
xhr.ontimeout = reject;
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function logger(...args) {
|
|
24
|
-
// console.warn('pushy', ...args);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
let backupEndpoints = [];
|
|
28
|
-
let backupEndpointsQueryUrl =
|
|
29
|
-
'https://cdn.jsdelivr.net/gh/reactnativecn/react-native-pushy@master/endpoints.json';
|
|
30
|
-
|
|
31
|
-
export async function tryBackupEndpoints() {
|
|
32
|
-
if (!backupEndpoints.length && !backupEndpointsQueryUrl) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
try {
|
|
36
|
-
await ping(getStatusUrl(), true);
|
|
37
|
-
logger('current endpoint ok');
|
|
38
|
-
return;
|
|
39
|
-
} catch (e) {
|
|
40
|
-
logger('current endpoint failed');
|
|
41
|
-
}
|
|
42
|
-
if (!backupEndpoints.length && backupEndpointsQueryUrl) {
|
|
43
|
-
try {
|
|
44
|
-
const resp = await fetch(backupEndpointsQueryUrl);
|
|
45
|
-
backupEndpoints = await resp.json();
|
|
46
|
-
logger('get remote endpoints:', backupEndpoints);
|
|
47
|
-
} catch (e) {
|
|
48
|
-
logger('get remote endpoints failed');
|
|
49
|
-
return;
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
await pickFatestAvailableEndpoint();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async function pickFatestAvailableEndpoint(endpoints = backupEndpoints) {
|
|
56
|
-
const fastestEndpoint = await Promise.race(
|
|
57
|
-
endpoints.map(pingAndReturnEndpoint),
|
|
58
|
-
);
|
|
59
|
-
if (typeof fastestEndpoint === 'string') {
|
|
60
|
-
logger(`pick endpoint: ${fastestEndpoint}`);
|
|
61
|
-
currentEndpoint = fastestEndpoint;
|
|
62
|
-
} else {
|
|
63
|
-
logger('all remote endpoints failed');
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async function pingAndReturnEndpoint(endpoint = currentEndpoint) {
|
|
68
|
-
return ping(getStatusUrl(endpoint)).then(() => endpoint);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function getStatusUrl(endpoint = currentEndpoint) {
|
|
72
|
-
return `${endpoint}/status`;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function getCheckUrl(APPKEY, endpoint = currentEndpoint) {
|
|
76
|
-
return `${endpoint}/checkUpdate/${APPKEY}`;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
export function getReportUrl(endpoint = currentEndpoint) {
|
|
80
|
-
return `${endpoint}/report`;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export function setCustomEndpoints({ main, backups, backupQueryUrl }) {
|
|
84
|
-
currentEndpoint = main;
|
|
85
|
-
backupEndpointsQueryUrl = null;
|
|
86
|
-
if (Array.isArray(backups) && backups.length > 0) {
|
|
87
|
-
backupEndpoints = backups;
|
|
88
|
-
pickFatestAvailableEndpoint();
|
|
89
|
-
}
|
|
90
|
-
if (typeof backupQueryUrl === 'string') {
|
|
91
|
-
backupEndpointsQueryUrl = backupQueryUrl;
|
|
92
|
-
}
|
|
93
|
-
}
|
package/lib/index.d.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
export const downloadRootDir: string;
|
|
2
|
-
export const packageVersion: string;
|
|
3
|
-
export const currentVersion: string;
|
|
4
|
-
export const isFirstTime: boolean;
|
|
5
|
-
export const isRolledBack: boolean;
|
|
6
|
-
|
|
7
|
-
export interface ExpiredResult {
|
|
8
|
-
upToDate?: false;
|
|
9
|
-
expired: true;
|
|
10
|
-
downloadUrl: string;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface UpTodateResult {
|
|
14
|
-
expired?: false;
|
|
15
|
-
upToDate: true;
|
|
16
|
-
paused?: 'app' | 'package';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface UpdateAvailableResult {
|
|
20
|
-
expired?: false;
|
|
21
|
-
upToDate?: false;
|
|
22
|
-
update: true;
|
|
23
|
-
name: string; // version name
|
|
24
|
-
hash: string;
|
|
25
|
-
description: string;
|
|
26
|
-
metaInfo: string;
|
|
27
|
-
pdiffUrl: string;
|
|
28
|
-
diffUrl?: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export type CheckResult =
|
|
32
|
-
| ExpiredResult
|
|
33
|
-
| UpTodateResult
|
|
34
|
-
| UpdateAvailableResult;
|
|
35
|
-
|
|
36
|
-
export function checkUpdate(appkey: string): Promise<CheckResult>;
|
|
37
|
-
|
|
38
|
-
export function downloadUpdate(
|
|
39
|
-
info: UpdateAvailableResult,
|
|
40
|
-
eventListeners?: {
|
|
41
|
-
onDownloadProgress?: (data: ProgressData) => void;
|
|
42
|
-
},
|
|
43
|
-
): Promise<undefined | string>;
|
|
44
|
-
|
|
45
|
-
export function switchVersion(hash: string): void;
|
|
46
|
-
|
|
47
|
-
export function switchVersionLater(hash: string): void;
|
|
48
|
-
|
|
49
|
-
export function markSuccess(): void;
|
|
50
|
-
|
|
51
|
-
export function downloadAndInstallApk({
|
|
52
|
-
url,
|
|
53
|
-
onDownloadProgress,
|
|
54
|
-
}: {
|
|
55
|
-
url: string;
|
|
56
|
-
onDownloadProgress?: (data: ProgressData) => void;
|
|
57
|
-
}): Promise<void>;
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* @param {string} main - The main api endpoint
|
|
61
|
-
* @param {string[]} [backups] - The back up endpoints.
|
|
62
|
-
* @param {string} [backupQueryUrl] - An url that return a json file containing an array of endpoint.
|
|
63
|
-
* like: ["https://backup.api/1", "https://backup.api/2"]
|
|
64
|
-
*/
|
|
65
|
-
export function setCustomEndpoints({
|
|
66
|
-
main,
|
|
67
|
-
backups,
|
|
68
|
-
backupQueryUrl,
|
|
69
|
-
}: {
|
|
70
|
-
main: string;
|
|
71
|
-
backups?: string[];
|
|
72
|
-
backupQueryUrl?: string;
|
|
73
|
-
}): void;
|
|
74
|
-
|
|
75
|
-
export function getCurrentVersionInfo(): Promise<{
|
|
76
|
-
name?: string;
|
|
77
|
-
description?: string;
|
|
78
|
-
metaInfo?: string;
|
|
79
|
-
}>;
|
|
80
|
-
|
|
81
|
-
interface ProgressData {
|
|
82
|
-
hash: string;
|
|
83
|
-
received: number;
|
|
84
|
-
total: number;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
interface SimpleUpdateOptions {
|
|
88
|
-
appKey: string;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
export function simpleUpdate(
|
|
92
|
-
wrappedComponent: any,
|
|
93
|
-
options: SimpleUpdateOptions,
|
|
94
|
-
): any;
|
|
File without changes
|