@usercentrics/react-native-sdk 2.0.4-rc1 → 2.3.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 (59) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +2 -0
  3. package/android/build.gradle +1 -1
  4. package/android/src/androidTest/java/com/usercentrics/reactnativemodule/RNUsercentricsModuleTest.kt +13 -110
  5. package/android/src/androidTest/java/com/usercentrics/reactnativemodule/api/FakeUsercentricsProxy.kt +21 -19
  6. package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/GetCMPDataMock.kt +2 -1
  7. package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/GetConsentsMock.kt +19 -1
  8. package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/GetTCFDataMock.kt +2 -0
  9. package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/SaveDecisionsForTCFMock.kt +18 -1
  10. package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/SaveDecisionsMock.kt +0 -1
  11. package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/ShowFirstLayerMock.kt +27 -0
  12. package/android/src/main/java/com/usercentrics/reactnativeusercentrics/RNUsercentricsModule.kt +41 -34
  13. package/android/src/main/java/com/usercentrics/reactnativeusercentrics/api/UsercentricsProxy.kt +36 -17
  14. package/android/src/main/java/com/usercentrics/reactnativeusercentrics/extensions/BannerFontExtensions.kt +26 -0
  15. package/android/src/main/java/com/usercentrics/reactnativeusercentrics/extensions/FirstLayerOptionsExtensions.kt +161 -0
  16. package/android/src/main/java/com/usercentrics/reactnativeusercentrics/extensions/TCFDataExtensions.kt +1 -0
  17. package/android/src/main/java/com/usercentrics/reactnativeusercentrics/extensions/UsercentricsReadyStatusExtensions.kt +1 -1
  18. package/android/src/main/java/com/usercentrics/reactnativeusercentrics/extensions/UsercentricsServiceConsentExtensions.kt +21 -0
  19. package/ios/Extensions/BannerSettings+Dict.swift +227 -0
  20. package/ios/Extensions/ReadyStatus+Dict.swift +1 -1
  21. package/ios/Extensions/TCFData+Dict.swift +2 -1
  22. package/ios/Extensions/UIColor+Extensions.swift +52 -0
  23. package/ios/Extensions/UIImage+UsercentricsLogoDict.swift +14 -0
  24. package/ios/Extensions/UsercentricsCMPData+Dict.swift +1 -1
  25. package/ios/Extensions/UsercentricsServiceConsents+Dict.swift +14 -0
  26. package/ios/Manager/UsercentricsManager.swift +29 -3
  27. package/ios/RNUsercentricsModule.m +8 -0
  28. package/ios/RNUsercentricsModule.swift +61 -34
  29. package/ios/RNUsercentricsModule.xcodeproj/project.pbxproj +12 -4
  30. package/lib/Usercentrics.d.ts +3 -2
  31. package/lib/Usercentrics.js +6 -2
  32. package/lib/Usercentrics.js.map +1 -1
  33. package/lib/models/BannerSettings.d.ts +18 -0
  34. package/lib/models/BannerSettings.js +21 -0
  35. package/lib/models/BannerSettings.js.map +1 -0
  36. package/lib/models/FirstLayerOptions.d.ts +85 -0
  37. package/lib/models/FirstLayerOptions.js +107 -0
  38. package/lib/models/FirstLayerOptions.js.map +1 -0
  39. package/lib/models/SecondLayerOptions.d.ts +6 -0
  40. package/lib/models/SecondLayerOptions.js +7 -0
  41. package/lib/models/SecondLayerOptions.js.map +1 -0
  42. package/lib/models/TCFData.d.ts +9 -8
  43. package/lib/models/TCFData.js +2 -1
  44. package/lib/models/TCFData.js.map +1 -1
  45. package/lib/models/UsercentricsReadyStatus.d.ts +2 -2
  46. package/lib/models/UsercentricsReadyStatus.js +2 -2
  47. package/lib/models/UsercentricsReadyStatus.js.map +1 -1
  48. package/lib/models/UsercentricsServiceConsent.d.ts +9 -1
  49. package/lib/models/UsercentricsServiceConsent.js +10 -1
  50. package/lib/models/UsercentricsServiceConsent.js.map +1 -1
  51. package/lib/models/index.d.ts +3 -1
  52. package/lib/models/index.js +3 -1
  53. package/lib/models/index.js.map +1 -1
  54. package/package.json +2 -2
  55. package/android/src/main/java/com/usercentrics/reactnativeusercentrics/extensions/UsercentricsUserInterfaceExtensions.kt +0 -45
  56. package/ios/Extensions/UsercentricsUISettings+Dict.swift +0 -26
  57. package/lib/models/UsercentricsUIOptions.d.ts +0 -17
  58. package/lib/models/UsercentricsUIOptions.js +0 -20
  59. package/lib/models/UsercentricsUIOptions.js.map +0 -1
