@usercentrics/react-native-sdk 2.0.3-rc1 → 2.2.1

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 (53) hide show
  1. package/README.md +2 -0
  2. package/android/build.gradle +1 -1
  3. package/android/src/androidTest/java/com/usercentrics/reactnativemodule/RNUsercentricsModuleTest.kt +41 -2
  4. package/android/src/androidTest/java/com/usercentrics/reactnativemodule/api/FakeUsercentricsProxy.kt +23 -1
  5. package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/GetCMPDataMock.kt +2 -1
  6. package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/GetConsentsMock.kt +19 -1
  7. package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/GetTCFDataMock.kt +2 -0
  8. package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/SaveDecisionsForTCFMock.kt +18 -1
  9. package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/SaveDecisionsMock.kt +0 -1
  10. package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/ShowFirstLayerMock.kt +27 -0
  11. package/android/src/main/java/com/usercentrics/reactnativeusercentrics/RNUsercentricsModule.kt +44 -0
  12. package/android/src/main/java/com/usercentrics/reactnativeusercentrics/api/UsercentricsProxy.kt +42 -1
  13. package/android/src/main/java/com/usercentrics/reactnativeusercentrics/extensions/FirstLayerOptionsExtensions.kt +160 -0
  14. package/android/src/main/java/com/usercentrics/reactnativeusercentrics/extensions/TCFDataExtensions.kt +1 -0
  15. package/android/src/main/java/com/usercentrics/reactnativeusercentrics/extensions/UsercentricsServiceConsentExtensions.kt +21 -0
  16. package/ios/Extensions/BannerSettings+Dict.swift +180 -0
  17. package/ios/Extensions/TCFData+Dict.swift +2 -1
  18. package/ios/Extensions/UIColor+Extensions.swift +52 -0
  19. package/ios/Extensions/UIFont+UsercentricsFontDict.swift +15 -0
  20. package/ios/Extensions/UIImage+UsercentricsLogoDict.swift +14 -0
  21. package/ios/Extensions/UsercentricsCMPData+Dict.swift +1 -1
  22. package/ios/Extensions/UsercentricsServiceConsents+Dict.swift +14 -0
  23. package/ios/Extensions/UsercentricsUISettings+Dict.swift +1 -0
  24. package/ios/Manager/UsercentricsManager.swift +43 -20
  25. package/ios/RNUsercentricsModule.m +8 -0
  26. package/ios/RNUsercentricsModule.swift +97 -36
  27. package/ios/RNUsercentricsModule.xcodeproj/project.pbxproj +16 -0
  28. package/lib/Usercentrics.d.ts +6 -1
  29. package/lib/Usercentrics.js +43 -16
  30. package/lib/Usercentrics.js.map +1 -1
  31. package/lib/models/BannerSettings.d.ts +6 -0
  32. package/lib/models/BannerSettings.js +7 -0
  33. package/lib/models/BannerSettings.js.map +1 -0
  34. package/lib/models/FirstLayerOptions.d.ts +82 -0
  35. package/lib/models/FirstLayerOptions.js +104 -0
  36. package/lib/models/FirstLayerOptions.js.map +1 -0
  37. package/lib/models/SecondLayerOptions.d.ts +6 -0
  38. package/lib/models/SecondLayerOptions.js +7 -0
  39. package/lib/models/SecondLayerOptions.js.map +1 -0
  40. package/lib/models/TCFData.d.ts +9 -8
  41. package/lib/models/TCFData.js +2 -1
  42. package/lib/models/TCFData.js.map +1 -1
  43. package/lib/models/UsercentricsServiceConsent.d.ts +9 -1
  44. package/lib/models/UsercentricsServiceConsent.js +10 -1
  45. package/lib/models/UsercentricsServiceConsent.js.map +1 -1
  46. package/lib/models/UsercentricsUIOptions.d.ts +2 -1
  47. package/lib/models/UsercentricsUIOptions.js +2 -1
  48. package/lib/models/UsercentricsUIOptions.js.map +1 -1
  49. package/lib/models/index.d.ts +3 -0
  50. package/lib/models/index.js +3 -0
  51. package/lib/models/index.js.map +1 -1
  52. package/package.json +2 -2
  53. package/react-native-usercentrics.podspec +0 -1
