@convep_mobilogy/react-native-qms-plugin 0.10.6 → 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.
@@ -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.13.37"
78
+ implementation "com.convep.qms:qms-plugin:1.14.0"
79
79
 
80
80
  implementation 'androidx.appcompat:appcompat:1.6.1'
81
81
 
@@ -461,11 +461,39 @@ static NSString * const kQmsRoutePayloadNotification = @"QmsRoutePayloadNotifica
461
461
  }
462
462
  }
463
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
+
464
491
  - (void)applyThemeColorIfNeeded {
465
492
  if (!_dashboardVC || !_dashboardVC.view) return;
466
493
  UIColor *theme = [self qms_colorFromHexString:_themeColor];
467
494
  if (!theme) return;
468
495
  [self applyThemeRecursivelyOnView:_dashboardVC.view color:theme];
496
+ [self applyDashboardHeaderOverlayTintRecursivelyOnView:_dashboardVC.view color:theme];
469
497
  }
470
498
 
471
499
  - (void)applyAccentRecursivelyOnView:(UIView *)view color:(UIColor *)color {
@@ -3,6 +3,7 @@
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";
@@ -106,30 +107,45 @@ RCT_EXPORT_METHOD(setPrimaryColor:(NSString *)hexColor) {
106
107
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
107
108
  if (hexColor.length > 0) [defaults setObject:hexColor forKey:kQmsThemePrimaryColorKey];
108
109
  else [defaults removeObjectForKey:kQmsThemePrimaryColorKey];
110
+ QmsThemeRuntime *runtime = [QmsThemeRuntime shared];
111
+ runtime.primaryColorHex = (hexColor.length > 0 ? hexColor : nil);
112
+ [runtime notifyThemeChanged];
109
113
  }
110
114
 
111
115
  RCT_EXPORT_METHOD(setHeaderTextColor:(NSString *)hexColor) {
112
116
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
113
117
  if (hexColor.length > 0) [defaults setObject:hexColor forKey:kQmsThemeHeaderTextColorKey];
114
118
  else [defaults removeObjectForKey:kQmsThemeHeaderTextColorKey];
119
+ QmsThemeRuntime *runtime = [QmsThemeRuntime shared];
120
+ runtime.headerTextColorHex = (hexColor.length > 0 ? hexColor : nil);
121
+ [runtime notifyThemeChanged];
115
122
  }
116
123
 
117
124
  RCT_EXPORT_METHOD(setHeaderIconColor:(NSString *)hexColor) {
118
125
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
119
126
  if (hexColor.length > 0) [defaults setObject:hexColor forKey:kQmsThemeHeaderIconColorKey];
120
127
  else [defaults removeObjectForKey:kQmsThemeHeaderIconColorKey];
128
+ QmsThemeRuntime *runtime = [QmsThemeRuntime shared];
129
+ runtime.headerIconColorHex = (hexColor.length > 0 ? hexColor : nil);
130
+ [runtime notifyThemeChanged];
121
131
  }
122
132
 
123
133
  RCT_EXPORT_METHOD(setAccentColor:(NSString *)hexColor) {
124
134
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
125
135
  if (hexColor.length > 0) [defaults setObject:hexColor forKey:kQmsThemeAccentColorKey];
126
136
  else [defaults removeObjectForKey:kQmsThemeAccentColorKey];
137
+ QmsThemeRuntime *runtime = [QmsThemeRuntime shared];
138
+ runtime.accentColorHex = (hexColor.length > 0 ? hexColor : nil);
139
+ [runtime notifyThemeChanged];
127
140
  }
128
141
 
129
142
  RCT_EXPORT_METHOD(setFontFamily:(NSString *)fontFamily) {
130
143
  NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
131
144
  if (fontFamily.length > 0) [defaults setObject:fontFamily forKey:kQmsThemeFontFamilyKey];
132
145
  else [defaults removeObjectForKey:kQmsThemeFontFamilyKey];
146
+ QmsThemeRuntime *runtime = [QmsThemeRuntime shared];
147
+ runtime.fontFamily = (fontFamily.length > 0 ? fontFamily : nil);
148
+ [runtime notifyThemeChanged];
133
149
  }
134
150
 
135
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,17 +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];
49
62
  [self qms_scheduleThemeReapplyPasses];
