react-native-3rddigital-appupdate 1.0.6 → 1.0.8

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/README.md CHANGED
@@ -44,6 +44,7 @@ const App = () => {
44
44
  androidPackage: 'com.example.android',
45
45
  loaderOptions: {
46
46
  text: 'Downloading update...',
47
+ showProgress: true,
47
48
  },
48
49
  dialogOptions: {
49
50
  title: 'Update Available',
@@ -91,14 +92,16 @@ Options:
91
92
 
92
93
  Props (LoaderOptions):
93
94
 
94
- | Key | Type | Default | Description |
95
- | ----------------- | --------- | ----------------- | ------------------------------------- |
96
- | `text` | string | `undefined` | Text displayed below the spinner |
97
- | `color` | string | `#2563EB` | Spinner color |
98
- | `backgroundColor` | string | `rgba(0,0,0,0.3)` | Overlay background color |
99
- | `textColor` | string | `#fff` | Loader text color |
100
- | `containerStyle` | ViewStyle | `{}` | Custom style for the loader container |
101
- | `textStyle` | TextStyle | `{}` | Custom style for the loader text |
95
+ | Key | Type | Default | Description |
96
+ | ------------------- | --------- | ----------------- | ----------------------------------------------- |
97
+ | `text` | string | `undefined` | Text displayed below the spinner |
98
+ | `color` | string | `#2563EB` | Spinner color |
99
+ | `backgroundColor` | string | `rgba(0,0,0,0.3)` | Overlay background color |
100
+ | `textColor` | string | `#fff` | Loader text color |
101
+ | `containerStyle` | ViewStyle | `{}` | Custom style for the loader container |
102
+ | `textStyle` | TextStyle | `{}` | Custom style for the loader text |
103
+ | `progressTextStyle` | TextStyle | `{}` | Custom style for the loader progress text |
104
+ | `showProgress` | boolean | `false` | Show real-time download percentage (e.g. “45%”) |
102
105
 
103
106
  🔹 Dialog (AppAlertDialog)
104
107
 
@@ -5,15 +5,22 @@ import { ActivityIndicator, StyleSheet, Text, View } from 'react-native';
5
5
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
6
6
  let showLoader;
7
7
  let hideLoader;
8
+ let updateProgressValue;
8
9
  export const AppLoader = () => {
9
10
  const [visible, setVisible] = useState(false);
10
11
  const [options, setOptions] = useState({});
12
+ const [progress, setProgress] = useState(0);
11
13
  showLoader = opts => {
12
14
  setOptions(opts || {});
15
+ setProgress(0);
13
16
  setVisible(true);
14
17
  };
15
18
  hideLoader = () => {
16
19
  setVisible(false);
20
+ setProgress(0);
21
+ };
22
+ updateProgressValue = value => {
23
+ setProgress(value);
17
24
  };
18
25
  if (!visible) return null;
19
26
  return /*#__PURE__*/_jsx(View, {
@@ -30,6 +37,11 @@ export const AppLoader = () => {
30
37
  color: options.textColor || '#fff'
31
38
  }, options.textStyle],
32
39
  children: options.text
40
+ }), options.showProgress && /*#__PURE__*/_jsxs(Text, {
41
+ style: [styles.progressText, {
42
+ color: options.textColor || '#fff'
43
+ }, options.progressTextStyle],
44
+ children: [progress.toFixed(0), "%"]
33
45
  })]
34
46
  })
35
47
  });
@@ -50,9 +62,17 @@ const styles = StyleSheet.create({
50
62
  text: {
51
63
  marginTop: 12,
52
64
  fontSize: 14,
53
- fontWeight: '500'
65
+ fontWeight: '500',
66
+ textAlign: 'center'
67
+ },
68
+ progressText: {
69
+ marginTop: 10,
70
+ fontSize: 13,
71
+ fontWeight: '600',
72
+ textAlign: 'center'
54
73
  }
55
74
  });
56
75
  AppLoader.show = options => showLoader?.(options);
57
76
  AppLoader.hide = () => hideLoader?.();
77
+ AppLoader.updateProgress = progress => updateProgressValue?.(progress);
58
78
  //# sourceMappingURL=AppLoader.js.map
