react-native-stallion 1.1.0 → 1.1.2

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.
Files changed (148) hide show
  1. package/README.md +10 -6
  2. package/android/build.gradle +4 -1
  3. package/android/src/main/java/com/stallion/StallionConstants.java +12 -2
  4. package/android/src/main/java/com/stallion/StallionDefaultErrorActivity.java +1 -1
  5. package/android/src/main/java/com/stallion/StallionErrorBoundary.java +49 -0
  6. package/android/src/main/java/com/stallion/StallionModule.java +104 -139
  7. package/ios/main/Stallion-Bridging-Header.h +1 -0
  8. package/ios/main/Stallion.swift +2 -3
  9. package/ios/main/StallionConstants.swift +3 -1
  10. package/ios/main/StallionDownloader.swift +9 -9
  11. package/ios/main/StallionErrorBoundary.h +17 -0
  12. package/ios/main/StallionErrorBoundary.m +62 -0
  13. package/ios/main/StallionModule.m +3 -35
  14. package/package.json +1 -1
  15. package/react-native-stallion.podspec +4 -2
  16. package/src/index.js +16 -11
  17. package/src/index.js.map +1 -1
  18. package/src/main/components/common/ErrorView/index.js +10 -4
  19. package/src/main/components/common/ErrorView/index.js.map +1 -1
  20. package/src/main/components/common/ErrorView/styles.js +12 -2
  21. package/src/main/components/common/ErrorView/styles.js.map +1 -1
  22. package/src/main/components/common/Footer/index.js +1 -1
  23. package/src/main/components/common/Footer/index.js.map +1 -1
  24. package/src/main/components/common/Footer/styles.js +7 -4
  25. package/src/main/components/common/Footer/styles.js.map +1 -1
  26. package/src/main/components/common/FooterLoader/index.js +11 -0
  27. package/src/main/components/common/FooterLoader/index.js.map +1 -0
  28. package/src/main/components/common/FooterLoader/styles.js +12 -0
  29. package/src/main/components/common/FooterLoader/styles.js.map +1 -0
  30. package/src/main/components/common/Header/styles.js +3 -2
  31. package/src/main/components/common/Header/styles.js.map +1 -1
  32. package/src/main/components/common/OverlayLoader/styles.js +2 -2
  33. package/src/main/components/common/OverlayLoader/styles.js.map +1 -1
  34. package/src/main/components/common/ProfileOverlay/styles.js +4 -2
  35. package/src/main/components/common/ProfileOverlay/styles.js.map +1 -1
  36. package/src/main/components/modules/listing/components/BundleCard.js +4 -2
  37. package/src/main/components/modules/listing/components/BundleCard.js.map +1 -1
  38. package/src/main/components/modules/listing/components/BundleCardInfoSection.js +6 -5
  39. package/src/main/components/modules/listing/components/BundleCardInfoSection.js.map +1 -1
  40. package/src/main/components/modules/listing/components/CardDescriptionContent.js +2 -1
  41. package/src/main/components/modules/listing/components/CardDescriptionContent.js.map +1 -1
  42. package/src/main/components/modules/listing/components/styles/index.js +6 -3
  43. package/src/main/components/modules/listing/components/styles/index.js.map +1 -1
  44. package/src/main/components/modules/listing/hooks/useListing.js +14 -2
  45. package/src/main/components/modules/listing/hooks/useListing.js.map +1 -1
  46. package/src/main/components/modules/listing/index.js +45 -16
  47. package/src/main/components/modules/listing/index.js.map +1 -1
  48. package/src/main/components/modules/listing/styles.js +7 -0
  49. package/src/main/components/modules/listing/styles.js.map +1 -1
  50. package/src/main/components/modules/login/components/Email.js +5 -1
  51. package/src/main/components/modules/login/components/Email.js.map +1 -1
  52. package/src/main/components/modules/login/components/Otp.js +5 -1
  53. package/src/main/components/modules/login/components/Otp.js.map +1 -1
  54. package/src/main/components/modules/login/components/styles/index.js +5 -1
  55. package/src/main/components/modules/login/components/styles/index.js.map +1 -1
  56. package/src/main/components/modules/login/styles/index.js +2 -1
  57. package/src/main/components/modules/login/styles/index.js.map +1 -1
  58. package/src/main/components/modules/modal/hooks/useStallionModal.js +3 -1
  59. package/src/main/components/modules/modal/hooks/useStallionModal.js.map +1 -1
  60. package/src/main/constants/apiConstants.js +5 -3
  61. package/src/main/constants/apiConstants.js.map +1 -1
  62. package/src/main/constants/appConstants.js +9 -2
  63. package/src/main/constants/appConstants.js.map +1 -1
  64. package/src/main/constants/colors.js +3 -1
  65. package/src/main/constants/colors.js.map +1 -1
  66. package/src/main/state/actionCreators/useBundleActions.js +24 -9
  67. package/src/main/state/actionCreators/useBundleActions.js.map +1 -1
  68. package/src/main/state/actionCreators/useDownloadActions.js +20 -7
  69. package/src/main/state/actionCreators/useDownloadActions.js.map +1 -1
  70. package/src/main/state/actionCreators/useUserActions.js +11 -4
  71. package/src/main/state/actionCreators/useUserActions.js.map +1 -1
  72. package/src/main/state/actions/bundleActions.js +18 -0
  73. package/src/main/state/actions/bundleActions.js.map +1 -1
  74. package/src/main/state/reducers/bundleReducer.js +34 -3
  75. package/src/main/state/reducers/bundleReducer.js.map +1 -1
  76. package/src/main/utils/ErrorBoundary.js +3 -3
  77. package/src/main/utils/ErrorBoundary.js.map +1 -1
  78. package/src/main/utils/SharedDataManager.js +12 -3
  79. package/src/main/utils/SharedDataManager.js.map +1 -1
  80. package/src/main/utils/apiUtils.js +1 -1
  81. package/src/main/utils/apiUtils.js.map +1 -1
  82. package/src/main/utils/withStallion.js +5 -2
  83. package/src/main/utils/withStallion.js.map +1 -1
  84. package/src/nativeScripts/getStallionEnabled.js +1 -1
  85. package/src/nativeScripts/getStallionEnabled.js.map +1 -1
  86. package/src/types/bundle.types.js +3 -0
  87. package/src/types/bundle.types.js.map +1 -1
  88. package/types/index.d.ts.map +1 -1
  89. package/types/main/components/common/ErrorView/index.d.ts +1 -0
  90. package/types/main/components/common/ErrorView/index.d.ts.map +1 -1
  91. package/types/main/components/common/ErrorView/styles.d.ts +10 -0
  92. package/types/main/components/common/ErrorView/styles.d.ts.map +1 -1
  93. package/types/main/components/common/Footer/styles.d.ts +4 -1
  94. package/types/main/components/common/Footer/styles.d.ts.map +1 -1
  95. package/types/main/components/common/FooterLoader/index.d.ts +4 -0
  96. package/types/main/components/common/FooterLoader/index.d.ts.map +1 -0
  97. package/types/main/components/common/FooterLoader/styles.d.ts +10 -0
  98. package/types/main/components/common/FooterLoader/styles.d.ts.map +1 -0
  99. package/types/main/components/common/Header/styles.d.ts +1 -0
  100. package/types/main/components/common/Header/styles.d.ts.map +1 -1
  101. package/types/main/components/common/ProfileOverlay/styles.d.ts +2 -0
  102. package/types/main/components/common/ProfileOverlay/styles.d.ts.map +1 -1
  103. package/types/main/components/modules/listing/components/BundleCard.d.ts +1 -0
  104. package/types/main/components/modules/listing/components/BundleCard.d.ts.map +1 -1
  105. package/types/main/components/modules/listing/components/BundleCardInfoSection.d.ts +3 -2
  106. package/types/main/components/modules/listing/components/BundleCardInfoSection.d.ts.map +1 -1
  107. package/types/main/components/modules/listing/components/styles/index.d.ts +3 -0
  108. package/types/main/components/modules/listing/components/styles/index.d.ts.map +1 -1
  109. package/types/main/components/modules/listing/hooks/useListing.d.ts +2 -0
  110. package/types/main/components/modules/listing/hooks/useListing.d.ts.map +1 -1
  111. package/types/main/components/modules/listing/index.d.ts +2 -2
  112. package/types/main/components/modules/listing/index.d.ts.map +1 -1
  113. package/types/main/components/modules/listing/styles.d.ts +6 -0
  114. package/types/main/components/modules/listing/styles.d.ts.map +1 -1
  115. package/types/main/components/modules/login/components/Email.d.ts.map +1 -1
  116. package/types/main/components/modules/login/components/Otp.d.ts.map +1 -1
  117. package/types/main/components/modules/login/components/styles/index.d.ts +4 -0
  118. package/types/main/components/modules/login/components/styles/index.d.ts.map +1 -1
  119. package/types/main/components/modules/login/styles/index.d.ts +1 -0
  120. package/types/main/components/modules/login/styles/index.d.ts.map +1 -1
  121. package/types/main/components/modules/modal/hooks/useStallionModal.d.ts.map +1 -1
  122. package/types/main/constants/apiConstants.d.ts +5 -3
  123. package/types/main/constants/apiConstants.d.ts.map +1 -1
  124. package/types/main/constants/appConstants.d.ts +8 -1
  125. package/types/main/constants/appConstants.d.ts.map +1 -1
  126. package/types/main/constants/colors.d.ts +2 -0
  127. package/types/main/constants/colors.d.ts.map +1 -1
  128. package/types/main/index.d.ts +1 -1
  129. package/types/main/state/actionCreators/useBundleActions.d.ts +1 -1
  130. package/types/main/state/actionCreators/useBundleActions.d.ts.map +1 -1
  131. package/types/main/state/actionCreators/useDownloadActions.d.ts +1 -1
  132. package/types/main/state/actionCreators/useDownloadActions.d.ts.map +1 -1
  133. package/types/main/state/actionCreators/useUserActions.d.ts.map +1 -1
  134. package/types/main/state/actions/bundleActions.d.ts +3 -0
  135. package/types/main/state/actions/bundleActions.d.ts.map +1 -1
  136. package/types/main/state/reducers/bundleReducer.d.ts.map +1 -1
  137. package/types/main/utils/SharedDataManager.d.ts +6 -2
  138. package/types/main/utils/SharedDataManager.d.ts.map +1 -1
  139. package/types/main/utils/apiUtils.d.ts +1 -1
  140. package/types/main/utils/apiUtils.d.ts.map +1 -1
  141. package/types/main/utils/withStallion.d.ts +2 -1
  142. package/types/main/utils/withStallion.d.ts.map +1 -1
  143. package/types/types/bundle.types.d.ts +22 -4
  144. package/types/types/bundle.types.d.ts.map +1 -1
  145. package/types/types/globalProvider.types.d.ts +2 -2
  146. package/types/types/globalProvider.types.d.ts.map +1 -1
  147. package/types/types/utils.types.d.ts +5 -2
  148. package/types/types/utils.types.d.ts.map +1 -1