@@ -0,0 +1,180 @@
1
+ import Foundation
2
+ import UsercentricsUI
3
+
4
+ extension BannerSettings {
5
+ init?(from dictionary: NSDictionary?) {
6
+ guard let dictionary = dictionary else { return nil }
7
+
8
+ self.init(font: UIFont(from: dictionary["font"] as? NSDictionary),
9
+ logo: UIImage(from: dictionary["logo"] as? NSDictionary))
10
+ }
11
+ }
12
+
13
+ extension FirstLayerStyleSettings {
14
+ init?(from dictionary: NSDictionary?) {
15
+ guard let dictionary = dictionary else { return nil }
16
+
17
+ self.init(headerImage: HeaderImageSettings.from(dictionary: dictionary["headerImage"] as? NSDictionary),
18
+ title: TitleSettings(from: dictionary["title"] as? NSDictionary),
19
+ message: MessageSettings(from: dictionary["message"] as? NSDictionary),
20
+ buttonLayout: ButtonLayout.from(dictionary: dictionary["buttonLayout"] as? NSDictionary),
21
+ backgroundColor: UIColor(unsafeHex: dictionary["backgroundColorHex"] as? String),
22
+ cornerRadius: dictionary["cornerRadius"] as? CGFloat,
23
+ overlayColor: UIColor(unsafeHex: dictionary["overlayColorHex"] as? String))
24
+ }
25
+ }
26
+
27
+
28
+ extension HeaderImageSettings {
29
+ static func from(dictionary: NSDictionary?) -> HeaderImageSettings? {
30
+ guard let dictionary = dictionary else { return nil }
31
+
32
+ if let isHidden = dictionary["isHidden"] as? Bool, isHidden {
33
+ return .hidden
34
+ }
35
+
36
+ let logoDict = dictionary["image"] as? NSDictionary
37
+ let logo = UIImage(from: logoDict)
38
+
39
+ if let isExtended = dictionary["isExtended"] as? Bool, isExtended {
40
+ return .extended(image: logo)
41
+ }
42
+
43
+ let logoUrlString = logoDict?["logoUrl"] as? String
44
+
45
+ return .logo(settings: LogoSettings(image: logo,
46
+ url: URL(string: logoUrlString ?? ""),
47
+ position: SectionPosition.from(enumString: dictionary["alignment"] as? String),
48
+ height: dictionary["height"] as? CGFloat))
49
+ }
50
+ }
51
+
52
+ extension TitleSettings {
53
+ init?(from dictionary: NSDictionary?) {
54
+ guard let dictionary = dictionary else { return nil }
55
+
56
+ self.init(font: UIFont(from: dictionary["font"] as? NSDictionary),
57
+ textColor: UIColor(unsafeHex: dictionary["textColorHex"] as? String ?? ""),
58
+ textAlignment: NSTextAlignment.from(enumString: dictionary["textAlignment"] as? String))
59
+ }
60
+ }
61
+
62
+
63
+ extension MessageSettings {
64
+ init?(from dictionary: NSDictionary?) {
65
+ guard let dictionary = dictionary else { return nil }
66
+
67
+ self.init(font: UIFont(from: dictionary["font"] as? NSDictionary),
68
+ textColor: UIColor(unsafeHex: dictionary["textColorHex"] as? String ?? ""),
69
+ textAlignment: NSTextAlignment.from(enumString: dictionary["textAlignment"] as? String),
70
+ linkTextColor: UIColor(unsafeHex: dictionary["linkTextColorHex"] as? String ?? ""),
71
+ linkTextUnderline: dictionary["linkTextUnderline"] as? Bool ?? true)
72
+ }
73
+ }
74
+
75
+ extension ButtonLayout {
76
+ static func from(dictionary: NSDictionary?) -> ButtonLayout? {
77
+ guard let dictionary = dictionary else { return nil }
78
+
79
+ let layoutDict = dictionary["layout"] as? String
80
+ let buttons = (dictionary["buttons"] as? [[NSDictionary]]) ?? []
81
+
82
+ switch layoutDict {
83
+ case "ROW":
84
+ return .row(buttons: buttons.flatMap { $0 }.compactMap { ButtonSettings(from: $0) })
85
+ case "COLUMN":
86
+ return .column(buttons: buttons.flatMap { $0 }.compactMap { ButtonSettings(from: $0) })
87
+ case "GRID":
88
+ let gridButtons = buttons.map { $0.compactMap { button in ButtonSettings(from: button) }}
89
+ return .grid(buttons: gridButtons)
90
+ default:
91
+ break
92
+ }
93
+
94
+ return nil
95
+ }
96
+ }
97
+
98
+ extension ButtonSettings {
99
+ init?(from dictionary: NSDictionary?) {
100
+ guard
101
+ let dictionary = dictionary,
102
+ let buttonTypeDict = dictionary["buttonType"] as? String,
103
+ let buttonType = ButtonType.from(enumString: buttonTypeDict)
104
+ else { return nil }
105
+
106
+ self.init(type: buttonType,
107
+ font: UIFont(from: dictionary["font"] as? NSDictionary),
108
+ textColor: UIColor(unsafeHex: dictionary["textColorHex"] as? String),
109
+ backgroundColor: UIColor(unsafeHex: dictionary["backgroundColorHex"] as? String),
110
+ cornerRadius: dictionary["cornerRadius"] as? CGFloat)
111
+ }
112
+ }
113
+
114
+ extension ButtonType {
115
+ static func from(enumString: String) -> ButtonType? {
116
+ switch enumString {
117
+ case "ACCEPT_ALL":
118
+ return .acceptAll
119
+ case "DENY_ALL":
120
+ return .denyAll
121
+ case "MORE":
122
+ return .more
123
+ case "SAVE":
124
+ return .save
125
+ default:
126
+ return nil
127
+ }
128
+ }
129
+ }
130
+
131
+ extension UsercentricsLayout {
132
+ static func from(enumString: String) -> UsercentricsLayout? {
133
+ switch enumString {
134
+ case "FULL":
135
+ return .full
136
+ case "SHEET":
137
+ return .sheet
138
+ case "POPUP_CENTER":
139
+ return .popup(position: .center)
140
+ case "POPUP_BOTTOM":
141
+ return .popup(position: .bottom)
142
+ default:
143
+ return nil
144
+ }
145
+ }
146
+ }
147
+
148
+ extension SectionPosition {
149
+ static func from(enumString: String?) -> SectionPosition? {
150
+ guard let enumString = enumString else { return nil }
151
+
152
+ switch enumString {
153
+ case "CENTER":
154
+ return .center
155
+ case "END":
156
+ return .right
157
+ case "START":
158
+ return .left
159
+ default:
160
+ return nil
161
+ }
162
+ }
163
+ }
164
+
165
+ extension NSTextAlignment {
166
+ static func from(enumString: String?) -> NSTextAlignment? {
167
+ guard let enumString = enumString else { return nil }
168
+
169
+ switch enumString {
170
+ case "CENTER":
171
+ return .center
172
+ case "RIGHT":
173
+ return .right
174
+ case "LEFT":
175
+ return .left
176
+ default:
177
+ return nil
178
+ }
179
+ }
180
+ }
@@ -102,7 +102,8 @@ extension TCFVendor {
102
102
  "usesNonCookieAccess" : usesNonCookieAccess,
103
103
  "deviceStorageDisclosureUrl" : deviceStorageDisclosureUrl as Any,
104
104
  "usesCookies" : usesCookies,
105
- "cookieRefresh" : cookieRefresh?.boolValue as Any ,
105
+ "cookieRefresh" : cookieRefresh?.boolValue as Any,
106
+ "dataSharedOutsideEU": dataSharedOutsideEU?.boolValue as Any
106
107
  ]
