Skip to content

Commit 5368d97

Browse files
committed
Inline package-mixins into CodePush.js
1 parent ad5bb01 commit 5368d97

2 files changed

Lines changed: 94 additions & 105 deletions

File tree

CodePush.js

Lines changed: 94 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,80 @@
1-
import { AcquisitionManager as Sdk } from "./lib/acquisition-sdk/acquisition-sdk";
1+
import { AcquisitionManager as Sdk, DownloadStatus } from "./lib/acquisition-sdk/acquisition-sdk";
22
import { Alert } from "./AlertAdapter";
33
import requestFetchAdapter from "./request-fetch-adapter";
4-
import { AppState, Platform } from "react-native";
4+
import { AppState, NativeEventEmitter, Platform } from "react-native";
55
import log from "./logging";
66
import hoistStatics from 'hoist-non-react-statics';
77

88
let NativeCodePush = require("react-native").NativeModules.CodePush;
9-
const PackageMixins = require("./package-mixins")(NativeCodePush);
9+
10+
// Reporting this event is important, but avoid blocking install()/restartApp() indefinitely
11+
// on a stalled network request.
12+
const REPORT_STATUS_DOWNLOAD_TIMEOUT_MS = 5000;
13+
14+
async function withTimeout(promise, timeoutMs) {
15+
let timer;
16+
const timeout = new Promise((_, reject) => {
17+
timer = setTimeout(() => reject(new Error(`Timed out after ${timeoutMs}ms`)), timeoutMs);
18+
});
19+
20+
try {
21+
return await Promise.race([promise, timeout]);
22+
} finally {
23+
clearTimeout(timer);
24+
}
25+
}
26+
27+
// Downloads a remote package, augmenting it with a bound install() method
28+
// beyond what's included in the metadata sent by the server.
29+
async function downloadUpdate(remotePackage, downloadProgressCallback, reportStatusDownload) {
30+
if (!remotePackage.downloadUrl) {
31+
throw new Error("Cannot download an update without a download url");
32+
}
33+
34+
let downloadProgressSubscription;
35+
if (downloadProgressCallback) {
36+
const codePushEventEmitter = new NativeEventEmitter(NativeCodePush);
37+
// Use event subscription to obtain download progress.
38+
downloadProgressSubscription = codePushEventEmitter.addListener(
39+
"CodePushDownloadProgress",
40+
downloadProgressCallback
41+
);
42+
}
43+
44+
const downloadStartTime = Date.now();
45+
const reportDownloadStatus = async (status) => {
46+
if (!reportStatusDownload) return;
47+
// Only report a duration on success: on failure, this would be the time until
48+
// the download broke rather than a completed download's duration, and could be misleading.
49+
const downloadDurationMs = status === DownloadStatus.Succeeded ? Date.now() - downloadStartTime : undefined;
50+
try {
51+
await withTimeout(reportStatusDownload({ ...remotePackage, downloadDurationMs, status }), REPORT_STATUS_DOWNLOAD_TIMEOUT_MS);
52+
} catch (err) {
53+
log(`Report download status failed: ${err}`);
54+
}
55+
};
56+
57+
// Use the downloaded package info. Native code will save the package info
58+
// so that the client knows what the current package version is.
59+
try {
60+
const updatePackageCopy = Object.assign({}, remotePackage);
61+
Object.keys(updatePackageCopy).forEach((key) => (typeof updatePackageCopy[key] === 'function') && delete updatePackageCopy[key]);
62+
63+
let downloadedPackage;
64+
try {
65+
downloadedPackage = await NativeCodePush.downloadUpdate(updatePackageCopy, !!downloadProgressCallback);
66+
} catch (err) {
67+
await reportDownloadStatus(DownloadStatus.Failed);
68+
throw err;
69+
}
70+
71+
await reportDownloadStatus(DownloadStatus.Succeeded);
72+
73+
return attachLocalPackageMethods({ ...downloadedPackage, isPending: false }); // A freshly downloaded package hasn't been installed yet
74+
} finally {
75+
downloadProgressSubscription && downloadProgressSubscription.remove();
76+
}
77+
}
1078

1179
async function checkForUpdate(deploymentKey = null, handleBinaryVersionMismatchCallback = null) {
1280
/*
@@ -82,7 +150,8 @@ async function checkForUpdate(deploymentKey = null, handleBinaryVersionMismatchC
82150

83151
return null;
84152
} else {
85-
const remotePackage = { ...update, ...PackageMixins.remote(sdk.reportStatusDownload) };
153+
const remotePackage = { ...update, isPending: false }; // A remote package could never be in a pending state
154+
remotePackage.download = (downloadProgressCallback) => downloadUpdate(remotePackage, downloadProgressCallback, sdk.reportStatusDownload);
86155
remotePackage.failedInstall = await NativeCodePush.isFailedUpdate(remotePackage.packageHash);
87156
remotePackage.deploymentKey = deploymentKey || nativeConfig.deploymentKey;
88157
return remotePackage;
@@ -107,10 +176,30 @@ async function getCurrentPackage() {
107176
return await getUpdateMetadata(CodePush.UpdateState.LATEST);
108177
}
109178

179+
async function installUpdate(localPackage, installMode = NativeCodePush.codePushInstallModeOnNextRestart, minimumBackgroundDuration = 0, updateInstalledCallback) {
180+
const localPackageCopy = Object.assign({}, localPackage); // In dev mode, React Native deep freezes any object queued over the bridge
181+
await NativeCodePush.installUpdate(localPackageCopy, installMode, minimumBackgroundDuration);
182+
updateInstalledCallback && updateInstalledCallback();
183+
if (installMode == NativeCodePush.codePushInstallModeImmediate) {
184+
NativeCodePush.restartApp(false);
185+
} else {
186+
NativeCodePush.clearPendingRestart();
187+
localPackage.isPending = true; // Mark the package as pending since it hasn't been applied yet
188+
}
189+
}
190+
191+
// Augments a raw local package (as returned by native code) with a bound
192+
// install() method beyond what's included in the metadata sent by the server.
193+
function attachLocalPackageMethods(localPackage) {
194+
localPackage.install = (installMode, minimumBackgroundDuration, updateInstalledCallback) =>
195+
installUpdate(localPackage, installMode, minimumBackgroundDuration, updateInstalledCallback);
196+
return localPackage;
197+
}
198+
110199
async function getUpdateMetadata(updateState) {
111200
let updateMetadata = await NativeCodePush.getUpdateMetadata(updateState || CodePush.UpdateState.RUNNING);
112201
if (updateMetadata) {
113-
updateMetadata = {...PackageMixins.local, ...updateMetadata};
202+
updateMetadata = attachLocalPackageMethods({ ...updateMetadata });
114203
updateMetadata.failedInstall = await NativeCodePush.isFailedUpdate(updateMetadata.packageHash);
115204
updateMetadata.isFirstRun = await NativeCodePush.isFirstRun(updateMetadata.packageHash);
116205
}

package-mixins.js

Lines changed: 0 additions & 100 deletions
This file was deleted.

0 commit comments

Comments
 (0)