@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.
- package/CHANGELOG.md +58 -0
- package/README.md +2 -0
- package/android/build.gradle +1 -1
- package/android/src/androidTest/java/com/usercentrics/reactnativemodule/RNUsercentricsModuleTest.kt +13 -110
- package/android/src/androidTest/java/com/usercentrics/reactnativemodule/api/FakeUsercentricsProxy.kt +21 -19
- package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/GetCMPDataMock.kt +2 -1
- package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/GetConsentsMock.kt +19 -1
- package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/GetTCFDataMock.kt +2 -0
- package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/SaveDecisionsForTCFMock.kt +18 -1
- package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/SaveDecisionsMock.kt +0 -1
- package/android/src/androidTest/java/com/usercentrics/reactnativemodule/mock/ShowFirstLayerMock.kt +27 -0
- package/android/src/main/java/com/usercentrics/reactnativeusercentrics/RNUsercentricsModule.kt +41 -34
- package/android/src/main/java/com/usercentrics/reactnativeusercentrics/api/UsercentricsProxy.kt +36 -17
- package/android/src/main/java/com/usercentrics/reactnativeusercentrics/extensions/BannerFontExtensions.kt +26 -0
- package/android/src/main/java/com/usercentrics/reactnativeusercentrics/extensions/FirstLayerOptionsExtensions.kt +161 -0
- package/android/src/main/java/com/usercentrics/reactnativeusercentrics/extensions/TCFDataExtensions.kt +1 -0
- package/android/src/main/java/com/usercentrics/reactnativeusercentrics/extensions/UsercentricsReadyStatusExtensions.kt +1 -1
- package/android/src/main/java/com/usercentrics/reactnativeusercentrics/extensions/UsercentricsServiceConsentExtensions.kt +21 -0
- package/ios/Extensions/BannerSettings+Dict.swift +227 -0
- package/ios/Extensions/ReadyStatus+Dict.swift +1 -1
- package/ios/Extensions/TCFData+Dict.swift +2 -1
- package/ios/Extensions/UIColor+Extensions.swift +52 -0
- package/ios/Extensions/UIImage+UsercentricsLogoDict.swift +14 -0
- package/ios/Extensions/UsercentricsCMPData+Dict.swift +1 -1
- package/ios/Extensions/UsercentricsServiceConsents+Dict.swift +14 -0
- package/ios/Manager/UsercentricsManager.swift +29 -3
- package/ios/RNUsercentricsModule.m +8 -0
- package/ios/RNUsercentricsModule.swift +61 -34
- package/ios/RNUsercentricsModule.xcodeproj/project.pbxproj +12 -4
- package/lib/Usercentrics.d.ts +3 -2
- package/lib/Usercentrics.js +6 -2
- package/lib/Usercentrics.js.map +1 -1
- package/lib/models/BannerSettings.d.ts +18 -0
- package/lib/models/BannerSettings.js +21 -0
- package/lib/models/BannerSettings.js.map +1 -0
- package/lib/models/FirstLayerOptions.d.ts +85 -0
- package/lib/models/FirstLayerOptions.js +107 -0
- package/lib/models/FirstLayerOptions.js.map +1 -0
- package/lib/models/SecondLayerOptions.d.ts +6 -0
- package/lib/models/SecondLayerOptions.js +7 -0
- package/lib/models/SecondLayerOptions.js.map +1 -0
- package/lib/models/TCFData.d.ts +9 -8
- package/lib/models/TCFData.js +2 -1
- package/lib/models/TCFData.js.map +1 -1
- package/lib/models/UsercentricsReadyStatus.d.ts +2 -2
- package/lib/models/UsercentricsReadyStatus.js +2 -2
- package/lib/models/UsercentricsReadyStatus.js.map +1 -1
- package/lib/models/UsercentricsServiceConsent.d.ts +9 -1
- package/lib/models/UsercentricsServiceConsent.js +10 -1
- package/lib/models/UsercentricsServiceConsent.js.map +1 -1
- package/lib/models/index.d.ts +3 -1
- package/lib/models/index.js +3 -1
- package/lib/models/index.js.map +1 -1
- package/package.json +2 -2
- package/android/src/main/java/com/usercentrics/reactnativeusercentrics/extensions/UsercentricsUserInterfaceExtensions.kt +0 -45
- package/ios/Extensions/UsercentricsUISettings+Dict.swift +0 -26
- package/lib/models/UsercentricsUIOptions.d.ts +0 -17
- package/lib/models/UsercentricsUIOptions.js +0 -20
- 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
|
+
}
|
|
@@ -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("
|
|
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
|
+
}
|
|
@@ -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
|
|
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
|
|
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
|
|
63
|
-
|
|
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)
|