@@ -0,0 +1,161 @@
1
+ package com.usercentrics.reactnativeusercentrics.extensions
2
+
3
+ import android.content.res.AssetManager
4
+ import android.graphics.Color
5
+ import androidx.annotation.ColorInt
6
+ import com.facebook.react.bridge.ReadableMap
7
+ import com.usercentrics.sdk.*
8
+
9
+ internal fun String.usercentricsLayoutFromEnumString(): UsercentricsLayout {
10
+ return when (this) {
11
+ "FULL" -> UsercentricsLayout.Full
12
+ "SHEET" -> UsercentricsLayout.Sheet
13
+ "POPUP_BOTTOM" -> UsercentricsLayout.Popup(PopupPosition.BOTTOM)
14
+ "POPUP_CENTER" -> UsercentricsLayout.Popup(PopupPosition.CENTER)
15
+ else -> throw IllegalArgumentException("Invalid layout: $this")
16
+ }
17
+ }
18
+
19
+ internal fun ReadableMap.bannerSettingsFromMap(assetManager: AssetManager): BannerSettings {
20
+ val customFont = getMap("font")
21
+ val customLogo = getMap("logo")
22
+
23
+ return BannerSettings(
24
+ customFont?.bannerFontFromMap(assetManager),
25
+ customLogo?.bannerLogoFromMap()
26
+ )
27
+ }
28
+
29
+ internal fun ReadableMap.bannerLogoFromMap(): UsercentricsImage? {
30
+ val logoPath = getString("logoPath") ?: return null
31
+ return UsercentricsImage.ImageUrl(logoPath)
32
+ }
33
+
34
+ internal fun ReadableMap.firstLayerStyleSettingsFromMap(assetManager: AssetManager): FirstLayerStyleSettings {
35
+ return FirstLayerStyleSettings(
36
+ headerImage = getMap("headerImage")?.headerImageFromMap(),
37
+ title = getMap("title")?.titleFromMap(assetManager),
38
+ message = getMap("message")?.messageFromMap(assetManager),
39
+ buttonLayout = getMap("buttonLayout")?.buttonLayoutFromMap(assetManager),
40
+ backgroundColor = getString("backgroundColorHex")?.deserializeColor(),
41
+ overlayColor = getString("overlayColorHex")?.deserializeColor(),
42
+ cornerRadius = getIntOrNull("cornerRadius")
43
+ )
44
+ }
45
+
46
+ internal fun ReadableMap.headerImageFromMap(): HeaderImageSettings? {
47
+ val isHidden = getBooleanOrNull("isHidden") ?: false
48
+ if (isHidden) {
49
+ return HeaderImageSettings.Hidden
50
+ }
51
+
52
+ val image = getMap("image")?.bannerLogoFromMap() ?: return null
53
+
54
+ val isExtended = getBooleanOrNull("isExtended") ?: false
55
+ return if (isExtended) {
56
+ HeaderImageSettings.ExtendedLogoSettings(image)
57
+ } else {
58
+ HeaderImageSettings.LogoSettings(
59
+ image = image,
60
+ alignment = getString("alignment")?.sectionAlignmentFromMap(),
61
+ heightInDp = getDoubleOrNull("height")?.toFloat()
62
+ )
63
+ }
64
+ }
65
+
66
+ internal fun ReadableMap.titleFromMap(assetManager: AssetManager): TitleSettings {
67
+ return TitleSettings(
68
+ alignment = getString("alignment")?.sectionAlignmentFromMap(),
69
+ textColor = getString("textColorHex")?.deserializeColor(),
70
+ font = assetManager.createFontFromName(getString("fontName")),
71
+ textSizeInSp = getDoubleOrNull("textSize")?.toFloat()
72
+ )
73
+ }
74
+
75
+ internal fun ReadableMap.messageFromMap(assetManager: AssetManager): MessageSettings {
76
+ return MessageSettings(
77
+ font = assetManager.createFontFromName(getString("fontName")),
78
+ textSizeInSp = getDoubleOrNull("textSize")?.toFloat(),
79
+ textColor = getString("textColorHex")?.deserializeColor(),
80
+ alignment = getString("alignment")?.sectionAlignmentFromMap(),
81
+ linkTextColor = getString("linkTextColorHex")?.deserializeColor(),
82
+ underlineLink = getBooleanOrNull("linkTextUnderline")
83
+ )
84
+ }
85
+
86
+ internal fun String.sectionAlignmentFromMap(): SectionAlignment {
87
+ return SectionAlignment.valueOf(this)
88
+ }
89
+
90
+ internal fun ReadableMap.buttonLayoutFromMap(
91
+ assetManager: AssetManager
92
+ ): ButtonLayout? {
93
+ val layout = getString("layout")
94
+ val buttons: List<List<ButtonSettings>> = getArray("buttons")?.let { buttonsArray ->
95
+ val buttonsList: MutableList<List<ButtonSettings>> = mutableListOf()
96
+
97
+ for (rowIndex in 0 until buttonsArray.size()) {
98
+ val listRow = mutableListOf<ButtonSettings>()
99
+ val row = buttonsArray.getArray(rowIndex)
100
+ for (rowElement in 0 until row.size()) {
101
+ val element = row.getMap(rowElement)
102
+ listRow.add(element.buttonSettingsFromMap(assetManager))
103
+ }
104
+ buttonsList.add(listRow)
105
+ }
106
+
107
+ return@let buttonsList.toList()
108
+ } ?: listOf()
109
+
110
+ when (layout) {
111
+ "ROW" -> {
112
+ return ButtonLayout.Row(
113
+ buttons.flatten()
114
+ )
115
+ }
116
+ "COLUMN" -> {
117
+ return ButtonLayout.Column(
118
+ buttons.flatten()
119
+ )
120
+ }
121
+ "GRID" -> {
122
+ return ButtonLayout.Grid(
123
+ buttons
124
+ )
125
+ }
126
+ }
127
+
128
+ return null
129
+ }
130
+
131
+ internal fun ReadableMap.buttonSettingsFromMap(
132
+ assetManager: AssetManager
133
+ ): ButtonSettings {
134
+ return ButtonSettings(
135
+ type = getString("buttonType")!!.deserializeButtonType(),
136
+ isAllCaps = getBooleanOrNull("isAllCaps"),
137
+ font = assetManager.createFontFromName(getString("fontName")),
138
+ textColor = getString("textColorHex")?.deserializeColor(),
139
+ backgroundColor = getString("backgroundColorHex")?.deserializeColor(),
140
+ cornerRadius = getIntOrNull("cornerRadius"),
141
+ textSizeInSp = getDoubleOrNull("textSize")?.toFloat()
142
+ )
143
+ }
144
+
145
+ internal fun String.deserializeButtonType(): ButtonType {
146
+ return ButtonType.valueOf(this)
147
+ }
148
+
149
+ @ColorInt
150
+ internal fun String.deserializeColor(): Int? {
151
+ val colorString: String =
152
+ if (!this.startsWith("#")) {
153
+ "#$this"
154
+ } else {
155
+ this
156
+ }
157
+
158
+ return runCatching {
159
+ Color.parseColor(colorString)
160
+ }.getOrNull()
161
+ }
@@ -93,5 +93,6 @@ private fun TCFVendor.serialize(): WritableMap {
93
93
  // "deviceStorage" to deviceStorage.serialize(),
94
94
  "usesCookies" to usesCookies,
95
95
  "cookieRefresh" to cookieRefresh,
96
+ "dataSharedOutsideEU" to dataSharedOutsideEU
96
97
  ).toWritableMap()
