@convep_mobilogy/react-native-qms-plugin 0.10.4 → 0.10.6

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 (27) hide show
  1. package/android/build.gradle +1 -1
  2. package/ios/QmsPlugin/QmsDashboardViewManager.mm +408 -2
  3. package/ios/QmsPlugin/QmsModule.mm +221 -0
  4. package/ios/QmsPlugin/UIViewController+QmsAnalytics.m +292 -19
  5. package/ios/QmsPluginFramework.xcframework/Info.plist +5 -5
  6. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Assets.car +0 -0
  7. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Info.plist +0 -0
  8. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Montserrat-Bold.ttf +0 -0
  9. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Montserrat-Medium.ttf +0 -0
  10. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Montserrat-Regular.ttf +0 -0
  11. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/QmsPluginFramework +0 -0
  12. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/_CodeSignature/CodeResources +36 -3
  13. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Assets.car +0 -0
  14. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Info.plist +0 -0
  15. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-Bold.ttf +0 -0
  16. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-Medium.ttf +0 -0
  17. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-Regular.ttf +0 -0
  18. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/QmsPluginFramework +0 -0
  19. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/_CodeSignature/CodeResources +36 -3
  20. package/lib/module/QmsDashboardView.android.js +16 -16
  21. package/lib/module/QmsDashboardView.android.js.map +1 -1
  22. package/lib/module/QmsDashboardView.ios.js +38 -12
  23. package/lib/module/QmsDashboardView.ios.js.map +1 -1
  24. package/lib/typescript/src/QmsDashboardView.ios.d.ts.map +1 -1
  25. package/package.json +1 -1
  26. package/src/QmsDashboardView.android.tsx +16 -16
  27. package/src/QmsDashboardView.ios.tsx +64 -25
