react-native-mytatva-rn-sdk 1.2.96 → 1.2.98

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 (32) hide show
  1. package/android/src/main/java/com/mytatvarnsdk/CgmTrackyLibModule.kt +84 -0
  2. package/android/src/main/java/com/mytatvarnsdk/activity/ConnectSensorActivity.kt +158 -18
  3. package/android/src/main/java/com/mytatvarnsdk/activity/HelpActivity.kt +3 -9
  4. package/android/src/main/java/com/mytatvarnsdk/activity/PermissionActivity.kt +51 -16
  5. package/android/src/main/java/com/mytatvarnsdk/activity/PlaceSensorActivity.kt +29 -13
  6. package/android/src/main/java/com/mytatvarnsdk/activity/PlaceTransmitterActivity.kt +29 -13
  7. package/android/src/main/java/com/mytatvarnsdk/activity/QRActivity.kt +146 -5
  8. package/android/src/main/java/com/mytatvarnsdk/activity/SearchTransmitterActivity.kt +108 -13
  9. package/android/src/main/java/com/mytatvarnsdk/activity/SensorConnectSuccessActivity.kt +43 -16
  10. package/android/src/main/java/com/mytatvarnsdk/activity/StartCGMActivity.kt +26 -15
  11. package/android/src/main/java/com/mytatvarnsdk/activity/VideoActivity.kt +96 -50
  12. package/android/src/main/java/com/mytatvarnsdk/network/AuthenticateSDKService.kt +113 -0
  13. package/android/src/main/java/com/mytatvarnsdk/utils/CgmModuleSentryLog.kt +205 -0
  14. package/android/src/main/java/com/mytatvarnsdk/utils/CgmSentryLog.kt +28 -0
  15. package/android/src/main/java/com/mytatvarnsdk/utils/CustomerSupportConfig.kt +137 -0
  16. package/android/src/main/java/com/mytatvarnsdk/utils/ToolbarSupportHelper.kt +83 -0
  17. package/ios/Custom/CustomTopUIView.swift +25 -0
  18. package/ios/MyReactNativeBridge.m +4 -0
  19. package/ios/Support/API.swift +117 -18
  20. package/ios/Support/CustomTopViewSupportHelper.swift +55 -0
  21. package/ios/Support/CustomerSupportConfig.swift +169 -0
  22. package/ios/Support/Global.swift +2 -2
  23. package/ios/Support/GlobalYouTubePlayerView.swift +102 -11
  24. package/ios/ViewControllers/AttachTransmitterViewController.swift +16 -21
  25. package/ios/ViewControllers/ChatWithExpertViewController.swift +1 -7
  26. package/ios/ViewControllers/ConnectToSensorViewController.swift +16 -21
  27. package/ios/ViewControllers/ConnectToTransmitterViewController.swift +16 -21
  28. package/ios/ViewControllers/ProvidePermissionViewController.swift +16 -21
  29. package/ios/ViewControllers/PutOnTheSensorViewController.swift +16 -21
  30. package/ios/ViewControllers/StartConnectionViewController.swift +18 -25
  31. package/ios/ViewModel/FinalViewModel.swift +2 -2
  32. package/package.json +1 -1