97
98
  }
@@ -6,7 +6,7 @@ import com.usercentrics.sdk.UsercentricsReadyStatus
6
6
 
7
7
  internal fun UsercentricsReadyStatus.toWritableMap(): WritableMap {
8
8
  return Arguments.createMap().apply {
9
- putBoolean("shouldShowCMP", shouldShowCMP)
9
+ putBoolean("shouldCollectConsent", shouldCollectConsent)
10
10
  putArray("consents", consents.toWritableArray())
11
11
  }
12
12
  }
@@ -1,8 +1,10 @@
1
1
  package com.usercentrics.reactnativeusercentrics.extensions
2
2
 
3
3
  import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.ReadableArray
4
5
  import com.facebook.react.bridge.WritableArray
5
6
  import com.facebook.react.bridge.WritableMap
7
+ import com.usercentrics.sdk.UsercentricsConsentHistoryEntry
6
8
  import com.usercentrics.sdk.UsercentricsServiceConsent
7
9
 
8
10
  internal fun List<UsercentricsServiceConsent>.toWritableArray(): WritableArray {
@@ -28,6 +30,25 @@ internal fun UsercentricsServiceConsent.toWritableMap(): WritableMap {
28
30
  }
29
31
  putString("version", version)
30
32
  putString("dataProcessor", dataProcessor)
33
+ putBoolean("isEssential", isEssential)
34
+ putArray("history", history.toWritableArray())
35
+ }
36
+ }
37
+
38
+ private fun List<UsercentricsConsentHistoryEntry>.toWritableArray(): ReadableArray? {
39
+ map {
40
+ Arguments.createMap().apply {
41
+ putBoolean("status", it.status)
42
+ putInt("type", it.type.ordinal)
43
+ putInt("timestampInMillis", it.timestampInMillis.toInt())
44
+ }
45
+ }.apply {
46
+ val array = Arguments.createArray()
47
+ forEach {
48
+ array.pushMap(it)
49
+ }
50
+
51
+ return array
31
52
  }
32
53
  }
