react-native-mytatva-rn-sdk 1.2.45 → 1.2.48

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.
@@ -2,6 +2,8 @@ package com.mytatvarnsdk
2
2
 
3
3
  import android.app.Application
4
4
  import android.bluetooth.BluetoothAdapter
5
+ import android.bluetooth.BluetoothManager
6
+ import android.content.Context
5
7
  import android.content.Intent
6
8
  import android.content.pm.PackageManager
7
9
  import android.os.Build
@@ -52,10 +54,13 @@ import kotlinx.coroutines.Job
52
54
  import kotlinx.coroutines.SupervisorJob
53
55
  import kotlinx.coroutines.delay
54
56
  import kotlinx.coroutines.launch
57
+ import kotlinx.coroutines.withContext
55
58
  import org.json.JSONObject
56
59
  import java.text.SimpleDateFormat
57
60
  import java.util.Date
58
61
  import java.util.Locale
62
+ import java.util.Timer
63
+ import java.util.TimerTask
59
64
  import kotlin.coroutines.resume
60
65
  import kotlin.coroutines.suspendCoroutine
61
66
 
@@ -69,12 +74,15 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
69
74
  var prefsHelper: SharedPreferencesLibraryUtil
70
75
  private val apiScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
71
76
  private var debounceJob: Job? = null
77
+ private var isBatchProcessing = false
72
78
 
73
79
  private var lastDeviceStatus: String? = null
74
80
 
75
81
  private var glucoseObserver: Observer<PocGlucose?>? = null
76
82
  private var isObserving = false
77
83
 