107
108
  }
108
109
  }
@@ -0,0 +1,52 @@
1
+ import Foundation
2
+
3
+ extension UIColor {
4
+ convenience init?(unsafeHex: String?) {
5
+ guard let unsafeHex = unsafeHex else { return nil }
6
+ if unsafeHex.hasPrefix("#") {
7
+ self.init(hex: unsafeHex)
8
+ } else {
9
+ self.init(hex: "#\(unsafeHex)")
10
+ }
11
+ }
12
+
13
+ convenience init?(hex: String) {
14
+ let r, g, b, a: CGFloat
15
+
16
+ if hex.hasPrefix("#") {
17
+ let start = hex.index(hex.startIndex, offsetBy: 1)
18
+ let hexColor = String(hex[start...])
19
+
20
+ if hexColor.count == 8 {
21
+ let scanner = Scanner(string: hexColor)
22
+ var hexNumber: UInt64 = 0
23
+
24
+ if scanner.scanHexInt64(&hexNumber) {
25
+ r = CGFloat((hexNumber & 0xff000000) >> 24) / 255
26
+ g = CGFloat((hexNumber & 0x00ff0000) >> 16) / 255
27
+ b = CGFloat((hexNumber & 0x0000ff00) >> 8) / 255
28
+ a = CGFloat(hexNumber & 0x000000ff) / 255
29
+
30
+ self.init(red: r, green: g, blue: b, alpha: a)
31
+ return
32
+ }
33
+ }
34
+
35
+ if hexColor.count == 6 {
36
+ let scanner = Scanner(string: hexColor)
37
+ var hexNumber: UInt64 = 0
38
+
39
+ if scanner.scanHexInt64(&hexNumber) {
40
+ r = CGFloat((hexNumber & 0xff0000) >> 16) / 255
41
+ g = CGFloat((hexNumber & 0x00ff00) >> 8) / 255
42
+ b = CGFloat(hexNumber & 0x0000ff) / 255
43
+
44
+ self.init(red: r, green: g, blue: b, alpha: 1.0)
45
+ return
46
+ }
47
+ }
48
+ }
49
+
50
+ return nil
51
+ }
52
+ }
@@ -0,0 +1,15 @@
1
+ import Foundation
2
+
3
+
4
+ extension UIFont {
5
+ convenience init?(from dictionary: NSDictionary?) {
6
+ guard let dictionary = dictionary else { return nil }
7
+
8
+ if let customFontName = dictionary["fontName"] as? String,
9
+ let customFontSize = dictionary["fontSize"] as? CGFloat {
10
+ self.init(name: customFontName, size: customFontSize)
11
+ } else {
12
+ return nil
13
+ }
14
+ }
15
+ }
@@ -0,0 +1,14 @@
1
+ import Foundation
2
+
3
+ extension UIImage {
4
+ convenience init?(from dictionary: NSDictionary?) {
5
+ guard
6
+ let dictionary = dictionary,
7
+ let logoName = dictionary["logoName"] as? String
8
+ else {
9
+ return nil
10
+ }
11
+
12
+ self.init(named: logoName)
13
+ }
14
+ }
@@ -22,7 +22,7 @@ extension UsercentricsSettings {
22
22
  "displayOnlyForEU" : self.displayOnlyForEU,
23
23
  "urlConsentInfo" : self.urlConsentInfo,
24
24
  "updatedAt" : self.updatedAt as Any,
25
- "secondLayer" : self.secondLayer?.toDictionary() as Any,
25
+ "secondLayer" : self.secondLayer.toDictionary() as Any,
26
26
  "cookiePolicyUrl": self.cookiePolicyUrl as Any,
27
27
  "tcf2": self.tcf2?.toDictionary() as Any,
28
28
  "ccpa": self.ccpa?.toDictionary() as Any,
@@ -14,6 +14,8 @@ public extension UsercentricsServiceConsent {
14
14
  "status": self.status,
15
15
  "version": self.version,
16
16
  "dataProcessor": self.dataProcessor,
17
+ "isEssential": self.isEssential,
18
+ "history": self.history.toDictionary()
17
19
  ]
18
20
 
19
21
  if let type = self.type?.ordinal {
@@ -23,3 +25,15 @@ public extension UsercentricsServiceConsent {
23
25
  return dict
24
26
  }
25
27
  }
28
+
29
+ public extension Array where Element == UsercentricsConsentHistoryEntry {
30
+ func toDictionary() -> [NSDictionary] {
31
+ self.map {
32
+ [
33
+ "status": $0.status,
34
+ "type": $0.type.ordinal,
35
+ "timestampInMillis": $0.timestampInMillis
36
+ ]
37
+ }
38
+ }
39
+ }
@@ -24,3 +24,4 @@ public extension UsercentricsUISettings {
24
24
  showCloseButton: showCloseButton)
25
25
  }