@@ -1 +1 @@
1
- {"version":3,"names":["useState","ActivityIndicator","StyleSheet","Text","View","jsx","_jsx","jsxs","_jsxs","showLoader","hideLoader","AppLoader","visible","setVisible","options","setOptions","opts","style","styles","overlay","backgroundColor","children","loaderBox","containerStyle","size","color","text","textColor","textStyle","create","absoluteFillObject","justifyContent","alignItems","zIndex","padding","borderRadius","marginTop","fontSize","fontWeight","show","hide"],"sourceRoot":"../../src","sources":["AppLoader.tsx"],"mappings":";;AAAA,SAASA,QAAQ,QAAQ,OAAO;AAChC,SACEC,iBAAiB,EACjBC,UAAU,EACVC,IAAI,EACJC,IAAI,QAGC,cAAc;AAAC,SAAAC,GAAA,IAAAC,IAAA,EAAAC,IAAA,IAAAC,KAAA;AAWtB,IAAIC,UAA6C;AACjD,IAAIC,UAAsB;AAE1B,OAAO,MAAMC,SAAS,GAAGA,CAAA,KAAM;EAC7B,MAAM,CAACC,OAAO,EAAEC,UAAU,CAAC,GAAGb,QAAQ,CAAC,KAAK,CAAC;EAC7C,MAAM,CAACc,OAAO,EAAEC,UAAU,CAAC,GAAGf,QAAQ,CAAgB,CAAC,CAAC,CAAC;EAEzDS,UAAU,GAAIO,IAAoB,IAAK;IACrCD,UAAU,CAACC,IAAI,IAAI,CAAC,CAAC,CAAC;IACtBH,UAAU,CAAC,IAAI,CAAC;EAClB,CAAC;EAEDH,UAAU,GAAGA,CAAA,KAAM;IACjBG,UAAU,CAAC,KAAK,CAAC;EACnB,CAAC;EAED,IAAI,CAACD,OAAO,EAAE,OAAO,IAAI;EAEzB,oBACEN,IAAA,CAACF,IAAI;IACHa,KAAK,EAAE,CACLC,MAAM,CAACC,OAAO,EACd;MAAEC,eAAe,EAAEN,OAAO,CAACM,eAAe,IAAI;IAAkB,CAAC,CACjE;IAAAC,QAAA,eAEFb,KAAA,CAACJ,IAAI;MAACa,KAAK,EAAE,CAACC,MAAM,CAACI,SAAS,EAAER,OAAO,CAACS,cAAc,CAAE;MAAAF,QAAA,gBACtDf,IAAA,CAACL,iBAAiB;QAACuB,IAAI,EAAC,OAAO;QAACC,KAAK,EAAEX,OAAO,CAACW,KAAK,IAAI;MAAU,CAAE,CAAC,EACpEX,OAAO,CAACY,IAAI,iBACXpB,IAAA,CAACH,IAAI;QACHc,KAAK,EAAE,CACLC,MAAM,CAACQ,IAAI,EACX;UAAED,KAAK,EAAEX,OAAO,CAACa,SAAS,IAAI;QAAO,CAAC,EACtCb,OAAO,CAACc,SAAS,CACjB;QAAAP,QAAA,EAEDP,OAAO,CAACY;MAAI,CACT,CACP;IAAA,CACG;EAAC,CACH,CAAC;AAEX,CAAC;AAED,MAAMR,MAAM,GAAGhB,UAAU,CAAC2B,MAAM,CAAC;EAC/BV,OAAO,EAAE;IACP,GAAGjB,UAAU,CAAC4B,kBAAkB;IAChCC,cAAc,EAAE,QAAQ;IACxBC,UAAU,EAAE,QAAQ;IACpBC,MAAM,EAAE;EACV,CAAC;EACDX,SAAS,EAAE;IACTY,OAAO,EAAE,EAAE;IACXC,YAAY,EAAE,EAAE;IAChBf,eAAe,EAAE,SAAS;IAC1BY,UAAU,EAAE;EACd,CAAC;EACDN,IAAI,EAAE;IACJU,SAAS,EAAE,EAAE;IACbC,QAAQ,EAAE,EAAE;IACZC,UAAU,EAAE;EACd;AACF,CAAC,CAAC;AAEF3B,SAAS,CAAC4B,IAAI,GAAIzB,OAAuB,IAAKL,UAAU,GAAGK,OAAO,CAAC;AACnEH,SAAS,CAAC6B,IAAI,GAAG,MAAM9B,UAAU,GAAG,CAAC","ignoreList":[]}
1
+ {"version":3,"names":["useState","ActivityIndicator","StyleSheet","Text","View","jsx","_jsx","jsxs","_jsxs","showLoader","hideLoader","updateProgressValue","AppLoader","visible","setVisible","options","setOptions","progress","setProgress","opts","value","style","styles","overlay","backgroundColor","children","loaderBox","containerStyle","size","color","text","textColor","textStyle","showProgress","progressText","progressTextStyle","toFixed","create","absoluteFillObject","justifyContent","alignItems","zIndex","padding","borderRadius","marginTop","fontSize","fontWeight","textAlign","show","hide","updateProgress"],"sourceRoot":"../../src","sources":["AppLoader.tsx"],"mappings":"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","ignoreList":[]}
@@ -7,15 +7,16 @@ import DeviceInfo from 'react-native-device-info';
7
7
  import hotUpdate from 'react-native-ota-hot-update';