package/README.md CHANGED
@@ -1,12 +1,14 @@
1
+ [![stallionBanner](https://assets.stalliontech.io./stallion_logo.png)](https://stalliontech.io/)
2
+
3
+ #### [Sign up With Stallion](https://console.stalliontech.io/) to start using
4
+
1
5
  # react-native-stallion
2
6
 
7
+ [![npm Version](https://img.shields.io/npm/v/react-native-stallion.svg)](https://www.npmjs.com/package/react-native-stallion) [![License](https://img.shields.io/npm/l/react-native-stallion.svg)](https://www.npmjs.com/package/react-native-stallion)
8
+
3
9
  Stallion is the ultimate testing framework for React Native apps !
4
10
  It is a simplified build sharing and testing system that skips all the tedious hastle of generating a new new APK or IPA build everytime you make react native code changes.
5
- With stallion a developer can directly publish a react native build to Stallion servers and any tester can simply download updates through the Stallion app SDK without re installing the app.
6
-
7
- ## Signup
8
-
9
- Signup to use Stallion [here](https://app.stalliontech.io/)
11
+ With stallion a developer can directly publish a react native build to Stallion servers and any tester can simply download updates through the Stallion app SDK without re-installing the app.
10
12
 
11
13
  ## SDK Installation
12
14
 
@@ -22,7 +24,9 @@ npm install --save react-native-stallion
22
24
  yarn add react-native-stallion
23
25
  ```
24
26
 
25
- check complete SDK installation steps from [here](https://docs.stalliontech.io/docs/install)
27
+ ### Complete installation steps
28
+
29
+ Check complete SDK installation steps from [here](https://docs.stalliontech.io/docs/install)
26
30
 
27
31
  ## Documentation
28
32
 
@@ -42,7 +42,10 @@ try {
42
42
  throw e;
43
43
  }
44
44
 
45
- def stallionEnabled = enabledOutput == "true"
45
+ def stallionEnabled = true
46
+ if(enabledOutput == "false") {
47
+ stallionEnabled = false
48
+ }
46
49
 
47
50
  android {
48
51
  sourceSets {
@@ -4,8 +4,8 @@ public class StallionConstants {
4
4
  public static final String MODULE_NAME = "Stallion";
5
5
  public static final String STALLION_PACKAGE_PATH = "/StallionPackage";
6
6
  public static final int DOWNLOAD_BUFFER_SIZE = 1024 * 256;
7
- public static final String API_BASE = "https://stallion-api.redhorse.tech/api/v1";
8
- public static final String DOWNLOAD_API_PATH = "/bundle/download";
7
+
8
+ public static final String DOWNLOAD_ERROR_PREFIX = "Stallion download error: ";
9
9
  public static final String DOWNLOAD_FOLDER_DIR = "/downloads";
10
10
  public static final String BUNDLE_DEST_FOLDER_DIR = "/stallion-build";
11
11
  public static final String SLOT_FOLDER_DIR = "/slots/";
@@ -23,5 +23,15 @@ public class StallionConstants {
23
23
  public static final String STALLION_SWITCH_ON = "STALLION_ON";
24
24
  public static final String STALLION_SWITCH_OFF = "DEFAULT";
25
25
 
26
+ public static final String DOWNLOAD_SUCCESS_MESSAGE = "Success";
27
+
28
+ public static final String DOWNLOAD_API_ERROR_MESSAGE = "Download API error";
29
+
30
+ public static final String DOWNLOAD_FILESYSTEM_ERROR_MESSAGE = "Filesystem error in download";
31
+
32
+ public static final String GENERIC_ERROR = "Something went wrong internally";
33
+
34
+ public static final String DOWNLOAD_DELETE_ERROR = "Download operation cleanup error";
35
+
26
36
  }
27
37
 
@@ -22,6 +22,6 @@ public class StallionDefaultErrorActivity extends Activity {
22
22
  continueButton.setOnClickListener(view -> continueExceptionFlow());
23
23
  }
24
24
  private void continueExceptionFlow() {
25
- StallionModule.continueExcetionFlow();
25
+ StallionErrorBoundary.continueExceptionFlow();
26
26
  }
27
27
  }
@@ -0,0 +1,49 @@
1
+ package com.stallion;
2
+
3
+ import android.app.Activity;
4
+ import android.content.Context;
5
+ import android.content.Intent;
6
+ import android.util.Log;
7
+
8
+ import com.facebook.react.bridge.ReactApplicationContext;
9
+
10
+ public class StallionErrorBoundary {
11
+ public static Thread.UncaughtExceptionHandler _androidUncaughtExceptionHandler;
12
+ public static Thread _exceptionThread;
13
+ public static Throwable _exceptionThrowable;
14
+
15
+ public static ReactApplicationContext _currentContext;
16
+ public static void initErrorBoundary(ReactApplicationContext currentContext) {
17
+ _androidUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
18
+ _currentContext = currentContext;
19
+ }
20
+ public static void toggleExceptionHandler(Boolean shouldEnableErrorHandler) {
21
+ if(shouldEnableErrorHandler) {
22
+ Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> {
23
+ _exceptionThread = thread;
24
+ _exceptionThrowable = throwable;
25
+ String stackTraceString = Log.getStackTraceString(throwable);
26
+ StallionStorage stallionStorage = StallionStorage.getInstance();
27
+ if(stallionStorage.get(StallionConstants.STALLION_SWITCH_STATE_IDENTIFIER).equals(StallionConstants.STALLION_SWITCH_ON)) {
28
+ stallionStorage.set(StallionConstants.STALLION_SWITCH_STATE_IDENTIFIER, StallionConstants.STALLION_SWITCH_OFF);
29
+ Activity currentActivity = _currentContext.getCurrentActivity();
30
+ if(currentActivity != null) {
31
+ Intent myIntent = new Intent(currentActivity, StallionDefaultErrorActivity.class);
32
+ myIntent.putExtra("stack_trace_string", stackTraceString);
33
+ currentActivity.startActivity(myIntent);
34
+ currentActivity.finish();
35
+ } else {
36
+ continueExceptionFlow();
37
+ }
38
+ } else {
39
+ continueExceptionFlow();
40
+ }
41
+ });
42
+ } else {
43
+ Thread.setDefaultUncaughtExceptionHandler(_androidUncaughtExceptionHandler);
44
+ }
45
+ }
46
+ public static void continueExceptionFlow() {
47
+ _androidUncaughtExceptionHandler.uncaughtException(_exceptionThread, _exceptionThrowable);
48
+ }
49
+ }
@@ -13,10 +13,6 @@ import com.facebook.react.bridge.WritableMap;
13
13
  import com.facebook.react.module.annotations.ReactModule;
14
14
  import com.facebook.react.modules.core.DeviceEventManagerModule;
15
15
 
16
- import android.app.Activity;
17
- import android.content.Intent;
18
- import android.os.Handler;
19
- import android.os.Looper;
20
16
  import android.util.Log;
21
17
 
22
18
  import java.io.BufferedInputStream;
@@ -24,7 +20,6 @@ import java.io.BufferedOutputStream;
24
20
  import java.io.File;
25
21
  import java.io.FileOutputStream;
26
22
  import java.io.IOException;
27
- import java.io.OutputStream;
28
23
  import java.net.HttpURLConnection;
29
24
  import java.net.URL;
30
25
  import java.nio.ByteBuffer;
@@ -37,9 +32,6 @@ public class StallionModule extends ReactContextBaseJavaModule {
37
32
  private String baseDir;
38
33
  private StallionStorage stallionStorage;
39
34
  private DeviceEventManagerModule.RCTDeviceEventEmitter eventEmitter;
40
- public static Thread.UncaughtExceptionHandler _androidUncaughtExceptionHandler;
41
- public static Thread _exceptionThread;
42
- public static Throwable _exceptionThrowable;
43
35
 
44
36
  public StallionModule(ReactApplicationContext reactContext) {
45
37
  super(reactContext);
@@ -47,32 +39,12 @@ public class StallionModule extends ReactContextBaseJavaModule {
47
39
  this.baseDir = reactContext.getFilesDir().getAbsolutePath() + StallionConstants.STALLION_PACKAGE_PATH;
48
40
  StallionStorage.getInstance().Initialize(reactContext);
49
41
  this.stallionStorage = StallionStorage.getInstance();
50
- _androidUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
51
- Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
52
- @Override
53
- public void uncaughtException(Thread thread, Throwable throwable) {
54
- _exceptionThread = thread;
55
- _exceptionThrowable = throwable;
56
- String stackTraceString = Log.getStackTraceString(throwable);
57
- if(stallionStorage.get(StallionConstants.STALLION_SWITCH_STATE_IDENTIFIER).equals(StallionConstants.STALLION_SWITCH_ON)) {
58
- toggleStallionSwitch(false);
59
- Activity currentActivity = getCurrentActivity();
60
- Intent myIntent = new Intent(currentActivity, StallionDefaultErrorActivity.class);
61
- myIntent.putExtra("stack_trace_string", stackTraceString);
62
- currentActivity.startActivity(myIntent);
63
- currentActivity.finish();
64
- } else {
65
- continueExcetionFlow();
66
- }
67
- }
68
- });
69
- }
70
-
71
- public static void continueExcetionFlow() {
72
- _androidUncaughtExceptionHandler.uncaughtException(_exceptionThread, _exceptionThrowable);
42
+ StallionErrorBoundary.initErrorBoundary(reactContext);
43
+ String switchState = this.stallionStorage.get(StallionConstants.STALLION_SWITCH_STATE_IDENTIFIER);
44
+ Boolean isStallionEnabled = switchState == null ? false : switchState.equals(StallionConstants.STALLION_SWITCH_ON);
45
+ StallionErrorBoundary.toggleExceptionHandler(isStallionEnabled);
73
46
  }
74
47
 
75
-
76
48
  @Override
77
49
  @NonNull
78
50
  public String getName() {
@@ -100,7 +72,7 @@ public class StallionModule extends ReactContextBaseJavaModule {
100
72
 
101
73
  bundleMeta.putString(StallionConstants.ACTIVE_BUCKET_IDENTIFIER, activeBucket);
102
74
  bundleMeta.putBoolean(StallionConstants.STALLION_SWITCH_STATE_IDENTIFIER,
103
- switchState == null ? false : this.stallionStorage.get(StallionConstants.STALLION_SWITCH_STATE_IDENTIFIER).equals(StallionConstants.STALLION_SWITCH_ON)
75
+ switchState == null ? false : switchState.equals(StallionConstants.STALLION_SWITCH_ON)
104
76
  );
105
77
  bundleMeta.putString(StallionConstants.ACTIVE_VERSION_IDENTIFIER, String.valueOf(activeVersion));
106
78
  callback.invoke(bundleMeta);
@@ -120,126 +92,119 @@ public class StallionModule extends ReactContextBaseJavaModule {
120
92
 
121
93
  @ReactMethod
122
94
  public void downloadPackage(ReadableMap bundleInfo, Promise promise) {
95
+ String receivedBucketId = bundleInfo.getString("bucketId");
96
+ String receivedDownloadUrl = bundleInfo.getString("url");
97
+ Integer receivedVersion = bundleInfo.getInt("version");
98
+
123
99
  ExecutorService executor = Executors.newSingleThreadExecutor();
124
- Handler handler = new Handler(Looper.getMainLooper());
125
-
126
- executor.execute(new Runnable() {
127
- @Override
128
- public void run() {
129
- String receivedBucketId = bundleInfo.getString("bucketId");
130
- String receivedProjectId = bundleInfo.getString("projectId");
131
- Integer receivedVersion = bundleInfo.getInt("version");
132
- String platformValue = "android";
133
- DeviceEventManagerModule.RCTDeviceEventEmitter eventEmitter = getEventEmitter();
134
-
135
- FileOutputStream fout = null;
136
- BufferedOutputStream bout = null;
137
- BufferedInputStream inputStream = null;
138
- HttpURLConnection connection = null;
139
- File downloadedZip = null;
140
- boolean isZip = false;
141
100
 
142
- try {
143
- int DOWNLOAD_BUFFER_SIZE = StallionConstants.DOWNLOAD_BUFFER_SIZE;
144
- URL url = new URL(StallionConstants.API_BASE + StallionConstants.DOWNLOAD_API_PATH);
145
- connection = (HttpURLConnection) url.openConnection();
146
- connection.setRequestMethod( "POST" );
147
- connection.setRequestProperty("x-access-token", stallionStorage.get(StallionConstants.API_KEY_IDENTIFIER));
148
- connection.setRequestProperty("Content-Type", "application/json");
149
-
150
- String jsonInputString = String.format("{\"bucketId\":\"%s\",\"projectId\":\"%s\",\"version\":%d,\"platform\":\"%s\"}", receivedBucketId, receivedProjectId, receivedVersion, platformValue);
151
-
152
- connection.setDoOutput(true);
153
- connection.setDoInput(true);
154
- OutputStream os = connection.getOutputStream();
155
- os.write(jsonInputString.getBytes("UTF-8"));
156
- os.close();
157
-
158
- connection.connect();
159
- inputStream = new BufferedInputStream(connection.getInputStream());
160
- File downloadFolder = new File(baseDir + StallionConstants.DOWNLOAD_FOLDER_DIR);
161
- downloadFolder.getParentFile().mkdirs();
162
-
163
- downloadedZip = new File(downloadFolder, StallionConstants.ZIP_FILE_NAME);
164
- downloadedZip.getParentFile().mkdirs();
165
-
166
- fout = new FileOutputStream(downloadedZip, false);
167
- bout = new BufferedOutputStream(fout, DOWNLOAD_BUFFER_SIZE);
168
- byte[] data = new byte[DOWNLOAD_BUFFER_SIZE];
169
- byte[] header = new byte[4];
170
-
171
- long totalBytes = connection.getContentLength();
172
- long receivedBytes = 0;
173
- int numBytesRead;
174
- double prevDownloadFraction = 0;
175
- double progressEventThreshold = 0.1;
176
- while ((numBytesRead = inputStream.read(data, 0, DOWNLOAD_BUFFER_SIZE)) >= 0) {
177
- if (receivedBytes < 4) {
178
- for (int i = 0; i < numBytesRead; i++) {
179
- int headerOffset = (int) (receivedBytes) + i;
180
- if (headerOffset >= 4) {
181
- break;
182
- }
183
- header[headerOffset] = data[i];
101
+ executor.execute(() -> {
102
+ DeviceEventManagerModule.RCTDeviceEventEmitter eventEmitter = getEventEmitter();
103
+
104
+ FileOutputStream fout = null;
105
+ BufferedOutputStream bout = null;
106
+ BufferedInputStream inputStream = null;
107
+ HttpURLConnection connection = null;
108
+ File downloadedZip = null;
109
+ boolean isZip = false;
110
+
111
+ try {
112
+ int DOWNLOAD_BUFFER_SIZE = StallionConstants.DOWNLOAD_BUFFER_SIZE;
113
+ URL url = new URL(receivedDownloadUrl);
114
+ connection = (HttpURLConnection) url.openConnection();
115
+ connection.setRequestMethod( "GET" );
116
+ connection.setRequestProperty("x-sdk-access-token", stallionStorage.get(StallionConstants.API_KEY_IDENTIFIER));
117
+ connection.setRequestProperty("Content-Type", "application/json");
118
+
119
+ connection.setDoInput(true);
120
+
121
+ connection.connect();
122
+ inputStream = new BufferedInputStream(connection.getInputStream());
123
+ File downloadFolder = new File(baseDir + StallionConstants.DOWNLOAD_FOLDER_DIR);
124
+ downloadFolder.getParentFile().mkdirs();
125
+
126
+ downloadedZip = new File(downloadFolder, StallionConstants.ZIP_FILE_NAME);
127
+ downloadedZip.getParentFile().mkdirs();
128
+
129
+ fout = new FileOutputStream(downloadedZip, false);
130
+ bout = new BufferedOutputStream(fout, DOWNLOAD_BUFFER_SIZE);
131
+ byte[] data = new byte[DOWNLOAD_BUFFER_SIZE];
132
+ byte[] header = new byte[4];
133
+
134
+ long totalBytes = connection.getContentLength();
135
+ long receivedBytes = 0;
136
+ int numBytesRead;
137
+ double prevDownloadFraction = 0;
138
+ double progressEventThreshold = 0.1;
139
+ while ((numBytesRead = inputStream.read(data, 0, DOWNLOAD_BUFFER_SIZE)) >= 0) {
140
+ if (receivedBytes < 4) {
141
+ for (int i = 0; i < numBytesRead; i++) {
142
+ int headerOffset = (int) (receivedBytes) + i;
143
+ if (headerOffset >= 4) {
144
+ break;
184
145
  }
185
- }
186
-
187
- receivedBytes += numBytesRead;
188
- bout.write(data, 0, numBytesRead);
189
- double currentDownloadFraction = (double) receivedBytes / (double) totalBytes;
190
- if(currentDownloadFraction - prevDownloadFraction > progressEventThreshold) {
191
- prevDownloadFraction = currentDownloadFraction;
192
- getReactApplicationContext().runOnUiQueueThread(() -> getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(StallionConstants.DOWNLOAD_PROGRESS_EVENT, currentDownloadFraction));
146
+ header[headerOffset] = data[i];
193
147
  }
194
148
  }
195
149
 
196
- isZip = ByteBuffer.wrap(header).getInt() == 0x504b0304;
197
- Log.d("", String.valueOf(isZip));
198
-
199
- } catch (Exception e) {
200
- Log.d("DOWNLOAD_ERROR", e.toString());
201
- promise.reject(e.toString());
202
- } finally {
203
- try {
204
- if (bout != null) bout.close();
205
- if (fout != null) fout.close();
206
- if (inputStream != null) inputStream.close();
207
- if (connection != null) connection.disconnect();
208
- } catch (IOException e) {
209
- promise.reject(e.toString());
150
+ receivedBytes += numBytesRead;
151
+ bout.write(data, 0, numBytesRead);
152
+ double currentDownloadFraction = (double) receivedBytes / (double) totalBytes;
153
+ if(currentDownloadFraction - prevDownloadFraction > progressEventThreshold) {
154
+ prevDownloadFraction = currentDownloadFraction;
155
+ getReactApplicationContext().runOnUiQueueThread(() -> getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(StallionConstants.DOWNLOAD_PROGRESS_EVENT, currentDownloadFraction));
210
156
  }
211
157
  }
212
158
 
213
- if (!isZip) {
214
- promise.reject("Not a zip file");
215
- return;
159
+ isZip = ByteBuffer.wrap(header).getInt() == 0x504b0304;
160
+ Log.d("", String.valueOf(isZip));
161
+
162
+ } catch (Exception e) {
163
+ promise.reject(StallionConstants.DOWNLOAD_ERROR_PREFIX, StallionConstants.DOWNLOAD_API_ERROR_MESSAGE);
164
+ } finally {
165
+ try {
166
+ if (bout != null) bout.close();
167
+ if (fout != null) fout.close();
168
+ if (inputStream != null) inputStream.close();
169
+ if (connection != null) connection.disconnect();
170
+ } catch (IOException e) {
171
+ promise.reject(StallionConstants.DOWNLOAD_ERROR_PREFIX, StallionConstants.DOWNLOAD_API_ERROR_MESSAGE);
216
172
  }
173
+ }
217
174
 
175
+ if (!isZip) {
176
+ promise.reject(StallionConstants.DOWNLOAD_ERROR_PREFIX, "Not a zip file");
177
+ return;
178
+ }
179
+
180
+ try {
181
+ int currentActiveSlot = stallionStorage.getInt(StallionConstants.ACTIVE_SLOT_IDENTIFIER);
182
+ int targetSlot;
183
+ if(currentActiveSlot == 1) {
184
+ targetSlot = 2;
185
+ } else if(currentActiveSlot == 2) {
186
+ targetSlot = 1;
187
+ } else {
188
+ targetSlot = 1;
189
+ }
190
+ StallionZip.unzipFile(downloadedZip.getAbsolutePath(), baseDir + StallionConstants.BUNDLE_DEST_FOLDER_DIR + StallionConstants.SLOT_FOLDER_DIR + targetSlot);
191
+ // setting active bucket ID, slot and version after downloading and all other jobs done
192
+ stallionStorage.setInt(StallionConstants.ACTIVE_SLOT_IDENTIFIER, targetSlot);
193
+ stallionStorage.set(StallionConstants.ACTIVE_BUCKET_IDENTIFIER, receivedBucketId);
194
+ if (receivedVersion != null) {
195
+ stallionStorage.setInt(StallionConstants.ACTIVE_VERSION_IDENTIFIER, receivedVersion);
196
+ }
197
+ promise.resolve(StallionConstants.DOWNLOAD_SUCCESS_MESSAGE);
198
+ getReactApplicationContext().runOnUiQueueThread(() -> getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(StallionConstants.DOWNLOAD_PROGRESS_EVENT, 1));
199
+ } catch (Exception e) {
200
+ promise.reject(StallionConstants.DOWNLOAD_ERROR_PREFIX, StallionConstants.DOWNLOAD_FILESYSTEM_ERROR_MESSAGE);
201
+ } finally {
218
202
  try {
219
- int currentActiveSlot = stallionStorage.getInt(StallionConstants.ACTIVE_SLOT_IDENTIFIER);
220
- int targetSlot;
221
- if(currentActiveSlot == 1) {
222
- targetSlot = 2;
223
- } else if(currentActiveSlot == 2) {
224
- targetSlot = 1;
225
- } else {
226
- targetSlot = 1;
227
- }
228
- StallionZip.unzipFile(downloadedZip.getAbsolutePath(), baseDir + StallionConstants.BUNDLE_DEST_FOLDER_DIR + StallionConstants.SLOT_FOLDER_DIR + targetSlot);
229
- // setting active bucket ID, slot and version after downloading and all other jobs done
230
- stallionStorage.setInt(StallionConstants.ACTIVE_SLOT_IDENTIFIER, targetSlot);
231
- stallionStorage.set(StallionConstants.ACTIVE_BUCKET_IDENTIFIER, receivedBucketId);
232
- if (receivedVersion != null) {
233
- stallionStorage.setInt(StallionConstants.ACTIVE_VERSION_IDENTIFIER, receivedVersion);
234
- }
235
- promise.resolve("Success");
236
- getReactApplicationContext().runOnUiQueueThread(() -> getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(StallionConstants.DOWNLOAD_PROGRESS_EVENT, 1));
237
- } catch (Exception e) {
238
- promise.reject(e.toString());
239
- } finally {
240
203
  StallionZip.deleteFileOrFolderSilently(downloadedZip);
241
- return;
204
+ } catch (Exception e) {
205
+ promise.reject(StallionConstants.DOWNLOAD_ERROR_PREFIX, StallionConstants.DOWNLOAD_DELETE_ERROR);
242
206
  }
207
+ return;
243
208
  }
244
209
  });
245
210
  }
@@ -1,3 +1,4 @@
1
1
  #import <React/RCTBridgeModule.h>
2
2
  #import <React/RCTViewManager.h>
3
3
  #import <React/RCTEventEmitter.h>
4
+ #import <StallionErrorBoundary.h>
@@ -16,17 +16,16 @@ class Stallion: RCTEventEmitter {
16
16
  @objc(downloadPackage:withResolver:withRejecter:)
17
17
  func downloadPackage(bundleInfo: NSDictionary, resolve: @escaping RCTPromiseResolveBlock,reject: @escaping RCTPromiseRejectBlock) -> Void {
18
18
  guard let receivedBucketId = bundleInfo.value(forKey: StallionConstants.DownloadReqBodyKeys.BucketId) else {return}
19
- guard let receiveProjectId = bundleInfo.value(forKey: StallionConstants.DownloadReqBodyKeys.ProjectId) else {return}
19
+ guard var receiveDownloadUrl = bundleInfo.value(forKey: StallionConstants.DownloadReqBodyKeys.DownloadUrl) else {return}
20
20
  let receivedVersion = bundleInfo.value(forKey: StallionConstants.DownloadReqBodyKeys.Version) as? Int ?? nil
21
21
  var reqJson: [String: Any] = [
22
22
  StallionConstants.DownloadReqBodyKeys.BucketId: receivedBucketId,
23
- StallionConstants.DownloadReqBodyKeys.ProjectId: receiveProjectId,
24
23
  StallionConstants.DownloadReqBodyKeys.Platform: StallionConstants.PlatformValue,
25
24
  ]
26
25
  if (receivedVersion != nil) {
27
26
  reqJson[StallionConstants.DownloadReqBodyKeys.Version] = receivedVersion
28
27
  }
29
- guard let fromUrl = URL(string: StallionConstants.DownloadApiUrl) else { return }
28
+ guard let fromUrl = URL(string: receiveDownloadUrl as? String ?? "") else { return }
30
29
 
31
30
  do {
32
31
  try StallionDownloader().load(
@@ -15,10 +15,11 @@ class StallionConstants {
15
15
  static let ZipExtension = "zip"
16
16
  }
17
17
  public struct HeaderKeys {
18
- static let AccessKey = "x-access-token"
18
+ static let AccessKey = "x-sdk-access-token"
19
19
  static let ContentType = "Content-Type"
20
20
  }
21
21
  public struct DownloadReqBodyKeys {
22
+ static let DownloadUrl = "url"
22
23
  static let BucketId = "bucketId"
23
24
  static let Version = "version"
24
25
  static let Platform = "platform"
@@ -39,4 +40,5 @@ class StallionConstants {
39
40
  static let SecretKey = "secretKey"
40
41
  }
41
42
  static let DOWNLOAD_PROGRESS_EVENT = "StallionDownloadProgress";
43
+ static let PROGRESS_EVENT_THRESHOLD: Float = 0.05;
42
44
  }
@@ -22,6 +22,7 @@ class StallionDownloader: NSObject {
22
22
 
23
23
  var _resolve: RCTPromiseResolveBlock?;
24
24
  var _reject: RCTPromiseRejectBlock?;
25
+ var lastSentProgress: Float = 0;
25
26
 
26
27
  override init() {
27
28
  super.init()
@@ -29,16 +30,13 @@ class StallionDownloader: NSObject {
29
30
 
30
31
  func load(url: URL, reqBody: [String: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) throws {
31
32
  self.reqJson = reqBody
32
- let reqBody = try? JSONSerialization.data(withJSONObject: reqBody)
33
33
  self._resolve = resolve
34
34
  self._reject = reject
35
35
 
36
36
  var request = URLRequest(url: url)
37
- request.httpMethod = "POST"
38
- request.httpBody = reqBody
39
-
40
- request.setValue("application/json", forHTTPHeaderField: StallionConstants.HeaderKeys.ContentType)
41
- request.setValue(StallionUtil.getLs(key: StallionUtil.LSKeys.apiKey) ?? "", forHTTPHeaderField: StallionConstants.HeaderKeys.AccessKey)
37
+ request.httpMethod = "GET"
38
+
39
+ request.setValue(StallionUtil.getLs(key: StallionUtil.LSKeys.apiKey) ?? "", forHTTPHeaderField: StallionConstants.HeaderKeys.AccessKey)
42
40
  let task = urlSession.downloadTask(with: request)
43
41
  task.resume()
44
42
  self.downloadTask = task
@@ -54,9 +52,11 @@ extension StallionDownloader: URLSessionDownloadDelegate {
54
52
  totalBytesExpectedToWrite: Int64) {
55
53
  if downloadTask == self.downloadTask {
56
54
  let calculatedProgress = Float(totalBytesWritten) / Float(totalBytesExpectedToWrite)
57
- DispatchQueue.main.async {
58
- print("progress: \(calculatedProgress)))")
59
- Stallion.shared?.sendEvent(withName: StallionConstants.DOWNLOAD_PROGRESS_EVENT, body: calculatedProgress)
55
+ if((calculatedProgress - self.lastSentProgress) > StallionConstants.PROGRESS_EVENT_THRESHOLD) {
56
+ self.lastSentProgress = calculatedProgress
57
+ DispatchQueue.main.async {
58
+ Stallion.shared?.sendEvent(withName: StallionConstants.DOWNLOAD_PROGRESS_EVENT, body: calculatedProgress)
59
+ }
60
60
  }
61
61
  }
62
62
  }
@@ -0,0 +1,17 @@
1
+ //
2
+ // StallionErrorBoundary.h
3
+ // react-native-stallion
4
+ //
5
+ // Created by Jasbir Singh on 24/12/23.
6
+ //
7
+
8
+ #import <Foundation/Foundation.h>
9
+
10
+ NS_ASSUME_NONNULL_BEGIN
11
+
12
+ @interface StallionErrorBoundary : NSObject
13
+ + (void)initErrorBoundary;
14
+ + (void)toggleExceptionHandler:(BOOL)shouldEnableErrorHandler;
15
+ @end
16
+
17
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,62 @@
1
+ //
2
+ // StallionErrorBoundary.m
3
+ // react-native-stallion
4
+ //
5
+ // Created by Jasbir Singh on 24/12/23.
6
+ //
7
+
8
+ #import "StallionErrorBoundary.h"
9
+
10
+ @implementation StallionErrorBoundary
11
+
12
+ NSUncaughtExceptionHandler *_defaultExceptionHandler;
13
+
14
+ + (void)initErrorBoundary {
15
+ if(_defaultExceptionHandler == nil) {
16
+ _defaultExceptionHandler = NSGetUncaughtExceptionHandler();
17
+ }
18
+ }
19
+
20
+ + (void)toggleExceptionHandler:(BOOL)shouldEnableErrorHandler {
21
+ if(shouldEnableErrorHandler) {
22
+ NSSetUncaughtExceptionHandler(&handleException);
23
+ } else {
24
+ NSSetUncaughtExceptionHandler(_defaultExceptionHandler);
25
+ }
26
+ }
27
+
28
+ BOOL exceptionAlertDismissed = FALSE;
29
+ void handleException(NSException *exception)
30
+ {
31
+ NSString *switchState = [[NSUserDefaults standardUserDefaults]
32
+ stringForKey:@"switchState"];
33
+ if([switchState isEqual:@"STALLION_ON"]) {
34
+ [[NSUserDefaults standardUserDefaults] setObject:@"DEFAULT" forKey:@"switchState"];
35
+ [[NSUserDefaults standardUserDefaults] synchronize];
36
+ NSString * readeableError = [exception reason];
37
+ UIAlertController* alert = [UIAlertController
38
+ alertControllerWithTitle:@"Stallion Error Boundary"
39
+ message:[NSString stringWithFormat:@"%@\n%@",
40
+ @"A crash occurred in the app. We have switched Stallion off. Check crash report below. Continue crash to invoke other exception handlers. \n \n",
41
+ readeableError]
42
+ preferredStyle:UIAlertControllerStyleAlert];
43
+
44
+ [alert addAction:[UIAlertAction actionWithTitle:@"Continue Crash"
45
+ style:UIAlertActionStyleDefault
46
+ handler:^(UIAlertAction *action) {
47
+ exceptionAlertDismissed = TRUE;
48
+ }]];
49
+
50
+ UIApplication* app = [UIApplication sharedApplication];
51
+ UIViewController * rootViewController = app.delegate.window.rootViewController;
52
+ dispatch_async(dispatch_get_main_queue(), ^{
53
+ [rootViewController presentViewController:alert animated:YES completion:nil];
54
+ });
55
+ while (exceptionAlertDismissed == FALSE)
56
+ {
57
+ [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:0.1]];
58
+ }
59
+ }
60
+ }
61
+
62
+ @end