@@ -75,7 +75,7 @@ dependencies {
75
75
  implementation "com.facebook.react:react-android"
76
76
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
77
77
  implementation 'com.facebook.react:react-native:+'
78
- implementation "com.convep.qms:qms-plugin:1.12.17"
78
+ implementation "com.convep.qms:qms-plugin:1.13.37"
79
79
 
80
80
  implementation 'androidx.appcompat:appcompat:1.6.1'
81
81
 
@@ -3,6 +3,12 @@
3
3
  #import <QmsPluginFramework/QmsPluginFramework.h>
4
4
  #import <QmsPluginFramework/QmsPlugin.h>
5
5
 
6
+ static NSString * const kQmsThemePrimaryColorKey = @"QmsThemePrimaryColor";
7
+ static NSString * const kQmsThemeHeaderTextColorKey = @"QmsThemeHeaderTextColor";
8
+ static NSString * const kQmsThemeAccentColorKey = @"QmsThemeAccentColor";
9
+ static NSString * const kQmsThemeFontFamilyKey = @"QmsThemeFontFamily";
10
+ static NSString * const kQmsRoutePayloadNotification = @"QmsRoutePayloadNotification";
11
+
6
12
  @interface QmsDashboardView : UIView
7
13
  @property (nonatomic, strong) UIViewController *dashboardVC;
8
14
  @property (nonatomic, assign) BOOL didSetupVC;
@@ -10,18 +16,51 @@
10
16
  @property (nonatomic, copy) NSString *ClientID;
11
17
  @property (nonatomic, copy) NSString *ClientCode;
12
18
  @property (nonatomic, copy) NSString *user_token;
19
+ @property (nonatomic, copy) NSString *themeColor;
13
20
  @property (nonatomic, copy) NSString *headerTextColor;
14
21
  @property (nonatomic, copy) NSString *headerIconColor;
15
22
  @property (nonatomic, copy) NSString *accentColor;
16
23
  @property (nonatomic, copy) NSString *fontFamily;
24
+ @property (nonatomic, assign) BOOL isOrigin;
25
+ @property (nonatomic, copy) NSDictionary *payload;
26
+ @property (nonatomic, assign) BOOL didRouteInitialPayload;
17
27
 
18
28
  @property (nonatomic, copy) RCTDirectEventBlock onClose;
29
+ @property (nonatomic, copy) RCTDirectEventBlock onLogout;
19
30
  @property (nonatomic, copy) RCTDirectEventBlock onAnalyticsScreen;
20
31
  @property (nonatomic, copy) RCTDirectEventBlock onAnalyticsEvent;
21
32
  @end
22
33
 
23
34
  @implementation QmsDashboardView
24
35
 
36
+ - (void)persistThemeDefaults {
37
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
38
+ if (_themeColor.length > 0) [defaults setObject:_themeColor forKey:kQmsThemePrimaryColorKey];
39
+ else [defaults removeObjectForKey:kQmsThemePrimaryColorKey];
40
+
41
+ if (_headerTextColor.length > 0) [defaults setObject:_headerTextColor forKey:kQmsThemeHeaderTextColorKey];
42
+ else [defaults removeObjectForKey:kQmsThemeHeaderTextColorKey];
43
+
44
+ if (_accentColor.length > 0) [defaults setObject:_accentColor forKey:kQmsThemeAccentColorKey];
45
+ else [defaults removeObjectForKey:kQmsThemeAccentColorKey];
46
+
47
+ if (_fontFamily.length > 0) [defaults setObject:_fontFamily forKey:kQmsThemeFontFamilyKey];
48
+ else [defaults removeObjectForKey:kQmsThemeFontFamilyKey];
49
+ }
50
+
51
+ - (void)applyAllThemeImmediatelyIfNeeded {
52
+ if (!_dashboardVC || !_dashboardVC.view) return;
53
+ dispatch_async(dispatch_get_main_queue(), ^{
54
+ [self persistThemeDefaults];
55
+ [self applyThemeColorIfNeeded];
56
+ [self applyHeaderThemeIfNeeded];
57
+ [self applyAccentColorIfNeeded];
58
+ [self applyFontFamilyIfNeeded];
59
+ [_dashboardVC.view setNeedsLayout];
60
+ [_dashboardVC.view layoutIfNeeded];
61
+ });
62
+ }
63
+
25
64
  - (instancetype)initWithFrame:(CGRect)frame {
26
65
  if (self = [super initWithFrame:frame]) {
27
66
  _dashboardVC = [QmsPluginUI makeViewController];
@@ -41,6 +80,14 @@
41
80
  selector:@selector(handleAnalyticsEvent:)
42
81
  name:@"QmsAnalyticsEventNotification"
43
82
  object:nil];
83
+ [[NSNotificationCenter defaultCenter] addObserver:self
84
+ selector:@selector(handleDashboardLogout:)
85
+ name:@"QmsDashboardDidLogoutNotification"
86
+ object:nil];
87
+ [[NSNotificationCenter defaultCenter] addObserver:self
88
+ selector:@selector(handleRoutePayloadNotification:)
89
+ name:kQmsRoutePayloadNotification
90
+ object:nil];
44
91
  }
45
92
  return self;
46
93
  }
@@ -50,6 +97,7 @@
50
97
  if (self.window && !_didSetupVC) {
51
98
  UIViewController *parentVC = [UIApplication sharedApplication].keyWindow.rootViewController;
52
99
  if (parentVC) {
100
+ [self persistThemeDefaults];
53
101
 
54
102
  // 1️⃣ create VC with props
55
103
  _dashboardVC = [QmsPluginUI makeViewController];
@@ -63,6 +111,8 @@
63
111
 
64
112
  // 2️⃣ forward props again if needed
65
113
  [self forwardPropsToVC];
114
+ [self routePayloadIfNeeded:self.payload force:NO];
115
+ [self applyAllThemeImmediatelyIfNeeded];
66
116
  }
67
117
  }
68
118
  }
@@ -89,6 +139,12 @@
89
139
  }
90
140
  }
91
141
 
