@hot-updater/react-native 0.3.1 → 0.4.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.
@@ -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
- s.dependency "React-Core"
29
+ s.dependency "React-Core"
27
30
 
28
- # Don't install the dependencies when we run `pod install` in the old architecture.
29
- if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then
30
- s.compiler_flags = folly_compiler_flags + " -DRCT_NEW_ARCH_ENABLED=1"
31
- s.pod_target_xcconfig = {
32
- "HEADER_SEARCH_PATHS" => "\"$(PODS_ROOT)/boost\"",
33
- "OTHER_CPLUSPLUSFLAGS" => "-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1",
34
- "CLANG_CXX_LANGUAGE_STANDARD" => "c++17"
35
- }
36
- s.dependency "React-Codegen"
37
- s.dependency "RCT-Folly"
38
- s.dependency "RCTRequired"
39
- s.dependency "RCTTypeSafety"
40
- s.dependency "ReactCommon/turbomodule/core"
41
- end
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?.addListener(eventName, listener);
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 getState = ()=>state;
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 setState = (newState)=>{
3144
+ const setProgress = (progress)=>{
3141
3145
  state = {
3142
- ...state,
3143
- ...newState
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
- getState,
3153
- setState,
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.getState, hotUpdaterStore.getState);
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 = (props)=>{
3205
- const [updateStatus, setUpdateStatus] = (0, react.useState)(null);
3206
- const [updateError, setUpdateError] = (0, react.useState)(null);
3207
- const { progress } = useHotUpdaterStore();
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
- setUpdateStatus("UP_TO_DATE");
3221
+ config.onCheckUpdateCompleted?.({
3222
+ isBundleUpdated: false
3223
+ });
3214
3224
  return;
3215
3225
  }
3216
- setUpdateStatus("UPDATING");
3226
+ setIsUpdating(true);
3217
3227
  const isSuccess = await installUpdate(updateInfo);
3218
- if (isSuccess) setUpdateStatus("INSTALLING_UPDATE");
3228
+ config.onCheckUpdateCompleted?.({
3229
+ isBundleUpdated: isSuccess
3230
+ });
3231
+ setIsUpdating(false);
3219
3232
  } catch (error) {
3220
- if (error instanceof HotUpdaterError) setUpdateError(error);
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 ("UPDATING" === updateStatus && config.fallbackComponent) {
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.setState({
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?.addListener(eventName, listener);
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 getState = ()=>state;
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 setState = (newState)=>{
3112
+ const setProgress = (progress)=>{
3109
3113
  state = {
3110
- ...state,
3111
- ...newState
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
- getState,
3121
- setState,
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.getState, hotUpdaterStore.getState);
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 = (props)=>{
3173
- const [updateStatus, setUpdateStatus] = (0, react.useState)(null);
3174
- const [updateError, setUpdateError] = (0, react.useState)(null);
3175
- const { progress } = useHotUpdaterStore();
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
- setUpdateStatus("UP_TO_DATE");
3189
+ config.onCheckUpdateCompleted?.({
3190
+ isBundleUpdated: false
3191
+ });
3182
3192
  return;
3183
3193
  }
3184
- setUpdateStatus("UPDATING");
3194
+ setIsUpdating(true);
3185
3195
  const isSuccess = await installUpdate(updateInfo);
3186
- if (isSuccess) setUpdateStatus("INSTALLING_UPDATE");
3196
+ config.onCheckUpdateCompleted?.({
3197
+ isBundleUpdated: isSuccess
3198
+ });
3199
+ setIsUpdating(false);
3187
3200
  } catch (error) {
3188
- if (error instanceof HotUpdaterError) setUpdateError(error);
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 ("UPDATING" === updateStatus && config.fallbackComponent) {
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.setState({
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
- getState: () => HotUpdaterState;
6
- setState: (newState: Partial<HotUpdaterState>) => void;
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
- export type HotUpdaterStatus = "INSTALLING_UPDATE" | "UP_TO_DATE" | "UPDATING";
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<HotUpdaterFallbackProps>;
11
- }
12
- export interface HotUpdaterFallbackProps {
13
- progress: number;
14
- }
15
- export interface WithHotUpdaterProps {
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<P & WithHotUpdaterProps>) => React.ComponentType<P>;
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.1",
3
+ "version": "0.4.1-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.3.1"
76
+ "@hot-updater/js": "0.4.1-0"
77
77
  },
78
78
  "dependencies": {
79
- "@hot-updater/core": "0.3.1"
79
+ "@hot-updater/core": "0.4.1-0"
80
80
  },
81
81
  "scripts": {
82
82
  "build": "rslib build",
package/src/index.ts CHANGED
@@ -16,7 +16,7 @@ export type * from "./native";
16
16
  export * from "./store";
17
17
 
18
18
  addListener("onProgress", ({ progress }) => {
19
- hotUpdaterStore.setState({ progress });
19
+ hotUpdaterStore.setProgress(progress);
20
20
  });
21
21
 
22
22
  export const HotUpdater = {
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
- eventEmitter?.addListener(eventName, listener);
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 getState = () => {
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 setState = (newState: Partial<HotUpdaterState>) => {
26
+ const setProgress = (progress: number) => {
25
27
  state = {
26
- ...state,
27
- ...newState,
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 { getState, setState, subscribe };
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.getState,
46
- hotUpdaterStore.getState,
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<HotUpdaterFallbackProps>;
20
- }
21
-
22
- export interface HotUpdaterFallbackProps {
23
- progress: number;
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> = (props) => {
97
- const [updateStatus, setUpdateStatus] = useState<HotUpdaterStatus | null>(
98
- null,
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
- const { progress } = useHotUpdaterStore();
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
- setUpdateStatus("UP_TO_DATE");
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
- if (isSuccess) {
119
- setUpdateStatus("INSTALLING_UPDATE");
120
- }
107
+ config.onCheckUpdateCompleted?.({ isBundleUpdated: isSuccess });
108
+ setIsUpdating(false);
121
109
  } catch (error) {
122
110
  if (error instanceof HotUpdaterError) {
123
- setUpdateError(error);
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 (updateStatus === "UPDATING" && config.fallbackComponent) {
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;