26
26
  }
27
+
@@ -1,16 +1,26 @@
1
1
  import Foundation
2
2
  import Usercentrics
3
3
  import UsercentricsUI
4
- import RxSwift
5
4
 
6
5
  public protocol UsercentricsManager {
7
- var alreadyConfigured: Bool { get }
8
6
  func configure(options: UsercentricsOptions)
9
7
 
10
8
  func isReady(onSuccess: @escaping ((UsercentricsReadyStatus) -> Void), onFailure: @escaping ((Error) -> Void))
11
9
  func restoreUserSession(controllerId: String, onSuccess: @escaping ((UsercentricsReadyStatus) -> Void), onFailure: @escaping ((Error) -> Void))
12
10
 
13
11
  func getPredefinedUI(settings: UsercentricsUISettings?, dismissViewHandler: @escaping (UsercentricsConsentUserResponse) -> Void) -> UIViewController
12
+
13
+ func showFirstLayer(bannerSettings: BannerSettings?,
14
+ hostView: UINavigationController,
15
+ layout: UsercentricsLayout,
16
+ settings: FirstLayerStyleSettings?,
17
+ dismissViewHandler: @escaping (UsercentricsConsentUserResponse) -> Void)
18
+
19
+ func showSecondLayer(bannerSettings: BannerSettings?,
20
+ hostView: UINavigationController,
21
+ showCloseButton: Bool,
22
+ dismissViewHandler: @escaping (UsercentricsConsentUserResponse) -> Void)
23
+
14
24
  func getTCString() -> String
15
25
  func getControllerId() -> String
16
26
  func getConsents() -> [UsercentricsServiceConsent]
@@ -40,39 +50,52 @@ public protocol UsercentricsManager {
40
50
 
41
51
  final class UsercentricsManagerImplementation: UsercentricsManager {
42
52
 
43
- private var bag: DisposeBag = DisposeBag()
44
- private let isConfiguredObservable: BehaviorSubject<Bool> = .init(value: false)
45
- var alreadyConfigured: Bool {
46
- try! isConfiguredObservable.value()
47
- }
48
-
49
53
  func isReady(onSuccess: @escaping ((UsercentricsReadyStatus) -> Void), onFailure: @escaping ((Error) -> Void)) {
50
- var subscription: Disposable?
51
-
52
- subscription = isConfiguredObservable
53
- .subscribe(onNext: { isConfigured in
54
- guard isConfigured else { return }
55
- UsercentricsCore.isReady(onSuccess: onSuccess, onFailure: onFailure)
56
- subscription?.dispose()
57
- })
58
-
59
- subscription?.disposed(by: bag)
54
+ UsercentricsCore.isReady(onSuccess: onSuccess, onFailure: onFailure)
60
55
  }
61
56
 
62
57
  func configure(options: UsercentricsOptions) {
58
+ var isConfigured: Bool = false
59
+ isReady { _ in
60
+ isConfigured = true
61
+ } onFailure: { _ in
62
+ isConfigured = true
63
+ }
64
+
65
+ guard !isConfigured else { return }
66
+
63
67
  UsercentricsCore.configure(options: options)
64
- isConfiguredObservable.onNext(true)
65
68
  }
66
69
 
67
70
  func reset() {
68
71
  UsercentricsCore.reset()
69
- isConfiguredObservable.onNext(false)
70
72
  }
71
73
 
72
74
  func getPredefinedUI(settings: UsercentricsUISettings?, dismissViewHandler: @escaping (UsercentricsConsentUserResponse) -> Void) -> UIViewController {
73
75
  return UsercentricsUserInterface.getPredefinedUI(settings: settings, dismissViewHandler: dismissViewHandler)
74
76
  }
75
77
 
78
+ func showFirstLayer(bannerSettings: BannerSettings?,
79
+ hostView: UINavigationController,
80
+ layout: UsercentricsLayout,
81
+ settings: FirstLayerStyleSettings?,
82
+ dismissViewHandler: @escaping (UsercentricsConsentUserResponse) -> Void) {
83
+ UsercentricsBanner(bannerSettings: bannerSettings).showFirstLayer(hostView: hostView,
84
+ layout: layout,
85
+ settings: settings,
86
+ completionHandler: dismissViewHandler)
87
+ }
88
+
89
+ func showSecondLayer(bannerSettings: BannerSettings?,
90
+ hostView: UINavigationController,
91
+ showCloseButton: Bool,
92
+ dismissViewHandler: @escaping (UsercentricsConsentUserResponse) -> Void) {
93
+ UsercentricsBanner(bannerSettings: bannerSettings).showSecondLayer(hostView: hostView,
94
+ showCloseButton: showCloseButton,
95
+ presentationMode: .present,
96
+ completionHandler: dismissViewHandler)
97
+ }
98
+
76
99
  func restoreUserSession(controllerId: String, onSuccess: @escaping ((UsercentricsReadyStatus) -> Void), onFailure: @escaping ((Error) -> Void)) {
77
100
  UsercentricsCore.shared.restoreUserSession(controllerId: controllerId, onSuccess: onSuccess, onFailure: onFailure)
78
101
  }
@@ -14,6 +14,14 @@ RCT_EXTERN_METHOD(showCMP:(NSDictionary *)dict
14
14
  resolve:(RCTPromiseResolveBlock)resolve
15
15
  reject:(RCTPromiseRejectBlock)reject)
16
16
 
17
+ RCT_EXTERN_METHOD(showFirstLayer:(NSDictionary *)dict
18
+ resolve:(RCTPromiseResolveBlock)resolve
19
+ reject:(RCTPromiseRejectBlock)reject)
20
+
21
+ RCT_EXTERN_METHOD(showSecondLayer:(NSDictionary *)dict
22
+ resolve:(RCTPromiseResolveBlock)resolve
23
+ reject:(RCTPromiseRejectBlock)reject)
24
+
17
25
  RCT_EXTERN_METHOD(restoreUserSession:(NSString *)controllerId
18
26
  resolve:(RCTPromiseResolveBlock)resolve
19
27
  reject:(RCTPromiseRejectBlock)reject)