142
+ - (void)handleDashboardLogout:(NSNotification *)note {
143
+ if (self.onLogout) {
144
+ self.onLogout(note.userInfo ?: @{});
145
+ }
146
+ }
147
+
92
148
  - (void)cleanupDashboard {
93
149
  if (!_dashboardVC) return;
94
150
 
@@ -116,27 +172,51 @@
116
172
  [self forwardProp:@"user_token" value:_user_token];
117
173
  }
118
174
 
175
+ - (void)setThemeColor:(NSString *)themeColor {
176
+ _themeColor = [themeColor copy];
177
+ [self persistThemeDefaults];
178
+ [self forwardProp:@"themeColor" value:_themeColor];
179
+ [self applyAllThemeImmediatelyIfNeeded];
180
+ }
181
+
119
182
  - (void)setHeaderTextColor:(NSString *)headerTextColor {
120
183
  _headerTextColor = [headerTextColor copy];
184
+ [self persistThemeDefaults];
121
185
  [self forwardProp:@"headerTextColor" value:_headerTextColor];
186
+ [self applyAllThemeImmediatelyIfNeeded];
122
187
  }
123
188
 
124
189
  - (void)setHeaderIconColor:(NSString *)headerIconColor {
125
190
  _headerIconColor = [headerIconColor copy];
126
191
  [self forwardProp:@"headerIconColor" value:_headerIconColor];
192
+ [self applyAllThemeImmediatelyIfNeeded];
127
193
  }
128
194
 
129
195
  - (void)setAccentColor:(NSString *)accentColor {
130
196
  _accentColor = [accentColor copy];
197
+ [self persistThemeDefaults];
131
198
  [self forwardProp:@"accentColor" value:_accentColor];
199
+ [self applyAllThemeImmediatelyIfNeeded];
132
200
  }
133
201
 
134
202
  - (void)setFontFamily:(NSString *)fontFamily {
135
203
  _fontFamily = [fontFamily copy];
204
+ [self persistThemeDefaults];
136
205
  [self forwardProp:@"fontFamily" value:_fontFamily];
206
+ [self applyAllThemeImmediatelyIfNeeded];
207
+ }
208
+
209
+ - (void)setIsOrigin:(BOOL)isOrigin {
210
+ _isOrigin = isOrigin;
211
+ [self forwardProp:@"isOrigin" value:@(isOrigin)];
212
+ }
213
+
214
+ - (void)setPayload:(NSDictionary *)payload {
215
+ _payload = [payload copy];
216
+ [self routePayloadIfNeeded:_payload force:NO];
137
217
  }
138
218
  #pragma mark - Forward Props via KVC