84
+ private var lastProcessedSyncTimeInMillis: Long? = null
85
+
78
86
  init {
79
87
  mReactContext = reactContext
80
88
  prefsHelper = SharedPreferencesLibraryUtil(mReactContext)
@@ -107,8 +115,7 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
107
115
  Handler(Looper.getMainLooper()).post {
108
116
  mModel.device.observeForever { device ->
109
117
  if (device != null) {
110
- postEventDataToAPI(device, "", device?.qrMessage ?: "")
111
-
118
+ postEventDataToAPI(device, "", device.qrMessage ?: "")
112
119
  Log.d("observeDeviceStatus: ", device.toString())
113
120
  }
114
121
  }
@@ -119,7 +126,7 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
119
126
  }
120
127
 
121
128
  @ReactMethod
122
- fun observeTransmitterUnbindStatus(token: String, apiResponse: String) {
129
+ fun observeTransmitterUnbindStatus(token: String, apiResponse: String?) {
123
130
  try {
124
131
  if (apiResponse != null && apiResponse.isNotEmpty()) {
125
132
  userToken = token
@@ -189,19 +196,13 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
189
196
  val endDate = sensor.endDate
190
197
  val sensorId = sensor.sensorId
191
198
 
192
- println("Start Date: $startDate")
193
- println("End Date: $endDate")
194
-
195
199
  if (isCurrentDateInRange(startDate, endDate)) {
196
-
197
200
  println("Current date is in range")
198
201
 
199
202
  val pocDevice =
200
203
  RepositoryDevice.getInstance(BApplication.getContext()).latestDeviceIoThread
201
204
 
202
205
  if (pocDevice != null) {
203
- Log.d("pocDevice logsss", pocDevice.toString())
204
-
205
206
  if (pocDevice.isUnBind) {
206
207
  postEventDataToAPI(
207
208
  pocDevice,
@@ -210,8 +211,6 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
210
211
  )
211
212
  }
212
213
  } else {
213
- Log.d("pocDevice logsss", "Data null")
214
-
215
214
  postEventDataToAPI(
216
215
  pocDevice,
217
216
  DeviceStatus.TRANSMITTER_DISCONNECT.id,
@@ -231,65 +230,100 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
231
230
  }
232
231
  }
233
232
 
233
+ private var debounceDeviceTimer: Timer? = null
234
+ private var isDebounceTimerActive: Boolean = false
235
+
236
+
234
237
  private fun postEventDataToAPI(device: PocDevice?, mStatus: String, sensorId: String?) {
235
- apiScope.launch {
238
+ debounceJob?.cancel()
239
+
240
+
241
+ debounceJob = apiScope.launch {
242
+ delay(500)
243
+
236
244
  try {
237
245
  val bluetoothAdapter: BluetoothAdapter? = BluetoothAdapter.getDefaultAdapter()
238
246
  val bleStatus = bluetoothAdapter?.isEnabled == true
239
- var status = ""
240
247
 
241
- status = if (mStatus.isEmpty()) {
248
+ val status: String = mStatus.ifEmpty {
242
249
  when {
243
250
  device?.isBoundAndConnect == true -> DeviceStatus.CONNECTED.id
244
251
  !bleStatus -> DeviceStatus.BLUETOOTH_OFF.id
245
252
  device?.isUnBind == true -> DeviceStatus.TRANSMITTER_DISCONNECT.id
246
253
  device?.isBoundButDisConnect == true -> DeviceStatus.DISCONNECTED.id
247
- else -> "" // fallback if no status matches
254
+ else -> ""
248
255
  }
249
- } else {
250
- mStatus
251
256
  }
252
257
 
253
- Log.d("postEventDataToAPI: ", "Device Event Status==> $status")
258
+ Log.d("Device event Status", "Device event Status: $status")
254
259
 
255
- if (status.isNotEmpty()) {
256
- if (status != lastDeviceStatus) {
260
+ if (status == DeviceStatus.DISCONNECTED.id && !isDebounceTimerActive) {
261
+ // Start debounce timer for 1 minutes
262
+ debounceDeviceTimer = Timer()
263
+ isDebounceTimerActive = true
257
264
 
258
- lastDeviceStatus = status
259
- Log.d("postEventDataToAPI: ", "Device Event Status API==> $status")
265
+ debounceDeviceTimer?.schedule(object : TimerTask() {
266
+ override fun run() {
267
+ isDebounceTimerActive = false
260
268
 
261
- val rawData = JSONObject().apply {
262
- put("transmitterName", device?.name ?: "")
263
- put("SensorId", sensorId ?: "")
264
- put("Sensor", sensorId ?: "")
265
- put("timeInMillis", Date().time)
269
+ callEventAPI(device, status, sensorId)
266
270
  }
267
271
 
268
- val obj = JSONObject().apply {
269
- put("sensorId", sensorId ?: "")
270
- put("status", status)
271
- put("rawData", rawData)
272
- }
272
+ }, 1 * 60 * 1000) // 1 minutes in milliseconds
273
273
 
274
- authenticateSDKService.postDeviceData(
275
- environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
276
- data = obj.toString(),
277
- token = userToken,
278
- loaderListener = object : LoaderListener {
279
- override fun onShowLoader() {}
280
- override fun onHideLoader() {}
281
- }
282
- )
274
+ Log.d("Debounce", "Timer started for disconnection reminder")
275
+ } else {
276
+ // Either device is not disconnected or timer already running
277
+ debounceDeviceTimer?.cancel()
278
+ debounceDeviceTimer = null
279
+ isDebounceTimerActive = false
283
280
 
284
- resetDebounceTimer()
285
- }
281
+ Log.d("Debounce", "Timer cancelled, event triggered immediately")
282
+
283
+ callEventAPI(device, status, sensorId)
286
284
  }
285
+
287
286
  } catch (e: Exception) {
288
287
  e.printStackTrace()
289
288
  }
290
289
  }
291
290
  }
292
291
 
292
+ private fun callEventAPI(device: PocDevice?, status: String, sensorId: String?) {
293
+ Log.d("Device event Status", "Last Device event Status API: $lastDeviceStatus")
294
+
295
+ if (status.isNotEmpty() && status != lastDeviceStatus) {
296
+ lastDeviceStatus = status
297
+
298
+ Log.d("Device event Status", "Device event Status API: $status")
299
+
300
+ val rawData = JSONObject().apply {
301
+ put("transmitterName", device?.name ?: "")
302
+ put("SensorId", sensorId ?: "")
303
+ put("Sensor", sensorId ?: "")
304
+ put("timeInMillis", Date().time)
305
+ }
306
+
307
+ val obj = JSONObject().apply {
308
+ put("sensorId", sensorId ?: "")
309
+ put("status", status)
310
+ put("rawData", rawData)
311
+ }
312
+
313
+ authenticateSDKService.postDeviceData(
314
+ environment = if ("uat".uppercase() == "PROD") TATVA_ENVIRONMENT.PROD else TATVA_ENVIRONMENT.STAGE,
315
+ data = obj.toString(),
316
+ token = userToken,
317
+ loaderListener = object : LoaderListener {
318
+ override fun onShowLoader() {}
319
+ override fun onHideLoader() {}
320
+ }
321
+ )
322
+
323
+ resetDebounceTimer()
324
+ }
325
+ }
326
+
293
327
  private fun resetDebounceTimer() {
294
328
  debounceJob?.cancel() // Cancel any existing timer
295
329
  debounceJob = apiScope.launch {
@@ -360,6 +394,10 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
360
394
  )
361
395
  return
362
396
  }
397
+ if (isBatchProcessing) {
398
+ Log.d("observeAllGlucoseData", "Batch processing in progress, skipping duplicate call")
399
+ return
400
+ }
363
401
 
364
402
  // Remove existing observer if any to prevent memory leaks
365
403
  stopObservingGlucoseData()
@@ -368,13 +406,42 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
368
406
  Handler(Looper.getMainLooper()).postDelayed({
369
407
  // Create new observer with explicit nullable parameter type
370
408
  glucoseObserver = Observer<PocGlucose?> { pocGlucose ->
371
- if (pocGlucose != null) {
409
+ val currentTime = System.currentTimeMillis()
410
+ var dataAge = System.currentTimeMillis()
411
+
412
+ val lastSyncData = prefsHelper.lastSyncData
413
+ val currentSyncTimeInMillis = lastSyncData?.timeInMillis
414
+
415
+ if (lastSyncData != null) {
416
+ dataAge = currentTime - lastSyncData.timeInMillis
417
+ }
418
+
419
+ // Only proceed if timeInMillis is different from last processed
420
+ if (currentSyncTimeInMillis != null && lastProcessedSyncTimeInMillis != null && currentSyncTimeInMillis == lastProcessedSyncTimeInMillis) {
421
+ Log.d("SyncTimeCheck", "Duplicate sync time, skipping function calls")
422
+ return@Observer
423
+ }
424
+
425
+
426
+ if ((pocGlucose != null && lastSyncData == null) || (pocGlucose != null && lastSyncData != null && dataAge <= 4 * 60 * 1000L)) {
372
427
  handleGlucoseData(pocGlucose)
428
+ Log.d(
429
+ "observeGlucoseData",
430
+ "Received glucose data - processing"
431
+ )
432
+ // Update last processed sync time
433
+ lastProcessedSyncTimeInMillis = currentSyncTimeInMillis
373
434
  } else {
374
- Log.w(
435
+
436
+ Handler(Looper.getMainLooper()).postDelayed({
437
+ observeAllGlucoseData(userToken)
438
+ }, 100)
439
+ Log.d(
375
440
  "observeGlucoseData",
376
441
  "Received null glucose data - skipping processing"
377
442
  )
443
+ // Update last processed sync time
444
+ lastProcessedSyncTimeInMillis = currentSyncTimeInMillis
378
445
  }
379
446
  }
380
447
 
@@ -503,6 +570,11 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
503
570
 
504
571
  @ReactMethod
505
572
  fun observeAllGlucoseData(token: String) {
573
+ Log.d("function call", "observeAllGlucoseData")
574
+ if (isBatchProcessing) {
575
+ Log.d("observeAllGlucoseData", "Batch processing in progress, skipping duplicate call")
576
+ return
577
+ }
506
578
  userToken = token
507
579
 
508
580
  // FIX: Ensure proper cleanup and reset state
@@ -513,10 +585,24 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
513
585
  try {
514
586
  val lastSyncData = prefsHelper.lastSyncData
515
587
  Log.d("lastSyncData: ", Gson().toJson(lastSyncData).toString())
588
+ val currentTime = System.currentTimeMillis()
589
+ var dataAge = System.currentTimeMillis()
516
590
 
517
- if (lastSyncData != null) {
591
+ if (prefsHelper.lastSyncData != null) {
592
+ dataAge = currentTime - prefsHelper.lastSyncData.timeInMillis
593
+ }
594
+
595
+ if (lastSyncData != null && dataAge > 3 * 60 * 1000L) {
596
+ isBatchProcessing = true
518
597
  CoroutineScope(Dispatchers.IO).launch {
519
- val glucoseData = mModel.getGlucoseBetweenTime(lastSyncData.lastSyncTime)
598
+
599
+ val glucoseData = mModel.getGlucoseBetweenTime(lastSyncData.timeInMillis)
600
+
601
+
602
+ Log.d("observeAllGlucoseData: ", glucoseData.toString())
603
+ Log.d("Last sync time: ", lastSyncData.lastSyncTime.toString())
604
+ Log.d("current time: ", System.currentTimeMillis().toString())
605
+
520
606
  if (glucoseData != null && glucoseData.isNotEmpty()) {
521
607
  processBatchDataAndStartObserver(glucoseData)
522
608
  } else {
@@ -524,16 +610,18 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
524
610
  "observeAllGlucoseData",
525
611
  "No historical data found, starting live observation"
526
612
  )
527
- Handler(Looper.getMainLooper()).post {
613
+
614
+ withContext(Dispatchers.Main) {
528
615
  observeGlucoseData(userToken)
529
616
  }
530
617
  }
531
618
  }
532
619
  } else {
620
+ isBatchProcessing = false
533
621
  observeGlucoseData(userToken)
534
622
  }
535
623
  } catch (e: Exception) {
536
- Log.e("observeAllGlucoseData", "observeAllGlucoseData Error: ${e.message}")
624
+ isBatchProcessing = false
537
625
  observeGlucoseData(userToken)
538
626
  }
539
627
  }, 100) // Small delay to ensure cleanup
@@ -556,7 +644,8 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
556
644
  }
557
645
 
558
646
  // Ensure we're on main thread and not already observing
559
- Handler(Looper.getMainLooper()).post {
647
+ isBatchProcessing = false
648
+ withContext(Dispatchers.Main) {
560
649
  if (!isObserving) {
561
650
  Log.d(
562
651
  "processBatchDataAndStartObserver",
@@ -574,7 +663,8 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
574
663
  } catch (e: Exception) {
575
664
  Log.e("processBatchDataAndStartObserver", "Error in batch processing: ${e.message}")
576
665
  // Start live observation even on error
577
- Handler(Looper.getMainLooper()).post {
666
+ isBatchProcessing = false
667
+ withContext(Dispatchers.Main) {
578
668
  if (!isObserving) {
579
669
  observeGlucoseData(userToken)
580
670
  }
@@ -603,6 +693,9 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
603
693
  // Clear any cached data if needed
604
694
  prefsHelper.clearQRInformation() // if you have such method
605
695
 
696
+ // Reset last processed sync time
697
+ lastProcessedSyncTimeInMillis = null
698
+
606
699
  } catch (e: Exception) {
607
700
  Log.e("resetCgmState", "Error resetting CGM state: ${e.message}")
608
701
  }
@@ -744,6 +837,9 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
744
837
  // Reset last device status
745
838
  lastDeviceStatus = null
746
839
 
840
+ // Reset last processed sync time
841
+ lastProcessedSyncTimeInMillis = null
842
+
747
843
  } catch (e: Exception) {
748
844
  Log.e("stopObservingGlucoseData", "Error stopping observer: ${e.message}")
749
845
  }
@@ -778,12 +874,13 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
778
874
  lastRecord?.let {
779
875
  try {
780
876
  val syncData = SyncMeta(
781
- Date().time,
877
+ System.currentTimeMillis(),
782
878
  it.timeInMillis,
783
879
  it.deviceId,
784
880
  it.glucoseId
785
881
  )
786
882
  prefsHelper.lastSyncData = syncData
883
+ lastProcessedSyncTimeInMillis = null
787
884
  Log.d(
788
885
  "Sync Metadata",
789
886
  "Sync metadata updated: glucoseId=${it.glucoseId}, time=${it.timeInMillis}"
@@ -875,6 +972,4 @@ class CgmTrackyLibModule(reactContext: ReactApplicationContext) :
875
972
  }
876
973
  }
877
974
 
878
-
879
- }
880
-
975
+ }
@@ -272,8 +272,8 @@ class SearchTransmitterActivity : BaseBleActivity() {
272
272
  binding.tvTransName.text = "Sno: ${transmitterDeviceInfo!!.pocDevice.name}"
273
273
  val formatter = SimpleDateFormat("hh:mm a", Locale.getDefault())
274
274
  binding.tvDateTime.text = "Connected on ${formatter.format(Date()).uppercase()}"
275
- binding.clSuccess.visibility = View.VISIBLE
276
275
  binding.clMain.visibility = View.GONE
276
+ binding.clSuccess.visibility = View.VISIBLE
277
277
  binding.commonButton.btnProceed.isEnabled = true
278
278
  binding.commonButton.btnProceed.alpha = 1.0f
279
279
  binding.view2.background = ContextCompat.getDrawable(this, R.drawable.bg_green_progress)
@@ -46,6 +46,9 @@ class VideoActivity : AppCompatActivity() {
46
46
  .build()
47
47
  binding.youtubePlayerView.enableAutomaticInitialization = false
48
48
 
49
+ binding.ivClose.setOnClickListener {
50
+ handleBackPressedManually()
51
+ }
49
52
 
50
53
  binding.youtubePlayerView.addFullscreenListener(object : FullscreenListener {
51
54
  override fun onEnterFullscreen(fullscreenView: View, exitFullscreen: () -> Unit) {
@@ -83,12 +86,15 @@ class VideoActivity : AppCompatActivity() {
83
86
 
84
87
  private val onBackPressedCallback = object : OnBackPressedCallback(true) {
85
88
  override fun handleOnBackPressed() {
86
- if (isFullscreen) {
87
- // if the player is in fullscreen, exit fullscreen
88
- youTubePlayer.toggleFullscreen()
89
- } else {
90
- finish()
91
- }
89
+ handleBackPressedManually()
90
+ }
91
+ }
92
+
93
+ private fun handleBackPressedManually() {
94
+ if (isFullscreen) {
95
+ youTubePlayer.toggleFullscreen()
96
+ } else {
97
+ finish()
92
98
  }
93
99
  }
94
100
  }
@@ -99,7 +99,7 @@ public class BaseViewModel extends AndroidViewModel {
99
99
  }
100
100
 
101
101
  public List<PocGlucose> getGlucoseBetweenTime(long startTime) {
102
- return mRepositoryGlucose.getGlucoseByTimeMillis(startTime, new Date().getTime());
102
+ return mRepositoryGlucose.getGlucoseByTimeMillis(startTime, System.currentTimeMillis());
103
103
  }
104
104
 
105
105
  public PocDevice getDeviceInfo(int deviceId) {
@@ -8,6 +8,15 @@
8
8
  android:background="#E6000000"
9
9
  tools:context=".activity.VideoActivity">
10
10
 
11
+ <androidx.appcompat.widget.AppCompatImageView
12
+ android:id="@+id/ivClose"
13
+ android:layout_width="40dp"
14
+ android:layout_height="40dp"
15
+ android:padding="7dp"
16
+ app:layout_constraintStart_toStartOf="parent"
17
+ app:layout_constraintTop_toTopOf="parent"
18
+ app:srcCompat="@drawable/ic_close_gray" />
19
+
11
20
  <com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView
12
21
  android:id="@+id/youtube_player_view"
13
22
  android:layout_width="match_parent"
@@ -23,10 +32,12 @@
23
32
  android:id="@+id/full_screen_view_container"
24
33
  android:layout_width="match_parent"
25
34
  android:layout_height="match_parent"
26
- android:visibility="gone"
35
+ android:visibility="visible"
27
36
  app:layout_constraintBottom_toBottomOf="parent"
28
37
  app:layout_constraintEnd_toEndOf="parent"
29
38
  app:layout_constraintStart_toStartOf="parent"
30
- app:layout_constraintTop_toTopOf="parent" />
39
+ app:layout_constraintTop_toTopOf="parent">
40
+
41
+ </FrameLayout>
31
42
 
32
43
  </androidx.constraintlayout.widget.ConstraintLayout>
@@ -20,7 +20,7 @@ RCT_EXPORT_MODULE();
20
20
 
21
21
  // Define the supported events
22
22
  - (NSArray<NSString *> *)supportedEvents {
23
- return @[@"cgmDeviceEvent"]; // Event names to listen to in JS
23
+ return @[@"cgmDeviceEvent", @"cgmwatchdemoclicked", @"cgmWAsupportclicked"]; // Event names to listen to in JS
24
24
  }
25
25
 
26
26
  // Example method to emit an event from native to JS
@@ -39,7 +39,7 @@ RCT_EXPORT_MODULE();
39
39
  // This method returns the list of events the module supports
40
40
  - (NSArray<NSString *> *)supportedEvents
41
41
  {
42
- return @[@"cgmDeviceEvent"];
42
+ return @[@"cgmDeviceEvent", @"cgmwatchdemoclicked", @"cgmWAsupportclicked"];
43
43
  }
44
44
 
45
45
  // Start listening for notifications
@@ -51,6 +51,17 @@ RCT_EXPORT_MODULE();
51
51
  selector:@selector(handleNotification:)
52
52
  name:@"cgmDeviceEvent"
53
53
  object:nil];
54
+
55
+ [[NSNotificationCenter defaultCenter] addObserver:self
56
+ selector:@selector(handleNotification:)
57
+ name:@"cgmwatchdemoclicked"
58
+ object:nil];
59
+
60
+
61
+ [[NSNotificationCenter defaultCenter] addObserver:self
62
+ selector:@selector(handleNotification:)
63
+ name:@"cgmWAsupportclicked"
64
+ object:nil];
54
65
  }
55
66
  return self;
56
67
  }
@@ -64,6 +75,9 @@ RCT_EXPORT_MODULE();
64
75
 
65
76
  // Send the data to React Native via event emitter
66
77
  [self sendEventWithName:@"cgmDeviceEvent" body:@{@"status": @"WARM_PERIOD_STARTED"}];
78
+ [self sendEventWithName:@"cgmwatchdemoclicked" body:@{@"status": @"cgm_watch_demo_clicked"}];
79
+ [self sendEventWithName:@"cgmWAsupportclicked" body:@{@"status": @"cgm_WA_support_clicked"}];
80
+
67
81
  }
68
82
 
69
83
  // Private helper
@@ -259,68 +273,59 @@ RCT_EXPORT_METHOD(observeAllGlucoseData:(NSString *)token)
259
273
  });
260
274
  }
261
275
 
262
- // Internal method — can be kept private using a class extension
263
- - (void)observeTransmitterUnbindStatus:(NSString *)token
264
- response:(NSDictionary *)responseDict
265
- completion:(void (^)(NSDictionary *response, NSError *error))completion
276
+ //--------------OLD FUNCTION-----------------
277
+ //RCT_EXPORT_METHOD(observeTransmitterUnbindStatus:(NSString *)token)
278
+ //{
279
+ // NSLog(@"Received token: %@", token);
280
+ // [[NSUserDefaults standardUserDefaults] setObject:token forKey:@"authToken"];
281
+ // [[NSUserDefaults standardUserDefaults] synchronize];
282
+ // FinalViewModelManager *manager = [FinalViewModelManager shared];
283
+ // [manager callForObserveTransmitterUnbindStatus];
284
+ //}
285
+
286
+
287
+
288
+ RCT_EXPORT_METHOD(observeTransmitterUnbindStatus:(NSString *)token
289
+ response:(NSString *)responseJsonString
290
+ resolver:(RCTPromiseResolveBlock)resolve
291
+ rejecter:(RCTPromiseRejectBlock)reject)
266
292
  {
293
+ if (responseJsonString == nil || ![responseJsonString isKindOfClass:[NSString class]]) {
294
+ NSError *error = [NSError errorWithDomain:@"CgmTrackyLib"
295
+ code:1001
296
+ userInfo:@{NSLocalizedDescriptionKey: @"Invalid or nil JSON string passed from JS"}];
297
+ reject(@"invalid_params", @"Response JSON string is nil or invalid", error);
298
+ return;
299
+ }
300
+
301
+ // Parse JSON string to NSDictionary
302
+ NSData *data = [responseJsonString dataUsingEncoding:NSUTF8StringEncoding];
303
+ NSError *parseError = nil;
304
+ NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&parseError];
305
+
306
+ if (parseError || ![responseDict isKindOfClass:[NSDictionary class]]) {
307
+ reject(@"json_parse_error", @"Failed to parse JSON string to dictionary", parseError);
308
+ return;
309
+ }
310
+
267
311
  // Save token
268
312
  [[NSUserDefaults standardUserDefaults] setObject:token forKey:@"authToken"];
269
- [[NSUserDefaults standardUserDefaults] synchronize];
270
313
 
271
- // Save CGM status item to UserDefaults (if needed by Swift)
272
- NSData *jsonData = [NSJSONSerialization dataWithJSONObject:responseDict options:0 error:nil];
273
- if (jsonData) {
274
- [[NSUserDefaults standardUserDefaults] setObject:jsonData forKey:@"CGMStatusItem"];
275
- [[NSUserDefaults standardUserDefaults] synchronize];
276
- }
314
+ // Save response data
315
+ [[NSUserDefaults standardUserDefaults] setObject:data forKey:@"CGMStatusItem"];
316
+ [[NSUserDefaults standardUserDefaults] synchronize];
277
317
 
278
- // Call Swift method which reads CGMStatusItem from UserDefaults
318
+ // Call manager logic
279
319
  FinalViewModelManager *manager = [FinalViewModelManager shared];
280
320
  [manager callForObserveTransmitterUnbindStatusWithCompletion:^(NSDictionary *response, NSError *error) {
281
- if (completion) {
282
- completion(response, error);
321
+ if (error) {
322
+ reject([NSString stringWithFormat:@"%ld", (long)error.code], error.localizedDescription, error);
323
+ } else {
324
+ dispatch_async(dispatch_get_main_queue(), ^{
325
+ resolve(response ?: @{});
326
+ });
283
327
  }
284
328
  }];
285
329
  }
286
330
 
287
- //RCT_EXPORT_METHOD(observeTransmitterUnbindStatus:(NSString *)token response:(NSString *)responseJsonString)
288
- //{
289
- // // Optional: Parse the JSON string if needed
290
- // NSData *jsonData = [responseJsonString dataUsingEncoding:NSUTF8StringEncoding];
291
- // NSError *jsonError;
292
- // NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:&jsonError];
293
- //
294
- // if (jsonError) {
295
- // NSLog(@"Failed to parse JSON: %@", jsonError.localizedDescription);
296
- // [self sendEventWithName:@"observeTransmitterUnbindStatusHandler" body:@{
297
- // @"token": token ?: @"",
298
- // @"success": @NO,
299
- // @"error": @"Invalid JSON response from JS"
300
- // }];
301
- // return;
302
- // }
303
- //
304
- // // Now you have responseDict, you can use it
305
- // [self observeTransmitterUnbindStatusInternal:token response:responseDict completion:^(NSDictionary *response, NSError *error) {
306
- // if (error) {
307
- // NSDictionary *errorPayload = @{
308
- // @"token": token ?: @"",
309
- // @"success": @NO,
310
- // @"error": error.localizedDescription ?: @"Unknown error"
311
- // };
312
- // [self sendEventWithName:@"observeTransmitterUnbindStatusHandler" body:errorPayload];
313
- // } else {
314
- // NSDictionary *successPayload = @{
315
- // @"token": token ?: @"",
316
- // @"responseFromBackend": response ?: @{},
317
- // @"responseFromJS": responseDict ?: @{},
318
- // @"success": @YES
319
- // };
320
- // [self sendEventWithName:@"observeTransmitterUnbindStatusHandler" body:successPayload];
321
- // }
322
- // }];
323
- //
324
- //}
325
-
326
331
  @end
@@ -188,7 +188,7 @@ class API {
188
188
  // Set request body
189
189
  let json: [String: Any] = [
190
190
  "sensorId": sensorId,
191
- "sensorName": sensorId,
191
+ // "sensorName": sensorId,
192
192
  "status": status.rawValue,
193
193
  "rawData": [
194
194
  "transmitterName": transmitterName,
@@ -72,6 +72,7 @@ class AttachTransmitterViewController: UIViewController {
72
72
  }
73
73
 
74
74
  customTopView.onDemoTapped = {
75
+ NotificationCenter.default.post(name: Notification.Name("cgmwatchdemoclicked"), object: nil, userInfo: ["a":"a"])
75
76
  let webVC = WebViewController()
76
77
  webVC.modalPresentationStyle = .formSheet
77
78
  webVC.videoURL = URL(string: watchCGMVideo)
@@ -27,6 +27,7 @@ class ChatWithExpertViewController: UIViewController {
27
27
  super.viewDidLoad()
28
28
  // Do any additional setup after loading the view.
29
29
  setupLayout()
30
+
30
31
  }
31
32
 
32
33
  func setupLayout() {
@@ -43,6 +44,9 @@ class ChatWithExpertViewController: UIViewController {
43
44
  buttonBack.titleLabel?.font = FontManager.font(ofSize: 16, weight: .medium)
44
45
  bottomButton.labelText = "Proceed"
45
46
  bottomButton.showLeftWhatsappImage()
47
+
48
+ NotificationCenter.default.post(name: Notification.Name("cgmWAsupportclicked"), object: nil, userInfo: ["a":"a"])
49
+
46
50
  bottomButton.buttonTapCallback = {
47
51
 
48
52
  if let url = URL(string: "https://wa.aisensy.com/aaa1qv") {
@@ -99,6 +99,7 @@ class ConnectToSensorViewController: UIViewController {
99
99
  }
100
100
 
101
101
  customTopView.onDemoTapped = {
102
+ NotificationCenter.default.post(name: Notification.Name("cgmwatchdemoclicked"), object: nil, userInfo: ["a":"a"])
102
103
  let webVC = WebViewController()
103
104
  webVC.modalPresentationStyle = .formSheet
104
105
  webVC.videoURL = URL(string: watchCGMVideo)
@@ -96,12 +96,21 @@ class ConnectToTransmitterViewController: UIViewController, KLTBluetoothDelegate
96
96
  }
97
97
  bottomButton.buttonTapCallback = {
98
98
  if self.isForReconnect {
99
- let vc = ConnectToSensorViewController.instantiate(fromStoryboard: Enum_stroyboard.Main.rawValue)
100
- vc.connected = {
101
- //self.showConfirmInsulinUser()
99
+ if self.bottomButton.labelText == "Retry" {
100
+ self.manager?.bluetoothDelegate = self
101
+ self.manager?.startScan()
102
+ self.screenType = .searching
103
+ self.tableView.reloadData()
104
+ self.bottomButton.hideRightArrow()
105
+ } else {
106
+ let vc = ConnectToSensorViewController.instantiate(fromStoryboard: Enum_stroyboard.Main.rawValue)
107
+ vc.connected = {
108
+ //self.showConfirmInsulinUser()
109
+ }
110
+ vc.isFromReconnect = true
111
+ self.navigationController?.pushViewController(vc, animated: false)
102
112
  }
103
- vc.isFromReconnect = true
104
- self.navigationController?.pushViewController(vc, animated: false)
113
+
105
114
  // NotificationCenter.default.post(name: Notification.Name("cgmDeviceEvent"), object: nil, userInfo: ["a":"a"])
106
115
  // if let rootVC = UIApplication.shared.connectedScenes
107
116
  // .compactMap({ ($0 as? UIWindowScene)?.windows.first?.rootViewController })
@@ -112,6 +121,8 @@ class ConnectToTransmitterViewController: UIViewController, KLTBluetoothDelegate
112
121
  // }
113
122
  } else {
114
123
  if self.screenType == .error {
124
+ self.manager?.bluetoothDelegate = self
125
+ self.manager?.startScan()
115
126
  self.bottomButton.disable()
116
127
  self.screenType = .searching
117
128
  self.tableView.reloadData()
@@ -126,6 +137,7 @@ class ConnectToTransmitterViewController: UIViewController, KLTBluetoothDelegate
126
137
  }
127
138
 
128
139
  customTopView.onDemoTapped = {
140
+ NotificationCenter.default.post(name: Notification.Name("cgmwatchdemoclicked"), object: nil, userInfo: ["a":"a"])
129
141
  let webVC = WebViewController()
130
142
  webVC.modalPresentationStyle = .formSheet
131
143
  webVC.videoURL = URL(string: watchCGMVideo)
@@ -297,6 +309,7 @@ extension ConnectToTransmitterViewController: UITableViewDelegate, UITableViewDa
297
309
 
298
310
  //cell.labelDesc.text = "Sno:\(devices[indexPath.row].name)"
299
311
  cell.button.buttonTapCallback = {
312
+
300
313
  if self.isForReconnect {
301
314
  self.sensorForceEnd()
302
315
  }
@@ -304,7 +317,7 @@ extension ConnectToTransmitterViewController: UITableViewDelegate, UITableViewDa
304
317
  if !KLTLocalSettingManager.shareInstance().canConnectOtherDevice {
305
318
  return
306
319
  }
307
-
320
+
308
321
  self.manager?.stopScan()
309
322
  if self.foundDevices.count > indexPath.row {
310
323
  let peripheral = self.foundDevices[indexPath.row] as! CBPeripheral
@@ -45,11 +45,14 @@ class ProvidePermissionViewController: UIViewController {
45
45
 
46
46
  bottomButton.labelText = "Proceed"
47
47
  bottomButton.buttonTapCallback = {
48
+ let isForReconnect = UserDefaults.standard.bool(forKey: "isForReconnect")
48
49
  let vc = ConnectToTransmitterViewController.instantiate(fromStoryboard: Enum_stroyboard.Main.rawValue)
50
+ vc.isForReconnect = isForReconnect
49
51
  self.navigationController?.pushViewController(vc, animated: false)
50
52
  }
51
53
 
52
54
  customTopView.onDemoTapped = {
55
+ NotificationCenter.default.post(name: Notification.Name("cgmwatchdemoclicked"), object: nil, userInfo: ["a":"a"])
53
56
  let webVC = WebViewController()
54
57
  webVC.modalPresentationStyle = .formSheet
55
58
  webVC.videoURL = URL(string: watchCGMVideo)
@@ -42,6 +42,7 @@ class PutOnTheSensorViewController: UIViewController {
42
42
  }
43
43
 
44
44
  customTopView.onDemoTapped = {
45
+ NotificationCenter.default.post(name: Notification.Name("cgmwatchdemoclicked"), object: nil, userInfo: ["a":"a"])
45
46
  let webVC = WebViewController()
46
47
  webVC.modalPresentationStyle = .formSheet
47
48
  webVC.videoURL = URL(string: watchCGMVideo)
@@ -51,8 +51,7 @@ class StartConnectionViewController: UIViewController {
51
51
  ImageTVC.self,
52
52
  ChargingIndicatorTVC.self])
53
53
  tableView.reloadData()
54
- // UserDefaults.standard.set(isLocationEnabled, forKey: "isForReconnect")
55
-
54
+
56
55
  let isForReconnect = UserDefaults.standard.bool(forKey: "isForReconnect")
57
56
  if isForReconnect{
58
57
  checkLocationPermission()
@@ -74,6 +73,7 @@ class StartConnectionViewController: UIViewController {
74
73
  }
75
74
 
76
75
  customTopView.onDemoTapped = {
76
+ NotificationCenter.default.post(name: Notification.Name("cgmwatchdemoclicked"), object: nil, userInfo: ["a":"a"])
77
77
  let webVC = WebViewController()
78
78
  webVC.modalPresentationStyle = .formSheet // or .pageSheet, .fullScreen
79
79
  webVC.videoURL = URL(string: watchCGMVideo)
@@ -95,7 +95,9 @@ class StartConnectionViewController: UIViewController {
95
95
 
96
96
  private func updateBottomButtonState() {
97
97
  if isBluetoothEnabled && isLocationEnabled {
98
+ let isForReconnect = UserDefaults.standard.bool(forKey: "isForReconnect")
98
99
  let vc = ConnectToTransmitterViewController.instantiate(fromStoryboard: Enum_stroyboard.Main.rawValue)
100
+ vc.isForReconnect = isForReconnect
99
101
  self.navigationController?.pushViewController(vc, animated: false)
100
102
  } else {
101
103
  let vc = ProvidePermissionViewController.instantiate(fromStoryboard: Enum_stroyboard.Main.rawValue)
@@ -105,29 +105,25 @@ import Foundation
105
105
  startDate: String,
106
106
  endDate: String
107
107
  ) -> [String: Any] {
108
- let updatedResponse = response
108
+ var updatedResponse = response
109
109
 
110
110
  if viewModel.isCurrentDateInRange(startDate: startDate, endDate: endDate) {
111
111
  print("Current date is in range")
112
- if ((viewModel.manager.connectedPeripheral == nil) && !KLTLocalSettingManager.shareInstance().canConnectOtherDevice) {
112
+
113
+ let canConnectOther = KLTLocalSettingManager.shareInstance().canConnectOtherDevice
114
+ let connectedPeripheral = viewModel.manager.connectedPeripheral
115
+
116
+ if connectedPeripheral == nil && !canConnectOther {
113
117
  viewModel.manager.startScan()
114
118
  viewModel.debouncer.update(with: .transmitterDisconnect)
115
- } else {
116
- print("here")
117
- //debouncer.update(with: .connected)
118
- //API.shared.sendStatus(status: .connected) 计算 算法入参
119
- if let last = viewModel.manager.currentDevice {
120
-
121
- }
122
- else {
123
-
124
- //self.manager.closeBleSensor()
125
- //manager.startScan()
126
- //self.debouncer.update(with: .disconnected)
127
- //debouncer.update(with: .transmitterDisconnectBox)
128
- }
129
-
130
- if KLTBluetoothManager.shared().currentDevice.connectedDateTime == nil && KLTBluetoothManager.shared().currentDevice.disconnectedDateTime == nil {
119
+ } else {
120
+ if let device = KLTBluetoothManager.shared().currentDevice {
121
+ if device.connectedDateTime == nil && device.disconnectedDateTime == nil {
122
+ viewModel.manager.startScan()
123
+ viewModel.debouncer.update(with: .transmitterDisconnect)
124
+ }
125
+ } else {
126
+ print("⚠️ currentDevice is nil")
131
127
  viewModel.manager.startScan()
132
128
  viewModel.debouncer.update(with: .transmitterDisconnect)
133
129
  }
@@ -136,9 +132,6 @@ import Foundation
136
132
 
137
133
  return updatedResponse
138
134
  }
139
-
140
-
141
-
142
135
  }
143
136
 
144
137
  class FinalViewModel: NSObject {
@@ -335,7 +328,7 @@ class FinalViewModel: NSObject {
335
328
  let batches = stride(from: 0, to: data.count, by: batchSize).map {
336
329
  Array(data[$0..<min($0 + batchSize, data.count)])
337
330
  }
338
-
331
+
339
332
  uploadBatch(batches: batches, index: 0)
340
333
  }
341
334
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-mytatva-rn-sdk",
3
- "version": "1.2.45",
3
+ "version": "1.2.48",
4
4
  "description": "a package to inject data into visit health pwa",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",