8
8
  import { AppAlertDialog } from "./AppAlertDialog.js";
9
9
  import { AppLoader } from "./AppLoader.js";
10
- const API_URL = 'https://dev.3rddigital.com/appupdate-api/api/';
11
10
  export const checkOTAUpdate = async ({
12
11
  key,
13
12
  iosPackage,
14
13
  androidPackage,
15
14
  loaderOptions,
16
- dialogOptions
15
+ dialogOptions,
16
+ baseUrl
17
17
  }) => {
18
18
  try {
19
+ const API_URL = baseUrl;
19
20
  const response = await axios.get(`${API_URL}projects/get-bundle?key=${key}&iosPackage=${iosPackage}&androidPackage=${androidPackage}`);
20
21
  const currentVersion = await hotUpdate.getCurrentVersion();
21
22
  const data = Platform.OS === 'android' ? response?.data?.android : response?.data?.ios;
@@ -25,12 +26,18 @@ export const checkOTAUpdate = async ({
25
26
  const bundleId = data?.bundleId ?? '';
26
27
  const currentAppVersion = DeviceInfo.getVersion();
27
28
  const bundleAppVersion = data?.appVersion ?? currentAppVersion;
28
- if (version <= currentVersion || currentAppVersion != bundleAppVersion) {
29
+ if (version <= currentVersion || currentAppVersion !== bundleAppVersion) {
29
30
  return;
30
31
  }
31
32
  const downloadAndReport = () => {
32
33
  AppLoader.show(loaderOptions);
33
34
  hotUpdate.downloadBundleUri(ReactNativeBlobUtil, url, version, {
35
+ progress: (received, total) => {
36
+ if (loaderOptions?.showProgress) {
37
+ const percentage = (Number(received) / Number(total) * 100).toFixed(1);
38
+ AppLoader.updateProgress(Number(percentage));
39
+ }
40
+ },
34
41
  updateSuccess: () => {
35
42
  axios.post(`${API_URL}bundles/${bundleId}/count`, {
36
43
  status: 'success'
@@ -1 +1 @@
1
- {"version":3,"names":["axios","Platform","ReactNativeBlobUtil","DeviceInfo","hotUpdate","AppAlertDialog","AppLoader","API_URL","checkOTAUpdate","key","iosPackage","androidPackage","loaderOptions","dialogOptions","response","get","currentVersion","getCurrentVersion","data","OS","android","ios","version","forceUpdate","url","bundleId","currentAppVersion","getVersion","bundleAppVersion","appVersion","downloadAndReport","show","downloadBundleUri","updateSuccess","post","status","headers","finally","hide","updateFail","error","JSON","stringify","deviceInfo","model","getModel","brand","getBrand","systemName","getSystemName","systemVersion","getSystemVersion","restartAfterInstall","restartDelay","showMessage","title","message","confirmText","cancelText","onConfirm","onCancel","err","console","warn"],"sourceRoot":"../../src","sources":["checkOTAUpdate.ts"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"names":["axios","Platform","ReactNativeBlobUtil","DeviceInfo","hotUpdate","AppAlertDialog","AppLoader","checkOTAUpdate","key","iosPackage","androidPackage","loaderOptions","dialogOptions","baseUrl","API_URL","response","get","currentVersion","getCurrentVersion","data","OS","android","ios","version","forceUpdate","url","bundleId","currentAppVersion","getVersion","bundleAppVersion","appVersion","downloadAndReport","show","downloadBundleUri","progress","received","total","showProgress","percentage","Number","toFixed","updateProgress","updateSuccess","post","status","headers","finally","hide","updateFail","error","JSON","stringify","deviceInfo","model","getModel","brand","getBrand","systemName","getSystemName","systemVersion","getSystemVersion","restartAfterInstall","restartDelay","showMessage","title","message","confirmText","cancelText","onConfirm","onCancel","err","console","warn"],"sourceRoot":"../../src","sources":["checkOTAUpdate.ts"],"mappings":"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","ignoreList":[]}
@@ -6,10 +6,13 @@ export type LoaderOptions = {
6
6
  textColor?: string;
7
7
  containerStyle?: ViewStyle;
8
8
  textStyle?: TextStyle;
9
+ progressTextStyle?: TextStyle;
10
+ showProgress?: boolean;
9
11
  };
10
12
  export declare const AppLoader: {
11
13
  (): import("react/jsx-runtime").JSX.Element | null;
12
14
  show(options?: LoaderOptions): void;
13
15
  hide(): void;
16
+ updateProgress(progress: number): void;
14
17
  };
15
18
  //# sourceMappingURL=AppLoader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AppLoader.d.ts","sourceRoot":"","sources":["../../../src/AppLoader.tsx"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB,CAAC;AAKF,eAAO,MAAM,SAAS;;mBA4DM,aAAa;;CAtBxC,CAAC"}
1
+ {"version":3,"file":"AppLoader.d.ts","sourceRoot":"","sources":["../../../src/AppLoader.tsx"],"names":[],"mappings":"AACA,OAAO,EAKL,KAAK,SAAS,EACd,KAAK,SAAS,EACf,MAAM,cAAc,CAAC;AAEtB,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,iBAAiB,CAAC,EAAE,SAAS,CAAC;IAC9B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB,CAAC;AAMF,eAAO,MAAM,SAAS;;mBAqFM,aAAa;;6BAEH,MAAM;CA/B3C,CAAC"}
@@ -6,6 +6,7 @@ export type OTAUpdateProps = {
6
6
  androidPackage: string;
7
7
  loaderOptions?: LoaderOptions;
8
8
  dialogOptions?: Omit<DialogOptions, 'onConfirm' | 'onCancel'>;
9
+ baseUrl: string;
9
10
  };
10
- export declare const checkOTAUpdate: ({ key, iosPackage, androidPackage, loaderOptions, dialogOptions, }: OTAUpdateProps) => Promise<void>;
11
+ export declare const checkOTAUpdate: ({ key, iosPackage, androidPackage, loaderOptions, dialogOptions, baseUrl, }: OTAUpdateProps) => Promise<void>;
11
12
  //# sourceMappingURL=checkOTAUpdate.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"checkOTAUpdate.d.ts","sourceRoot":"","sources":["../../../src/checkOTAUpdate.ts"],"names":[],"mappings":"AAKA,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAa,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAI5D,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,aAAa,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,WAAW,GAAG,UAAU,CAAC,CAAC;CAC/D,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,oEAMlC,cAAc,kBAuEhB,CAAC"}
1
+ {"version":3,"file":"checkOTAUpdate.d.ts","sourceRoot":"","sources":["../../../src/checkOTAUpdate.ts"],"names":[],"mappings":"AAKA,OAAO,EAAkB,KAAK,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAa,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5D,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,aAAa,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,WAAW,GAAG,UAAU,CAAC,CAAC;IAC9D,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,cAAc,GAAU,6EAOlC,cAAc,kBAiFhB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-3rddigital-appupdate",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "A React Native library for seamless over-the-air (OTA) updates with version checks, automatic bundle download, and customizable user prompts for iOS and Android.",
5
5
  "main": "./lib/module/index.js",
6
6
  "types": "./lib/typescript/src/index.d.ts",
@@ -157,12 +157,15 @@
157
157
  "version": "0.54.3"
158
158
  },
159
159
  "dependencies": {
160
+ "@aws-sdk/client-s3": "^3.995.0",
160
161
  "@inquirer/prompts": "^7.8.6",
161
162
  "axios": "^1.12.2",
163
+ "dotenv": "^17.3.1",
162
164
  "form-data": "^4.0.4",
163
165
  "react-native-blob-util": "^0.22.2",
164
166
  "react-native-device-info": "^14.1.1",
165
- "react-native-ota-hot-update": "^2.3.4"
167
+ "react-native-ota-hot-update": "^2.3.4",
168
+ "uuid": "^13.0.0"
166
169
  },
167
170
  "bin": {
168
171
  "appupdate": "./scripts/bundle.js"
package/scripts/bundle.js CHANGED
@@ -1,12 +1,71 @@
1
1
  #!/usr/bin/env node
2
+ require('dotenv').config();
2
3
  const { execSync } = require('child_process');
3
4
  const path = require('path');
4
5
  const fs = require('fs');
5
6
  const axios = require('axios');
6
- const FormData = require('form-data');
7
7
  const { input, select, confirm } = require('@inquirer/prompts');
8
+ const { PutObjectCommand, S3Client } = require('@aws-sdk/client-s3');
9
+ const { v4: uuidv4 } = require('uuid');
8
10
 
9
- const API_BASE_URL = 'https://dev.3rddigital.com/appupdate-api/api/';
11
+ const APPUPDATE_BASE_URL = process.env.APPUPDATE_BASE_URL;
12
+ const APPUPDATE_AWS_REGION = process.env.APPUPDATE_AWS_REGION;
13
+ const APPUPDATE_AWS_ACCESS_KEY_ID = process.env.APPUPDATE_AWS_ACCESS_KEY_ID;
14
+ const APPUPDATE_AWS_SECRET_ACCESS_KEY =
15
+ process.env.APPUPDATE_AWS_SECRET_ACCESS_KEY;
16
+ const APPUPDATE_AWS_BUCKET_NAME = process.env.APPUPDATE_AWS_BUCKET_NAME;
17
+
18
+ /**
19
+ * Decrypt helper logic
20
+ */
21
+ function DecriptEnv(wrappedKey) {
22
+ if (!wrappedKey) return '';
23
+ if (typeof wrappedKey !== 'string')
24
+ throw new TypeError('wrappedKey must be a string');
25
+ if (wrappedKey.length <= 8) throw new Error('wrappedKey too short to unwrap');
26
+ const trimmed = wrappedKey.slice(4, -2);
27
+ const result = trimmed.slice(0, 2) + trimmed.slice(4);
28
+ return result;
29
+ }
30
+
31
+ const s3Client = new S3Client({
32
+ region: DecriptEnv(APPUPDATE_AWS_REGION),
33
+ credentials: {
34
+ accessKeyId: DecriptEnv(APPUPDATE_AWS_ACCESS_KEY_ID),
35
+ secretAccessKey: DecriptEnv(APPUPDATE_AWS_SECRET_ACCESS_KEY),
36
+ },
37
+ });
38
+
39
+ /**
40
+ * Uploads local file to S3
41
+ */
42
+ async function uploadFileToS3(filePath, bucketName, folder) {
43
+ const fileName = path.basename(filePath);
44
+ const cleanFileName = fileName.replace(/\s+/g, '_');
45
+ const uniqueId = uuidv4();
46
+ const fileKey = `${folder}/${uniqueId}/${cleanFileName}`;
47
+ const fileBuffer = fs.readFileSync(filePath);
48
+
49
+ try {
50
+ const command = new PutObjectCommand({
51
+ Bucket: bucketName,
52
+ Key: fileKey,
53
+ Body: fileBuffer,
54
+ ContentType: 'application/zip',
55
+ ACL: 'public-read',
56
+ });
57
+
58
+ await s3Client.send(command);
59
+
60
+ const region = DecriptEnv(APPUPDATE_AWS_REGION);
61
+ const location = `https://${bucketName}.s3.${region}.amazonaws.com/${fileKey}`;
62
+
63
+ return { Location: location, Key: fileKey, Bucket: bucketName };
64
+ } catch (error) {
65
+ console.error('❌ S3 Upload Error:', error);
66
+ throw error;
67
+ }
68
+ }
10
69
 
11
70
  /**
12
71
  * Run a shell command synchronously.
@@ -23,38 +82,56 @@ function run(command) {
23
82
  }
24
83
 
25
84
  /**
26
- * Upload bundle file to server.
85
+ * Step 1: Upload to S3
86
+ * Step 2: Register with Backend
27
87
  */
28
88
  async function uploadBundle({ filePath, platform, config }) {
29
- console.log(`📤 Uploading ${platform} bundle to server...`);
89
+ console.log(`📤 Starting upload process for ${platform}...`);
30
90
 
31
91
  if (!fs.existsSync(filePath)) {
32
92
  console.error(`❌ File not found: ${filePath}`);
33
93
  process.exit(1);
34
94
  }
35
95
 
36
- const fileStream = fs.createReadStream(filePath);
37
- const form = new FormData();
38
- form.append('bundle', fileStream);
39
- form.append('projectId', config.PROJECT_ID);
40
- form.append('environment', config.ENVIRONMENT);
41
- form.append('platform', platform);
42
- form.append('version', config.VERSION);
43
- form.append('forceUpdate', String(config.FORCE_UPDATE));
44
-
45
96
  try {
46
- const res = await axios.post(`${API_BASE_URL}/bundles`, form, {
97
+ // 1. Upload to S3
98
+ const s3Result = await uploadFileToS3(
99
+ filePath,
100
+ DecriptEnv(APPUPDATE_AWS_BUCKET_NAME),
101
+ config.ENVIRONMENT === 'development'
102
+ ? 'uploads/development'
103
+ : 'uploads/production'
104
+ );
105
+
106
+ console.log(`✅ S3 Upload Complete: ${s3Result.Key}`);
107
+
108
+ // 2. Prepare Payload for Backend
109
+ const stats = fs.statSync(filePath);
110
+ const payload = {
111
+ projectId: config.PROJECT_ID,
112
+ environment: config.ENVIRONMENT,
113
+ platform: platform,
114
+ version: config.VERSION,
115
+ forceUpdate: config.FORCE_UPDATE,
116
+ s3Key: s3Result.Key,
117
+ s3Url: s3Result.Location,
118
+ fileName: path.basename(filePath),
119
+ fileSize: stats.size,
120
+ };
121
+
122
+ const res = await axios.post(`${APPUPDATE_BASE_URL}/bundles`, payload, {
47
123
  headers: {
48
- ...form.getHeaders(),
49
- Authorization: `Bearer ${config.API_TOKEN}`,
124
+ 'Content-Type': 'application/json',
125
+ 'Authorization': `Bearer ${config.API_TOKEN}`,
50
126
  },
51
127
  });
128
+
52
129
  console.log(
53
- `✅ ${platform} bundle uploaded successfully! Response:`,
130
+ `✅ ${platform} bundle registered! Response:`,
54
131
  JSON.stringify(res.data, null, 2)
55
132
  );
56
133
  } catch (err) {
57
- console.error(`❌ ${platform} bundle upload failed!`);
134
+ console.error(`❌ ${platform} bundle upload/registration failed!`);
58
135
  if (err.response) {
59
136
  console.error('Status:', err.response.status);
60
137
  console.error('Data:', err.response.data);
package/src/AppLoader.tsx CHANGED
@@ -15,22 +15,32 @@ export type LoaderOptions = {
15
15
  textColor?: string;
16
16
  containerStyle?: ViewStyle;
17
17
  textStyle?: TextStyle;
18
+ progressTextStyle?: TextStyle;
19
+ showProgress?: boolean;
18
20
  };
19
21
 
20
22
  let showLoader: (options?: LoaderOptions) => void;
21
23
  let hideLoader: () => void;
24
+ let updateProgressValue: (progress: number) => void;
22
25
 
23
26
  export const AppLoader = () => {
24
27
  const [visible, setVisible] = useState(false);
25
28
  const [options, setOptions] = useState<LoaderOptions>({});
29
+ const [progress, setProgress] = useState(0);
26
30
 
27
31
  showLoader = (opts?: LoaderOptions) => {
28
32
  setOptions(opts || {});
33
+ setProgress(0);
29
34
  setVisible(true);
30
35
  };
31
36
 
32
37
  hideLoader = () => {
33
38
  setVisible(false);
39
+ setProgress(0);
40
+ };
41
+
42
+ updateProgressValue = (value: number) => {
43
+ setProgress(value);
34
44
  };
35
45
 
36
46
  if (!visible) return null;
@@ -55,6 +65,17 @@ export const AppLoader = () => {
55
65
  {options.text}
56
66
  </Text>
57
67
  )}
68
+ {options.showProgress && (
69
+ <Text
70
+ style={[
71
+ styles.progressText,
72
+ { color: options.textColor || '#fff' },
73
+ options.progressTextStyle,
74
+ ]}
75
+ >
76
+ {progress.toFixed(0)}%
77
+ </Text>
78
+ )}
58
79
  </View>
59
80
  </View>
60
81
  );
@@ -77,8 +98,17 @@ const styles = StyleSheet.create({
77
98
  marginTop: 12,
78
99
  fontSize: 14,
79
100
  fontWeight: '500',
101
+ textAlign: 'center',
102
+ },
103
+ progressText: {
104
+ marginTop: 10,
105
+ fontSize: 13,
106
+ fontWeight: '600',
107
+ textAlign: 'center',
80
108
  },
81
109
  });
82
110
 
83
111
  AppLoader.show = (options?: LoaderOptions) => showLoader?.(options);
84
112
  AppLoader.hide = () => hideLoader?.();
113
+ AppLoader.updateProgress = (progress: number) =>
114
+ updateProgressValue?.(progress);
@@ -6,14 +6,13 @@ import hotUpdate from 'react-native-ota-hot-update';
6
6
  import { AppAlertDialog, type DialogOptions } from './AppAlertDialog';
7
7
  import { AppLoader, type LoaderOptions } from './AppLoader';
8
8
 
9
- const API_URL = 'https://dev.3rddigital.com/appupdate-api/api/';
10
-
11
9
  export type OTAUpdateProps = {
12
10
  key: string;
13
11
  iosPackage: string;
14
12
  androidPackage: string;
15
13
  loaderOptions?: LoaderOptions;
16
14
  dialogOptions?: Omit<DialogOptions, 'onConfirm' | 'onCancel'>;
15
+ baseUrl: string;
17
16
  };
18
17
 
19
18
  export const checkOTAUpdate = async ({
@@ -22,8 +21,10 @@ export const checkOTAUpdate = async ({
22
21
  androidPackage,
23
22
  loaderOptions,
24
23
  dialogOptions,
24
+ baseUrl,
25
25
  }: OTAUpdateProps) => {
26
26
  try {
27
+ const API_URL = baseUrl;
27
28
  const response = await axios.get(
28
29
  `${API_URL}projects/get-bundle?key=${key}&iosPackage=${iosPackage}&androidPackage=${androidPackage}`
29
30
  );
@@ -38,13 +39,22 @@ export const checkOTAUpdate = async ({
38
39
  const currentAppVersion = DeviceInfo.getVersion();
39
40
  const bundleAppVersion = data?.appVersion ?? currentAppVersion;
40
41
 
41
- if (version <= currentVersion || currentAppVersion != bundleAppVersion) {
42
+ if (version <= currentVersion || currentAppVersion !== bundleAppVersion) {
42
43
  return;
43
44
  }
44
45
 
45
46
  const downloadAndReport = () => {
46
47
  AppLoader.show(loaderOptions);
47
48
  hotUpdate.downloadBundleUri(ReactNativeBlobUtil, url, version, {
49
+ progress: (received, total) => {
50
+ if (loaderOptions?.showProgress) {
51
+ const percentage = (
52
+ (Number(received) / Number(total)) *
53
+ 100
54
+ ).toFixed(1);
55
+ AppLoader.updateProgress(Number(percentage));
56
+ }
57
+ },
48
58
  updateSuccess: () => {
49
59
  axios
50
60
  .post(