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.
- package/README.md +10 -6
- package/android/build.gradle +4 -1
- package/android/src/main/java/com/stallion/StallionConstants.java +12 -2
- package/android/src/main/java/com/stallion/StallionDefaultErrorActivity.java +1 -1
- package/android/src/main/java/com/stallion/StallionErrorBoundary.java +49 -0
- package/android/src/main/java/com/stallion/StallionModule.java +104 -139
- package/ios/main/Stallion-Bridging-Header.h +1 -0
- package/ios/main/Stallion.swift +2 -3
- package/ios/main/StallionConstants.swift +3 -1
- package/ios/main/StallionDownloader.swift +9 -9
- package/ios/main/StallionErrorBoundary.h +17 -0
- package/ios/main/StallionErrorBoundary.m +62 -0
- package/ios/main/StallionModule.m +3 -35
- package/package.json +1 -1
- package/react-native-stallion.podspec +4 -2
- package/src/index.js +16 -11
- package/src/index.js.map +1 -1
- package/src/main/components/common/ErrorView/index.js +10 -4
- package/src/main/components/common/ErrorView/index.js.map +1 -1
- package/src/main/components/common/ErrorView/styles.js +12 -2
- package/src/main/components/common/ErrorView/styles.js.map +1 -1
- package/src/main/components/common/Footer/index.js +1 -1
- package/src/main/components/common/Footer/index.js.map +1 -1
- package/src/main/components/common/Footer/styles.js +7 -4
- package/src/main/components/common/Footer/styles.js.map +1 -1
- package/src/main/components/common/FooterLoader/index.js +11 -0
- package/src/main/components/common/FooterLoader/index.js.map +1 -0
- package/src/main/components/common/FooterLoader/styles.js +12 -0
- package/src/main/components/common/FooterLoader/styles.js.map +1 -0
- package/src/main/components/common/Header/styles.js +3 -2
- package/src/main/components/common/Header/styles.js.map +1 -1
- package/src/main/components/common/OverlayLoader/styles.js +2 -2
- package/src/main/components/common/OverlayLoader/styles.js.map +1 -1
- package/src/main/components/common/ProfileOverlay/styles.js +4 -2
- package/src/main/components/common/ProfileOverlay/styles.js.map +1 -1
- package/src/main/components/modules/listing/components/BundleCard.js +4 -2
- package/src/main/components/modules/listing/components/BundleCard.js.map +1 -1
- package/src/main/components/modules/listing/components/BundleCardInfoSection.js +6 -5
- package/src/main/components/modules/listing/components/BundleCardInfoSection.js.map +1 -1
- package/src/main/components/modules/listing/components/CardDescriptionContent.js +2 -1
- package/src/main/components/modules/listing/components/CardDescriptionContent.js.map +1 -1
- package/src/main/components/modules/listing/components/styles/index.js +6 -3
- package/src/main/components/modules/listing/components/styles/index.js.map +1 -1
- package/src/main/components/modules/listing/hooks/useListing.js +14 -2
- package/src/main/components/modules/listing/hooks/useListing.js.map +1 -1
- package/src/main/components/modules/listing/index.js +45 -16
- package/src/main/components/modules/listing/index.js.map +1 -1
- package/src/main/components/modules/listing/styles.js +7 -0
- package/src/main/components/modules/listing/styles.js.map +1 -1
- package/src/main/components/modules/login/components/Email.js +5 -1
- package/src/main/components/modules/login/components/Email.js.map +1 -1
- package/src/main/components/modules/login/components/Otp.js +5 -1
- package/src/main/components/modules/login/components/Otp.js.map +1 -1
- package/src/main/components/modules/login/components/styles/index.js +5 -1
- package/src/main/components/modules/login/components/styles/index.js.map +1 -1
- package/src/main/components/modules/login/styles/index.js +2 -1
- package/src/main/components/modules/login/styles/index.js.map +1 -1
- package/src/main/components/modules/modal/hooks/useStallionModal.js +3 -1
- package/src/main/components/modules/modal/hooks/useStallionModal.js.map +1 -1
- package/src/main/constants/apiConstants.js +5 -3
- package/src/main/constants/apiConstants.js.map +1 -1
- package/src/main/constants/appConstants.js +9 -2
- package/src/main/constants/appConstants.js.map +1 -1
- package/src/main/constants/colors.js +3 -1
- package/src/main/constants/colors.js.map +1 -1
- package/src/main/state/actionCreators/useBundleActions.js +24 -9
- package/src/main/state/actionCreators/useBundleActions.js.map +1 -1
- package/src/main/state/actionCreators/useDownloadActions.js +20 -7
- package/src/main/state/actionCreators/useDownloadActions.js.map +1 -1
- package/src/main/state/actionCreators/useUserActions.js +11 -4
- package/src/main/state/actionCreators/useUserActions.js.map +1 -1
- package/src/main/state/actions/bundleActions.js +18 -0
- package/src/main/state/actions/bundleActions.js.map +1 -1
- package/src/main/state/reducers/bundleReducer.js +34 -3
- package/src/main/state/reducers/bundleReducer.js.map +1 -1
- package/src/main/utils/ErrorBoundary.js +3 -3
- package/src/main/utils/ErrorBoundary.js.map +1 -1
- package/src/main/utils/SharedDataManager.js +12 -3
- package/src/main/utils/SharedDataManager.js.map +1 -1
- package/src/main/utils/apiUtils.js +1 -1
- package/src/main/utils/apiUtils.js.map +1 -1
- package/src/main/utils/withStallion.js +5 -2
- package/src/main/utils/withStallion.js.map +1 -1
- package/src/nativeScripts/getStallionEnabled.js +1 -1
- package/src/nativeScripts/getStallionEnabled.js.map +1 -1
- package/src/types/bundle.types.js +3 -0
- package/src/types/bundle.types.js.map +1 -1
- package/types/index.d.ts.map +1 -1
- package/types/main/components/common/ErrorView/index.d.ts +1 -0
- package/types/main/components/common/ErrorView/index.d.ts.map +1 -1
- package/types/main/components/common/ErrorView/styles.d.ts +10 -0
- package/types/main/components/common/ErrorView/styles.d.ts.map +1 -1
- package/types/main/components/common/Footer/styles.d.ts +4 -1
- package/types/main/components/common/Footer/styles.d.ts.map +1 -1
- package/types/main/components/common/FooterLoader/index.d.ts +4 -0
- package/types/main/components/common/FooterLoader/index.d.ts.map +1 -0
- package/types/main/components/common/FooterLoader/styles.d.ts +10 -0
- package/types/main/components/common/FooterLoader/styles.d.ts.map +1 -0
- package/types/main/components/common/Header/styles.d.ts +1 -0
- package/types/main/components/common/Header/styles.d.ts.map +1 -1
- package/types/main/components/common/ProfileOverlay/styles.d.ts +2 -0
- package/types/main/components/common/ProfileOverlay/styles.d.ts.map +1 -1
- package/types/main/components/modules/listing/components/BundleCard.d.ts +1 -0
- package/types/main/components/modules/listing/components/BundleCard.d.ts.map +1 -1
- package/types/main/components/modules/listing/components/BundleCardInfoSection.d.ts +3 -2
- package/types/main/components/modules/listing/components/BundleCardInfoSection.d.ts.map +1 -1
- package/types/main/components/modules/listing/components/styles/index.d.ts +3 -0
- package/types/main/components/modules/listing/components/styles/index.d.ts.map +1 -1
- package/types/main/components/modules/listing/hooks/useListing.d.ts +2 -0
- package/types/main/components/modules/listing/hooks/useListing.d.ts.map +1 -1
- package/types/main/components/modules/listing/index.d.ts +2 -2
- package/types/main/components/modules/listing/index.d.ts.map +1 -1
- package/types/main/components/modules/listing/styles.d.ts +6 -0
- package/types/main/components/modules/listing/styles.d.ts.map +1 -1
- package/types/main/components/modules/login/components/Email.d.ts.map +1 -1
- package/types/main/components/modules/login/components/Otp.d.ts.map +1 -1
- package/types/main/components/modules/login/components/styles/index.d.ts +4 -0
- package/types/main/components/modules/login/components/styles/index.d.ts.map +1 -1
- package/types/main/components/modules/login/styles/index.d.ts +1 -0
- package/types/main/components/modules/login/styles/index.d.ts.map +1 -1
- package/types/main/components/modules/modal/hooks/useStallionModal.d.ts.map +1 -1
- package/types/main/constants/apiConstants.d.ts +5 -3
- package/types/main/constants/apiConstants.d.ts.map +1 -1
- package/types/main/constants/appConstants.d.ts +8 -1
- package/types/main/constants/appConstants.d.ts.map +1 -1
- package/types/main/constants/colors.d.ts +2 -0
- package/types/main/constants/colors.d.ts.map +1 -1
- package/types/main/index.d.ts +1 -1
- package/types/main/state/actionCreators/useBundleActions.d.ts +1 -1
- package/types/main/state/actionCreators/useBundleActions.d.ts.map +1 -1
- package/types/main/state/actionCreators/useDownloadActions.d.ts +1 -1
- package/types/main/state/actionCreators/useDownloadActions.d.ts.map +1 -1
- package/types/main/state/actionCreators/useUserActions.d.ts.map +1 -1
- package/types/main/state/actions/bundleActions.d.ts +3 -0
- package/types/main/state/actions/bundleActions.d.ts.map +1 -1
- package/types/main/state/reducers/bundleReducer.d.ts.map +1 -1
- package/types/main/utils/SharedDataManager.d.ts +6 -2
- package/types/main/utils/SharedDataManager.d.ts.map +1 -1
- package/types/main/utils/apiUtils.d.ts +1 -1
- package/types/main/utils/apiUtils.d.ts.map +1 -1
- package/types/main/utils/withStallion.d.ts +2 -1
- package/types/main/utils/withStallion.d.ts.map +1 -1
- package/types/types/bundle.types.d.ts +22 -4
- package/types/types/bundle.types.d.ts.map +1 -1
- package/types/types/globalProvider.types.d.ts +2 -2
- package/types/types/globalProvider.types.d.ts.map +1 -1
- package/types/types/utils.types.d.ts +5 -2
- package/types/types/utils.types.d.ts.map +1 -1
package/README.md
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
+
[](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
|
+
[](https://www.npmjs.com/package/react-native-stallion) [](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
|
|
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
|
-
|
|
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
|
|
package/android/build.gradle
CHANGED
|
@@ -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
|
-
|
|
8
|
-
public static final String
|
|
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
|
-
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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 :
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
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
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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
|
-
|
|
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
|
}
|
package/ios/main/Stallion.swift
CHANGED
|
@@ -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
|
|
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:
|
|
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 = "
|
|
38
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|