@convep_mobilogy/react-native-qms-plugin 0.10.5 → 0.11.0

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/build.gradle +1 -1
  2. package/ios/QmsPlugin/QmsDashboardViewManager.mm +64 -1
  3. package/ios/QmsPlugin/QmsModule.mm +19 -2
  4. package/ios/QmsPlugin/QmsThemeRuntime.h +22 -0
  5. package/ios/QmsPlugin/QmsThemeRuntime.m +39 -0
  6. package/ios/QmsPlugin/UIViewController+QmsAnalytics.m +148 -7
  7. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/Assets.car +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 +68 -2
  13. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/bm.json +780 -0
  14. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/cn.json +781 -0
  15. package/ios/QmsPluginFramework.xcframework/ios-arm64/QmsPluginFramework.framework/en.json +781 -0
  16. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Assets.car +0 -0
  17. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-Bold.ttf +0 -0
  18. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-Medium.ttf +0 -0
  19. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/Montserrat-Regular.ttf +0 -0
  20. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/QmsPluginFramework +0 -0
  21. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/_CodeSignature/CodeResources +68 -2
  22. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/bm.json +780 -0
  23. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/cn.json +781 -0
  24. package/ios/QmsPluginFramework.xcframework/ios-arm64_x86_64-simulator/QmsPluginFramework.framework/en.json +781 -0
  25. package/lib/module/QmsDashboardView.android.js +16 -16
  26. package/lib/module/QmsDashboardView.android.js.map +1 -1
  27. package/lib/module/QmsDashboardView.ios.js +12 -2
  28. package/lib/module/QmsDashboardView.ios.js.map +1 -1
  29. package/lib/typescript/src/QmsDashboardView.ios.d.ts.map +1 -1
  30. package/package.json +1 -1
  31. package/src/QmsDashboardView.android.tsx +16 -16
  32. package/src/QmsDashboardView.ios.tsx +28 -2
@@ -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.14.0"
79
79
 
80
80
  implementation 'androidx.appcompat:appcompat:1.6.1'
81
81
 
@@ -241,6 +241,9 @@ static NSString * const kQmsRoutePayloadNotification = @"QmsRoutePayloadNotifica
241
241
 
242
242
  - (NSString *)stringFromPayload:(NSDictionary *)payload key:(NSString *)key {
243
243
  id value = payload[key];
244
+ if ((!value || value == [NSNull null]) && [payload[@"data"] isKindOfClass:[NSDictionary class]]) {
245
+ value = payload[@"data"][key];
246
+ }
244
247
  if (!value || value == [NSNull null]) return nil;
245
248
  return [value description];
246
249
  }
@@ -306,6 +309,23 @@ static NSString * const kQmsRoutePayloadNotification = @"QmsRoutePayloadNotifica
306
309
  }
307
310
  }
308
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
+
309
329
  - (void)routePayloadIfNeeded:(NSDictionary *)payload force:(BOOL)force {
310
330
  if (![payload isKindOfClass:[NSDictionary class]] || payload.count == 0) return;
311
331
  if (!_didSetupVC || !self.dashboardVC) return;
@@ -315,12 +335,27 @@ static NSString * const kQmsRoutePayloadNotification = @"QmsRoutePayloadNotifica
315
335
  [self applyPayloadContext:payload];
316
336
 
317
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"] ?: @"");
318
342
  if ([self isIssueType:type]) {
319
- [self pushViewControllerNamed:@"IssuesViewController"];
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
+ }
320
351
  } else if ([self isAppointmentType:type]) {
352
+ NSLog(@"🧭 routing appointment type to AppointmentViewController");
321
353
  [self pushViewControllerNamed:@"AppointmentViewController"];
322
354
  } else if ([type isEqualToString:@"clearance_letter"]) {
355
+ NSLog(@"🧭 routing clearance_letter to GeneralInfoViewController");
323
356
  [self pushViewControllerNamed:@"GeneralInfoViewController"];
357
+ } else {
358
+ NSLog(@"🧭 no route matched for type=%@", type ?: @"");
324
359
  }
325
360
  }
326
361
 
@@ -426,11 +461,39 @@ static NSString * const kQmsRoutePayloadNotification = @"QmsRoutePayloadNotifica
426
461
  }
427
462
  }