63
+ [self qms_startThemeGuardWindow];
50
64
  }
51
65
 
52
66
  - (void)qms_viewDidAppear:(BOOL)animated {
@@ -58,6 +72,7 @@ static void qms_swizzleInstanceMethod(Class cls, SEL originalSelector, SEL swizz
58
72
 
59
73
  [self qms_applyThemeIfNeeded];
60
74
  [self qms_scheduleThemeReapplyPasses];
75
+ [self qms_startThemeGuardWindow];
61
76
 
62
77
  NSString *screenName = NSStringFromClass([self class]);
63
78
  NSString *screenClass = screenName;
@@ -83,23 +98,82 @@ static void qms_swizzleInstanceMethod(Class cls, SEL originalSelector, SEL swizz
83
98
  userInfo:eventInfo];
84
99
  }
85
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
+
86
121
  - (void)qms_scheduleThemeReapplyPasses {
87
122
  __weak typeof(self) weakSelf = self;
88
- NSArray<NSNumber *> *delays = @[@0.02, @0.12, @0.30, @0.55];
123
+ NSArray<NSNumber *> *delays = @[@0.02, @0.12, @0.30, @0.55, @1.0, @1.35];
89
124
  for (NSNumber *delay in delays) {
90
125
  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay.doubleValue * NSEC_PER_SEC)),
91
126
  dispatch_get_main_queue(), ^{
92
127
  __strong typeof(weakSelf) strongSelf = weakSelf;
93
- if (!strongSelf || !strongSelf.view.window) return;
94
- if (![strongSelf qms_shouldTrackAnalytics]) return;
128
+ if (!strongSelf || !strongSelf.isViewLoaded) return;
129
+ if (![strongSelf qms_shouldApplyTheme]) return;
95
130
  [strongSelf qms_applyThemeIfNeeded];
96
131
  });
97
132
  }
98
133
  }
99
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
+
100
174
  - (void)qms_viewDidLayoutSubviews {
101
175
  [self qms_viewDidLayoutSubviews];
102
- if (![self qms_shouldTrackAnalytics]) return;
176
+ if (![self qms_shouldApplyTheme]) return;
103
177
  [self qms_applyThemeIfNeeded];
104
178
  }
105
179
 
@@ -215,11 +289,18 @@ static void qms_swizzleInstanceMethod(Class cls, SEL originalSelector, SEL swizz
215
289
  }
216
290
 
217
291
  - (void)qms_applyThemeIfNeeded {
218
- NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
219
- UIColor *themeColor = [self qms_colorFromHexString:[defaults stringForKey:QmsThemePrimaryColorKey]];
220
- UIColor *headerTextColor = [self qms_colorFromHexString:[defaults stringForKey:QmsThemeHeaderTextColorKey]];
221
- UIColor *accentColor = [self qms_colorFromHexString:[defaults stringForKey:QmsThemeAccentColorKey]];
222
- 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
+ }
223
304
  if (themeColor) {
224
305
  [self qms_applyPrimaryRecursively:self.view themeColor:themeColor];
225
306
  [self qms_applyForegroundRecursively:self.view
@@ -351,4 +432,48 @@ static void qms_swizzleInstanceMethod(Class cls, SEL originalSelector, SEL swizz
351
432
  return NO;
352
433
  }
353
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
+
354
479
  @end
@@ -36,6 +36,18 @@
36
36
  <data>
37
37
  7BPvHyyZ+HgNby6z+fJ++J8wFhs=
38
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>
39
51
  </dict>
40
52
  <key>files2</key>
41
53
  <dict>
@@ -88,6 +100,27 @@
88
100
  75z5nwF1vvUwuIk0vZBPz1b3c87G/U37jMr3ziu9OV4=
89
101
  </data>
90
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>
91
124
  </dict>
92
125
  <key>rules</key>
93
126
  <dict>