react-native-stallion 2.0.0-alpha.3 → 2.0.0-alpha.5
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/android/src/main/java/com/stallion/StallionModule.java +12 -9
- package/android/src/main/java/com/stallion/events/StallionEventManager.java +11 -5
- package/android/src/main/java/com/stallion/networkmanager/StallionFileDownloader.java +7 -4
- package/android/src/main/java/com/stallion/networkmanager/StallionSyncHandler.java +44 -32
- package/android/src/main/java/com/stallion/storage/StallionMeta.java +1 -1
- package/ios/main/StallionSyncHandler.swift +16 -3
- package/package.json +1 -1
|
@@ -27,19 +27,27 @@ import java.util.List;
|
|
|
27
27
|
|
|
28
28
|
@ReactModule(name = StallionConfigConstants.MODULE_NAME)
|
|
29
29
|
public class StallionModule extends ReactContextBaseJavaModule implements LifecycleEventListener {
|
|
30
|
-
private final ReactApplicationContext currentReactContext;
|
|
31
30
|
private final StallionStateManager stallionStateManager;
|
|
32
31
|
|
|
33
32
|
public StallionModule(ReactApplicationContext reactContext) {
|
|
34
33
|
super(reactContext);
|
|
35
34
|
StallionStateManager.init(reactContext);
|
|
36
35
|
this.stallionStateManager = StallionStateManager.getInstance();
|
|
37
|
-
this.
|
|
36
|
+
StallionEventManager.init(this.stallionStateManager);
|
|
38
37
|
reactContext.addLifecycleEventListener(this);
|
|
39
38
|
}
|
|
40
39
|
|
|
41
40
|
@Override
|
|
42
41
|
public void onHostResume() {
|
|
42
|
+
if (
|
|
43
|
+
StallionEventManager.getInstance().getEmitter() == null
|
|
44
|
+
&& getReactApplicationContext().getCatalystInstance() != null
|
|
45
|
+
) {
|
|
46
|
+
DeviceEventManagerModule.RCTDeviceEventEmitter eventEmitter = getReactApplicationContext().getJSModule(
|
|
47
|
+
DeviceEventManagerModule.RCTDeviceEventEmitter.class
|
|
48
|
+
);
|
|
49
|
+
StallionEventManager.getInstance().setEmitter(eventEmitter);
|
|
50
|
+
}
|
|
43
51
|
StallionSyncHandler.sync();
|
|
44
52
|
}
|
|
45
53
|
|
|
@@ -52,7 +60,8 @@ public class StallionModule extends ReactContextBaseJavaModule implements Lifecy
|
|
|
52
60
|
@Override
|
|
53
61
|
public void onCatalystInstanceDestroy() {
|
|
54
62
|
super.onCatalystInstanceDestroy();
|
|
55
|
-
|
|
63
|
+
stallionStateManager.setIsMounted(false);
|
|
64
|
+
getReactApplicationContext().removeLifecycleEventListener(this);
|
|
56
65
|
}
|
|
57
66
|
|
|
58
67
|
@Override
|
|
@@ -64,12 +73,6 @@ public class StallionModule extends ReactContextBaseJavaModule implements Lifecy
|
|
|
64
73
|
@ReactMethod
|
|
65
74
|
public void onLaunch(String launchData) {
|
|
66
75
|
stallionStateManager.setIsMounted(true);
|
|
67
|
-
|
|
68
|
-
DeviceEventManagerModule.RCTDeviceEventEmitter eventEmitter = this.currentReactContext.getJSModule(
|
|
69
|
-
DeviceEventManagerModule.RCTDeviceEventEmitter.class
|
|
70
|
-
);
|
|
71
|
-
StallionEventManager.getInstance().setEmitter(eventEmitter);
|
|
72
|
-
|
|
73
76
|
checkPendingDownloads();
|
|
74
77
|
}
|
|
75
78
|
|
|
@@ -12,6 +12,7 @@ import org.json.JSONObject;
|
|
|
12
12
|
import java.util.Iterator;
|
|
13
13
|
import java.util.List;
|
|
14
14
|
import java.util.UUID;
|
|
15
|
+
import java.util.concurrent.atomic.AtomicReference;
|
|
15
16
|
|
|
16
17
|
public class StallionEventManager {
|
|
17
18
|
public static final String STALLION_NATIVE_EVENT_NAME = "STALLION_NATIVE_EVENT";
|
|
@@ -20,7 +21,7 @@ public class StallionEventManager {
|
|
|
20
21
|
|
|
21
22
|
private static StallionEventManager instance;
|
|
22
23
|
private final StallionStateManager stallionStateManager;
|
|
23
|
-
private DeviceEventManagerModule.RCTDeviceEventEmitter
|
|
24
|
+
private final AtomicReference<DeviceEventManagerModule.RCTDeviceEventEmitter> eventEmitterRef = new AtomicReference<>();
|
|
24
25
|
|
|
25
26
|
// Private constructor for Singleton
|
|
26
27
|
private StallionEventManager(StallionStateManager stateManager) {
|
|
@@ -35,7 +36,11 @@ public class StallionEventManager {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
public void setEmitter(DeviceEventManagerModule.RCTDeviceEventEmitter deviceEmitter) {
|
|
38
|
-
|
|
39
|
+
eventEmitterRef.set(deviceEmitter);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
public DeviceEventManagerModule.RCTDeviceEventEmitter getEmitter() {
|
|
43
|
+
return eventEmitterRef.get();
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
// Get instance method
|
|
@@ -49,9 +54,9 @@ public class StallionEventManager {
|
|
|
49
54
|
public void sendEventWithoutCaching(String eventName, JSONObject eventPayload) {
|
|
50
55
|
try {
|
|
51
56
|
eventPayload.put("type", eventName);
|
|
52
|
-
|
|
57
|
+
DeviceEventManagerModule.RCTDeviceEventEmitter eventEmitter = eventEmitterRef.get();
|
|
53
58
|
// Emit the event to React Native
|
|
54
|
-
if (eventEmitter != null) {
|
|
59
|
+
if (eventEmitter != null && stallionStateManager.getIsMounted()) {
|
|
55
60
|
eventEmitter.emit(STALLION_NATIVE_EVENT_NAME, eventPayload.toString());
|
|
56
61
|
}
|
|
57
62
|
|
|
@@ -69,8 +74,9 @@ public class StallionEventManager {
|
|
|
69
74
|
|
|
70
75
|
eventPayload.put("type", eventName);
|
|
71
76
|
|
|
77
|
+
DeviceEventManagerModule.RCTDeviceEventEmitter eventEmitter = eventEmitterRef.get();
|
|
72
78
|
// Emit the event to React Native
|
|
73
|
-
if (eventEmitter != null) {
|
|
79
|
+
if (eventEmitter != null && stallionStateManager.getIsMounted()) {
|
|
74
80
|
eventEmitter.emit(STALLION_NATIVE_EVENT_NAME, eventPayload.toString());
|
|
75
81
|
}
|
|
76
82
|
|
|
@@ -128,7 +128,7 @@ public class StallionFileDownloader {
|
|
|
128
128
|
|
|
129
129
|
// Ensure totalBytes is valid
|
|
130
130
|
if (totalBytes <= 0) {
|
|
131
|
-
callback.onReject(StallionApiConstants.DOWNLOAD_ERROR_PREFIX, "Invalid content length: "
|
|
131
|
+
callback.onReject(StallionApiConstants.DOWNLOAD_ERROR_PREFIX, "Invalid content length: ");
|
|
132
132
|
return;
|
|
133
133
|
}
|
|
134
134
|
|
|
@@ -158,7 +158,7 @@ public class StallionFileDownloader {
|
|
|
158
158
|
return;
|
|
159
159
|
}
|
|
160
160
|
} catch (IOException e) {
|
|
161
|
-
callback.onReject(StallionApiConstants.DOWNLOAD_ERROR_PREFIX, "IOException occurred: "
|
|
161
|
+
callback.onReject(StallionApiConstants.DOWNLOAD_ERROR_PREFIX, "IOException occurred: ");
|
|
162
162
|
throw e;
|
|
163
163
|
} finally {
|
|
164
164
|
if (connection != null) {
|
|
@@ -205,8 +205,11 @@ public class StallionFileDownloader {
|
|
|
205
205
|
callback.onReject(StallionApiConstants.DOWNLOAD_ERROR_PREFIX, StallionApiConstants.CORRUPTED_FILE_ERROR);
|
|
206
206
|
}
|
|
207
207
|
} catch (Exception e) {
|
|
208
|
-
String filesystemError = e.getMessage();
|
|
209
|
-
callback.onReject(
|
|
208
|
+
String filesystemError = e.getMessage() != null ? e.getMessage() : "Unknown filesystem error";
|
|
209
|
+
callback.onReject(
|
|
210
|
+
StallionApiConstants.DOWNLOAD_ERROR_PREFIX,
|
|
211
|
+
StallionApiConstants.DOWNLOAD_FILESYSTEM_ERROR_MESSAGE + filesystemError
|
|
212
|
+
);
|
|
210
213
|
} finally {
|
|
211
214
|
StallionFileManager.deleteFileOrFolderSilently(downloadedZip);
|
|
212
215
|
}
|
|
@@ -14,6 +14,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
14
14
|
public class StallionSyncHandler {
|
|
15
15
|
|
|
16
16
|
private static final AtomicBoolean isSyncInProgress = new AtomicBoolean(false);
|
|
17
|
+
private static final AtomicBoolean isDownloadInProgress = new AtomicBoolean(false);
|
|
17
18
|
|
|
18
19
|
public static void sync() {
|
|
19
20
|
// Ensure only one sync job runs at a time
|
|
@@ -99,47 +100,58 @@ public class StallionSyncHandler {
|
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
public static void downloadNewRelease(String newReleaseHash, String newReleaseUrl) {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
103
|
+
// Ensure only one download job runs at a time
|
|
104
|
+
if (!isDownloadInProgress.compareAndSet(false, true)) {
|
|
105
|
+
return; // Exit if another job is already running
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
StallionStateManager stateManager = StallionStateManager.getInstance();
|
|
109
|
+
String downloadPath = stateManager.getStallionConfig().getFilesDirectory()
|
|
110
|
+
+ StallionConfigConstants.PROD_DIRECTORY
|
|
111
|
+
+ StallionConfigConstants.TEMP_FOLDER_SLOT;
|
|
112
|
+
String projectId = stateManager.getStallionConfig().getProjectId();
|
|
113
|
+
|
|
114
|
+
emitDownloadStarted(newReleaseHash);
|
|
115
|
+
|
|
116
|
+
StallionFileDownloader.downloadBundle(
|
|
117
|
+
newReleaseUrl + "?projectId=" + projectId,
|
|
118
|
+
downloadPath,
|
|
119
|
+
new StallionDownloadCallback() {
|
|
120
|
+
@Override
|
|
121
|
+
public void onReject(String prefix, String error) {
|
|
122
|
+
isDownloadInProgress.set(false);
|
|
123
|
+
emitDownloadError(newReleaseHash, prefix + error);
|
|
124
|
+
}
|
|
118
125
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
+
@Override
|
|
127
|
+
public void onSuccess(String successPayload) {
|
|
128
|
+
isDownloadInProgress.set(false);
|
|
129
|
+
stateManager.stallionMeta.setCurrentProdSlot(StallionMetaConstants.SlotStates.NEW_SLOT);
|
|
130
|
+
stateManager.stallionMeta.setProdTempHash(newReleaseHash);
|
|
131
|
+
String currentProdNewHash = stateManager.stallionMeta.getProdNewHash();
|
|
132
|
+
if(currentProdNewHash != null && !currentProdNewHash.isEmpty()) {
|
|
133
|
+
StallionSlotManager.stabilizeProd();
|
|
134
|
+
}
|
|
135
|
+
stateManager.syncStallionMeta();
|
|
136
|
+
emitDownloadSuccess(newReleaseHash);
|
|
126
137
|
}
|
|
127
|
-
stateManager.syncStallionMeta();
|
|
128
|
-
emitDownloadSuccess(newReleaseHash);
|
|
129
|
-
}
|
|
130
138
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
139
|
+
@Override
|
|
140
|
+
public void onProgress(double downloadFraction) {
|
|
141
|
+
// Optional: Handle progress updates
|
|
142
|
+
}
|
|
134
143
|
}
|
|
135
|
-
|
|
136
|
-
)
|
|
144
|
+
);
|
|
145
|
+
} catch (Exception ignored) {
|
|
146
|
+
isDownloadInProgress.set(false);
|
|
147
|
+
}
|
|
137
148
|
}
|
|
138
149
|
|
|
139
150
|
private static void emitSyncError(Exception e) {
|
|
140
151
|
JSONObject syncErrorPayload = new JSONObject();
|
|
141
152
|
try {
|
|
142
|
-
|
|
153
|
+
String syncErrorString = e.getMessage() != null ? e.getMessage() : "Unknown error";
|
|
154
|
+
syncErrorPayload.put("meta", syncErrorString);
|
|
143
155
|
} catch (Exception ignored) { }
|
|
144
156
|
StallionEventManager.getInstance().sendEvent(
|
|
145
157
|
NativeProdEventTypes.SYNC_ERROR_PROD.toString(),
|
|
@@ -10,17 +10,16 @@ import Foundation
|
|
|
10
10
|
class StallionSyncHandler {
|
|
11
11
|
|
|
12
12
|
private static var isSyncInProgress = false
|
|
13
|
+
private static var isDownloadInProgress = false
|
|
13
14
|
private static let syncQueue = DispatchQueue(label: "com.stallion.syncQueue")
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
static func sync() {
|
|
16
17
|
syncQueue.async {
|
|
17
18
|
guard !isSyncInProgress else { return }
|
|
18
19
|
isSyncInProgress = true
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
DispatchQueue.global().async {
|
|
22
|
-
defer { isSyncInProgress = false }
|
|
23
|
-
|
|
24
23
|
do {
|
|
25
24
|
// Fetch StallionStateManager and StallionConfig
|
|
26
25
|
let stateManager = StallionStateManager.sharedInstance()
|
|
@@ -44,6 +43,7 @@ class StallionSyncHandler {
|
|
|
44
43
|
makeApiCall(payload: requestPayload, appVersion: appVersion)
|
|
45
44
|
|
|
46
45
|
} catch {
|
|
46
|
+
defer { isSyncInProgress = false }
|
|
47
47
|
emitSyncError(error)
|
|
48
48
|
}
|
|
49
49
|
}
|
|
@@ -69,17 +69,20 @@ class StallionSyncHandler {
|
|
|
69
69
|
let jsonData = try JSONSerialization.data(withJSONObject: payload, options: [])
|
|
70
70
|
request.httpBody = jsonData
|
|
71
71
|
} catch {
|
|
72
|
+
defer { isSyncInProgress = false }
|
|
72
73
|
emitSyncError(error)
|
|
73
74
|
return
|
|
74
75
|
}
|
|
75
76
|
|
|
76
77
|
let task = URLSession.shared.dataTask(with: request) { data, response, error in
|
|
77
78
|
if let error = error {
|
|
79
|
+
defer { isSyncInProgress = false }
|
|
78
80
|
emitSyncError(error)
|
|
79
81
|
return
|
|
80
82
|
}
|
|
81
83
|
|
|
82
84
|
guard let data = data, let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
|
|
85
|
+
defer { isSyncInProgress = false }
|
|
83
86
|
let responseError = NSError(domain: "Invalid response from server", code: -2)
|
|
84
87
|
emitSyncError(responseError)
|
|
85
88
|
return
|
|
@@ -88,12 +91,15 @@ class StallionSyncHandler {
|
|
|
88
91
|
// Parse the JSON response
|
|
89
92
|
do {
|
|
90
93
|
if let releaseMeta = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
|
|
94
|
+
defer { isSyncInProgress = false }
|
|
91
95
|
processReleaseMeta(releaseMeta, appVersion: appVersion)
|
|
92
96
|
} else {
|
|
97
|
+
defer { isSyncInProgress = false }
|
|
93
98
|
let parsingError = NSError(domain: "Invalid JSON format", code: -3)
|
|
94
99
|
emitSyncError(parsingError)
|
|
95
100
|
}
|
|
96
101
|
} catch {
|
|
102
|
+
defer { isSyncInProgress = false }
|
|
97
103
|
emitSyncError(error)
|
|
98
104
|
}
|
|
99
105
|
}
|
|
@@ -145,6 +151,11 @@ class StallionSyncHandler {
|
|
|
145
151
|
static func downloadNewRelease(newReleaseHash: String, newReleaseUrl: String) {
|
|
146
152
|
guard let stateManager = StallionStateManager.sharedInstance(),
|
|
147
153
|
let config = stateManager.stallionConfig else { return }
|
|
154
|
+
|
|
155
|
+
syncQueue.async {
|
|
156
|
+
guard !isDownloadInProgress else { return }
|
|
157
|
+
isDownloadInProgress = true
|
|
158
|
+
}
|
|
148
159
|
|
|
149
160
|
let downloadPath = config.filesDirectory + "/" + StallionConstants.PROD_DIRECTORY + "/" + StallionConstants.TEMP_FOLDER_SLOT
|
|
150
161
|
let projectId = config.projectId ?? ""
|
|
@@ -156,6 +167,7 @@ class StallionSyncHandler {
|
|
|
156
167
|
StallionFileDownloader().downloadBundle(url: fromUrl, downloadDirectory: downloadPath, onProgress: { progress in
|
|
157
168
|
// Handle progress updates if necessary
|
|
158
169
|
}, resolve: { _ in
|
|
170
|
+
defer { isDownloadInProgress = false }
|
|
159
171
|
stateManager.stallionMeta?.currentProdSlot = SlotStates.newSlot
|
|
160
172
|
stateManager.stallionMeta?.prodTempHash = newReleaseHash
|
|
161
173
|
if let currentProdNewHash = stateManager.stallionMeta?.prodNewHash,
|
|
@@ -165,6 +177,7 @@ class StallionSyncHandler {
|
|
|
165
177
|
stateManager.syncStallionMeta()
|
|
166
178
|
emitDownloadSuccess(releaseHash: newReleaseHash)
|
|
167
179
|
}, reject: { code, prefix, error in
|
|
180
|
+
defer { isDownloadInProgress = false }
|
|
168
181
|
emitDownloadError(
|
|
169
182
|
releaseHash: newReleaseHash,
|
|
170
183
|
error: "\(String(describing: prefix))\(String(describing: error))"
|