@@ -0,0 +1,137 @@
1
+ package com.mytatvarnsdk.utils
2
+
3
+ import android.os.Handler
4
+ import android.os.Looper
5
+ import android.util.Log
6
+ import org.json.JSONObject
7
+ import java.util.concurrent.CopyOnWriteArraySet
8
+
9
+ object CustomerSupportConfig {
10
+ private const val TAG = "CustomerSupportConfig"
11
+
12
+ const val DEFAULT_WHATSAPP_NUMBER = "918511975757"
13
+ const val DEFAULT_DEMO_VIDEO_ID = "65vl2aE1K_I"
14
+ const val DEFAULT_DEMO_VIDEO_URL =
15
+ "https://www.youtube.com/embed/65vl2aE1K_I"
16
+
17
+ private val mainHandler = Handler(Looper.getMainLooper())
18
+ private val updateListeners = CopyOnWriteArraySet<() -> Unit>()
19
+
20
+ @Volatile
21
+ var isLoaded: Boolean = false
22
+
23
+ @Volatile
24
+ var isShowWhatsapp: Boolean = false
25
+
26
+ @Volatile
27
+ var whatsAppNumber: String = DEFAULT_WHATSAPP_NUMBER
28
+
29
+ @Volatile
30
+ var watchCGMDemoUrl: String = DEFAULT_DEMO_VIDEO_URL
31
+
32
+ fun addUpdateListener(listener: () -> Unit) {
33
+ updateListeners.add(listener)
34
+ }
35
+
36
+ fun removeUpdateListener(listener: () -> Unit) {
37
+ updateListeners.remove(listener)
38
+ }
39
+
40
+ fun applyFailureDefaults() {
41
+ isShowWhatsapp = true
42
+ whatsAppNumber = DEFAULT_WHATSAPP_NUMBER
43
+ watchCGMDemoUrl = DEFAULT_DEMO_VIDEO_URL
44
+ isLoaded = true
45
+ Log.d(
46
+ TAG,
47
+ "Failure defaults applied | showWhatsapp=$isShowWhatsapp | demoUrl=$watchCGMDemoUrl"
48
+ )
49
+ notifyConfigUpdated()
50
+ }
51
+
52
+ fun updateFromResponse(response: String) {
53
+ try {
54
+ val json = JSONObject(response)
55
+ if (json.optInt("code", 0) != 1) {
56
+ Log.w(TAG, "Customer support API returned non-success code, using failure defaults")
57
+ applyFailureDefaults()
58
+ return
59
+ }
60
+
61
+ val data = json.optJSONObject("data")
62
+ if (data == null) {
63
+ Log.w(TAG, "Customer support API missing data, using failure defaults")
64
+ applyFailureDefaults()
65
+ return
66
+ }
67
+
68
+ isShowWhatsapp = parseBoolean(data, "isShowWhatsapp")
69
+ whatsAppNumber = data.optString("whatsAppNumber", "").trim()
70
+ .ifEmpty { DEFAULT_WHATSAPP_NUMBER }
71
+ watchCGMDemoUrl = data.optString("watchCGMDemoUrl", "").trim()
72
+ .ifEmpty { DEFAULT_DEMO_VIDEO_URL }
73
+ isLoaded = true
74
+
75
+ Log.d(
76
+ TAG,
77
+ "Customer support loaded | showWhatsapp=$isShowWhatsapp | demoUrl=$watchCGMDemoUrl"
78
+ )
79
+ notifyConfigUpdated()
80
+ } catch (e: Exception) {
81
+ Log.w(TAG, "Failed to parse customer support response: ${e.message}, using failure defaults")
82
+ applyFailureDefaults()
83
+ }
84
+ }
85
+
86
+ private fun notifyConfigUpdated() {
87
+ mainHandler.post {
88
+ updateListeners.forEach { listener ->
89
+ try {
90
+ listener.invoke()
91
+ } catch (e: Exception) {
92
+ Log.w(TAG, "Customer support listener failed: ${e.message}")
93
+ }
94
+ }
95
+ }
96
+ }
97
+
98
+ fun effectiveWhatsAppNumber(): String =
99
+ whatsAppNumber.ifEmpty { DEFAULT_WHATSAPP_NUMBER }
100
+
101
+ fun shouldShowWhatsapp(): Boolean =
102
+ isLoaded && isShowWhatsapp && effectiveWhatsAppNumber().isNotEmpty()
103
+
104
+ fun getDemoVideoId(isForReconnect: Boolean): String {
105
+ val url = watchCGMDemoUrl.ifEmpty { DEFAULT_DEMO_VIDEO_URL }
106
+ extractYouTubeVideoId(url)?.let { return it }
107
+ return DEFAULT_DEMO_VIDEO_ID
108
+ }
109
+
110
+ fun getDemoVideoUrl(isForReconnect: Boolean): String =
111
+ watchCGMDemoUrl.ifEmpty { DEFAULT_DEMO_VIDEO_URL }
112
+
113
+ fun extractYouTubeVideoId(url: String): String? {
114
+ val trimmed = url.trim()
115
+ if (trimmed.isEmpty()) return null
116
+
117
+ val patterns = listOf(
118
+ Regex("(?:embed/|/v/|watch\\?v=|youtu\\.be/)([\\w-]{11})"),
119
+ Regex("^([\\w-]{11})$")
120
+ )
121
+
122
+ for (pattern in patterns) {
123
+ pattern.find(trimmed)?.groupValues?.getOrNull(1)?.let { return it }
124
+ }
125
+ return null
126
+ }
127
+
128
+ private fun parseBoolean(json: JSONObject, key: String): Boolean {
129
+ if (!json.has(key)) return false
130
+ return when (val value = json.get(key)) {
131
+ is Boolean -> value
132
+ is Int -> value == 1
133
+ is String -> value.equals("true", ignoreCase = true) || value == "1"
134
+ else -> false
135
+ }
136
+ }
137
+ }
@@ -0,0 +1,83 @@
1
+ package com.mytatvarnsdk.utils
2
+
3
+ import android.content.Context
4
+ import android.content.Intent
5
+ import android.net.Uri
6
+ import android.view.View
7
+ import android.widget.Toast
8
+ import androidx.appcompat.app.AppCompatActivity
9
+ import androidx.lifecycle.DefaultLifecycleObserver
10
+ import androidx.lifecycle.LifecycleOwner
11
+ import com.mytatvarnsdk.activity.VideoActivity
12
+
13
+ object ToolbarSupportHelper {
14
+
15
+ fun configureWhatsappButton(
16
+ context: Context,
17
+ btnWhatsapp: View,
18
+ onClickAnalytics: (() -> Unit)? = null
19
+ ) {
20
+ if (CustomerSupportConfig.shouldShowWhatsapp()) {
21
+ btnWhatsapp.visibility = View.VISIBLE
22
+ btnWhatsapp.setOnClickListener {
23
+ onClickAnalytics?.invoke()
24
+ openWhatsApp(context, CustomerSupportConfig.effectiveWhatsAppNumber())
25
+ }
26
+ } else {
27
+ btnWhatsapp.visibility = View.GONE
28
+ btnWhatsapp.setOnClickListener(null)
29
+ }
30
+ }
31
+
32
+ fun bindWhatsappButton(
33
+ activity: AppCompatActivity,
34
+ btnWhatsapp: View,
35
+ onClickAnalytics: (() -> Unit)? = null
36
+ ) {
37
+ btnWhatsapp.setOnClickListener(null)
38
+ val refresh: () -> Unit = {
39
+ configureWhatsappButton(activity, btnWhatsapp, onClickAnalytics)
40
+ }
41
+
42
+ refresh()
43
+ CustomerSupportConfig.addUpdateListener(refresh)
44
+
45
+ activity.lifecycle.addObserver(object : DefaultLifecycleObserver {
46
+ override fun onDestroy(owner: LifecycleOwner) {
47
+ CustomerSupportConfig.removeUpdateListener(refresh)
48
+ }
49
+ })
50
+ }
51
+
52
+ fun configureWatchDemoButton(
53
+ context: Context,
54
+ btnWatchDemo: View,
55
+ isForReconnect: Boolean,
56
+ onClickAnalytics: (() -> Unit)? = null
57
+ ) {
58
+ btnWatchDemo.setOnClickListener {
59
+ onClickAnalytics?.invoke()
60
+ val intent = Intent(context, VideoActivity::class.java)
61
+ val videoId = CustomerSupportConfig.getDemoVideoId(isForReconnect)
62
+ intent.putExtra("VideoId", videoId)
63
+ context.startActivity(intent)
64
+ }
65
+ }
66
+
67
+ fun openWhatsApp(context: Context, number: String) {
68
+ val cleanedNumber = number.replace(Regex("[^0-9]"), "")
69
+ if (cleanedNumber.isEmpty()) {
70
+ Toast.makeText(context, "WhatsApp number unavailable", Toast.LENGTH_SHORT).show()
71
+ return
72
+ }
73
+
74
+ val waUri = Uri.parse("https://wa.me/$cleanedNumber")
75
+ val intent = Intent(Intent.ACTION_VIEW, waUri)
76
+ try {
77
+ context.startActivity(intent)
78
+ } catch (e: Exception) {
79
+ e.printStackTrace()
80
+ Toast.makeText(context, "Error opening WhatsApp", Toast.LENGTH_SHORT).show()
81
+ }
82
+ }
83
+ }
@@ -48,6 +48,8 @@ class CustomTopUIView: UIView {
48
48
  var onWhatsAppTapped: (() -> Void)?
49
49
  var onCloseTapped: (() -> Void)?
50
50
 
51
+ private var customerSupportConfigObserver: NSObjectProtocol?
52
+
51
53
 
52
54
  // For Interface Builder
53
55
  override init(frame: CGRect) {
@@ -124,6 +126,21 @@ class CustomTopUIView: UIView {
124
126
  whatsappButton.addTarget(self, action: #selector(whatsappButtonTapped), for: .touchUpInside)
125
127
  closeButton.addTarget(self, action: #selector(closeButtonTapped), for: .touchUpInside)
126
128
 
129
+ customerSupportConfigObserver = NotificationCenter.default.addObserver(
130
+ forName: .customerSupportConfigDidUpdate,
131
+ object: nil,
132
+ queue: .main
133
+ ) { [weak self] _ in
134
+ self?.refreshWhatsappVisibility()
135
+ }
136
+
137
+ refreshWhatsappVisibility()
138
+ }
139
+ }
140
+
141
+ deinit {
142
+ if let customerSupportConfigObserver {
143
+ NotificationCenter.default.removeObserver(customerSupportConfigObserver)
127
144
  }
128
145
  }
129
146
 
@@ -131,6 +148,14 @@ class CustomTopUIView: UIView {
131
148
  demoButton.isHidden = true
132
149
  whatsappButton.isHidden = true
133
150
  }
151
+
152
+ func setWhatsappVisible(_ isVisible: Bool) {
153
+ whatsappButton.isHidden = !isVisible
154
+ }
155
+
156
+ func refreshWhatsappVisibility() {
157
+ setWhatsappVisible(CustomerSupportConfig.shared.shouldShowWhatsapp())
158
+ }
134
159
 
135
160
  @objc private func demoButtonTapped() {
136
161
  onDemoTapped?()
@@ -471,6 +471,8 @@ RCT_EXPORT_METHOD(startCgmTracky:(NSString *)token envType:(NSString *)envType
471
471
 
472
472
  [defaults synchronize];
473
473
 
474
+ [[CustomerSupportConfig shared] fetchCustomerSupportWithToken:token];
475
+
474
476
  dispatch_async(dispatch_get_main_queue(), ^{
475
477
  UIWindow *keyWindow = [UIApplication sharedApplication].delegate.window;
476
478
  UIViewController *rootVC = keyWindow.rootViewController;
@@ -513,6 +515,8 @@ RCT_EXPORT_METHOD(reconnectCgmTracky:(NSString *)token envType:(NSString *)envT
513
515
 
514
516
  [defaults synchronize];
515
517
 
518
+ [[CustomerSupportConfig shared] fetchCustomerSupportWithToken:token];
519
+
516
520
  dispatch_async(dispatch_get_main_queue(), ^{
517
521
  UIWindow *keyWindow = [UIApplication sharedApplication].delegate.window;
518
522
  UIViewController *rootVC = keyWindow.rootViewController;
@@ -23,8 +23,22 @@ let STAGE_ENC_IV = "9Ddyaf6rfywpiTvT"
23
23
 
24
24
 
25
25
  let defaultToken = "ILtFcw+xtbuy8IgsBCSyD6nSpgZd5AOz7T+g3N8Tef/INZi+dxwPJhnBc2kfdq2e8Kw2yayjaKjuji64coUJsK7e4QcF7sqXCp3Cy5S2OVE+hONwCipkrn9d1yjsBWIDqfLK9ModJCWdigDj5ZeSwkWYSSeTEVoN3Dc2Y8cuqKUQvN9ZcNGmdRrXz2oo3rm4EHQrXPo1Ijufm0HfcqzSH/Gh0TC5gFKjde/GEXIm5Z2Ju1/TTXVSJg/zxNuOF4iUKNEh4UIFUYxYpnqlALRCdF75J+9WAZ7/LBGyB1Wrx+D7bsrzRiQ5sCTwxE3TCM3s66lYJizX22VIzYKOIrJ6GTOYGTINHeIJKEPEvFceR+cG54ez15V1r77ErQElevItD3xIGe0uDezfnixJYPxPhSEOPOyIpxUFInXBRdGjllHPIevzBqqHeP76nz+1U7mFNs1kofugN+5huT3/tCifTQqFyWuxD+VIhl0UJxhxPMbBgoryANgKCToWrK2FqdJwBtZKwCj3S+yp0rff0fwrbMXXsYytJyVLLY2oNviHtzDa/kih0AnMA3vEbqYkf1PQ6LZv+AmtpNYHKh7kMDrhVE3wVxH4+ga0+sfuxYgfiZXCoQW7yY0wOZTiFbgvL2TQflDZR6ttK5abtyKYV4gl5KqbFLZW+d4pKLQoZb02NbJc+5h5gAH2VclTJc6fbkd0qIWuXyqGIBEc2uYrNl2v3vmVvbcGevSqb900ozo6SbFccQLjLcEon+okmgBIDPvBEZFj8fZgQV4z8/yzayDcfqZQPc9Wrw8JNrFLCd8emvo7QHFfGttnIsAtmR8Yp3x49sjEn3hFW6nEKl9FRtRT1r3LzHmIdokL5tz3nO/T3FzLn5sqfp5QBJKKK2dgJdmAz0KC6vKtJ0PtVR4shL7pqjoZuJimFObYKyQbQudW7gLH8WjqORNG2dsplOLzDQ0y7nQdQ0tWrLJtnAch0p5PyTXtunlkNWeuzrldkqVMTUPaAtGcEnmS4RFncD3C6sq9V6lpBKLdrh99vnH3EX81DTH2Hrqm0bN5Tl3UqzUJ7n/JQCR3phDmOo+Uk81y3z3ikiGZZpZikN7c3DtSP34Vc+cNUosWrChqj1gLL4Eg/jMDxE4+bdAGr03ALxEAj2uLGEsDaZRuio6e8/iMcA0z3oqhtAxQ0nU1ivAK7cHeddjRFtvrNb0l+TgrcQI9UVffMOoGDEfAQNUpT2+R5dIysw=="
26
- let TOKEN = UserDefaults.standard.string(forKey: "authToken") ?? defaultToken
27
- let envType = UserDefaults.standard.string(forKey: "envType") ?? "uat"
26
+
27
+ func currentAuthToken() -> String {
28
+ UserDefaults.standard.string(forKey: "authToken") ?? defaultToken
29
+ }
30
+
31
+ func currentEnvType() -> String {
32
+ UserDefaults.standard.string(forKey: "envType") ?? "uat"
33
+ }
34
+
35
+ func normalizedCipherText(_ response: String) -> String {
36
+ var text = response.trimmingCharacters(in: .whitespacesAndNewlines)
37
+ if text.hasPrefix("\""), text.hasSuffix("\""), text.count >= 2 {
38
+ text = String(text.dropFirst().dropLast())
39
+ }
40
+ return text
41
+ }
28
42
 
29
43
  /*TatvaEncryptionConfig {
30
44
  const val STAGE_BASE_URL = "https://api-feature2.mytatva.in/api/v8"
@@ -99,7 +113,7 @@ class API {
99
113
  func postCGMData(
100
114
  data: Payload,
101
115
  environment: TatvaEnvironment,
102
- token: String = TOKEN,
116
+ token: String = currentAuthToken(),
103
117
  encryptionKey: String = PROD_ENC_KEY,
104
118
  encryptionIv: String = PROD_ENC_IV,
105
119
  onSuccess: @escaping () -> Void,
@@ -137,7 +151,8 @@ class API {
137
151
  print("===>Decrypted (for verification): \(decrypted)")
138
152
  }
139
153
 
140
- let urlString = (envType.lowercased() == "uat" ? STAGE_BASE_URL : PROD_BASE_URL) + "/cgm/logs"
154
+ let env = currentEnvType()
155
+ let urlString = (env.lowercased() == "uat" ? STAGE_BASE_URL : PROD_BASE_URL) + "/cgm/logs"
141
156
  // let urlString = (environment == .stage ? STAGE_BASE_URL : PROD_BASE_URL) + "/cgm/logs"
142
157
  guard let url = URL(string: urlString) else {
143
158
  throw URLError(.badURL)
@@ -146,7 +161,7 @@ class API {
146
161
  var request = URLRequest(url: url)
147
162
  request.httpMethod = "POST"
148
163
  request.timeoutInterval = 30
149
- request.setValue(envType.lowercased() == "uat" ? STAGE_API_KEY : PROD_API_KEY, forHTTPHeaderField: "api-key")
164
+ request.setValue(env.lowercased() == "uat" ? STAGE_API_KEY : PROD_API_KEY, forHTTPHeaderField: "api-key")
150
165
  // request.setValue(environment == .stage ? STAGE_API_KEY : PROD_API_KEY, forHTTPHeaderField: "api-key")
151
166
  request.setValue(token, forHTTPHeaderField: "token")
152
167
  request.setValue("text/plain", forHTTPHeaderField: "Content-Type")
@@ -172,7 +187,8 @@ class API {
172
187
 
173
188
  if let data = data, let responseStr = String(data: data, encoding: .utf8) {
174
189
  print("===>Server Response: \(responseStr)")
175
- if let decrypted = Crypto.shared.getDecryptedData(cipherText: responseStr, encryptionKey: encryptionKey, encryptionIv: encryptionIv) {
190
+ let cipherText = normalizedCipherText(responseStr)
191
+ if let decrypted = Crypto.shared.getDecryptedData(cipherText: cipherText, encryptionKey: encryptionKey, encryptionIv: encryptionIv) {
176
192
  print("===>Decrypted post cgm data response (for verification): \(decrypted)")
177
193
  }
178
194
  }
@@ -203,9 +219,9 @@ class API {
203
219
  encryptionKey: String = PROD_ENC_KEY,
204
220
  encryptionIv: String = PROD_ENC_IV) {
205
221
  if sensorId.isEmpty { return }
206
- // let envType = UserDefaults.standard.string(forKey: "envType") ?? "uat"
207
- print("envType: \(envType.lowercased())")
208
- let urlString = (envType.lowercased() == "uat" ? STAGE_BASE_URL : PROD_BASE_URL) + "/cgm/connection"
222
+ let env = currentEnvType()
223
+ print("envType: \(env.lowercased())")
224
+ let urlString = (env.lowercased() == "uat" ? STAGE_BASE_URL : PROD_BASE_URL) + "/cgm/connection"
209
225
  //let urlString = (environment == .stage ? STAGE_BASE_URL : PROD_BASE_URL) + "/cgm/connection"
210
226
  let url = URL(string: urlString)!
211
227
  var request = URLRequest(url: url)
@@ -214,13 +230,14 @@ class API {
214
230
 
215
231
  // Set headers
216
232
  request.addValue("text/plain", forHTTPHeaderField: "Content-Type")
217
- request.setValue(envType.lowercased() == "uat" ? STAGE_API_KEY : PROD_API_KEY, forHTTPHeaderField: "api-key")
233
+ request.setValue(env.lowercased() == "uat" ? STAGE_API_KEY : PROD_API_KEY, forHTTPHeaderField: "api-key")
218
234
  //request.setValue(environment == .stage ? STAGE_API_KEY : PROD_API_KEY, forHTTPHeaderField: "api-key")
219
- request.addValue(TOKEN, forHTTPHeaderField: "token")
235
+ let token = currentAuthToken()
236
+ request.addValue(token, forHTTPHeaderField: "token")
220
237
  print("===>url:", urlString)
221
238
  print("===>api-key:", PROD_API_KEY)
222
239
  print("===>httpmMethod:","POST")
223
- print("===>ios token:", TOKEN)
240
+ print("===>ios token:", token)
224
241
  // Set request body
225
242
  let json: [String: Any] = [
226
243
  "sensorId": sensorId,
@@ -259,7 +276,8 @@ class API {
259
276
  }
260
277
  if let responseString = String(data: data, encoding: .utf8) {
261
278
  print("===>Server Response: \(responseString)")
262
- if let decrypted = Crypto.shared.getDecryptedData(cipherText: responseString, encryptionKey: encryptionKey, encryptionIv: encryptionIv) {
279
+ let cipherText = normalizedCipherText(responseString)
280
+ if let decrypted = Crypto.shared.getDecryptedData(cipherText: cipherText, encryptionKey: encryptionKey, encryptionIv: encryptionIv) {
263
281
  print("===>Decrypted connection api response (for verification): \(decrypted)")
264
282
  }
265
283
  }
@@ -330,6 +348,84 @@ class API {
330
348
 
331
349
  }*/
332
350
 
351
+ func fetchCustomerSupportDetails(
352
+ token: String = currentAuthToken(),
353
+ encryptionKey: String = PROD_ENC_KEY,
354
+ encryptionIv: String = PROD_ENC_IV,
355
+ onSuccess: @escaping (String) -> Void,
356
+ onFailure: @escaping () -> Void
357
+ ) {
358
+ guard !token.isEmpty else {
359
+ print("===>fetchCustomerSupportDetails: empty token")
360
+ onFailure()
361
+ return
362
+ }
363
+
364
+ let env = currentEnvType()
365
+ let urlString = (env.lowercased() == "uat" ? STAGE_BASE_URL : PROD_BASE_URL) + "/helper/customer_support_details"
366
+
367
+ guard let url = URL(string: urlString) else {
368
+ print("===>fetchCustomerSupportDetails: Invalid URL")
369
+ onFailure()
370
+ return
371
+ }
372
+
373
+ var request = URLRequest(url: url)
374
+ request.httpMethod = "GET"
375
+ request.setValue(env.lowercased() == "uat" ? STAGE_API_KEY : PROD_API_KEY, forHTTPHeaderField: "api-key")
376
+ request.setValue(token, forHTTPHeaderField: "token")
377
+
378
+ print("===>fetchCustomerSupportDetails URL:", urlString)
379
+ print("===>fetchCustomerSupportDetails env:", env)
380
+ print("===>fetchCustomerSupportDetails usingDefaultToken:", token == defaultToken)
381
+
382
+ let task = URLSession.shared.dataTask(with: request) { data, response, error in
383
+ if let error = error {
384
+ print("===>fetchCustomerSupportDetails Error: \(error.localizedDescription)")
385
+ DispatchQueue.main.async { onFailure() }
386
+ return
387
+ }
388
+
389
+ let statusCode = (response as? HTTPURLResponse)?.statusCode ?? 0
390
+ if statusCode > 0 {
391
+ print("===>fetchCustomerSupportDetails HTTP Status: \(statusCode)")
392
+ }
393
+
394
+ guard let data = data, let responseString = String(data: data, encoding: .utf8) else {
395
+ print("===>fetchCustomerSupportDetails: No data received")
396
+ DispatchQueue.main.async { onFailure() }
397
+ return
398
+ }
399
+
400
+ print("===>fetchCustomerSupportDetails Server Response: \(responseString)")
401
+
402
+ let cipherText = normalizedCipherText(responseString)
403
+ if let decrypted = Crypto.shared.getDecryptedData(
404
+ cipherText: cipherText,
405
+ encryptionKey: encryptionKey,
406
+ encryptionIv: encryptionIv
407
+ ) {
408
+ print("===>fetchCustomerSupportDetails Decrypted: \(decrypted)")
409
+ if statusCode >= 400 {
410
+ print("===>fetchCustomerSupportDetails: API error \(statusCode)")
411
+ DispatchQueue.main.async { onFailure() }
412
+ return
413
+ }
414
+ DispatchQueue.main.async { onSuccess(decrypted) }
415
+ return
416
+ }
417
+
418
+ if responseString.contains("\"code\"") {
419
+ DispatchQueue.main.async { onSuccess(responseString) }
420
+ return
421
+ }
422
+
423
+ print("===>fetchCustomerSupportDetails: Decryption failed")
424
+ DispatchQueue.main.async { onFailure() }
425
+ }
426
+ task.resume()
427
+ }
428
+
333
429
  func verifyDevice(
334
430
  sensorId: String,
335
431
  encryptionKey: String = PROD_ENC_KEY,
@@ -337,7 +433,9 @@ class API {
337
433
  onSuccess: @escaping (Int, Bool, String) -> Void,
338
434
  onFailure: @escaping () -> Void
339
435
  ) {
340
- let urlString = (envType.lowercased() == "uat" ? STAGE_BASE_URL : PROD_BASE_URL) + "/helper/device_verification?deviceId=\(sensorId)&deviceType=CGM&vendor=diasens"
436
+ let env = currentEnvType()
437
+ let token = currentAuthToken()
438
+ let urlString = (env.lowercased() == "uat" ? STAGE_BASE_URL : PROD_BASE_URL) + "/helper/device_verification?deviceId=\(sensorId)&deviceType=CGM&vendor=diasens"
341
439
 
342
440
  guard let url = URL(string: urlString) else {
343
441
  print("===>verifyDevice: Invalid URL")
@@ -347,11 +445,11 @@ class API {
347
445
 
348
446
  var request = URLRequest(url: url)
349
447
  request.httpMethod = "GET"
350
- request.setValue(envType.lowercased() == "uat" ? STAGE_API_KEY : PROD_API_KEY, forHTTPHeaderField: "api-key")
351
- request.setValue(TOKEN, forHTTPHeaderField: "token")
448
+ request.setValue(env.lowercased() == "uat" ? STAGE_API_KEY : PROD_API_KEY, forHTTPHeaderField: "api-key")
449
+ request.setValue(token, forHTTPHeaderField: "token")
352
450
 
353
451
  print("===>verifyDevice URL:", urlString)
354
- print("===>verifyDevice token:", TOKEN)
452
+ print("===>verifyDevice token:", token)
355
453
 
356
454
  let task = URLSession.shared.dataTask(with: request) { data, response, error in
357
455
  if let error = error {
@@ -368,7 +466,8 @@ class API {
368
466
 
369
467
  print("===>verifyDevice Server Response: \(responseString)")
370
468
 
371
- guard let decrypted = Crypto.shared.getDecryptedData(cipherText: responseString, encryptionKey: encryptionKey, encryptionIv: encryptionIv) else {
469
+ let cipherText = normalizedCipherText(responseString)
470
+ guard let decrypted = Crypto.shared.getDecryptedData(cipherText: cipherText, encryptionKey: encryptionKey, encryptionIv: encryptionIv) else {
372
471
  print("===>verifyDevice: Decryption failed")
373
472
  DispatchQueue.main.async { onFailure() }
374
473
  return
@@ -0,0 +1,55 @@
1
+ //
2
+ // CustomTopViewSupportHelper.swift
3
+ // MyTatvaCare
4
+ //
5
+
6
+ import UIKit
7
+
8
+ enum CustomTopViewSupportHelper {
9
+ static func configureWhatsapp(customTopView: CustomTopUIView) {
10
+ customTopView.refreshWhatsappVisibility()
11
+ }
12
+
13
+ static func bindDemoAction(
14
+ customTopView: CustomTopUIView,
15
+ isForReconnect: Bool,
16
+ presenter: UIViewController,
17
+ analyticsHandler: (() -> Void)? = nil
18
+ ) {
19
+ customTopView.onDemoTapped = {
20
+ analyticsHandler?()
21
+
22
+ let payload = ["status": "cgm_watch_demo_clicked"]
23
+ NotificationCenter.default.post(
24
+ name: Notification.Name("cgmwatchdemoclicked"),
25
+ object: nil,
26
+ userInfo: payload
27
+ )
28
+
29
+ let webVC = WebViewController()
30
+ webVC.modalPresentationStyle = .formSheet
31
+ webVC.videoURL = URL(string: CustomerSupportConfig.shared.demoVideoURL(isForReconnect: isForReconnect))
32
+ presenter.present(webVC, animated: true, completion: nil)
33
+ }
34
+ }
35
+
36
+ static func bindWhatsappAction(
37
+ customTopView: CustomTopUIView,
38
+ navigationController: UINavigationController?
39
+ ) {
40
+ customTopView.onWhatsAppTapped = {
41
+ let payload = [
42
+ "screen_name": "connection journey page",
43
+ "status": "cgm_WA_support_clicked"
44
+ ]
45
+
46
+ NotificationCenter.default.post(
47
+ name: Notification.Name("cgmWAsupportclicked"),
48
+ object: nil,
49
+ userInfo: payload
50
+ )
51
+
52
+ CustomerSupportConfig.shared.openWhatsApp()
53
+ }
54
+ }
55
+ }