428
463
 
464
+ - (void)applyDashboardHeaderOverlayTintRecursivelyOnView:(UIView *)view color:(UIColor *)themeColor {
465
+ if (!view || !themeColor || !_dashboardVC || !_dashboardVC.view) return;
466
+
467
+ if ([view isKindOfClass:[UIImageView class]]) {
468
+ UIImageView *imageView = (UIImageView *)view;
469
+ CGPoint originInRoot = [imageView.superview convertPoint:imageView.frame.origin toView:_dashboardVC.view];
470
+ CGFloat rootWidth = CGRectGetWidth(_dashboardVC.view.bounds);
471
+ BOOL isHeaderBand = originInRoot.y <= 220.0 &&
472
+ CGRectGetHeight(imageView.bounds) >= 56.0 &&
473
+ CGRectGetHeight(imageView.bounds) <= 120.0 &&
474
+ CGRectGetWidth(imageView.bounds) >= (rootWidth * 0.75) &&
475
+ imageView.contentMode == UIViewContentModeScaleToFill &&
476
+ !imageView.userInteractionEnabled;
477
+ if (isHeaderBand && imageView.image) {
478
+ UIImage *templated = [imageView.image imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
479
+ if (templated != imageView.image) {
480
+ imageView.image = templated;
481
+ }
482
+ imageView.tintColor = themeColor;
483
+ }
484
+ }
485
+
486
+ for (UIView *subview in view.subviews) {
487
+ [self applyDashboardHeaderOverlayTintRecursivelyOnView:subview color:themeColor];
488
+ }
489
+ }
490
+
429
491
  - (void)applyThemeColorIfNeeded {
430
492
  if (!_dashboardVC || !_dashboardVC.view) return;
431
493
  UIColor *theme = [self qms_colorFromHexString:_themeColor];
432
494
  if (!theme) return;
433
495
  [self applyThemeRecursivelyOnView:_dashboardVC.view color:theme];
496
+ [self applyDashboardHeaderOverlayTintRecursivelyOnView:_dashboardVC.view color:theme];
434
497
  }
435
498
 
436
499
  - (void)applyAccentRecursivelyOnView:(UIView *)view color:(UIColor *)color {
@@ -3,9 +3,11 @@
3
3
  #import <UIKit/UIKit.h>
4
4
  #import <objc/runtime.h>
5
5
  #import <QmsPluginFramework/QmsPlugin.h>
6
+ #import "QmsThemeRuntime.h"
6
7
 
7
8
  static NSString * const kQmsThemePrimaryColorKey = @"QmsThemePrimaryColor";
8
9
  static NSString * const kQmsThemeHeaderTextColorKey = @"QmsThemeHeaderTextColor";
10
+ static NSString * const kQmsThemeHeaderIconColorKey = @"QmsThemeHeaderIconColor";
9
11
  static NSString * const kQmsThemeAccentColorKey = @"QmsThemeAccentColor";
10
12
  static NSString * const kQmsThemeFontFamilyKey = @"QmsThemeFontFamily";
11
13
  static NSString * const kQmsRoutePayloadNotification = @"QmsRoutePayloadNotification";
@@ -105,30 +107,45 @@ RCT_EXPORT_METHOD(setPrimaryColor:(NSString *)hexColor) {
105
107
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
106
108
  if (hexColor.length > 0) [defaults setObject:hexColor forKey:kQmsThemePrimaryColorKey];
107
109
  else [defaults removeObjectForKey:kQmsThemePrimaryColorKey];
110
+ QmsThemeRuntime *runtime = [QmsThemeRuntime shared];
111
+ runtime.primaryColorHex = (hexColor.length > 0 ? hexColor : nil);
112
+ [runtime notifyThemeChanged];
108
113
  }
109
114
 
110
115
  RCT_EXPORT_METHOD(setHeaderTextColor:(NSString *)hexColor) {
111
116
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
112
117
  if (hexColor.length > 0) [defaults setObject:hexColor forKey:kQmsThemeHeaderTextColorKey];
113
118
  else [defaults removeObjectForKey:kQmsThemeHeaderTextColorKey];
119
+ QmsThemeRuntime *runtime = [QmsThemeRuntime shared];
120
+ runtime.headerTextColorHex = (hexColor.length > 0 ? hexColor : nil);
121
+ [runtime notifyThemeChanged];
114
122
  }
115
123
 
116
124
  RCT_EXPORT_METHOD(setHeaderIconColor:(NSString *)hexColor) {
117
125
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
118
- if (hexColor.length > 0) [defaults setObject:hexColor forKey:kQmsThemeHeaderTextColorKey];
119
- else [defaults removeObjectForKey:kQmsThemeHeaderTextColorKey];
126
+ if (hexColor.length > 0) [defaults setObject:hexColor forKey:kQmsThemeHeaderIconColorKey];
127
+ else [defaults removeObjectForKey:kQmsThemeHeaderIconColorKey];
128
+ QmsThemeRuntime *runtime = [QmsThemeRuntime shared];
129
+ runtime.headerIconColorHex = (hexColor.length > 0 ? hexColor : nil);
130
+ [runtime notifyThemeChanged];
120
131
  }
121
132
 
122
133
  RCT_EXPORT_METHOD(setAccentColor:(NSString *)hexColor) {
123
134
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
124
135
  if (hexColor.length > 0) [defaults setObject:hexColor forKey:kQmsThemeAccentColorKey];
125
136
  else [defaults removeObjectForKey:kQmsThemeAccentColorKey];
137
+ QmsThemeRuntime *runtime = [QmsThemeRuntime shared];
138
+ runtime.accentColorHex = (hexColor.length > 0 ? hexColor : nil);
139
+ [runtime notifyThemeChanged];
126
140
  }
127
141
 
128
142
  RCT_EXPORT_METHOD(setFontFamily:(NSString *)fontFamily) {
129
143
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
130
144
  if (fontFamily.length > 0) [defaults setObject:fontFamily forKey:kQmsThemeFontFamilyKey];
131
145
  else [defaults removeObjectForKey:kQmsThemeFontFamilyKey];
146
+ QmsThemeRuntime *runtime = [QmsThemeRuntime shared];
147
+ runtime.fontFamily = (fontFamily.length > 0 ? fontFamily : nil);
148
+ [runtime notifyThemeChanged];
132
149
  }
133
150
 
134
151
  RCT_EXPORT_METHOD(emitInAppNotification:(NSString *)title body:(NSString *)body) {
@@ -0,0 +1,22 @@
1
+ #import <Foundation/Foundation.h>
2
+
3
+ NS_ASSUME_NONNULL_BEGIN
4
+
5
+ extern NSString * const QmsThemeDidChangeNotification;
6
+
7
+ @interface QmsThemeRuntime : NSObject
8
+
9
+ + (instancetype)shared;
10
+
11
+ @property (nonatomic, copy, nullable) NSString *primaryColorHex;
12
+ @property (nonatomic, copy, nullable) NSString *headerTextColorHex;
13
+ @property (nonatomic, copy, nullable) NSString *headerIconColorHex;
14
+ @property (nonatomic, copy, nullable) NSString *accentColorHex;
15
+ @property (nonatomic, copy, nullable) NSString *fontFamily;
16
+
17
+ - (void)reloadFromDefaults;
18
+ - (void)notifyThemeChanged;
19
+
20
+ @end
21
+
22
+ NS_ASSUME_NONNULL_END
@@ -0,0 +1,39 @@
1
+ #import "QmsThemeRuntime.h"
2
+
3
+ static NSString * const kQmsThemePrimaryColorKey = @"QmsThemePrimaryColor";
4
+ static NSString * const kQmsThemeHeaderTextColorKey = @"QmsThemeHeaderTextColor";
5
+ static NSString * const kQmsThemeHeaderIconColorKey = @"QmsThemeHeaderIconColor";
6
+ static NSString * const kQmsThemeAccentColorKey = @"QmsThemeAccentColor";
7
+ static NSString * const kQmsThemeFontFamilyKey = @"QmsThemeFontFamily";
8
+
9
+ NSString * const QmsThemeDidChangeNotification = @"QmsThemeDidChangeNotification";
10
+
11
+ @implementation QmsThemeRuntime
12
+
13
+ + (instancetype)shared {
14
+ static QmsThemeRuntime *instance = nil;
15
+ static dispatch_once_t onceToken;
16
+ dispatch_once(&onceToken, ^{
17
+ instance = [[QmsThemeRuntime alloc] init];
18
+ [instance reloadFromDefaults];
19
+ });
20
+ return instance;
21
+ }
22
+
23
+ - (void)reloadFromDefaults {
24
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
25
+ self.primaryColorHex = [defaults stringForKey:kQmsThemePrimaryColorKey];
26
+ self.headerTextColorHex = [defaults stringForKey:kQmsThemeHeaderTextColorKey];
27
+ self.headerIconColorHex = [defaults stringForKey:kQmsThemeHeaderIconColorKey];
28
+ self.accentColorHex = [defaults stringForKey:kQmsThemeAccentColorKey];
29
+ self.fontFamily = [defaults stringForKey:kQmsThemeFontFamilyKey];
30
+ }
31
+
32
+ - (void)notifyThemeChanged {
33
+ dispatch_async(dispatch_get_main_queue(), ^{
34
+ [[NSNotificationCenter defaultCenter] postNotificationName:QmsThemeDidChangeNotification
35
+ object:nil];
36
+ });
37
+ }
38
+
39
+ @end
@@ -1,6 +1,7 @@
1
1
  #import <Foundation/Foundation.h>
2
2
  #import <UIKit/UIKit.h>
3
3
  #import <objc/runtime.h>
4
+ #import "QmsThemeRuntime.h"
4
5
 
5
6
  static NSString * const QmsAnalyticsScreenNotification = @"QmsAnalyticsScreenNotification";
6
7
  static NSString * const QmsAnalyticsEventNotification = @"QmsAnalyticsEventNotification";
@@ -9,6 +10,8 @@ static NSString * const QmsThemeHeaderTextColorKey = @"QmsThemeHeaderTextColor";
9
10
  static NSString * const QmsThemeAccentColorKey = @"QmsThemeAccentColor";
10
11
  static NSString * const QmsThemeFontFamilyKey = @"QmsThemeFontFamily";
11
12
  static char QmsStatusBarFillViewKey;
13
+ static char QmsThemeObserverAttachedKey;
14
+ static char QmsThemeGuardTokenKey;
12
15
 
13
16
  static void qms_swizzleInstanceMethod(Class cls, SEL originalSelector, SEL swizzledSelector) {
14
17
  Method originalMethod = class_getInstanceMethod(cls, originalSelector);
@@ -36,16 +39,28 @@ static void qms_swizzleInstanceMethod(Class cls, SEL originalSelector, SEL swizz
36
39
  static dispatch_once_t onceToken;
37
40
  dispatch_once(&onceToken, ^{
38
41
  Class class = [self class];
42
+ qms_swizzleInstanceMethod(class, @selector(viewDidLoad), @selector(qms_viewDidLoad));
39
43
  qms_swizzleInstanceMethod(class, @selector(viewWillAppear:), @selector(qms_viewWillAppear:));
40
44
  qms_swizzleInstanceMethod(class, @selector(viewDidAppear:), @selector(qms_viewDidAppear:));
41
45
  qms_swizzleInstanceMethod(class, @selector(viewDidLayoutSubviews), @selector(qms_viewDidLayoutSubviews));
42
46
  });
43
47
  }
44
48
 
49
+ - (void)qms_viewDidLoad {
50
+ [self qms_viewDidLoad];
51
+ if (![self qms_shouldApplyTheme]) return;
52
+ [self qms_registerThemeObserverIfNeeded];
53
+ [self qms_applyThemeIfNeeded];
54
+ [self qms_startThemeGuardWindow];
55
+ }
56
+
45
57
  - (void)qms_viewWillAppear:(BOOL)animated {
46
58
  [self qms_viewWillAppear:animated];
47
- if (![self qms_shouldTrackAnalytics]) return;
59
+ if (![self qms_shouldApplyTheme]) return;
60
+ [self qms_registerThemeObserverIfNeeded];
48
61
  [self qms_applyThemeIfNeeded];
62
+ [self qms_scheduleThemeReapplyPasses];
63
+ [self qms_startThemeGuardWindow];
49
64
  }
50
65
 
51
66
  - (void)qms_viewDidAppear:(BOOL)animated {
@@ -56,6 +71,8 @@ static void qms_swizzleInstanceMethod(Class cls, SEL originalSelector, SEL swizz
56
71
  }
57
72
 
58
73
  [self qms_applyThemeIfNeeded];
74
+ [self qms_scheduleThemeReapplyPasses];
75
+ [self qms_startThemeGuardWindow];
59
76
 
60
77
  NSString *screenName = NSStringFromClass([self class]);
61
78
  NSString *screenClass = screenName;
@@ -81,9 +98,82 @@ static void qms_swizzleInstanceMethod(Class cls, SEL originalSelector, SEL swizz
81
98
  userInfo:eventInfo];
82
99
  }
83
100
 
101
+ - (void)qms_registerThemeObserverIfNeeded {
102
+ NSNumber *attached = objc_getAssociatedObject(self, &QmsThemeObserverAttachedKey);
103
+ if (attached.boolValue) {
104
+ return;
105
+ }
106
+ [[NSNotificationCenter defaultCenter] addObserver:self
107
+ selector:@selector(qms_handleThemeDidChange:)
108
+ name:QmsThemeDidChangeNotification
109
+ object:nil];
110
+ objc_setAssociatedObject(self, &QmsThemeObserverAttachedKey, @(YES), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
111
+ }
112
+
113
+ - (void)qms_handleThemeDidChange:(NSNotification *)note {
114
+ if (![self qms_shouldApplyTheme]) return;
115
+ if (!self.isViewLoaded) return;
116
+ [self qms_applyThemeIfNeeded];
117
+ [self qms_scheduleThemeReapplyPasses];
118
+ [self qms_startThemeGuardWindow];
119
+ }
120
+
121
+ - (void)qms_scheduleThemeReapplyPasses {
122
+ __weak typeof(self) weakSelf = self;
123
+ NSArray<NSNumber *> *delays = @[@0.02, @0.12, @0.30, @0.55, @1.0, @1.35];
124
+ for (NSNumber *delay in delays) {
125
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay.doubleValue * NSEC_PER_SEC)),
126
+ dispatch_get_main_queue(), ^{
127
+ __strong typeof(weakSelf) strongSelf = weakSelf;
128
+ if (!strongSelf || !strongSelf.isViewLoaded) return;
129
+ if (![strongSelf qms_shouldApplyTheme]) return;
130
+ [strongSelf qms_applyThemeIfNeeded];
131
+ });
132
+ }
133
+ }
134
+
135
+ - (void)qms_startThemeGuardWindow {
136
+ NSNumber *currentToken = objc_getAssociatedObject(self, &QmsThemeGuardTokenKey);
137
+ NSInteger nextToken = currentToken.integerValue + 1;
138
+ objc_setAssociatedObject(self, &QmsThemeGuardTokenKey, @(nextToken), OBJC_ASSOCIATION_RETAIN_NONATOMIC);
139
+ [self qms_runThemeGuardTickWithToken:nextToken elapsed:0.0];
140
+ }
141
+
142
+ - (void)qms_runThemeGuardTickWithToken:(NSInteger)token elapsed:(NSTimeInterval)elapsed {
143
+ NSNumber *activeToken = objc_getAssociatedObject(self, &QmsThemeGuardTokenKey);
144
+ if (activeToken.integerValue != token) {
145
+ return;
146
+ }
147
+ if (![self qms_shouldApplyTheme]) {
148
+ return;
149
+ }
150
+ if (!self.isViewLoaded) {
151
+ NSTimeInterval retryElapsed = elapsed + 0.05;
152
+ if (retryElapsed > 2.2) {
153
+ return;
154
+ }
155
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)),
156
+ dispatch_get_main_queue(), ^{
157
+ [self qms_runThemeGuardTickWithToken:token elapsed:retryElapsed];
158
+ });
159
+ return;
160
+ }
161
+
162
+ [self qms_applyThemeIfNeeded];
163
+
164
+ NSTimeInterval nextElapsed = elapsed + 0.05;
165
+ if (nextElapsed > 2.2) {
166
+ return;
167
+ }
168
+ dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.05 * NSEC_PER_SEC)),
169
+ dispatch_get_main_queue(), ^{
170
+ [self qms_runThemeGuardTickWithToken:token elapsed:nextElapsed];
171
+ });
172
+ }
173
+
84
174
  - (void)qms_viewDidLayoutSubviews {
85
175
  [self qms_viewDidLayoutSubviews];
86
- if (![self qms_shouldTrackAnalytics]) return;
176
+ if (![self qms_shouldApplyTheme]) return;
87
177
  [self qms_applyThemeIfNeeded];
88
178
  }