139
- - (void)forwardProp:(NSString *)key value:(NSString *)value {
219
+ - (void)forwardProp:(NSString *)key value:(id)value {
140
220
  if (_dashboardVC && [_dashboardVC respondsToSelector:NSSelectorFromString(key)]) {
141
221
  @try {
142
222
  [_dashboardVC setValue:value forKey:key];
@@ -150,10 +230,315 @@
150
230
  [self forwardProp:@"ClientID" value:_ClientID];
151
231
  [self forwardProp:@"ClientCode" value:_ClientCode];
152
232
  [self forwardProp:@"user_token" value:_user_token];
233
+ [self forwardProp:@"themeColor" value:_themeColor];
153
234
  [self forwardProp:@"headerTextColor" value:_headerTextColor];
154
235
  [self forwardProp:@"headerIconColor" value:_headerIconColor];
155
236
  [self forwardProp:@"accentColor" value:_accentColor];
156
237
  [self forwardProp:@"fontFamily" value:_fontFamily];
238
+ [self forwardProp:@"isOrigin" value:@(_isOrigin)];
239
+ [self applyAllThemeImmediatelyIfNeeded];
240
+ }
241
+
242
+ - (NSString *)stringFromPayload:(NSDictionary *)payload key:(NSString *)key {
243
+ id value = payload[key];
244
+ if ((!value || value == [NSNull null]) && [payload[@"data"] isKindOfClass:[NSDictionary class]]) {
245
+ value = payload[@"data"][key];
246
+ }
247
+ if (!value || value == [NSNull null]) return nil;
248
+ return [value description];
249
+ }
250
+
251
+ - (BOOL)isIssueType:(NSString *)type {
252
+ static NSSet<NSString *> *types;
253
+ static dispatch_once_t onceToken;
254
+ dispatch_once(&onceToken, ^{
255
+ types = [NSSet setWithArray:@[
256
+ @"new_issue", @"new_info", @"accept_issue", @"reject_issue", @"decline_issue",
257
+ @"start_fix", @"redo", @"completed", @"close_internal", @"close_external",
258
+ @"pending_owner_acceptance", @"assign_issue", @"reassign_issue", @"poa_reminder",
259
+ @"auto_close_reminder", @"pending_access_reminder", @"overdue_reminder",
260
+ @"issue_summary_reminder", @"lodge_summary_reminder", @"overdue_summary_reminder"
261
+ ]];
262
+ });
263
+ return type.length > 0 && [types containsObject:type];
264
+ }
265
+
266
+ - (BOOL)isAppointmentType:(NSString *)type {
267
+ return [type isEqualToString:@"appointment_reminder"] ||
268
+ [type isEqualToString:@"unattend_appt_reminder"];
269
+ }
270
+
271
+ - (void)applyPayloadContext:(NSDictionary *)payload {
272
+ Class apiConfig = NSClassFromString(@"APIConfig");
273
+ if (!apiConfig) return;
274
+
275
+ NSString *projectId = [self stringFromPayload:payload key:@"project_id"];
276
+ NSString *planId = [self stringFromPayload:payload key:@"plan_id"];
277
+
278
+ if (projectId.length > 0 && [apiConfig respondsToSelector:NSSelectorFromString(@"setProjectId:")]) {
279
+ [apiConfig performSelector:NSSelectorFromString(@"setProjectId:") withObject:projectId];
280
+ }
281
+ if (planId.length > 0 && [apiConfig respondsToSelector:NSSelectorFromString(@"setDrawingPlanId:")]) {
282
+ [apiConfig performSelector:NSSelectorFromString(@"setDrawingPlanId:") withObject:planId];
283
+ }
284
+ }
285
+
286
+ - (UINavigationController *)mainNavigationController {
287
+ id mainVC = nil;
288
+ @try {
289
+ mainVC = [self.dashboardVC valueForKey:@"mainViewController"];
290
+ } @catch (__unused NSException *exception) {}
291
+
292
+ if ([mainVC isKindOfClass:[UINavigationController class]]) {
293
+ return (UINavigationController *)mainVC;
294
+ }
295
+ if ([self.dashboardVC isKindOfClass:[UINavigationController class]]) {
296
+ return (UINavigationController *)self.dashboardVC;
297
+ }
298
+ return self.dashboardVC.navigationController;
299
+ }
300
+
301
+ - (void)pushViewControllerNamed:(NSString *)className {
302
+ Class cls = NSClassFromString(className);
303
+ if (!cls) return;
304
+ UIViewController *vc = [[cls alloc] init];
305
+ if (!vc) return;
306
+ UINavigationController *nav = [self mainNavigationController];
307
+ if (nav) {
308
+ [nav pushViewController:vc animated:YES];
309
+ }
310
+ }
311
+
312
+ - (void)pushIssuesViewControllerWithPayload:(NSDictionary *)payload {
313
+ Class cls = NSClassFromString(@"IssuesViewController");
314
+ if (!cls) return;
315
+ UIViewController *vc = [[cls alloc] init];
316
+ if (!vc) return;
317
+ NSString *issueId = [self stringFromPayload:payload key:@"issue_id"];
318
+ if (issueId.length > 0 && [vc respondsToSelector:NSSelectorFromString(@"setDeepLinkIssueId:")]) {
319
+ @try {
320
+ [vc setValue:issueId forKey:@"deepLinkIssueId"];
321
+ } @catch (__unused NSException *exception) {}
322
+ }
323
+ UINavigationController *nav = [self mainNavigationController];
324
+ if (nav) {
325
+ [nav pushViewController:vc animated:YES];
326
+ }
327
+ }
328
+
329
+ - (void)routePayloadIfNeeded:(NSDictionary *)payload force:(BOOL)force {
330
+ if (![payload isKindOfClass:[NSDictionary class]] || payload.count == 0) return;
331
+ if (!_didSetupVC || !self.dashboardVC) return;
332
+ if (_didRouteInitialPayload && !force) return;
333
+
334
+ _didRouteInitialPayload = YES;
335
+ [self applyPayloadContext:payload];
336
+
337
+ NSString *type = [self stringFromPayload:payload key:@"type"];
338
+ NSLog(@"🧭 routePayloadIfNeeded type=%@ issue_id=%@ plan_id=%@ project_id=%@",
339
+ type ?: @"", [self stringFromPayload:payload key:@"issue_id"] ?: @"",
340
+ [self stringFromPayload:payload key:@"plan_id"] ?: @"",
341
+ [self stringFromPayload:payload key:@"project_id"] ?: @"");
342
+ if ([self isIssueType:type]) {
343
+ NSString *issueId = [self stringFromPayload:payload key:@"issue_id"];
344
+ if (issueId.length > 0) {
345
+ NSLog(@"🧭 routing issue deep link to IssuesViewController issue_id=%@", issueId);
346
+ [self pushIssuesViewControllerWithPayload:payload];
347
+ } else {
348
+ NSLog(@"🧭 routing issue type without issue_id to IssuesViewController");
349
+ [self pushViewControllerNamed:@"IssuesViewController"];
350
+ }
351
+ } else if ([self isAppointmentType:type]) {
352
+ NSLog(@"🧭 routing appointment type to AppointmentViewController");
353
+ [self pushViewControllerNamed:@"AppointmentViewController"];
354
+ } else if ([type isEqualToString:@"clearance_letter"]) {
355
+ NSLog(@"🧭 routing clearance_letter to GeneralInfoViewController");
356
+ [self pushViewControllerNamed:@"GeneralInfoViewController"];
357
+ } else {
358
+ NSLog(@"🧭 no route matched for type=%@", type ?: @"");
359
+ }
360
+ }
361
+
362
+ - (void)handleRoutePayloadNotification:(NSNotification *)note {
363
+ NSDictionary *payload = [note.userInfo isKindOfClass:[NSDictionary class]] ? note.userInfo : nil;
364
+ [self routePayloadIfNeeded:payload force:YES];
365
+ }
366
+
367
+ - (UIColor *)qms_colorFromHexString:(NSString *)hexString {
368
+ if (hexString.length == 0) return nil;
369
+ NSString *value = [[hexString stringByTrimmingCharactersInSet:
370
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]]
371
+ uppercaseString];
372
+ if ([value hasPrefix:@"#"]) value = [value substringFromIndex:1];
373
+ if (value.length == 3) {
374
+ unichar c0 = [value characterAtIndex:0];
375
+ unichar c1 = [value characterAtIndex:1];
376
+ unichar c2 = [value characterAtIndex:2];
377
+ value = [NSString stringWithFormat:@"%C%C%C%C%C%C", c0, c0, c1, c1, c2, c2];
378
+ }
379
+ if (value.length != 6 && value.length != 8) return nil;
380
+
381
+ unsigned int hex = 0;
382
+ [[NSScanner scannerWithString:value] scanHexInt:&hex];
383
+
384
+ CGFloat a = 1.0;
385
+ CGFloat r = 0, g = 0, b = 0;
386
+ if (value.length == 8) {
387
+ a = ((hex >> 24) & 0xFF) / 255.0;
388
+ r = ((hex >> 16) & 0xFF) / 255.0;
389
+ g = ((hex >> 8) & 0xFF) / 255.0;
390
+ b = (hex & 0xFF) / 255.0;
391
+ } else {
392
+ r = ((hex >> 16) & 0xFF) / 255.0;
393
+ g = ((hex >> 8) & 0xFF) / 255.0;
394
+ b = (hex & 0xFF) / 255.0;
395
+ }
396
+ return [UIColor colorWithRed:r green:g blue:b alpha:a];
397
+ }
398
+
399
+ - (void)applyHeaderThemeRecursivelyOnView:(UIView *)view textColor:(UIColor *)textColor iconColor:(UIColor *)iconColor {
400
+ if (!view) return;
401
+
402
+ if (textColor && [view isKindOfClass:[UILabel class]]) {
403
+ UILabel *label = (UILabel *)view;
404
+ CGPoint originInRoot = [label.superview convertPoint:label.frame.origin toView:_dashboardVC.view];
405
+ if (originInRoot.y <= 120) {
406
+ label.textColor = textColor;
407
+ }
408
+ }
409
+
410
+ if (iconColor && [view isKindOfClass:[UIButton class]]) {
411
+ UIButton *button = (UIButton *)view;
412
+ CGPoint originInRoot = [button.superview convertPoint:button.frame.origin toView:_dashboardVC.view];
413
+ if (originInRoot.y <= 120) {
414
+ button.tintColor = iconColor;
415
+ [button setTitleColor:iconColor forState:UIControlStateNormal];
416
+ }
417
+ }
418
+
419
+ for (UIView *subview in view.subviews) {
420
+ [self applyHeaderThemeRecursivelyOnView:subview textColor:textColor iconColor:iconColor];
421
+ }
422
+ }
423
+
424
+ - (void)applyHeaderThemeIfNeeded {
425
+ if (!_dashboardVC || !_dashboardVC.view) return;
426
+ UIColor *text = [self qms_colorFromHexString:_headerTextColor];
427
+ UIColor *icon = [self qms_colorFromHexString:_headerIconColor];
428
+ if (!text && !icon) return;
429
+ [self applyHeaderThemeRecursivelyOnView:_dashboardVC.view textColor:text iconColor:icon];
430
+ }
431
+
432
+ - (BOOL)isDefaultQmsBlueColor:(UIColor *)color {
433
+ if (!color) return NO;
434
+ CGFloat r = 0, g = 0, b = 0, a = 0;
435
+ if (![color getRed:&r green:&g blue:&b alpha:&a]) return NO;
436
+ if (a < 0.01) return NO;
437
+
438
+ BOOL closeToBlueA = (fabs(r - 0.0) < 0.12 && fabs(g - 0.50) < 0.16 && fabs(b - 0.60) < 0.16);
439
+ BOOL closeToBlueB = (fabs(r - 0.0) < 0.12 && fabs(g - 0.43) < 0.16 && fabs(b - 0.55) < 0.16);
440
+ return closeToBlueA || closeToBlueB;
441
+ }
442
+
443
+ - (void)applyThemeRecursivelyOnView:(UIView *)view color:(UIColor *)themeColor {
444
+ if (!view || !themeColor) return;
445
+
446
+ CGPoint originInRoot = [view.superview convertPoint:view.frame.origin toView:_dashboardVC.view];
447
+ BOOL nearHeader = originInRoot.y <= 140.0 && view.bounds.size.height <= 120.0;
448
+ if (nearHeader && [self isDefaultQmsBlueColor:view.backgroundColor]) {
449
+ view.backgroundColor = themeColor;
450
+ }
451
+
452
+ if ([view isKindOfClass:[UIButton class]]) {
453
+ UIButton *button = (UIButton *)view;
454
+ if ([self isDefaultQmsBlueColor:button.backgroundColor]) {
455
+ button.backgroundColor = themeColor;
456
+ }
457
+ }
458
+
459
+ for (UIView *subview in view.subviews) {
460
+ [self applyThemeRecursivelyOnView:subview color:themeColor];
461
+ }
462
+ }
463
+
464
+ - (void)applyThemeColorIfNeeded {
465
+ if (!_dashboardVC || !_dashboardVC.view) return;
466
+ UIColor *theme = [self qms_colorFromHexString:_themeColor];
467
+ if (!theme) return;
468
+ [self applyThemeRecursivelyOnView:_dashboardVC.view color:theme];
469
+ }
470
+
471
+ - (void)applyAccentRecursivelyOnView:(UIView *)view color:(UIColor *)color {
472
+ if (!view || !color) return;
473
+
474
+ if ([view isKindOfClass:[UIButton class]]) {
475
+ UIButton *button = (UIButton *)view;
476
+ button.tintColor = color;
477
+ } else if ([view isKindOfClass:[UITextField class]]) {
478
+ ((UITextField *)view).tintColor = color;
479
+ } else if ([view isKindOfClass:[UITextView class]]) {
480
+ ((UITextView *)view).tintColor = color;
481
+ } else if ([view isKindOfClass:[UISwitch class]]) {
482
+ ((UISwitch *)view).onTintColor = color;
483
+ } else if ([view isKindOfClass:[UISlider class]]) {
484
+ ((UISlider *)view).minimumTrackTintColor = color;
485
+ } else if ([view isKindOfClass:[UIPageControl class]]) {
486
+ ((UIPageControl *)view).currentPageIndicatorTintColor = color;
487
+ }
488
+
489
+ for (UIView *subview in view.subviews) {
490
+ [self applyAccentRecursivelyOnView:subview color:color];
491
+ }
492
+ }
493
+
494
+ - (void)applyAccentColorIfNeeded {
495
+ if (!_dashboardVC || !_dashboardVC.view) return;
496
+ UIColor *accent = [self qms_colorFromHexString:_accentColor];
497
+ if (!accent) return;
498
+ [self applyAccentRecursivelyOnView:_dashboardVC.view color:accent];
499
+ }
500
+
501
+ - (UIFont *)fontByPreservingStyleFromFont:(UIFont *)sourceFont {
502
+ if (sourceFont == nil || _fontFamily.length == 0) return sourceFont;
503
+
504
+ UIFont *base = [UIFont fontWithName:_fontFamily size:sourceFont.pointSize];
505
+ if (!base) return sourceFont;
506
+
507
+ UIFontDescriptorSymbolicTraits traits = sourceFont.fontDescriptor.symbolicTraits;
508
+ UIFontDescriptor *descriptor = [base.fontDescriptor fontDescriptorWithSymbolicTraits:traits];
509
+ if (descriptor) {
510
+ return [UIFont fontWithDescriptor:descriptor size:sourceFont.pointSize];
511
+ }
512
+ return base;
513
+ }
514
+
515
+ - (void)applyFontFamilyRecursivelyOnView:(UIView *)view {
516
+ if (!view) return;
517
+
518
+ if ([view isKindOfClass:[UILabel class]]) {
519
+ UILabel *label = (UILabel *)view;
520
+ label.font = [self fontByPreservingStyleFromFont:label.font];
521
+ } else if ([view isKindOfClass:[UIButton class]]) {
522
+ UIButton *button = (UIButton *)view;
523
+ if (button.titleLabel) {
524
+ button.titleLabel.font = [self fontByPreservingStyleFromFont:button.titleLabel.font];
525
+ }
526
+ } else if ([view isKindOfClass:[UITextField class]]) {
527
+ UITextField *textField = (UITextField *)view;
528
+ textField.font = [self fontByPreservingStyleFromFont:textField.font];
529
+ } else if ([view isKindOfClass:[UITextView class]]) {
530
+ UITextView *textView = (UITextView *)view;
531
+ textView.font = [self fontByPreservingStyleFromFont:textView.font];
532
+ }
533
+
534
+ for (UIView *subview in view.subviews) {
535
+ [self applyFontFamilyRecursivelyOnView:subview];
536
+ }
537
+ }
538
+
539
+ - (void)applyFontFamilyIfNeeded {
540
+ if (_fontFamily.length == 0 || !_dashboardVC || !_dashboardVC.view) return;
541
+ [self applyFontFamilyRecursivelyOnView:_dashboardVC.view];
157
542
  }
158
543
 
159
544
  - (void)removeFromSuperview {
@@ -178,10 +563,14 @@ RCT_EXPORT_MODULE(QmsDashboardView)
178
563
  RCT_EXPORT_VIEW_PROPERTY(ClientID, NSString)
179
564
  RCT_EXPORT_VIEW_PROPERTY(ClientCode, NSString)
180
565
  RCT_EXPORT_VIEW_PROPERTY(user_token, NSString)
566
+ RCT_EXPORT_VIEW_PROPERTY(themeColor, NSString)
181
567
  RCT_EXPORT_VIEW_PROPERTY(headerTextColor, NSString)
182
568
  RCT_EXPORT_VIEW_PROPERTY(headerIconColor, NSString)
183
569
  RCT_EXPORT_VIEW_PROPERTY(accentColor, NSString)
184
570
  RCT_EXPORT_VIEW_PROPERTY(fontFamily, NSString)
571
+ RCT_EXPORT_VIEW_PROPERTY(isOrigin, BOOL)
572
+ RCT_EXPORT_VIEW_PROPERTY(payload, NSDictionary)
573
+ RCT_EXPORT_VIEW_PROPERTY(onLogout, RCTDirectEventBlock)
185
574
  RCT_EXPORT_VIEW_PROPERTY(onAnalyticsScreen, RCTDirectEventBlock)
186
575
  RCT_EXPORT_VIEW_PROPERTY(onAnalyticsEvent, RCTDirectEventBlock)
187
576
 
@@ -221,12 +610,23 @@ RCT_EXPORT_MODULE();
221
610
  selector:@selector(handleAnalyticsEvent:)
222
611
  name:@"QmsAnalyticsEventNotification"
223
612
  object:nil];
613
+ [[NSNotificationCenter defaultCenter] addObserver:self
614
+ selector:@selector(handleDashboardLogout:)
615
+ name:@"QmsDashboardDidLogoutNotification"
616
+ object:nil];
224
617
  }
225
618
  return self;
226
619
  }
