react-native-instantpay-code-push 1.1.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/InstantpayCodePush.podspec +20 -0
- package/LICENSE +20 -0
- package/README.md +158 -0
- package/android/build.gradle +91 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/com/instantpaycodepush/BundleFileStorageService.kt +835 -0
- package/android/src/main/java/com/instantpaycodepush/BundleMetadata.kt +249 -0
- package/android/src/main/java/com/instantpaycodepush/CommonHelper.kt +39 -0
- package/android/src/main/java/com/instantpaycodepush/DecompressService.kt +85 -0
- package/android/src/main/java/com/instantpaycodepush/DecompressionStrategy.kt +24 -0
- package/android/src/main/java/com/instantpaycodepush/FileManagerService.kt +105 -0
- package/android/src/main/java/com/instantpaycodepush/HashUtils.kt +50 -0
- package/android/src/main/java/com/instantpaycodepush/InstantpayCodePushModule.kt +182 -0
- package/android/src/main/java/com/instantpaycodepush/InstantpayCodePushPackage.kt +33 -0
- package/android/src/main/java/com/instantpaycodepush/IpayCodePush.kt +101 -0
- package/android/src/main/java/com/instantpaycodepush/IpayCodePushException.kt +135 -0
- package/android/src/main/java/com/instantpaycodepush/IpayCodePushImpl.kt +329 -0
- package/android/src/main/java/com/instantpaycodepush/OkHttpDownloadService.kt +283 -0
- package/android/src/main/java/com/instantpaycodepush/ReactIntegrationManager.kt +141 -0
- package/android/src/main/java/com/instantpaycodepush/ReactIntegrationManagerBase.kt +35 -0
- package/android/src/main/java/com/instantpaycodepush/SignatureVerifier.kt +354 -0
- package/android/src/main/java/com/instantpaycodepush/VersionedPreferencesService.kt +70 -0
- package/android/src/main/java/com/instantpaycodepush/ZipDecompressionStrategy.kt +198 -0
- package/ios/InstantpayCodePush.h +5 -0
- package/ios/InstantpayCodePush.mm +21 -0
- package/lib/module/DefaultResolver.js +34 -0
- package/lib/module/DefaultResolver.js.map +1 -0
- package/lib/module/NativeInstantpayCodePush.js +5 -0
- package/lib/module/NativeInstantpayCodePush.js.map +1 -0
- package/lib/module/checkForUpdate.js +68 -0
- package/lib/module/checkForUpdate.js.map +1 -0
- package/lib/module/error.js +137 -0
- package/lib/module/error.js.map +1 -0
- package/lib/module/fetchUpdateInfo.js +36 -0
- package/lib/module/fetchUpdateInfo.js.map +1 -0
- package/lib/module/global.d.js +8 -0
- package/lib/module/global.d.js.map +1 -0
- package/lib/module/hooks/useEventCallback.js +13 -0
- package/lib/module/hooks/useEventCallback.js.map +1 -0
- package/lib/module/index.js +291 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/native.js +233 -0
- package/lib/module/native.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/store.js +53 -0
- package/lib/module/store.js.map +1 -0
- package/lib/module/types.js +62 -0
- package/lib/module/types.js.map +1 -0
- package/lib/module/wrap.js +171 -0
- package/lib/module/wrap.js.map +1 -0
- package/lib/typescript/package.json +1 -0
- package/lib/typescript/src/DefaultResolver.d.ts +10 -0
- package/lib/typescript/src/DefaultResolver.d.ts.map +1 -0
- package/lib/typescript/src/NativeInstantpayCodePush.d.ts +100 -0
- package/lib/typescript/src/NativeInstantpayCodePush.d.ts.map +1 -0
- package/lib/typescript/src/checkForUpdate.d.ts +29 -0
- package/lib/typescript/src/checkForUpdate.d.ts.map +1 -0
- package/lib/typescript/src/error.d.ts +124 -0
- package/lib/typescript/src/error.d.ts.map +1 -0
- package/lib/typescript/src/fetchUpdateInfo.d.ts +8 -0
- package/lib/typescript/src/fetchUpdateInfo.d.ts.map +1 -0
- package/lib/typescript/src/hooks/useEventCallback.d.ts +5 -0
- package/lib/typescript/src/hooks/useEventCallback.d.ts.map +1 -0
- package/lib/typescript/src/index.d.ts +203 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/native.d.ts +128 -0
- package/lib/typescript/src/native.d.ts.map +1 -0
- package/lib/typescript/src/store.d.ts +11 -0
- package/lib/typescript/src/store.d.ts.map +1 -0
- package/lib/typescript/src/types.d.ts +174 -0
- package/lib/typescript/src/types.d.ts.map +1 -0
- package/lib/typescript/src/wrap.d.ts +179 -0
- package/lib/typescript/src/wrap.d.ts.map +1 -0
- package/package.json +174 -0
- package/src/DefaultResolver.ts +36 -0
- package/src/NativeInstantpayCodePush.ts +111 -0
- package/src/checkForUpdate.ts +122 -0
- package/src/error.ts +159 -0
- package/src/fetchUpdateInfo.ts +47 -0
- package/src/global.d.ts +23 -0
- package/src/hooks/useEventCallback.ts +30 -0
- package/src/index.tsx +379 -0
- package/src/native.ts +280 -0
- package/src/store.ts +69 -0
- package/src/types.ts +227 -0
- package/src/wrap.tsx +384 -0
package/src/wrap.tsx
ADDED
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import React,{ useEffect, useLayoutEffect, useState } from "react";
|
|
2
|
+
import { checkForUpdate } from "./checkForUpdate";
|
|
3
|
+
import {
|
|
4
|
+
getBundleId,
|
|
5
|
+
type NotifyAppReadyResult,
|
|
6
|
+
notifyAppReady as nativeNotifyAppReady,
|
|
7
|
+
reload,
|
|
8
|
+
} from "./native";
|
|
9
|
+
import type { IpayCodePushResolver } from "./types";
|
|
10
|
+
import type { IpayCodePushError } from "./error";
|
|
11
|
+
import { useEventCallback } from "./hooks/useEventCallback";
|
|
12
|
+
import { useIpayCodePushStore } from "./store";
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
export interface RunUpdateProcessResponse {
|
|
16
|
+
status: "ROLLBACK" | "UPDATE" | "UP_TO_DATE";
|
|
17
|
+
shouldForceUpdate: boolean;
|
|
18
|
+
message: string | null;
|
|
19
|
+
id: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type UpdateStatus =
|
|
23
|
+
| "CHECK_FOR_UPDATE"
|
|
24
|
+
| "UPDATING"
|
|
25
|
+
| "UPDATE_PROCESS_COMPLETED";
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Common options shared between auto and manual update modes
|
|
30
|
+
*/
|
|
31
|
+
interface CommonIpayCodePushOptions {
|
|
32
|
+
/**
|
|
33
|
+
* Custom request headers for update checks
|
|
34
|
+
*/
|
|
35
|
+
requestHeaders?: Record<string, string>;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Request timeout in milliseconds
|
|
39
|
+
* @default 5000
|
|
40
|
+
*/
|
|
41
|
+
requestTimeout?: number;
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Callback invoked when the app is ready and bundle verification completes.
|
|
45
|
+
* Provides information about bundle promotion, recovery from crashes, or stable state.
|
|
46
|
+
*
|
|
47
|
+
* @param result - Bundle state information
|
|
48
|
+
* @param result.status - Current bundle state:
|
|
49
|
+
* - "PROMOTED": Staging bundle was promoted to stable (new update applied)
|
|
50
|
+
* - "RECOVERED": App recovered from a crash, rollback occurred
|
|
51
|
+
* - "STABLE": No changes, bundle is stable
|
|
52
|
+
* @param result.crashedBundleId - Present only when status is "RECOVERED"
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```tsx
|
|
56
|
+
* IpayCodePush.wrap({
|
|
57
|
+
* baseURL: "https://api.example.com",
|
|
58
|
+
* updateMode: "manual",
|
|
59
|
+
* onNotifyAppReady: ({ status, crashedBundleId }) => {
|
|
60
|
+
* if (status === "RECOVERED") {
|
|
61
|
+
* analytics.track('bundle_rollback', { crashedBundleId });
|
|
62
|
+
* } else if (status === "PROMOTED") {
|
|
63
|
+
* analytics.track('bundle_promoted');
|
|
64
|
+
* }
|
|
65
|
+
* }
|
|
66
|
+
* })(App);
|
|
67
|
+
* ```
|
|
68
|
+
*/
|
|
69
|
+
onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Configuration with baseURL for standard server-based updates
|
|
74
|
+
*/
|
|
75
|
+
interface BaseURLConfig {
|
|
76
|
+
/**
|
|
77
|
+
* Base URL for update server
|
|
78
|
+
* @example "https://update.example.com"
|
|
79
|
+
*/
|
|
80
|
+
baseURL: string;
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Resolver is not allowed when using baseURL
|
|
84
|
+
*/
|
|
85
|
+
resolver?: never;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Configuration with resolver for custom network operations
|
|
90
|
+
*/
|
|
91
|
+
interface ResolverConfig {
|
|
92
|
+
/**
|
|
93
|
+
* Custom resolver for network operations
|
|
94
|
+
*/
|
|
95
|
+
resolver: IpayCodePushResolver;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* baseURL is not allowed when using resolver
|
|
99
|
+
*/
|
|
100
|
+
baseURL?: never;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Union type ensuring baseURL and resolver are mutually exclusive
|
|
105
|
+
*/
|
|
106
|
+
type NetworkConfig = BaseURLConfig | ResolverConfig;
|
|
107
|
+
|
|
108
|
+
export type AutoUpdateOptions = CommonIpayCodePushOptions & NetworkConfig & {
|
|
109
|
+
/**
|
|
110
|
+
* Update strategy
|
|
111
|
+
* - "fingerprint": Use fingerprint hash to check for updates
|
|
112
|
+
* - "appVersion": Use app version to check for updates
|
|
113
|
+
*/
|
|
114
|
+
updateStrategy: "fingerprint" | "appVersion";
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Update mode
|
|
118
|
+
* - "auto": Automatically check and download updates
|
|
119
|
+
*/
|
|
120
|
+
updateMode: "auto";
|
|
121
|
+
|
|
122
|
+
onError?: (error: IpayCodePushError | Error | unknown) => void;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Component to show while downloading a new bundle update.
|
|
126
|
+
*
|
|
127
|
+
* When an update exists and the bundle is being downloaded, this component will block access
|
|
128
|
+
* to the entry point and show download progress.
|
|
129
|
+
*
|
|
130
|
+
*
|
|
131
|
+
* ```tsx
|
|
132
|
+
* IpayCodePush.wrap({
|
|
133
|
+
* baseURL: "<update-server-url>",
|
|
134
|
+
* updateStrategy: "appVersion",
|
|
135
|
+
* fallbackComponent: ({ progress = 0 }) => (
|
|
136
|
+
* <View style={styles.container}>
|
|
137
|
+
* <Text style={styles.text}>Updating... {progress}%</Text>
|
|
138
|
+
* </View>
|
|
139
|
+
* )
|
|
140
|
+
* })(App)
|
|
141
|
+
* ```
|
|
142
|
+
*
|
|
143
|
+
* If not defined, the bundle will download in the background without blocking the screen.
|
|
144
|
+
*/
|
|
145
|
+
fallbackComponent?: React.FC<{
|
|
146
|
+
status: Exclude<UpdateStatus, "UPDATE_PROCESS_COMPLETED">;
|
|
147
|
+
progress: number;
|
|
148
|
+
message: string | null;
|
|
149
|
+
}>;
|
|
150
|
+
|
|
151
|
+
onProgress?: (progress: number) => void;
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* When a force update exists, the app will automatically reload.
|
|
155
|
+
* If `false`, When a force update exists, the app will not reload. `shouldForceUpdate` will be returned as `true` in `onUpdateProcessCompleted`.
|
|
156
|
+
* If `true`, When a force update exists, the app will automatically reload.
|
|
157
|
+
* @default true
|
|
158
|
+
*/
|
|
159
|
+
reloadOnForceUpdate?: boolean;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Callback function that is called when the update process is completed.
|
|
163
|
+
*
|
|
164
|
+
*/
|
|
165
|
+
onUpdateProcessCompleted?: (response: RunUpdateProcessResponse) => void;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
export type ManualUpdateOptions = CommonIpayCodePushOptions & NetworkConfig & {
|
|
169
|
+
/**
|
|
170
|
+
* Update mode
|
|
171
|
+
* - "manual": Only notify app ready, user manually calls checkForUpdate()
|
|
172
|
+
*/
|
|
173
|
+
updateMode: "manual";
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
export type IpayCodePushOptions = AutoUpdateOptions | ManualUpdateOptions;
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Internal options after normalization in index.ts
|
|
180
|
+
* Always has resolver (never baseURL)
|
|
181
|
+
*/
|
|
182
|
+
type InternalCommonOptions = {
|
|
183
|
+
resolver: IpayCodePushResolver;
|
|
184
|
+
requestHeaders?: Record<string, string>;
|
|
185
|
+
requestTimeout?: number;
|
|
186
|
+
onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
type InternalAutoUpdateOptions = InternalCommonOptions & {
|
|
190
|
+
updateStrategy: "fingerprint" | "appVersion";
|
|
191
|
+
updateMode: "auto";
|
|
192
|
+
onError?: (error: IpayCodePushError | Error | unknown) => void;
|
|
193
|
+
fallbackComponent?: React.FC<{
|
|
194
|
+
status: Exclude<UpdateStatus, "UPDATE_PROCESS_COMPLETED">;
|
|
195
|
+
progress: number;
|
|
196
|
+
message: string | null;
|
|
197
|
+
}>;
|
|
198
|
+
onProgress?: (progress: number) => void;
|
|
199
|
+
reloadOnForceUpdate?: boolean;
|
|
200
|
+
onUpdateProcessCompleted?: (response: RunUpdateProcessResponse) => void;
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
type InternalManualUpdateOptions = InternalCommonOptions & {
|
|
204
|
+
updateMode: "manual";
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
export type InternalWrapOptions =
|
|
208
|
+
| InternalAutoUpdateOptions
|
|
209
|
+
| InternalManualUpdateOptions;
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Helper function to handle notifyAppReady flow
|
|
214
|
+
*/
|
|
215
|
+
const handleNotifyAppReady = async (options: {
|
|
216
|
+
resolver?: IpayCodePushResolver;
|
|
217
|
+
requestHeaders?: Record<string, string>;
|
|
218
|
+
requestTimeout?: number;
|
|
219
|
+
onNotifyAppReady?: (result: NotifyAppReadyResult) => void;
|
|
220
|
+
}): Promise<void> => {
|
|
221
|
+
try {
|
|
222
|
+
// Always call native notifyAppReady for bundle promotion
|
|
223
|
+
const nativeResult = nativeNotifyAppReady();
|
|
224
|
+
|
|
225
|
+
// If resolver.notifyAppReady exists, call it with simplified params
|
|
226
|
+
if (options.resolver?.notifyAppReady) {
|
|
227
|
+
await options.resolver.notifyAppReady({
|
|
228
|
+
status: nativeResult.status,
|
|
229
|
+
crashedBundleId: nativeResult.crashedBundleId,
|
|
230
|
+
requestHeaders: options.requestHeaders,
|
|
231
|
+
requestTimeout: options.requestTimeout,
|
|
232
|
+
})
|
|
233
|
+
.catch((e: unknown) => {
|
|
234
|
+
console.warn("[IpayCodePush] Resolver notifyAppReady failed:", e);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
options.onNotifyAppReady?.(nativeResult);
|
|
239
|
+
} catch (e) {
|
|
240
|
+
console.warn("[IpayCodePush] Failed to notify app ready:", e);
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* @usage IpayCodePushHOC.wrap({})(App); App Component or any other component
|
|
246
|
+
* @param options
|
|
247
|
+
* @returns
|
|
248
|
+
*/
|
|
249
|
+
export function wrap<T extends React.JSX.IntrinsicAttributes = object>(
|
|
250
|
+
options: InternalWrapOptions,
|
|
251
|
+
): (WrappedComponent: React.ComponentType<T>) => React.ComponentType<T> {
|
|
252
|
+
|
|
253
|
+
if (options.updateMode === "manual") {
|
|
254
|
+
return (WrappedComponent: React.ComponentType<T>) => {
|
|
255
|
+
const ManualHOC: React.FC<T> = (props: T) => {
|
|
256
|
+
useLayoutEffect(() => {
|
|
257
|
+
void handleNotifyAppReady(options);
|
|
258
|
+
}, []);
|
|
259
|
+
|
|
260
|
+
return <WrappedComponent {...props} />;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
return ManualHOC as React.ComponentType<T>;
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// updateMode: "auto"
|
|
268
|
+
const { reloadOnForceUpdate = true, ...restOptions } = options;
|
|
269
|
+
|
|
270
|
+
return (WrappedComponent: React.ComponentType<T>) => {
|
|
271
|
+
const IpayCodePushHOC: React.FC<T> = (props: T) => {
|
|
272
|
+
|
|
273
|
+
const progress = useIpayCodePushStore((state) => state.progress);
|
|
274
|
+
|
|
275
|
+
const [message, setMessage] = useState<string | null>(null);
|
|
276
|
+
|
|
277
|
+
const [updateStatus, setUpdateStatus] = useState<UpdateStatus>("CHECK_FOR_UPDATE");
|
|
278
|
+
|
|
279
|
+
const initIpayCodePush = useEventCallback(async () => {
|
|
280
|
+
|
|
281
|
+
try {
|
|
282
|
+
setUpdateStatus("CHECK_FOR_UPDATE");
|
|
283
|
+
|
|
284
|
+
const updateInfo = await checkForUpdate({
|
|
285
|
+
resolver: restOptions.resolver,
|
|
286
|
+
updateStrategy: restOptions.updateStrategy,
|
|
287
|
+
requestHeaders: restOptions.requestHeaders,
|
|
288
|
+
requestTimeout: restOptions.requestTimeout,
|
|
289
|
+
onError: restOptions.onError,
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
setMessage(updateInfo?.message ?? null);
|
|
293
|
+
|
|
294
|
+
if (!updateInfo) {
|
|
295
|
+
restOptions.onUpdateProcessCompleted?.({
|
|
296
|
+
status: "UP_TO_DATE",
|
|
297
|
+
shouldForceUpdate: false,
|
|
298
|
+
message: null,
|
|
299
|
+
id: getBundleId(),
|
|
300
|
+
});
|
|
301
|
+
setUpdateStatus("UPDATE_PROCESS_COMPLETED");
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
//If forceupdate is not allowed for the bunlde
|
|
306
|
+
if (updateInfo.shouldForceUpdate === false) {
|
|
307
|
+
void updateInfo.updateBundle().catch((error: unknown) => {
|
|
308
|
+
restOptions.onError?.(error);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
restOptions.onUpdateProcessCompleted?.({
|
|
312
|
+
id: updateInfo.id,
|
|
313
|
+
status: updateInfo.status,
|
|
314
|
+
shouldForceUpdate: updateInfo.shouldForceUpdate,
|
|
315
|
+
message: updateInfo.message,
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
setUpdateStatus("UPDATE_PROCESS_COMPLETED");
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Force Update Scenario
|
|
323
|
+
setUpdateStatus("UPDATING");
|
|
324
|
+
const isSuccess = await updateInfo.updateBundle();
|
|
325
|
+
|
|
326
|
+
if (!isSuccess) {
|
|
327
|
+
throw new Error(
|
|
328
|
+
"New update was found but failed to download the bundle.",
|
|
329
|
+
);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (reloadOnForceUpdate) {
|
|
333
|
+
await reload();
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
restOptions.onUpdateProcessCompleted?.({
|
|
337
|
+
id: updateInfo.id,
|
|
338
|
+
status: updateInfo.status,
|
|
339
|
+
shouldForceUpdate: updateInfo.shouldForceUpdate,
|
|
340
|
+
message: updateInfo.message,
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
setUpdateStatus("UPDATE_PROCESS_COMPLETED");
|
|
344
|
+
|
|
345
|
+
} catch (error) {
|
|
346
|
+
restOptions.onError?.(error);
|
|
347
|
+
setUpdateStatus("UPDATE_PROCESS_COMPLETED");
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
useEffect(() => {
|
|
352
|
+
restOptions.onProgress?.(progress);
|
|
353
|
+
}, [progress]);
|
|
354
|
+
|
|
355
|
+
// Notify native side that app is ready (JS bundle fully loaded)
|
|
356
|
+
useLayoutEffect(() => {
|
|
357
|
+
void handleNotifyAppReady(restOptions);
|
|
358
|
+
}, []);
|
|
359
|
+
|
|
360
|
+
// Start update check
|
|
361
|
+
useLayoutEffect(() => {
|
|
362
|
+
initIpayCodePush();
|
|
363
|
+
}, []);
|
|
364
|
+
|
|
365
|
+
if (
|
|
366
|
+
restOptions.fallbackComponent &&
|
|
367
|
+
updateStatus !== "UPDATE_PROCESS_COMPLETED"
|
|
368
|
+
) {
|
|
369
|
+
const Fallback = restOptions.fallbackComponent;
|
|
370
|
+
return (
|
|
371
|
+
<Fallback
|
|
372
|
+
progress={progress}
|
|
373
|
+
status={updateStatus}
|
|
374
|
+
message={message}
|
|
375
|
+
/>
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return <WrappedComponent {...props} />;
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
return IpayCodePushHOC as React.ComponentType<T>;
|
|
383
|
+
}
|
|
384
|
+
}
|