@hot-updater/react-native 0.3.1 → 0.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/HotUpdater.podspec +19 -16
- package/android/src/oldarch/HotUpdaterModule.kt +49 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +34 -26
- package/dist/index.mjs +34 -26
- package/dist/native.d.ts +1 -1
- package/dist/store.d.ts +3 -2
- package/dist/wrap.d.ts +8 -10
- package/package.json +3 -3
- package/src/index.ts +1 -1
- package/src/native.ts +4 -1
- package/src/store.ts +9 -7
- package/src/wrap.tsx +22 -39
- /package/android/src/{main/java/com/hotupdater → newarch}/HotUpdaterModule.kt +0 -0
package/HotUpdater.podspec
CHANGED
|
@@ -5,7 +5,7 @@ folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1
|
|
|
5
5
|
|
|
6
6
|
Pod::Spec.new do |s|
|
|
7
7
|
s.name = "HotUpdater"
|
|
8
|
-
s.version = package["version"]
|
|
8
|
+
s.version = package["version"].split('.')[0..1].join('.') + '.0'
|
|
9
9
|
s.summary = package["description"]
|
|
10
10
|
s.homepage = package["homepage"]
|
|
11
11
|
s.license = package["license"]
|
|
@@ -15,6 +15,9 @@ Pod::Spec.new do |s|
|
|
|
15
15
|
s.source = { :git => "https://github.com/gronxb/hot-updater.git", :tag => "#{s.version}" }
|
|
16
16
|
|
|
17
17
|
s.source_files = "ios/**/*.{h,m,mm}"
|
|
18
|
+
if ENV['RCT_NEW_ARCH_ENABLED'] != '1' then
|
|
19
|
+
s.exclude_files = "ios/generated/**/*"
|
|
20
|
+
end
|
|
18
21
|
|
|
19
22
|
s.dependency "SSZipArchive", "~> 2.2.2"
|
|
20
23
|
|
|
@@ -23,21 +26,21 @@ Pod::Spec.new do |s|
|
|
|
23
26
|
if respond_to?(:install_modules_dependencies, true)
|
|
24
27
|
install_modules_dependencies(s)
|
|
25
28
|
else
|
|
26
|
-
|
|
29
|
+
s.dependency "React-Core"
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
31
|
+
# Don't install the dependencies when we run `pod install` in the old architecture.
|
|
32
|
+
if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
|
|
33
|
+
s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
|
|
34
|
+
s.pod_target_xcconfig = {
|
|
35
|
+
"HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
|
|
36
|
+
"OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
|
|
37
|
+
"CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
|
|
38
|
+
}
|
|
39
|
+
s.dependency "React-Codegen"
|
|
40
|
+
s.dependency "RCT-Folly"
|
|
41
|
+
s.dependency "RCTRequired"
|
|
42
|
+
s.dependency "RCTTypeSafety"
|
|
43
|
+
s.dependency "ReactCommon/turbomodule/core"
|
|
44
|
+
end
|
|
42
45
|
end
|
|
43
46
|
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
package com.hotupdater
|
|
2
|
+
|
|
3
|
+
import com.facebook.react.bridge.Promise
|
|
4
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
5
|
+
import com.facebook.react.bridge.ReactMethod
|
|
6
|
+
import com.facebook.react.bridge.WritableNativeMap
|
|
7
|
+
import com.facebook.react.modules.core.DeviceEventManagerModule
|
|
8
|
+
|
|
9
|
+
class HotUpdaterModule internal constructor(
|
|
10
|
+
context: ReactApplicationContext,
|
|
11
|
+
) : HotUpdaterSpec(context) {
|
|
12
|
+
private val mReactApplicationContext: ReactApplicationContext = context
|
|
13
|
+
|
|
14
|
+
override fun getName(): String = NAME
|
|
15
|
+
|
|
16
|
+
@ReactMethod
|
|
17
|
+
override fun reload() {
|
|
18
|
+
HotUpdater.reload(mReactApplicationContext)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@ReactMethod
|
|
22
|
+
override fun getAppVersion(promise: Promise) {
|
|
23
|
+
promise.resolve(HotUpdater.getAppVersion(mReactApplicationContext))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@ReactMethod
|
|
27
|
+
override fun updateBundle(
|
|
28
|
+
bundleId: String,
|
|
29
|
+
zipUrl: String,
|
|
30
|
+
promise: Promise,
|
|
31
|
+
) {
|
|
32
|
+
val isSuccess =
|
|
33
|
+
HotUpdater.updateBundle(mReactApplicationContext, bundleId, zipUrl) { progress ->
|
|
34
|
+
val params =
|
|
35
|
+
WritableNativeMap().apply {
|
|
36
|
+
putDouble("progress", progress)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
this.mReactApplicationContext
|
|
40
|
+
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
|
|
41
|
+
.emit("onProgress", params)
|
|
42
|
+
}
|
|
43
|
+
promise.resolve(isSuccess)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
companion object {
|
|
47
|
+
const val NAME = "HotUpdater"
|
|
48
|
+
}
|
|
49
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare const HotUpdater: {
|
|
|
7
7
|
reload: () => void;
|
|
8
8
|
getAppVersion: () => Promise<string | null>;
|
|
9
9
|
getBundleId: () => string;
|
|
10
|
-
addListener: <T extends keyof import("./native").HotUpdaterEvent>(eventName: T, listener: (event: import("./native").HotUpdaterEvent[T]) => void) => void;
|
|
10
|
+
addListener: <T extends keyof import("./native").HotUpdaterEvent>(eventName: T, listener: (event: import("./native").HotUpdaterEvent[T]) => void) => () => void;
|
|
11
11
|
ensureUpdateInfo: (source: import("@hot-updater/core").BundleArg, { appVersion, bundleId, platform }: import("@hot-updater/core").GetBundlesArgs, requestHeaders?: Record<string, string>) => Promise<import("@hot-updater/core").Bundle[] | import("@hot-updater/core").UpdateInfo>;
|
|
12
12
|
updateBundle: (bundleId: string, zipUrl: string | null) => Promise<boolean>;
|
|
13
13
|
getUpdateInfo: (bundles: import("@hot-updater/core").Bundle[], { platform, bundleId, appVersion }: import("@hot-updater/core").GetBundlesArgs) => Promise<import("@hot-updater/core").UpdateInfo | null>;
|
package/dist/index.js
CHANGED
|
@@ -3119,7 +3119,10 @@ const HotUpdaterNative = HotUpdaterModule ? HotUpdaterModule : new Proxy({}, {
|
|
|
3119
3119
|
});
|
|
3120
3120
|
const addListener = (eventName, listener)=>{
|
|
3121
3121
|
const eventEmitter = new external_react_native_.NativeEventEmitter(HotUpdaterNative);
|
|
3122
|
-
eventEmitter
|
|
3122
|
+
const subscription = eventEmitter.addListener(eventName, listener);
|
|
3123
|
+
return ()=>{
|
|
3124
|
+
subscription.remove();
|
|
3125
|
+
};
|
|
3123
3126
|
};
|
|
3124
3127
|
const updateBundle = (bundleId, zipUrl)=>HotUpdaterNative.updateBundle(bundleId, zipUrl);
|
|
3125
3128
|
const getAppVersion = ()=>HotUpdaterNative.getAppVersion();
|
|
@@ -3130,17 +3133,18 @@ const getBundleId = ()=>HotUpdater.HOT_UPDATER_BUNDLE_ID;
|
|
|
3130
3133
|
var react = __webpack_require__("../../node_modules/.pnpm/react@18.3.1/node_modules/react/index.js");
|
|
3131
3134
|
const createHotUpdaterStore = ()=>{
|
|
3132
3135
|
let state = {
|
|
3133
|
-
progress: 0
|
|
3136
|
+
progress: 0,
|
|
3137
|
+
isBundleUpdated: false
|
|
3134
3138
|
};
|
|
3135
|
-
const
|
|
3139
|
+
const getSnapshot = ()=>state;
|
|
3136
3140
|
const listeners = new Set();
|
|
3137
3141
|
const emitChange = ()=>{
|
|
3138
3142
|
for (const listener of listeners)listener();
|
|
3139
3143
|
};
|
|
3140
|
-
const
|
|
3144
|
+
const setProgress = (progress)=>{
|
|
3141
3145
|
state = {
|
|
3142
|
-
|
|
3143
|
-
|
|
3146
|
+
isBundleUpdated: 1 === progress,
|
|
3147
|
+
progress
|
|
3144
3148
|
};
|
|
3145
3149
|
emitChange();
|
|
3146
3150
|
};
|
|
@@ -3149,13 +3153,13 @@ const createHotUpdaterStore = ()=>{
|
|
|
3149
3153
|
return ()=>listeners.delete(listener);
|
|
3150
3154
|
};
|
|
3151
3155
|
return {
|
|
3152
|
-
|
|
3153
|
-
|
|
3156
|
+
getSnapshot,
|
|
3157
|
+
setProgress,
|
|
3154
3158
|
subscribe
|
|
3155
3159
|
};
|
|
3156
3160
|
};
|
|
3157
3161
|
const hotUpdaterStore = createHotUpdaterStore();
|
|
3158
|
-
const useHotUpdaterStore = ()=>(0, react.useSyncExternalStore)(hotUpdaterStore.subscribe, hotUpdaterStore.
|
|
3162
|
+
const useHotUpdaterStore = ()=>(0, react.useSyncExternalStore)(hotUpdaterStore.subscribe, hotUpdaterStore.getSnapshot, hotUpdaterStore.getSnapshot);
|
|
3159
3163
|
class HotUpdaterError extends Error {
|
|
3160
3164
|
constructor(message){
|
|
3161
3165
|
super(message);
|
|
@@ -3201,23 +3205,33 @@ async function installUpdate(updateInfo) {
|
|
|
3201
3205
|
}
|
|
3202
3206
|
function wrap(config) {
|
|
3203
3207
|
return (WrappedComponent)=>{
|
|
3204
|
-
const HotUpdaterHOC = (
|
|
3205
|
-
const
|
|
3206
|
-
const [
|
|
3207
|
-
|
|
3208
|
+
const HotUpdaterHOC = ()=>{
|
|
3209
|
+
const { progress, isBundleUpdated } = useHotUpdaterStore();
|
|
3210
|
+
const [isUpdating, setIsUpdating] = (0, react.useState)(false);
|
|
3211
|
+
(0, react.useEffect)(()=>{
|
|
3212
|
+
config.onProgress?.(progress);
|
|
3213
|
+
}, [
|
|
3214
|
+
progress
|
|
3215
|
+
]);
|
|
3208
3216
|
(0, react.useEffect)(()=>{
|
|
3209
3217
|
const initHotUpdater = async ()=>{
|
|
3210
3218
|
try {
|
|
3211
3219
|
const updateInfo = await checkUpdate(config);
|
|
3212
3220
|
if (!updateInfo) {
|
|
3213
|
-
|
|
3221
|
+
config.onCheckUpdateCompleted?.({
|
|
3222
|
+
isBundleUpdated: false
|
|
3223
|
+
});
|
|
3214
3224
|
return;
|
|
3215
3225
|
}
|
|
3216
|
-
|
|
3226
|
+
setIsUpdating(true);
|
|
3217
3227
|
const isSuccess = await installUpdate(updateInfo);
|
|
3218
|
-
|
|
3228
|
+
config.onCheckUpdateCompleted?.({
|
|
3229
|
+
isBundleUpdated: isSuccess
|
|
3230
|
+
});
|
|
3231
|
+
setIsUpdating(false);
|
|
3219
3232
|
} catch (error) {
|
|
3220
|
-
if (error instanceof HotUpdaterError)
|
|
3233
|
+
if (error instanceof HotUpdaterError) config.onError?.(error);
|
|
3234
|
+
setIsUpdating(false);
|
|
3221
3235
|
throw error;
|
|
3222
3236
|
}
|
|
3223
3237
|
};
|
|
@@ -3226,25 +3240,19 @@ function wrap(config) {
|
|
|
3226
3240
|
config.source,
|
|
3227
3241
|
config.requestHeaders
|
|
3228
3242
|
]);
|
|
3229
|
-
if (
|
|
3243
|
+
if (config.fallbackComponent && isUpdating) {
|
|
3230
3244
|
const Fallback = config.fallbackComponent;
|
|
3231
3245
|
return /*#__PURE__*/ React.createElement(Fallback, {
|
|
3232
3246
|
progress: progress
|
|
3233
3247
|
});
|
|
3234
3248
|
}
|
|
3235
|
-
return /*#__PURE__*/ React.createElement(WrappedComponent,
|
|
3236
|
-
...props,
|
|
3237
|
-
updateStatus: updateStatus,
|
|
3238
|
-
updateError: updateError
|
|
3239
|
-
});
|
|
3249
|
+
return /*#__PURE__*/ React.createElement(WrappedComponent, null);
|
|
3240
3250
|
};
|
|
3241
3251
|
return HotUpdaterHOC;
|
|
3242
3252
|
};
|
|
3243
3253
|
}
|
|
3244
3254
|
addListener("onProgress", ({ progress })=>{
|
|
3245
|
-
hotUpdaterStore.
|
|
3246
|
-
progress
|
|
3247
|
-
});
|
|
3255
|
+
hotUpdaterStore.setProgress(progress);
|
|
3248
3256
|
});
|
|
3249
3257
|
const src_HotUpdater = {
|
|
3250
3258
|
wrap: wrap,
|
package/dist/index.mjs
CHANGED
|
@@ -3087,7 +3087,10 @@ const HotUpdaterNative = HotUpdaterModule ? HotUpdaterModule : new Proxy({}, {
|
|
|
3087
3087
|
});
|
|
3088
3088
|
const addListener = (eventName, listener)=>{
|
|
3089
3089
|
const eventEmitter = new external_react_native_.NativeEventEmitter(HotUpdaterNative);
|
|
3090
|
-
eventEmitter
|
|
3090
|
+
const subscription = eventEmitter.addListener(eventName, listener);
|
|
3091
|
+
return ()=>{
|
|
3092
|
+
subscription.remove();
|
|
3093
|
+
};
|
|
3091
3094
|
};
|
|
3092
3095
|
const updateBundle = (bundleId, zipUrl)=>HotUpdaterNative.updateBundle(bundleId, zipUrl);
|
|
3093
3096
|
const getAppVersion = ()=>HotUpdaterNative.getAppVersion();
|
|
@@ -3098,17 +3101,18 @@ const getBundleId = ()=>HotUpdater.HOT_UPDATER_BUNDLE_ID;
|
|
|
3098
3101
|
var react = __webpack_require__("../../node_modules/.pnpm/react@18.3.1/node_modules/react/index.js");
|
|
3099
3102
|
const createHotUpdaterStore = ()=>{
|
|
3100
3103
|
let state = {
|
|
3101
|
-
progress: 0
|
|
3104
|
+
progress: 0,
|
|
3105
|
+
isBundleUpdated: false
|
|
3102
3106
|
};
|
|
3103
|
-
const
|
|
3107
|
+
const getSnapshot = ()=>state;
|
|
3104
3108
|
const listeners = new Set();
|
|
3105
3109
|
const emitChange = ()=>{
|
|
3106
3110
|
for (const listener of listeners)listener();
|
|
3107
3111
|
};
|
|
3108
|
-
const
|
|
3112
|
+
const setProgress = (progress)=>{
|
|
3109
3113
|
state = {
|
|
3110
|
-
|
|
3111
|
-
|
|
3114
|
+
isBundleUpdated: 1 === progress,
|
|
3115
|
+
progress
|
|
3112
3116
|
};
|
|
3113
3117
|
emitChange();
|
|
3114
3118
|
};
|
|
@@ -3117,13 +3121,13 @@ const createHotUpdaterStore = ()=>{
|
|
|
3117
3121
|
return ()=>listeners.delete(listener);
|
|
3118
3122
|
};
|
|
3119
3123
|
return {
|
|
3120
|
-
|
|
3121
|
-
|
|
3124
|
+
getSnapshot,
|
|
3125
|
+
setProgress,
|
|
3122
3126
|
subscribe
|
|
3123
3127
|
};
|
|
3124
3128
|
};
|
|
3125
3129
|
const hotUpdaterStore = createHotUpdaterStore();
|
|
3126
|
-
const useHotUpdaterStore = ()=>(0, react.useSyncExternalStore)(hotUpdaterStore.subscribe, hotUpdaterStore.
|
|
3130
|
+
const useHotUpdaterStore = ()=>(0, react.useSyncExternalStore)(hotUpdaterStore.subscribe, hotUpdaterStore.getSnapshot, hotUpdaterStore.getSnapshot);
|
|
3127
3131
|
class HotUpdaterError extends Error {
|
|
3128
3132
|
constructor(message){
|
|
3129
3133
|
super(message);
|
|
@@ -3169,23 +3173,33 @@ async function installUpdate(updateInfo) {
|
|
|
3169
3173
|
}
|
|
3170
3174
|
function wrap(config) {
|
|
3171
3175
|
return (WrappedComponent)=>{
|
|
3172
|
-
const HotUpdaterHOC = (
|
|
3173
|
-
const
|
|
3174
|
-
const [
|
|
3175
|
-
|
|
3176
|
+
const HotUpdaterHOC = ()=>{
|
|
3177
|
+
const { progress, isBundleUpdated } = useHotUpdaterStore();
|
|
3178
|
+
const [isUpdating, setIsUpdating] = (0, react.useState)(false);
|
|
3179
|
+
(0, react.useEffect)(()=>{
|
|
3180
|
+
config.onProgress?.(progress);
|
|
3181
|
+
}, [
|
|
3182
|
+
progress
|
|
3183
|
+
]);
|
|
3176
3184
|
(0, react.useEffect)(()=>{
|
|
3177
3185
|
const initHotUpdater = async ()=>{
|
|
3178
3186
|
try {
|
|
3179
3187
|
const updateInfo = await checkUpdate(config);
|
|
3180
3188
|
if (!updateInfo) {
|
|
3181
|
-
|
|
3189
|
+
config.onCheckUpdateCompleted?.({
|
|
3190
|
+
isBundleUpdated: false
|
|
3191
|
+
});
|
|
3182
3192
|
return;
|
|
3183
3193
|
}
|
|
3184
|
-
|
|
3194
|
+
setIsUpdating(true);
|
|
3185
3195
|
const isSuccess = await installUpdate(updateInfo);
|
|
3186
|
-
|
|
3196
|
+
config.onCheckUpdateCompleted?.({
|
|
3197
|
+
isBundleUpdated: isSuccess
|
|
3198
|
+
});
|
|
3199
|
+
setIsUpdating(false);
|
|
3187
3200
|
} catch (error) {
|
|
3188
|
-
if (error instanceof HotUpdaterError)
|
|
3201
|
+
if (error instanceof HotUpdaterError) config.onError?.(error);
|
|
3202
|
+
setIsUpdating(false);
|
|
3189
3203
|
throw error;
|
|
3190
3204
|
}
|
|
3191
3205
|
};
|
|
@@ -3194,25 +3208,19 @@ function wrap(config) {
|
|
|
3194
3208
|
config.source,
|
|
3195
3209
|
config.requestHeaders
|
|
3196
3210
|
]);
|
|
3197
|
-
if (
|
|
3211
|
+
if (config.fallbackComponent && isUpdating) {
|
|
3198
3212
|
const Fallback = config.fallbackComponent;
|
|
3199
3213
|
return /*#__PURE__*/ React.createElement(Fallback, {
|
|
3200
3214
|
progress: progress
|
|
3201
3215
|
});
|
|
3202
3216
|
}
|
|
3203
|
-
return /*#__PURE__*/ React.createElement(WrappedComponent,
|
|
3204
|
-
...props,
|
|
3205
|
-
updateStatus: updateStatus,
|
|
3206
|
-
updateError: updateError
|
|
3207
|
-
});
|
|
3217
|
+
return /*#__PURE__*/ React.createElement(WrappedComponent, null);
|
|
3208
3218
|
};
|
|
3209
3219
|
return HotUpdaterHOC;
|
|
3210
3220
|
};
|
|
3211
3221
|
}
|
|
3212
3222
|
addListener("onProgress", ({ progress })=>{
|
|
3213
|
-
hotUpdaterStore.
|
|
3214
|
-
progress
|
|
3215
|
-
});
|
|
3223
|
+
hotUpdaterStore.setProgress(progress);
|
|
3216
3224
|
});
|
|
3217
3225
|
const src_HotUpdater = {
|
|
3218
3226
|
wrap: wrap,
|
package/dist/native.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export type HotUpdaterEvent = {
|
|
|
3
3
|
progress: number;
|
|
4
4
|
};
|
|
5
5
|
};
|
|
6
|
-
export declare const addListener: <T extends keyof HotUpdaterEvent>(eventName: T, listener: (event: HotUpdaterEvent[T]) => void) => void;
|
|
6
|
+
export declare const addListener: <T extends keyof HotUpdaterEvent>(eventName: T, listener: (event: HotUpdaterEvent[T]) => void) => () => void;
|
|
7
7
|
/**
|
|
8
8
|
* Downloads files from given URLs.
|
|
9
9
|
*
|
package/dist/store.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export type HotUpdaterState = {
|
|
2
2
|
progress: number;
|
|
3
|
+
isBundleUpdated: boolean;
|
|
3
4
|
};
|
|
4
5
|
export declare const hotUpdaterStore: {
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
getSnapshot: () => HotUpdaterState;
|
|
7
|
+
setProgress: (progress: number) => void;
|
|
7
8
|
subscribe: (listener: () => void) => () => boolean;
|
|
8
9
|
};
|
|
9
10
|
export declare const useHotUpdaterStore: () => HotUpdaterState;
|
package/dist/wrap.d.ts
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import type { BundleArg, UpdateInfo } from "@hot-updater/core";
|
|
2
2
|
import type React from "react";
|
|
3
3
|
import { HotUpdaterError } from "./error";
|
|
4
|
-
|
|
4
|
+
import { type HotUpdaterState } from "./store";
|
|
5
5
|
export interface CheckUpdateConfig {
|
|
6
6
|
source: BundleArg;
|
|
7
7
|
requestHeaders?: Record<string, string>;
|
|
8
8
|
}
|
|
9
9
|
export interface HotUpdaterConfig extends CheckUpdateConfig {
|
|
10
|
-
fallbackComponent?: React.FC<
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
updateStatus: HotUpdaterStatus | null;
|
|
17
|
-
updateError: HotUpdaterError | null;
|
|
10
|
+
fallbackComponent?: React.FC<Pick<HotUpdaterState, "progress">>;
|
|
11
|
+
onError?: (error: HotUpdaterError) => void;
|
|
12
|
+
onProgress?: (progress: number) => void;
|
|
13
|
+
onCheckUpdateCompleted?: ({ isBundleUpdated, }: {
|
|
14
|
+
isBundleUpdated: boolean;
|
|
15
|
+
}) => void;
|
|
18
16
|
}
|
|
19
17
|
export declare function checkUpdate(config: CheckUpdateConfig): Promise<UpdateInfo | null>;
|
|
20
|
-
export declare function wrap<P>(config: HotUpdaterConfig): (WrappedComponent: React.ComponentType
|
|
18
|
+
export declare function wrap<P>(config: HotUpdaterConfig): (WrappedComponent: React.ComponentType) => React.ComponentType<P>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/react-native",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "React Native OTA solution for self-hosted",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -73,10 +73,10 @@
|
|
|
73
73
|
"react": "18.3.1",
|
|
74
74
|
"react-native": "0.76.2",
|
|
75
75
|
"react-native-builder-bob": "^0.33.1",
|
|
76
|
-
"@hot-updater/js": "0.
|
|
76
|
+
"@hot-updater/js": "0.4.0"
|
|
77
77
|
},
|
|
78
78
|
"dependencies": {
|
|
79
|
-
"@hot-updater/core": "0.
|
|
79
|
+
"@hot-updater/core": "0.4.0"
|
|
80
80
|
},
|
|
81
81
|
"scripts": {
|
|
82
82
|
"build": "rslib build",
|
package/src/index.ts
CHANGED
package/src/native.ts
CHANGED
|
@@ -41,8 +41,11 @@ export const addListener = <T extends keyof HotUpdaterEvent>(
|
|
|
41
41
|
listener: (event: HotUpdaterEvent[T]) => void,
|
|
42
42
|
) => {
|
|
43
43
|
const eventEmitter = new NativeEventEmitter(HotUpdaterNative);
|
|
44
|
+
const subscription = eventEmitter.addListener(eventName, listener);
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
return () => {
|
|
47
|
+
subscription.remove();
|
|
48
|
+
};
|
|
46
49
|
};
|
|
47
50
|
|
|
48
51
|
/**
|
package/src/store.ts
CHANGED
|
@@ -2,14 +2,16 @@ import { useSyncExternalStore } from "react";
|
|
|
2
2
|
|
|
3
3
|
export type HotUpdaterState = {
|
|
4
4
|
progress: number;
|
|
5
|
+
isBundleUpdated: boolean;
|
|
5
6
|
};
|
|
6
7
|
|
|
7
8
|
const createHotUpdaterStore = () => {
|
|
8
9
|
let state: HotUpdaterState = {
|
|
9
10
|
progress: 0,
|
|
11
|
+
isBundleUpdated: false,
|
|
10
12
|
};
|
|
11
13
|
|
|
12
|
-
const
|
|
14
|
+
const getSnapshot = () => {
|
|
13
15
|
return state;
|
|
14
16
|
};
|
|
15
17
|
|
|
@@ -21,10 +23,10 @@ const createHotUpdaterStore = () => {
|
|
|
21
23
|
}
|
|
22
24
|
};
|
|
23
25
|
|
|
24
|
-
const
|
|
26
|
+
const setProgress = (progress: number) => {
|
|
25
27
|
state = {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
+
isBundleUpdated: progress === 1,
|
|
29
|
+
progress,
|
|
28
30
|
};
|
|
29
31
|
emitChange();
|
|
30
32
|
};
|
|
@@ -34,7 +36,7 @@ const createHotUpdaterStore = () => {
|
|
|
34
36
|
return () => listeners.delete(listener);
|
|
35
37
|
};
|
|
36
38
|
|
|
37
|
-
return {
|
|
39
|
+
return { getSnapshot, setProgress, subscribe };
|
|
38
40
|
};
|
|
39
41
|
|
|
40
42
|
export const hotUpdaterStore = createHotUpdaterStore();
|
|
@@ -42,7 +44,7 @@ export const hotUpdaterStore = createHotUpdaterStore();
|
|
|
42
44
|
export const useHotUpdaterStore = () => {
|
|
43
45
|
return useSyncExternalStore(
|
|
44
46
|
hotUpdaterStore.subscribe,
|
|
45
|
-
hotUpdaterStore.
|
|
46
|
-
hotUpdaterStore.
|
|
47
|
+
hotUpdaterStore.getSnapshot,
|
|
48
|
+
hotUpdaterStore.getSnapshot,
|
|
47
49
|
);
|
|
48
50
|
};
|
package/src/wrap.tsx
CHANGED
|
@@ -6,9 +6,7 @@ import { Platform } from "react-native";
|
|
|
6
6
|
import { ensureUpdateInfo } from "./ensureUpdateInfo";
|
|
7
7
|
import { HotUpdaterError } from "./error";
|
|
8
8
|
import { getAppVersion, getBundleId, reload, updateBundle } from "./native";
|
|
9
|
-
import { useHotUpdaterStore } from "./store";
|
|
10
|
-
|
|
11
|
-
export type HotUpdaterStatus = "INSTALLING_UPDATE" | "UP_TO_DATE" | "UPDATING";
|
|
9
|
+
import { type HotUpdaterState, useHotUpdaterStore } from "./store";
|
|
12
10
|
|
|
13
11
|
export interface CheckUpdateConfig {
|
|
14
12
|
source: BundleArg;
|
|
@@ -16,16 +14,12 @@ export interface CheckUpdateConfig {
|
|
|
16
14
|
}
|
|
17
15
|
|
|
18
16
|
export interface HotUpdaterConfig extends CheckUpdateConfig {
|
|
19
|
-
fallbackComponent?: React.FC<
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface WithHotUpdaterProps {
|
|
27
|
-
updateStatus: HotUpdaterStatus | null;
|
|
28
|
-
updateError: HotUpdaterError | null;
|
|
17
|
+
fallbackComponent?: React.FC<Pick<HotUpdaterState, "progress">>;
|
|
18
|
+
onError?: (error: HotUpdaterError) => void;
|
|
19
|
+
onProgress?: (progress: number) => void;
|
|
20
|
+
onCheckUpdateCompleted?: ({
|
|
21
|
+
isBundleUpdated,
|
|
22
|
+
}: { isBundleUpdated: boolean }) => void;
|
|
29
23
|
}
|
|
30
24
|
|
|
31
25
|
export async function checkUpdate(config: CheckUpdateConfig) {
|
|
@@ -89,39 +83,34 @@ async function installUpdate(updateInfo: UpdateInfo) {
|
|
|
89
83
|
|
|
90
84
|
export function wrap<P>(
|
|
91
85
|
config: HotUpdaterConfig,
|
|
92
|
-
): (
|
|
93
|
-
WrappedComponent: React.ComponentType<P & WithHotUpdaterProps>,
|
|
94
|
-
) => React.ComponentType<P> {
|
|
86
|
+
): (WrappedComponent: React.ComponentType) => React.ComponentType<P> {
|
|
95
87
|
return (WrappedComponent) => {
|
|
96
|
-
const HotUpdaterHOC: React.FC<P> = (
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
);
|
|
100
|
-
const [updateError, setUpdateError] = useState<HotUpdaterError | null>(
|
|
101
|
-
null,
|
|
102
|
-
);
|
|
88
|
+
const HotUpdaterHOC: React.FC<P> = () => {
|
|
89
|
+
const { progress, isBundleUpdated } = useHotUpdaterStore();
|
|
90
|
+
const [isUpdating, setIsUpdating] = useState(false);
|
|
103
91
|
|
|
104
|
-
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
config.onProgress?.(progress);
|
|
94
|
+
}, [progress]);
|
|
105
95
|
|
|
106
96
|
useEffect(() => {
|
|
107
97
|
const initHotUpdater = async () => {
|
|
108
98
|
try {
|
|
109
99
|
const updateInfo = await checkUpdate(config);
|
|
110
|
-
|
|
111
100
|
if (!updateInfo) {
|
|
112
|
-
|
|
101
|
+
config.onCheckUpdateCompleted?.({ isBundleUpdated: false });
|
|
113
102
|
return;
|
|
114
103
|
}
|
|
104
|
+
setIsUpdating(true);
|
|
115
105
|
|
|
116
|
-
setUpdateStatus("UPDATING");
|
|
117
106
|
const isSuccess = await installUpdate(updateInfo);
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
107
|
+
config.onCheckUpdateCompleted?.({ isBundleUpdated: isSuccess });
|
|
108
|
+
setIsUpdating(false);
|
|
121
109
|
} catch (error) {
|
|
122
110
|
if (error instanceof HotUpdaterError) {
|
|
123
|
-
|
|
111
|
+
config.onError?.(error);
|
|
124
112
|
}
|
|
113
|
+
setIsUpdating(false);
|
|
125
114
|
throw error;
|
|
126
115
|
}
|
|
127
116
|
};
|
|
@@ -129,18 +118,12 @@ export function wrap<P>(
|
|
|
129
118
|
initHotUpdater();
|
|
130
119
|
}, [config.source, config.requestHeaders]);
|
|
131
120
|
|
|
132
|
-
if (
|
|
121
|
+
if (config.fallbackComponent && isUpdating) {
|
|
133
122
|
const Fallback = config.fallbackComponent;
|
|
134
123
|
return <Fallback progress={progress} />;
|
|
135
124
|
}
|
|
136
125
|
|
|
137
|
-
return
|
|
138
|
-
<WrappedComponent
|
|
139
|
-
{...props}
|
|
140
|
-
updateStatus={updateStatus}
|
|
141
|
-
updateError={updateError}
|
|
142
|
-
/>
|
|
143
|
-
);
|
|
126
|
+
return <WrappedComponent />;
|
|
144
127
|
};
|
|
145
128
|
|
|
146
129
|
return HotUpdaterHOC;
|
|
File without changes
|