227
620
 
228
621
  - (NSArray<NSString *> *)supportedEvents {
229
- return @[@"onClose", @"onAnalyticsScreen", @"onAnalyticsEvent"];
622
+ return @[
623
+ @"onClose",
624
+ @"onLogout",
625
+ @"onAnalyticsScreen",
626
+ @"onAnalyticsEvent",
627
+ @"QmsSdkOnAnalyticsScreen",
628
+ @"QmsSdkOnAnalyticsEvent"
629
+ ];
230
630
  }
231
631
 
232
632
  - (void)handleDashboardClose:(NSNotification *)note {
@@ -236,10 +636,16 @@ RCT_EXPORT_MODULE();
236
636
 
237
637
  - (void)handleAnalyticsScreen:(NSNotification *)note {
238
638
  [self sendEventWithName:@"onAnalyticsScreen" body:note.userInfo];
639
+ [self sendEventWithName:@"QmsSdkOnAnalyticsScreen" body:note.userInfo];
239
640
  }
240
641
 
241
642
  - (void)handleAnalyticsEvent:(NSNotification *)note {
242
643
  [self sendEventWithName:@"onAnalyticsEvent" body:note.userInfo];
644
+ [self sendEventWithName:@"QmsSdkOnAnalyticsEvent" body:note.userInfo];
645
+ }
646
+
647
+ - (void)handleDashboardLogout:(NSNotification *)note {
648
+ [self sendEventWithName:@"onLogout" body:note.userInfo];
243
649
  }
244
650
 
245
651
  - (void)dealloc {