89
179
 
@@ -199,11 +289,18 @@ static void qms_swizzleInstanceMethod(Class cls, SEL originalSelector, SEL swizz
199
289
  }
200
290
 
201
291
  - (void)qms_applyThemeIfNeeded {
202
- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
203
- UIColor *themeColor = [self qms_colorFromHexString:[defaults stringForKey:QmsThemePrimaryColorKey]];
204
- UIColor *headerTextColor = [self qms_colorFromHexString:[defaults stringForKey:QmsThemeHeaderTextColorKey]];
205
- UIColor *accentColor = [self qms_colorFromHexString:[defaults stringForKey:QmsThemeAccentColorKey]];
206
- NSString *fontFamily = [defaults stringForKey:QmsThemeFontFamilyKey];
292
+ QmsThemeRuntime *runtime = [QmsThemeRuntime shared];
293
+ UIColor *themeColor = [self qms_colorFromHexString:runtime.primaryColorHex];
294
+ UIColor *headerTextColor = [self qms_colorFromHexString:runtime.headerTextColorHex];
295
+ UIColor *accentColor = [self qms_colorFromHexString:runtime.accentColorHex];
296
+ NSString *fontFamily = runtime.fontFamily;
297
+ if (!themeColor && !headerTextColor && !accentColor && fontFamily.length == 0) {
298
+ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
299
+ themeColor = [self qms_colorFromHexString:[defaults stringForKey:QmsThemePrimaryColorKey]];
300
+ headerTextColor = [self qms_colorFromHexString:[defaults stringForKey:QmsThemeHeaderTextColorKey]];
301
+ accentColor = [self qms_colorFromHexString:[defaults stringForKey:QmsThemeAccentColorKey]];
302
+ fontFamily = [defaults stringForKey:QmsThemeFontFamilyKey];
303
+ }
207
304
  if (themeColor) {
208
305
  [self qms_applyPrimaryRecursively:self.view themeColor:themeColor];
209
306
  [self qms_applyForegroundRecursively:self.view
@@ -335,4 +432,48 @@ static void qms_swizzleInstanceMethod(Class cls, SEL originalSelector, SEL swizz
335
432
  return NO;
336
433
  }
337
434
 
435
+ - (BOOL)qms_themeIsConfigured {
436
+ QmsThemeRuntime *runtime = [QmsThemeRuntime shared];
437
+ return runtime.primaryColorHex.length > 0 ||
438
+ runtime.headerTextColorHex.length > 0 ||
439
+ runtime.accentColorHex.length > 0 ||
440
+ runtime.fontFamily.length > 0;
441
+ }
442
+
443
+ - (BOOL)qms_isKnownSdkControllerClassName:(NSString *)className {
444
+ static NSSet<NSString *> *knownClasses;
445
+ static dispatch_once_t onceToken;
446
+ dispatch_once(&onceToken, ^{
447
+ knownClasses = [NSSet setWithArray:@[
448
+ @"DashboardViewController",
449
+ @"UnitViewController",
450
+ @"GeneralInfoViewController",
451
+ @"ProfileViewController",
452
+ @"AppointmentViewController",
453
+ @"AppointmentDetailsViewController",
454
+ @"AppointmentConfirmationViewController",
455
+ @"AppointmentTutorialViewController",
456
+ @"IssuesViewController",
457
+ @"IssueDetailViewController",
458
+ @"AccessItemViewController",
459
+ @"AccessTransactionViewController",
460
+ @"SyncScreenViewController",
461
+ @"FormsListViewController",
462
+ @"AnnouncementViewController",
463
+ @"NotificationViewController"
464
+ ]];
465
+ });
466
+ return [knownClasses containsObject:className];
467
+ }
468
+
469
+ - (BOOL)qms_shouldApplyTheme {
470
+ if (![self qms_themeIsConfigured]) {
471
+ return NO;
472
+ }
473
+ if ([self qms_shouldTrackAnalytics]) {
474
+ return YES;
475
+ }
476
+ return [self qms_isKnownSdkControllerClassName:NSStringFromClass([self class])];
477
+ }
478
+
338
479
  @end
@@ -6,7 +6,7 @@
6
6
  <dict>
7
7
  <key>Assets.car</key>
8
8
  <data>
9
- 00D3s4tCHF95KIOJSPQ7tHGoCaQ=
9
+ kEpAubglJYNFlRT4LSRI2MmXQlg=
10
10
  </data>
11
11
  <key>Headers/QmsPlugin.h</key>
12
12
  <data>
@@ -24,6 +24,30 @@
24
24
  <data>
25
25
  BD9YnWBkeFmDcuOl4cAjALd/7yw=
26
26
  </data>
27
+ <key>Montserrat-Bold.ttf</key>
28
+ <data>
29
+ vnwUOAcKtjfhFxaRXk5QkDcm3Mg=
30
+ </data>
31
+ <key>Montserrat-Medium.ttf</key>
32
+ <data>
33
+ d/NVNBXYWMZTuupFqsqyAZZ3YzQ=
34
+ </data>
35
+ <key>Montserrat-Regular.ttf</key>
36
+ <data>
37
+ 7BPvHyyZ+HgNby6z+fJ++J8wFhs=
38
+ </data>
39
+ <key>bm.json</key>
40
+ <data>
41
+ PWeiaoATJ5Tmok3tqZ2zbqPpvY0=
42
+ </data>
43
+ <key>cn.json</key>
44
+ <data>
45
+ IeWp7CtgjE4S1AXI2avoC5pUwMA=
46
+ </data>
47
+ <key>en.json</key>
48
+ <data>
49
+ X7Dm5XAAyo7V/dbykrhTjHVVkn4=
50
+ </data>
27
51
  </dict>
28
52
  <key>files2</key>
29
53
  <dict>
@@ -31,7 +55,7 @@
31
55
  <dict>
32
56
  <key>hash2</key>
33
57
  <data>
34
- ffZ3DkdMHhszup3FyQDYfqABRKNgMXrwJDCpn/sZDAM=
58
+ 0sUdnIkwFavEcnM4vnqYtEL76ynKxgMhnZSeTzEv99c=
35
59
  </data>
36
60
  </dict>
37
61
  <key>Headers/QmsPlugin.h</key>
@@ -55,6 +79,48 @@
55
79
  e+P6RtiuicqWfZ3H2B43r960DPkFwUWqXKWU+w7OVD0=
56
80
  </data>
57
81
  </dict>
82
+ <key>Montserrat-Bold.ttf</key>
83
+ <dict>
84
+ <key>hash2</key>
85
+ <data>
86
+ f6k9fapcL4gaZycinCEXgt5rdgCPvWNGs9dmTo/IP0M=
87
+ </data>
88
+ </dict>
89
+ <key>Montserrat-Medium.ttf</key>
90
+ <dict>
91
+ <key>hash2</key>
92
+ <data>
93
+ zRYcujo83PaK1yGSD80WTIfnt8yea61RAnjD+jeNkdI=
94
+ </data>
95
+ </dict>
96
+ <key>Montserrat-Regular.ttf</key>
97
+ <dict>
98
+ <key>hash2</key>
99
+ <data>
100
+ 75z5nwF1vvUwuIk0vZBPz1b3c87G/U37jMr3ziu9OV4=
101
+ </data>
102
+ </dict>
103
+ <key>bm.json</key>
104
+ <dict>
105
+ <key>hash2</key>
106
+ <data>
107
+ 66O9IXhz9EzcdoULJI0EQ2d+TxbKeqB/ojI1dKuPF5M=
108
+ </data>
109
+ </dict>
110
+ <key>cn.json</key>
111
+ <dict>
112
+ <key>hash2</key>
113
+ <data>
114
+ iBpiD/sGrHsIjhadlFeetj9OBEd+QOCIlXDBBibjEw4=
115
+ </data>
116
+ </dict>
117
+ <key>en.json</key>
118
+ <dict>
119
+ <key>hash2</key>
120
+ <data>
121
+ S+vrXUroDoLwz8Wj8IbMLzSfNPdBglOGEIJmJos1nrg=
122
+ </data>
123
+ </dict>
58
124
  </dict>
59
125
  <key>rules</key>
60
126
  <dict>