33
54
 
@@ -0,0 +1,227 @@
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: BannerFontHolder(from: dictionary["font"] as? NSDictionary)?.font,
9
+ logo: UIImage(from: dictionary["logo"] as? NSDictionary))
10
+ }
11
+ }
12
+
13
+ struct BannerFontHolder {
14
+ public let font: BannerFont
15
+
16
+ public let regularFont: UIFont
17
+ public let boldFont: UIFont
18
+
19
+ init?(from dictionary: NSDictionary?) {
20
+ guard let dictionary = dictionary,
21
+ let regularFontName: String = dictionary["regularFont"] as? String,
22
+ let boldFontName: String = dictionary["boldFont"] as? String,
23
+ let fontSize: CGFloat = dictionary["fontSize"] as? CGFloat,
24
+ let regularFont = UIFont(name: regularFontName, size: fontSize),
25
+ let boldFont = UIFont(name: boldFontName, size: fontSize)
26
+ else { return nil }
27
+
28
+ self.regularFont = regularFont
29
+ self.boldFont = boldFont
30
+ self.font = .init(regularFont: regularFont, boldFont: boldFont)
31
+ }
32
+ }
33
+
34
+ extension UIFont {
35
+ static func initialize(from fontName: String?, fontSizeValue: CGFloat?, fallbackFont: UIFont?) -> UIFont? {
36
+ let fontSize = fontSizeValue ?? UIFont.systemFontSize
37
+
38
+ // System font with custom size
39
+ if fontName == nil, fontSizeValue != nil {
40
+ return fallbackFont?.withSize(fontSize) ?? UIFont.systemFont(ofSize: fontSize)
41
+ }
42
+
43
+ if let fontName = fontName {
44
+ return UIFont(name: fontName, size: fontSize)
45
+ }
46
+
47
+ return fallbackFont?.withSize(fontSize)
48
+ }
49
+ }
50
+
51
+ extension FirstLayerStyleSettings {
52
+ init?(from dictionary: NSDictionary?, bannerFontHolder: BannerFontHolder?) {
53
+ guard let dictionary = dictionary else { return nil }
54
+
55
+ self.init(headerImage: HeaderImageSettings.from(dictionary: dictionary["headerImage"] as? NSDictionary),
56
+ title: TitleSettings(from: dictionary["title"] as? NSDictionary, fallbackFont: bannerFontHolder?.boldFont),
57
+ message: MessageSettings(from: dictionary["message"] as? NSDictionary, fallbackFont: bannerFontHolder?.regularFont),
58
+ buttonLayout: ButtonLayout.from(dictionary: dictionary["buttonLayout"] as? NSDictionary, fallbackFont: bannerFontHolder?.boldFont),
59
+ backgroundColor: UIColor(unsafeHex: dictionary["backgroundColorHex"] as? String),
60
+ cornerRadius: dictionary["cornerRadius"] as? CGFloat,
61
+ overlayColor: UIColor(unsafeHex: dictionary["overlayColorHex"] as? String))
62
+ }
63
+ }
64
+
65
+
66
+ extension HeaderImageSettings {
67
+ static func from(dictionary: NSDictionary?) -> HeaderImageSettings? {
68
+ guard let dictionary = dictionary else { return nil }
69
+
70
+ if let isHidden = dictionary["isHidden"] as? Bool, isHidden {
71
+ return .hidden
72
+ }
73
+
74
+ let logoDict = dictionary["image"] as? NSDictionary
75
+ let logo = UIImage(from: logoDict)
76
+
77
+ if let isExtended = dictionary["isExtended"] as? Bool, isExtended {
78
+ return .extended(image: logo)
79
+ }
80
+
81
+ let logoUrlString = logoDict?["logoUrl"] as? String
82
+
83
+ return .logo(settings: LogoSettings(image: logo,
84
+ url: URL(string: logoUrlString ?? ""),
85
+ position: SectionPosition.from(enumString: dictionary["alignment"] as? String),
86
+ height: dictionary["height"] as? CGFloat))
87
+ }
88
+ }
89
+
90
+ extension TitleSettings {
91
+ init?(from dictionary: NSDictionary?, fallbackFont: UIFont? = nil) {
92
+ guard let dictionary = dictionary else { return nil }
93
+
94
+ let fontName: String? = dictionary["fontName"] as? String
95
+ let fontSize: CGFloat? = dictionary["textSize"] as? CGFloat
96
+
97
+ self.init(font: UIFont.initialize(from: fontName, fontSizeValue: fontSize, fallbackFont: fallbackFont),
98
+ textColor: UIColor(unsafeHex: dictionary["textColorHex"] as? String ?? ""),
99
+ textAlignment: NSTextAlignment.from(enumString: dictionary["textAlignment"] as? String))
100
+ }
101
+ }
102
+
103
+
104
+ extension MessageSettings {
105
+ init?(from dictionary: NSDictionary?, fallbackFont: UIFont? = nil) {
106
+ guard let dictionary = dictionary else { return nil }
107
+
108
+ let fontName: String? = dictionary["fontName"] as? String
109
+ let textSize: CGFloat? = dictionary["textSize"] as? CGFloat
110
+
111
+ self.init(font: UIFont.initialize(from: fontName, fontSizeValue: textSize, fallbackFont: fallbackFont),
112
+ textColor: UIColor(unsafeHex: dictionary["textColorHex"] as? String ?? ""),
113
+ textAlignment: NSTextAlignment.from(enumString: dictionary["textAlignment"] as? String),
114
+ linkTextColor: UIColor(unsafeHex: dictionary["linkTextColorHex"] as? String ?? ""),
115
+ linkTextUnderline: dictionary["linkTextUnderline"] as? Bool ?? true)
116
+ }
117
+ }
118
+
119
+ extension ButtonLayout {
120
+ static func from(dictionary: NSDictionary?, fallbackFont: UIFont? = nil) -> ButtonLayout? {
121
+ guard let dictionary = dictionary else { return nil }
122
+
123
+ let layoutDict = dictionary["layout"] as? String
124
+ let buttons = (dictionary["buttons"] as? [[NSDictionary]]) ?? []
125
+
126
+ switch layoutDict {
127
+ case "ROW":
128
+ return .row(buttons: buttons.flatMap { $0 }.compactMap { ButtonSettings(from: $0, fallbackFont: fallbackFont) })
129
+ case "COLUMN":
130
+ return .column(buttons: buttons.flatMap { $0 }.compactMap { ButtonSettings(from: $0, fallbackFont: fallbackFont) })
131
+ case "GRID":
132
+ let gridButtons = buttons.map { $0.compactMap { button in ButtonSettings(from: button, fallbackFont: fallbackFont) }}
133
+ return .grid(buttons: gridButtons)
134
+ default:
135
+ break
136
+ }
137
+
138
+ return nil
139
+ }
140
+ }
141
+
142
+ extension ButtonSettings {
143
+ init?(from dictionary: NSDictionary?, fallbackFont: UIFont? = nil) {
144
+ guard
145
+ let dictionary = dictionary,
146
+ let buttonTypeDict = dictionary["buttonType"] as? String,
147
+ let buttonType = ButtonType.from(enumString: buttonTypeDict)
148
+ else { return nil }
149
+
150
+ let fontName: String? = dictionary["fontName"] as? String
151
+ let textSize: CGFloat? = dictionary["textSize"] as? CGFloat
152
+
153
+ self.init(type: buttonType,
154
+ font: UIFont.initialize(from: fontName, fontSizeValue: textSize, fallbackFont: fallbackFont),
155
+ textColor: UIColor(unsafeHex: dictionary["textColorHex"] as? String),
156
+ backgroundColor: UIColor(unsafeHex: dictionary["backgroundColorHex"] as? String),
157
+ cornerRadius: dictionary["cornerRadius"] as? CGFloat)
158
+ }
159
+ }
160
+
161
+ extension ButtonType {
162
+ static func from(enumString: String) -> ButtonType? {
163
+ switch enumString {
164
+ case "ACCEPT_ALL":
165
+ return .acceptAll
166
+ case "DENY_ALL":
167
+ return .denyAll
168
+ case "MORE":
169
+ return .more
170
+ case "SAVE":
171
+ return .save
172
+ default:
173
+ return nil
174
+ }
175
+ }
176
+ }
177
+
178
+ extension UsercentricsLayout {
179
+ static func from(enumString: String) -> UsercentricsLayout? {
180
+ switch enumString {
181
+ case "FULL":
182
+ return .full
183
+ case "SHEET":
184
+ return .sheet
185
+ case "POPUP_CENTER":
186
+ return .popup(position: .center)
187
+ case "POPUP_BOTTOM":
188
+ return .popup(position: .bottom)
189
+ default:
190
+ return nil
191
+ }
192
+ }
193
+ }
194
+
195
+ extension SectionPosition {
196
+ static func from(enumString: String?) -> SectionPosition? {
197
+ guard let enumString = enumString else { return nil }
198
+
199
+ switch enumString {
200
+ case "CENTER":
201
+ return .center
202
+ case "END":
203
+ return .right
204
+ case "START":
205
+ return .left
206
+ default:
207
+ return nil
208
+ }
209
+ }
210
+ }
211
+
212
+ extension NSTextAlignment {
213
+ static func from(enumString: String?) -> NSTextAlignment? {
214
+ guard let enumString = enumString else { return nil }
215
+
216
+ switch enumString {
217
+ case "CENTER":
218
+ return .center
219
+ case "RIGHT":
220
+ return .right
221
+ case "LEFT":
222
+ return .left
223
+ default:
224
+ return nil
225
+ }
226
+ }
227
+ }
@@ -4,7 +4,7 @@ import Usercentrics
4
4
  public extension UsercentricsReadyStatus {
5
5
  func toDictionary() -> NSDictionary {
6
6
  return [
7
- "shouldShowCMP": self.shouldShowCMP,
7
+ "shouldCollectConsent": self.shouldCollectConsent,
8
8
  "consents": self.consents.toListOfDictionary()
9
9
  ]
10
10
  }
@@ -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,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
+ }
@@ -8,7 +8,17 @@ public protocol UsercentricsManager {
8
8
  func isReady(onSuccess: @escaping ((UsercentricsReadyStatus) -> Void), onFailure: @escaping ((Error) -> Void))
9
9
  func restoreUserSession(controllerId: String, onSuccess: @escaping ((UsercentricsReadyStatus) -> Void), onFailure: @escaping ((Error) -> Void))
10
10
 
11
- func getPredefinedUI(settings: UsercentricsUISettings?, dismissViewHandler: @escaping (UsercentricsConsentUserResponse) -> Void) -> UIViewController
11
+ func showFirstLayer(bannerSettings: BannerSettings?,
12
+ hostView: UIViewController,
13
+ layout: UsercentricsLayout,
14
+ settings: FirstLayerStyleSettings?,
15
+ dismissViewHandler: @escaping (UsercentricsConsentUserResponse) -> Void)
16
+
17
+ func showSecondLayer(bannerSettings: BannerSettings?,
18
+ hostView: UIViewController,
19
+ showCloseButton: Bool,
20
+ dismissViewHandler: @escaping (UsercentricsConsentUserResponse) -> Void)
21
+
12
22
  func getTCString() -> String
13
23
  func getControllerId() -> String
14
24
  func getConsents() -> [UsercentricsServiceConsent]
@@ -59,8 +69,24 @@ final class UsercentricsManagerImplementation: UsercentricsManager {
59
69
  UsercentricsCore.reset()
60
70
  }
61
71
 
62
- func getPredefinedUI(settings: UsercentricsUISettings?, dismissViewHandler: @escaping (UsercentricsConsentUserResponse) -> Void) -> UIViewController {
63
- return UsercentricsUserInterface.getPredefinedUI(settings: settings, dismissViewHandler: dismissViewHandler)
72
+ func showFirstLayer(bannerSettings: BannerSettings?,
73
+ hostView: UIViewController,
74
+ layout: UsercentricsLayout,
75
+ settings: FirstLayerStyleSettings?,
76
+ dismissViewHandler: @escaping (UsercentricsConsentUserResponse) -> Void) {
77
+ UsercentricsBanner(bannerSettings: bannerSettings).showFirstLayer(hostView: hostView,
78
+ layout: layout,
79
+ settings: settings,
80
+ completionHandler: dismissViewHandler)
81
+ }
82
+
83
+ func showSecondLayer(bannerSettings: BannerSettings?,
84
+ hostView: UIViewController,
85
+ showCloseButton: Bool,
86
+ dismissViewHandler: @escaping (UsercentricsConsentUserResponse) -> Void) {
87
+ UsercentricsBanner(bannerSettings: bannerSettings).showSecondLayer(hostView: hostView,
88
+ showCloseButton: showCloseButton,
89
+ completionHandler: dismissViewHandler)
64
90
  }
65
91
 
66
92
  func restoreUserSession(controllerId: String, onSuccess: @escaping ((UsercentricsReadyStatus) -> Void), onFailure: @escaping ((Error) -> Void)) {
@@ -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)