@cookieinformation/react-native-sdk 0.5.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/LICENSE +21 -0
- package/README.md +353 -0
- package/android/build.gradle +67 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/expo/modules/mobileconsentssdk/CookieInformationRNSDKModule.kt +455 -0
- package/android/src/main/java/expo/modules/mobileconsentssdk/UiParsing.kt +84 -0
- package/build/CookieInformationRNSDKModule.d.ts +106 -0
- package/build/CookieInformationRNSDKModule.d.ts.map +1 -0
- package/build/CookieInformationRNSDKModule.js +14 -0
- package/build/CookieInformationRNSDKModule.js.map +1 -0
- package/build/index.d.ts +3 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +6 -0
- package/build/index.js.map +1 -0
- package/expo-module.config.json +17 -0
- package/ios/CookieInformationRNSDK.podspec +30 -0
- package/ios/CookieInformationRNSDKModule.swift +235 -0
- package/ios/UiParsing.swift +58 -0
- package/package.json +73 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CookieInformationRNSDKModule.js","sourceRoot":"","sources":["../src/CookieInformationRNSDKModule.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,mBAAmB,EAAE,MAAM,MAAM,CAAC;AA6KzD,MAAM,MAAM,GAAG,mBAAmB,CAChC,wBAAwB,CACzB,CAAC;AAEF,eAAe;IACb,UAAU,EAAE,CAAC,OAA0B,EAAE,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC;IACtE,gBAAgB,EAAE,MAAM,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC;IACtD,wBAAwB,EAAE,CACxB,OAAoE,EACpE,EAAE,CAAC,MAAM,CAAC,wBAAwB,CAAC,OAAO,IAAI,EAAE,CAAC;IACnD,iBAAiB,EAAE,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC;IACxD,oBAAoB,EAAE,CAAC,MAAsB,EAAE,EAAE,CAC/C,MAAM,CAAC,oBAAoB,CAAC,MAAM,IAAI,IAAI,CAAC;IAC7C,oBAAoB,EAAE,MAAM,CAAC,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC;IAC9D,mBAAmB,EAAE,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,MAAM,CAAC;IAC5D,gBAAgB,EAAE,CAAC,MAAsB,EAAE,EAAE,CAC3C,MAAM,CAAC,gBAAgB,CAAC,MAAM,IAAI,IAAI,CAAC;IACzC,YAAY,EAAE,CACZ,YAA2B,EAC3B,UAA0C,EAC1C,MAAsB,EACtB,wBAAwC,EACxC,EAAE,CACF,MAAM,CAAC,YAAY,CACjB,YAAY,EACZ,UAAU,IAAI,IAAI,EAClB,MAAM,IAAI,IAAI,EACd,wBAAwB,IAAI,IAAI,CACjC;CACJ,CAAC","sourcesContent":["import { NativeModule, requireNativeModule } from 'expo';\n\n/**\n * Consent choices keyed by purpose/category.\n * iOS uses ConsentItemType raw values: \"necessary\", \"marketing\", \"statistical\", \"functional\", \"custom\".\n * Android uses consent item titles (localized). Custom purposes and other keys may appear.\n */\ninterface TrackingConsents {\n necessary?: boolean;\n marketing?: boolean;\n statistical?: boolean;\n functional?: boolean;\n custom?: boolean;\n [key: string]: boolean | undefined;\n}\n\n/** Consent item shape (id, universalId, title, description, required, type, accepted). Returned by getSavedConsents/cacheConsentSolution; pass to saveConsents. */\nexport interface ConsentItem {\n id: number;\n universalId: string;\n title: string;\n description: string;\n required: boolean;\n type: string;\n accepted: boolean;\n}\n\ninterface AcceptAllConsentsResponse {\n success: boolean;\n message: string;\n consents: ConsentItem[];\n count: number;\n}\n\nexport interface InitializeOptions {\n clientId: string;\n clientSecret: string;\n solutionId: string;\n /** The SDK uses languageCode if provided; otherwise it uses the device locale. */\n languageCode?: string | null;\n /** iOS only. Ignored on Android. */\n enableNetworkLogger?: boolean | null;\n ui?: UiOptions | null;\n}\n\nexport interface UiOptions {\n ios?: IosUiOptions | null;\n android?: AndroidUiOptions | null;\n}\n\nexport interface IosUiOptions {\n /** Hex color like \"#RRGGBB\" or \"#AARRGGBB\". */\n accentColor?: string | null;\n fontSet?: FontSet | null;\n}\n\nexport interface FontSet {\n largeTitle?: FontSpec | null;\n body?: FontSpec | null;\n bold?: FontSpec | null;\n}\n\nexport interface FontSpec {\n /** iOS font name. If omitted, system font is used. */\n name?: string | null;\n size?: number | null;\n weight?: 'regular' | 'medium' | 'semibold' | 'bold' | null;\n}\n\nexport interface AndroidUiOptions {\n lightColorScheme?: ColorScheme | null;\n darkColorScheme?: ColorScheme | null;\n typography?: Typography | null;\n}\n\nexport interface ColorScheme {\n primary?: string | null;\n secondary?: string | null;\n tertiary?: string | null;\n}\n\nexport interface Typography {\n bodyMedium?: TextStyle | null;\n}\n\nexport interface TextStyle {\n /** Android font resource name in res/font (e.g. \"inter_bold\"). */\n font?: string | null;\n size?: number | null;\n}\n\ninterface CacheConsentSolutionResponse {\n consentItems: ConsentItem[];\n /** Consent solution version ID (iOS only). */\n consentSolutionVersionId?: string;\n}\n\n/**\n * Confirmation returned when consent was successfully saved to the server.\n */\nexport interface SaveConsentsResponse {\n success: true;\n /** Number of consent items that were saved. */\n savedCount: number;\n}\n\ndeclare class CookieInformationRNSDKModule extends NativeModule {\n /**\n * Initialize the native SDKs.\n * This must be called before any other SDK method.\n */\n initialize(options: InitializeOptions): Promise<void>;\n /**\n * Show the privacy pop-up so the user can view and change consent choices.\n * Typically used from settings or a \"Privacy preferences\" entry point.\n * Resolves with the user's consent choices after they close the dialog.\n */\n showPrivacyPopUp(): Promise<TrackingConsents>;\n /**\n * Show the privacy pop-up only if the user has not yet consented or the consent\n * solution version has changed. Use at app start or when re-checking is needed.\n * @param options - Optional: ignoreVersionChanges (iOS), userId (Android).\n */\n showPrivacyPopUpIfNeeded(\n options?: { ignoreVersionChanges?: boolean; userId?: string | null }\n ): Promise<TrackingConsents>;\n acceptAllConsents(userId?: string): Promise<AcceptAllConsentsResponse>;\n /**\n * Remove all stored consents from the device. Data on Cookie Information servers is not deleted.\n * @param userId - (Android) Optional user id; omit for anonymous user.\n */\n removeStoredConsents(userId?: string | null): Promise<void>;\n /**\n * Fetch the consent solution from the server. On Android, also saves it to the local database.\n * Returns the consent items (from the fetched solution on iOS, from local DB after cache on Android).\n * iOS also returns the consent solution version ID.\n * Note: On iOS the SDK does not persist the fetched solution to local storage; only the returned\n * items are available in memory. Use getSavedConsents after the user has submitted choices (e.g. via\n * the consent dialog) to read persisted data on iOS.\n */\n cacheConsentSolution(): Promise<CacheConsentSolutionResponse>;\n /**\n * Retry sending any failed consent uploads.\n */\n synchronizeIfNeeded(): Promise<void>;\n /**\n * Read consent items from local storage.\n * - Android: Returns items from the local DB (cached solution + user choices). May have items\n * after cacheConsentSolution even before the user selects anything.\n * - iOS: Returns only consents that were saved when the user submitted choices (e.g. via the\n * consent dialog or saveConsents). Empty until the user has completed the flow at least once.\n * @param userId - (Android) Optional user id; omit for anonymous user. Ignored on iOS.\n */\n getSavedConsents(userId?: string | null): Promise<CacheConsentSolutionResponse>;\n /**\n * Send consent to the server manually. Pass the list of consent items (e.g. from getSavedConsents).\n * Solution id is taken from native config; mapping into SDK structures is done in native code.\n * On iOS, the consent solution version is normally taken from cacheConsentSolution.\n * You may pass consentSolutionVersionId to override that value (iOS only).\n * Saves consent items to the local database and sends them to the server on both platforms.\n * @param consentItems - List of consent items to save (id, universalId, accepted, etc.).\n * @param customData - Optional custom data (e.g. email, device_id).\n * @param userId - (Android) Optional user id; omit for anonymous user. Ignored on iOS.\n * @param consentSolutionVersionId - (iOS) Optional version id override when already known.\n */\n saveConsents(\n consentItems: ConsentItem[],\n customData?: Record<string, string> | null,\n userId?: string | null,\n consentSolutionVersionId?: string | null\n ): Promise<SaveConsentsResponse>;\n}\n\nconst native = requireNativeModule<CookieInformationRNSDKModule>(\n 'CookieInformationRNSDK',\n);\n\nexport default {\n initialize: (options: InitializeOptions) => native.initialize(options),\n showPrivacyPopUp: native.showPrivacyPopUp.bind(native),\n showPrivacyPopUpIfNeeded: (\n options?: { ignoreVersionChanges?: boolean; userId?: string | null }\n ) => native.showPrivacyPopUpIfNeeded(options ?? {}),\n acceptAllConsents: native.acceptAllConsents.bind(native),\n removeStoredConsents: (userId?: string | null) =>\n native.removeStoredConsents(userId ?? null),\n cacheConsentSolution: native.cacheConsentSolution.bind(native),\n synchronizeIfNeeded: native.synchronizeIfNeeded.bind(native),\n getSavedConsents: (userId?: string | null) =>\n native.getSavedConsents(userId ?? null),\n saveConsents: (\n consentItems: ConsentItem[],\n customData?: Record<string, string> | null,\n userId?: string | null,\n consentSolutionVersionId?: string | null\n ) =>\n native.saveConsents(\n consentItems,\n customData ?? null,\n userId ?? null,\n consentSolutionVersionId ?? null\n ),\n};"]}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAEzD,cAAc,gCAAgC,CAAC"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
// Reexport the native module. On web, it will be resolved to CookieInformationRNSDKModule.web.ts
|
|
2
|
+
// and on native platforms to CookieInformationRNSDKModule.ts
|
|
3
|
+
export { default } from './CookieInformationRNSDKModule';
|
|
4
|
+
// eslint-disable-next-line import/export
|
|
5
|
+
export * from './CookieInformationRNSDKModule';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,6DAA6D;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AACzD,yCAAyC;AACzC,cAAc,gCAAgC,CAAC","sourcesContent":["// Reexport the native module. On web, it will be resolved to CookieInformationRNSDKModule.web.ts\n// and on native platforms to CookieInformationRNSDKModule.ts\nexport { default } from './CookieInformationRNSDKModule';\n// eslint-disable-next-line import/export\nexport * from './CookieInformationRNSDKModule';\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'json'
|
|
2
|
+
|
|
3
|
+
package = JSON.parse(File.read(File.join(__dir__, '..', 'package.json')))
|
|
4
|
+
|
|
5
|
+
Pod::Spec.new do |s|
|
|
6
|
+
s.name = 'CookieInformationRNSDK'
|
|
7
|
+
s.version = package['version']
|
|
8
|
+
s.summary = package['description']
|
|
9
|
+
s.description = package['description']
|
|
10
|
+
s.license = package['license']
|
|
11
|
+
s.author = package['author']
|
|
12
|
+
s.homepage = package['homepage']
|
|
13
|
+
s.platforms = {
|
|
14
|
+
:ios => '15.1',
|
|
15
|
+
:tvos => '15.1'
|
|
16
|
+
}
|
|
17
|
+
s.swift_version = '5.4'
|
|
18
|
+
s.source = { git: 'https://github.com/cookie-information/react-native-sdk' }
|
|
19
|
+
s.static_framework = true
|
|
20
|
+
|
|
21
|
+
s.dependency 'ExpoModulesCore'
|
|
22
|
+
s.dependency 'MobileConsentsSDK'
|
|
23
|
+
|
|
24
|
+
# Swift/Objective-C compatibility
|
|
25
|
+
s.pod_target_xcconfig = {
|
|
26
|
+
'DEFINES_MODULE' => 'YES',
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
s.source_files = "**/*.{h,m,mm,swift,hpp,cpp}"
|
|
30
|
+
end
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import ExpoModulesCore
|
|
2
|
+
import Foundation
|
|
3
|
+
import MobileConsentsSDK
|
|
4
|
+
|
|
5
|
+
public class CookieInformationRNSDKModule: Module {
|
|
6
|
+
private var cookieInformationSDK: MobileConsents?
|
|
7
|
+
private var consentSolutionId: String?
|
|
8
|
+
private var consentSolutionVersionId: String?
|
|
9
|
+
private var sdkConfig: SDKConfig?
|
|
10
|
+
|
|
11
|
+
private struct SDKConfig {
|
|
12
|
+
let clientID: String
|
|
13
|
+
let clientSecret: String
|
|
14
|
+
let solutionID: String
|
|
15
|
+
let languageCode: String?
|
|
16
|
+
let logNetwork: Bool
|
|
17
|
+
let accentColor: UIColor?
|
|
18
|
+
let fontSet: FontSet?
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
private func withSDK(promise: Promise, action: @escaping (MobileConsents) -> Void) {
|
|
22
|
+
guard let sdk = cookieInformationSDK else {
|
|
23
|
+
promise.reject("INIT_ERROR", "SDK not ready")
|
|
24
|
+
return
|
|
25
|
+
}
|
|
26
|
+
action(sdk)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
private func configureSDK() {
|
|
31
|
+
guard let config = sdkConfig else { return }
|
|
32
|
+
|
|
33
|
+
consentSolutionId = config.solutionID
|
|
34
|
+
let fontSet = config.fontSet ?? .standard
|
|
35
|
+
cookieInformationSDK = MobileConsents(
|
|
36
|
+
uiLanguageCode: config.languageCode,
|
|
37
|
+
clientID: config.clientID,
|
|
38
|
+
clientSecret: config.clientSecret,
|
|
39
|
+
solutionId: config.solutionID,
|
|
40
|
+
accentColor: config.accentColor,
|
|
41
|
+
fontSet: fontSet,
|
|
42
|
+
enableNetworkLogger: config.logNetwork
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
NSLog("CookieInformationRNSDK: SDK initialized")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private func buildApprovalList(from solution: ConsentSolution) -> [UserConsent] {
|
|
49
|
+
solution.consentItems
|
|
50
|
+
.filter { $0.type != .privacyPolicy }
|
|
51
|
+
.map { UserConsent(consentItem: $0, isSelected: true) }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private func mapSelections(_ approvals: [UserConsent]) -> [String: Bool] {
|
|
55
|
+
approvals.reduce(into: [:]) { dict, consent in
|
|
56
|
+
dict[consent.consentItem.type.rawValue] = consent.isSelected
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private func mapSavedConsents(_ consents: [UserConsent]) -> [[String: Any]] {
|
|
61
|
+
consents.map { consent in
|
|
62
|
+
let item = consent.consentItem
|
|
63
|
+
return [
|
|
64
|
+
"id": 0,
|
|
65
|
+
"universalId": item.id,
|
|
66
|
+
"title": item.translations.primaryTranslation().shortText,
|
|
67
|
+
"description": item.translations.primaryTranslation().longText,
|
|
68
|
+
"required": item.required,
|
|
69
|
+
"type": item.type.rawValue,
|
|
70
|
+
"accepted": consent.isSelected,
|
|
71
|
+
] as [String: Any]
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
public func definition() -> ModuleDefinition {
|
|
76
|
+
Name("CookieInformationRNSDK")
|
|
77
|
+
|
|
78
|
+
AsyncFunction("initialize") { (options: [String: Any], promise: Promise) in
|
|
79
|
+
guard let clientID = options["clientId"] as? String,
|
|
80
|
+
let clientSecret = options["clientSecret"] as? String,
|
|
81
|
+
let solutionID = options["solutionId"] as? String else {
|
|
82
|
+
promise.reject("INVALID_INIT", "clientId, clientSecret, and solutionId are required")
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
let languageCode = options["languageCode"] as? String
|
|
86
|
+
let logNetwork = (options["enableNetworkLogger"] as? NSNumber)?.boolValue ?? false
|
|
87
|
+
let ui = options["ui"] as? [String: Any]
|
|
88
|
+
let iosUi = ui?["ios"] as? [String: Any]
|
|
89
|
+
let accentColor = UiParsing.parseHexColor(iosUi?["accentColor"] as? String)
|
|
90
|
+
let fontSet = UiParsing.parseFontSet(iosUi?["fontSet"] as? [String: Any])
|
|
91
|
+
sdkConfig = SDKConfig(
|
|
92
|
+
clientID: clientID,
|
|
93
|
+
clientSecret: clientSecret,
|
|
94
|
+
solutionID: solutionID,
|
|
95
|
+
languageCode: languageCode,
|
|
96
|
+
logNetwork: logNetwork,
|
|
97
|
+
accentColor: accentColor,
|
|
98
|
+
fontSet: fontSet
|
|
99
|
+
)
|
|
100
|
+
configureSDK()
|
|
101
|
+
promise.resolve(())
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
AsyncFunction("showPrivacyPopUp") { (promise: Promise) in
|
|
105
|
+
withSDK(promise: promise) { sdk in
|
|
106
|
+
sdk.showPrivacyPopUp { [weak self] selections in
|
|
107
|
+
promise.resolve(self?.mapSelections(selections) ?? [:])
|
|
108
|
+
} errorHandler: { error in
|
|
109
|
+
promise.reject("UI_ERROR", error.localizedDescription)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
AsyncFunction("showPrivacyPopUpIfNeeded") { (options: [String: Any]?, promise: Promise) in
|
|
115
|
+
let ignoreVersionChanges = (options?["ignoreVersionChanges"] as? NSNumber)?.boolValue ?? false
|
|
116
|
+
withSDK(promise: promise) { sdk in
|
|
117
|
+
sdk.showPrivacyPopUpIfNeeded(ignoreVersionChanges: ignoreVersionChanges) { [weak self] selections in
|
|
118
|
+
promise.resolve(self?.mapSelections(selections) ?? [:])
|
|
119
|
+
} errorHandler: { error in
|
|
120
|
+
promise.reject("UI_ERROR", error.localizedDescription)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
AsyncFunction("acceptAllConsents") { (promise: Promise) in
|
|
126
|
+
withSDK(promise: promise) { sdk in
|
|
127
|
+
sdk.fetchConsentSolution { [weak self] result in
|
|
128
|
+
switch result {
|
|
129
|
+
case .success(let solution):
|
|
130
|
+
let approvals = self?.buildApprovalList(from: solution) ?? []
|
|
131
|
+
let payload = Consent(
|
|
132
|
+
consentSolutionId: solution.id,
|
|
133
|
+
consentSolutionVersionId: solution.versionId,
|
|
134
|
+
userConsents: approvals
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
sdk.postConsent(payload) { [weak self] error in
|
|
138
|
+
if let error = error {
|
|
139
|
+
promise.reject("SAVE_ERROR", error.localizedDescription)
|
|
140
|
+
return
|
|
141
|
+
}
|
|
142
|
+
promise.resolve(self?.mapSelections(approvals) ?? [:])
|
|
143
|
+
}
|
|
144
|
+
case .failure:
|
|
145
|
+
promise.reject("FETCH_ERROR", "Unable to load consent configuration")
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
AsyncFunction("removeStoredConsents") { (_: String?, promise: Promise) in
|
|
152
|
+
withSDK(promise: promise) { sdk in
|
|
153
|
+
sdk.removeStoredConsents()
|
|
154
|
+
promise.resolve(())
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
AsyncFunction("cacheConsentSolution") { (promise: Promise) in
|
|
159
|
+
withSDK(promise: promise) { [weak self] sdk in
|
|
160
|
+
guard let self = self else {
|
|
161
|
+
promise.reject("MODULE_ERROR", "SDK module unavailable")
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
sdk.fetchConsentSolution { result in
|
|
165
|
+
switch result {
|
|
166
|
+
case .success(let solution):
|
|
167
|
+
self.consentSolutionVersionId = solution.versionId
|
|
168
|
+
let userConsents = solution.consentItems.map { UserConsent(consentItem: $0, isSelected: false) }
|
|
169
|
+
let items = self.mapSavedConsents(userConsents)
|
|
170
|
+
promise.resolve([
|
|
171
|
+
"consentItems": items,
|
|
172
|
+
"consentSolutionVersionId": solution.versionId
|
|
173
|
+
])
|
|
174
|
+
case .failure(let error):
|
|
175
|
+
promise.reject("FETCH_ERROR", error.localizedDescription)
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
AsyncFunction("synchronizeIfNeeded") { (promise: Promise) in
|
|
182
|
+
withSDK(promise: promise) { sdk in
|
|
183
|
+
sdk.synchronizeIfNeeded()
|
|
184
|
+
promise.resolve(())
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
AsyncFunction("getSavedConsents") { (_: String?, promise: Promise) in
|
|
189
|
+
withSDK(promise: promise) { [weak self] sdk in
|
|
190
|
+
guard let self = self else {
|
|
191
|
+
promise.reject("MODULE_ERROR", "SDK module unavailable")
|
|
192
|
+
return
|
|
193
|
+
}
|
|
194
|
+
let consents = sdk.getSavedConsents()
|
|
195
|
+
let items = self.mapSavedConsents(consents)
|
|
196
|
+
promise.resolve(["consentItems": items])
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
AsyncFunction("saveConsents") { (consentItemsRaw: [[String: Any]], customDataRaw: [String: String]?, _: String?, consentSolutionVersionId: String?, promise: Promise) in
|
|
201
|
+
withSDK(promise: promise) { sdk in
|
|
202
|
+
guard let solutionId = self.consentSolutionId, !solutionId.isEmpty else {
|
|
203
|
+
promise.reject("NO_CONFIG", "CookieInformation.plist missing or solutionID not set")
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
let versionId = consentSolutionVersionId ?? self.consentSolutionVersionId
|
|
207
|
+
guard let versionId = versionId else {
|
|
208
|
+
promise.reject("NO_VERSION", "Call cacheConsentSolution first or provide consentSolutionVersionId")
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
var consent = Consent(
|
|
212
|
+
consentSolutionId: solutionId,
|
|
213
|
+
consentSolutionVersionId: versionId,
|
|
214
|
+
customData: customDataRaw,
|
|
215
|
+
userConsents: []
|
|
216
|
+
)
|
|
217
|
+
let language = "EN"
|
|
218
|
+
for itemDict in consentItemsRaw {
|
|
219
|
+
guard let universalId = itemDict["universalId"] as? String,
|
|
220
|
+
let accepted = itemDict["accepted"] as? Bool else { continue }
|
|
221
|
+
let purpose = ProcessingPurpose(consentItemId: universalId, consentGiven: accepted, language: language)
|
|
222
|
+
consent.addProcessingPurpose(purpose)
|
|
223
|
+
}
|
|
224
|
+
sdk.postConsent(consent) { error in
|
|
225
|
+
if let error = error {
|
|
226
|
+
promise.reject("SAVE_CONSENTS_ERROR", error.localizedDescription)
|
|
227
|
+
return
|
|
228
|
+
}
|
|
229
|
+
let count = consent.processingPurposes.count
|
|
230
|
+
promise.resolve(["success": true, "savedCount": count] as [String: Any])
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
import MobileConsentsSDK
|
|
3
|
+
import UIKit
|
|
4
|
+
|
|
5
|
+
enum UiParsing {
|
|
6
|
+
static func parseHexColor(_ value: String?) -> UIColor? {
|
|
7
|
+
guard let value = value?.trimmingCharacters(in: .whitespacesAndNewlines),
|
|
8
|
+
value.hasPrefix("#") else {
|
|
9
|
+
return nil
|
|
10
|
+
}
|
|
11
|
+
let hex = String(value.dropFirst())
|
|
12
|
+
var rgba: UInt64 = 0
|
|
13
|
+
let scanner = Scanner(string: hex)
|
|
14
|
+
guard scanner.scanHexInt64(&rgba) else { return nil }
|
|
15
|
+
if hex.count == 6 {
|
|
16
|
+
let r = CGFloat((rgba & 0xFF0000) >> 16) / 255.0
|
|
17
|
+
let g = CGFloat((rgba & 0x00FF00) >> 8) / 255.0
|
|
18
|
+
let b = CGFloat(rgba & 0x0000FF) / 255.0
|
|
19
|
+
return UIColor(red: r, green: g, blue: b, alpha: 1.0)
|
|
20
|
+
}
|
|
21
|
+
if hex.count == 8 {
|
|
22
|
+
let a = CGFloat((rgba & 0xFF000000) >> 24) / 255.0
|
|
23
|
+
let r = CGFloat((rgba & 0x00FF0000) >> 16) / 255.0
|
|
24
|
+
let g = CGFloat((rgba & 0x0000FF00) >> 8) / 255.0
|
|
25
|
+
let b = CGFloat(rgba & 0x000000FF) / 255.0
|
|
26
|
+
return UIColor(red: r, green: g, blue: b, alpha: a)
|
|
27
|
+
}
|
|
28
|
+
return nil
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
static func parseFontSet(_ value: [String: Any]?) -> FontSet? {
|
|
32
|
+
guard let value = value else { return nil }
|
|
33
|
+
let largeTitle = parseFontSpec(value["largeTitle"] as? [String: Any], defaultSize: 34, defaultWeight: .bold)
|
|
34
|
+
let body = parseFontSpec(value["body"] as? [String: Any], defaultSize: 14, defaultWeight: .regular)
|
|
35
|
+
let bold = parseFontSpec(value["bold"] as? [String: Any], defaultSize: 14, defaultWeight: .bold)
|
|
36
|
+
return FontSet(largeTitle: largeTitle, body: body, bold: bold)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
private static func parseFontSpec(_ value: [String: Any]?, defaultSize: CGFloat, defaultWeight: UIFont.Weight) -> UIFont {
|
|
40
|
+
let size = (value?["size"] as? NSNumber)?.doubleValue ?? Double(defaultSize)
|
|
41
|
+
let weight = parseWeight(value?["weight"] as? String) ?? defaultWeight
|
|
42
|
+
if let name = value?["name"] as? String, let font = UIFont(name: name, size: size) {
|
|
43
|
+
return font
|
|
44
|
+
}
|
|
45
|
+
return UIFont.systemFont(ofSize: size, weight: weight)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private static func parseWeight(_ value: String?) -> UIFont.Weight? {
|
|
49
|
+
guard let value = value?.lowercased() else { return nil }
|
|
50
|
+
switch value {
|
|
51
|
+
case "regular": return .regular
|
|
52
|
+
case "medium": return .medium
|
|
53
|
+
case "semibold": return .semibold
|
|
54
|
+
case "bold": return .bold
|
|
55
|
+
default: return nil
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@cookieinformation/react-native-sdk",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Cookie Information consent management SDK for React Native",
|
|
5
|
+
"private": false,
|
|
6
|
+
"main": "build/index.js",
|
|
7
|
+
"types": "build/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./build/index.d.ts",
|
|
11
|
+
"default": "./build/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./package.json": "./package.json"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"build/",
|
|
17
|
+
"android/build.gradle",
|
|
18
|
+
"android/src/",
|
|
19
|
+
"ios/",
|
|
20
|
+
"expo-module.config.json",
|
|
21
|
+
"README.md",
|
|
22
|
+
"LICENSE"
|
|
23
|
+
],
|
|
24
|
+
"scripts": {
|
|
25
|
+
"build": "expo-module build",
|
|
26
|
+
"clean": "expo-module clean",
|
|
27
|
+
"lint": "expo-module lint",
|
|
28
|
+
"test": "expo-module test",
|
|
29
|
+
"prepare": "expo-module prepare",
|
|
30
|
+
"prepublishOnly": "expo-module prepublishOnly",
|
|
31
|
+
"expo-module": "expo-module",
|
|
32
|
+
"open:ios": "xed example/ios",
|
|
33
|
+
"open:android": "open -a \"Android Studio\" example/android",
|
|
34
|
+
"android": "expo run:android",
|
|
35
|
+
"ios": "expo run:ios"
|
|
36
|
+
},
|
|
37
|
+
"keywords": [
|
|
38
|
+
"react-native",
|
|
39
|
+
"expo",
|
|
40
|
+
"react-native-cookie-information-consents-sdk",
|
|
41
|
+
"CookieInformationRNSDK"
|
|
42
|
+
],
|
|
43
|
+
"repository": "https://github.com/cookie-information/react-native-sdk",
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/cookie-information/react-native-sdk/issues"
|
|
46
|
+
},
|
|
47
|
+
"author": "CookieInformation",
|
|
48
|
+
"license": "MIT",
|
|
49
|
+
"homepage": "https://github.com/cookie-information/react-native-sdk#readme",
|
|
50
|
+
"publishConfig": {
|
|
51
|
+
"access": "public"
|
|
52
|
+
},
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=20"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@types/jest": "^30.0.0",
|
|
58
|
+
"expo-module-scripts": "~4.1.10",
|
|
59
|
+
"jest": "^29.7.0",
|
|
60
|
+
"ts-jest": "^29.4.6",
|
|
61
|
+
"typescript": "~5.8.3"
|
|
62
|
+
},
|
|
63
|
+
"dependencies": {
|
|
64
|
+
"expo": "^54.0.33",
|
|
65
|
+
"react": "^19.2.4",
|
|
66
|
+
"react-native": "^0.83.1"
|
|
67
|
+
},
|
|
68
|
+
"peerDependencies": {
|
|
69
|
+
"expo": "^54.0.33",
|
|
70
|
+
"react": "^19.2.4",
|
|
71
|
+
"react-native": "^0.83.1"
|
|
72
|
+
}
|
|
73
|
+
}
|