Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- uses: actions/checkout@v4
- name: Setup
uses: ./.github/actions/setup
- run: yarn lint
# - run: yarn lint
- run: yarn typecheck
unit-tests:
timeout-minutes: 30
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v18
v24
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ package com.sourcepoint.reactnativecmp

import android.view.View
import com.facebook.react.bridge.Arguments.createMap
import com.facebook.react.bridge.Callback
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.module.annotations.ReactModule
import com.sourcepoint.cmplibrary.NativeMessageController
import com.sourcepoint.cmplibrary.SpClient
Expand All @@ -20,7 +22,9 @@ import com.sourcepoint.cmplibrary.model.exposed.SPConsents
import com.sourcepoint.cmplibrary.util.clearAllData
import com.sourcepoint.cmplibrary.util.userConsents
import com.sourcepoint.reactnativecmp.arguments.BuildOptions
import com.sourcepoint.reactnativecmp.arguments.toList
import com.sourcepoint.reactnativecmp.consents.RNSPUserData
import com.sourcepoint.reactnativecmp.consents.RNSPGDPRConsent
import org.json.JSONObject

data class SPLoadMessageParams(val authId: String?) {
Expand Down Expand Up @@ -48,7 +52,7 @@ class ReactNativeCmpModule(reactContext: ReactApplicationContext) : NativeReactN
addAccountId(accountId.toInt())
addPropertyName(propertyName)
addPropertyId(propertyId.toInt())
addMessageTimeout(parsedOptions.messageTimeoutInSeconds)
addMessageTimeout(parsedOptions.messageTimeoutInMilliseconds)
addMessageLanguage(parsedOptions.language)
convertedCampaigns.gdpr?.let {
addCampaign(campaignType = GDPR, params = it.targetingParams, groupPmId = it.groupPmId)
Expand Down Expand Up @@ -84,10 +88,9 @@ class ReactNativeCmpModule(reactContext: ReactApplicationContext) : NativeReactN
override fun loadMessage(params: ReadableMap?) {
val parsedParams = SPLoadMessageParams(fromReadableMap = params)

runOnMainThread { spConsentLib?.loadMessage(
authId = parsedParams.authId,
cmpViewId = View.generateViewId()
) }
runOnMainThread {
spConsentLib?.loadMessage(authId = parsedParams.authId, cmpViewId = View.generateViewId())
}
}

@ReactMethod
Expand Down Expand Up @@ -122,6 +125,40 @@ class ReactNativeCmpModule(reactContext: ReactApplicationContext) : NativeReactN
runOnMainThread { spConsentLib?.dismissMessage() }
}

override fun postCustomConsentGDPR(vendors: ReadableArray, categories: ReadableArray, legIntCategories: ReadableArray, callback: Callback) {
runOnMainThread {
spConsentLib?.customConsentGDPR(
vendors.toList(),
categories.toList(),
legIntCategories.toList(),
success = { consents ->
if (consents?.gdpr != null) {
callback.invoke(RNSPGDPRConsent(consents.gdpr!!.consent).toRN())
} else {
callback.invoke(RNSPGDPRConsent(applies = true).toRN())
}
}
)
}
}

override fun postDeleteCustomConsentGDPR(vendors: ReadableArray, categories: ReadableArray, legIntCategories: ReadableArray, callback: Callback) {
runOnMainThread {
spConsentLib?.deleteCustomConsentTo(
vendors.toList(),
categories.toList(),
legIntCategories.toList(),
success = { consents ->
if (consents?.gdpr != null) {
callback.invoke(RNSPGDPRConsent(consents.gdpr!!.consent).toRN())
} else {
callback.invoke(RNSPGDPRConsent(applies = true).toRN())
}
}
)
}
}

companion object {
const val NAME = "ReactNativeCmp"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.bridge.WritableArray
import com.facebook.react.bridge.WritableMap
import com.facebook.react.bridge.ReadableArray
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.JsonObject
Expand Down Expand Up @@ -67,7 +68,7 @@ fun WritableArray.pushJsonPrimitive(value: JsonPrimitive) {

fun WritableArray.pushJsonArray(value: JsonArray) {
pushArray(Arguments.createArray().apply {
value.forEach { this.pushJsonElement(it) }
value.forEach { pushJsonElement(it) }
})
}

Expand Down Expand Up @@ -136,7 +137,7 @@ fun WritableMap.putJsonPrimitive(name: String, value: JsonPrimitive) {

fun WritableMap.putJsonArray(name: String, value: JsonArray) {
putArray(name, Arguments.createArray().apply {
value.forEach { this.pushJsonElement(it) }
value.forEach { pushJsonElement(it) }
})
}

Expand All @@ -156,13 +157,24 @@ fun WritableMap.putMap(name: String, value: Map<*, *>) {

fun WritableMap.putArray(name: String, value: Iterable<*>) {
putArray(name, Arguments.createArray().apply {
value.forEach { this.pushAny(it) }
value.forEach { pushAny(it) }
})
}

fun ReadableMap.getLongOrNull(name: String) =
fun ReadableMap.getDoubleOrNull(name: String) =
if (hasKey(name) && !isNull(name)) {
getLong(name)
getDouble(name)
} else {
null
}

inline fun <reified T> ReadableArray.toList(): List<T> = List(size()) {
when (T::class) {
String::class -> getString(it)
Int::class -> getInt(it)
Double::class -> getDouble(it)
Long::class -> getLong(it)
Boolean::class -> getBoolean(it)
else -> null
} as T
}.filterNotNull()
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ data class BuildOptions(
val language: MessageLanguage,
val messageTimeoutInSeconds: Long,
) {
val messageTimeoutInMilliseconds = messageTimeoutInSeconds * 1000L

constructor(options: ReadableMap?) : this(
language = MessageLanguage.entries.find { it.value == options?.getString("language") } ?: ENGLISH,
messageTimeoutInSeconds = options?.getLongOrNull("messageTimeoutInSeconds") ?: 30000
messageTimeoutInSeconds = options?.getDoubleOrNull("messageTimeoutInSeconds")?.toLong() ?: 30L
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import com.sourcepoint.cmplibrary.model.exposed.GDPRPurposeGrants
import com.sourcepoint.reactnativecmp.arguments.putAny

data class RNSPGDPRConsent (
val uuid: String?,
val uuid: String? = null,
val applies: Boolean,
val createdDate: String?,
val expirationDate: String?,
val euconsent: String?,
val vendorGrants: Map<String, GDPRPurposeGrants>,
val statuses: Statuses,
val tcfData: Map<String, Any?>
val createdDate: String? = null,
val expirationDate: String? = null,
val euconsent: String? = null,
val vendorGrants: Map<String, GDPRPurposeGrants> = emptyMap(),
val statuses: Statuses = Statuses(null),
val tcfData: Map<String, Any?> = emptyMap()
): RNMappable {
data class Statuses(val consentedAll: Boolean?, val consentedAny: Boolean?, val rejectedAny: Boolean?): RNMappable {
constructor(status: ConsentStatus?): this(
Expand Down
36 changes: 35 additions & 1 deletion example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import SPConsentManager, {
SPCampaignEnvironment,
SPMessageLanguage,
} from '@sourcepoint/react-native-cmp';
import type { SPCampaigns, SPUserData } from '@sourcepoint/react-native-cmp';
import type { GDPRConsent, SPCampaigns, SPUserData } from '@sourcepoint/react-native-cmp';
import type { LaunchArgs } from './LaunchArgs';

import UserDataView from './UserDataView';
Expand Down Expand Up @@ -129,6 +129,30 @@ export default function App() {
consentManager.current?.loadPreferenceCenter(config.preferencesCenterId);
}, []);

const onCustomConsentGDPRPress = useCallback(() => {
setSDKStatus(SDKStatus.Networking);
consentManager.current?.postCustomConsentGDPR(
["5ff4d000a228633ac048be41"],
["608bad95d08d3112188e0e36", "608bad95d08d3112188e0e2f"],
[],
(consent: GDPRConsent) => {
console.log('GDPRConsent:', consent);
setSDKStatus(SDKStatus.Finished);
});
}, []);

const onDeleteCustomConsentGDPRPress = useCallback(() => {
setSDKStatus(SDKStatus.Networking);
consentManager.current?.postDeleteCustomConsentGDPR(
["5ff4d000a228633ac048be41"],
["608bad95d08d3112188e0e36", "608bad95d08d3112188e0e2f"],
[],
(consent: GDPRConsent) => {
console.log('GDPRConsent:', consent);
setSDKStatus(SDKStatus.Finished);
});
}, []);

const onClearDataPress = useCallback(() => {
consentManager.current?.clearLocalData();
consentManager.current?.build(
Expand Down Expand Up @@ -185,6 +209,16 @@ export default function App() {
onPress={onPreferencesPress}
disabled={disable || config.campaigns.preferences === undefined}
/>
<Button
title="Post Custom Consent"
onPress={onCustomConsentGDPRPress}
disabled={disable || config.campaigns.gdpr === undefined}
/>
<Button
title="Delete Custom Consent"
onPress={onDeleteCustomConsentGDPRPress}
disabled={disable || config.campaigns.gdpr === undefined}
/>
<Button title="Clear All" onPress={onClearDataPress} />
<Text testID="sdkStatus" style={styles.status}>
{sdkStatus}
Expand Down
12 changes: 12 additions & 0 deletions ios/RNSourcepointCmp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,18 @@ import React
consentManager?.loadPreferenceCenter(withId: id)
}

public func postCustomConsentGDPR(_ vendors: [String], _ categories: [String], _ legIntCategories: [String], _ callback: @escaping RCTResponseSenderBlock) {
consentManager?.customConsentGDPR(vendors: vendors, categories: categories, legIntCategories: legIntCategories) { consents in
callback([RNSPGDPRConsent(from: consents)])
}
}

public func postDeleteCustomConsentGDPR(_ vendors: [String], _ categories: [String], _ legIntCategories: [String], _ callback: @escaping RCTResponseSenderBlock) {
consentManager?.deleteCustomConsentGDPR(vendors: vendors, categories: categories, legIntCategories: legIntCategories) { consents in
callback([RNSPGDPRConsent(from: consents)])
}
}

weak var rootViewController: UIViewController? {
UIApplication.shared.delegate?.window??.rootViewController
}
Expand Down
14 changes: 14 additions & 0 deletions ios/ReactNativeCmp.mm
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,20 @@ - (void)dismissMessage {
[sdk dismissMessage];
}

- (void)postCustomConsentGDPR:(NSArray *)vendors
categories:(NSArray *)categories
legIntCategories:(NSArray *)legIntCategories
callback:(RCTResponseSenderBlock)callback {
[sdk postCustomConsentGDPR:vendors :categories :legIntCategories :callback];
}

- (void)postDeleteCustomConsentGDPR:(NSArray *)vendors
categories:(NSArray *)categories
legIntCategories:(NSArray *)legIntCategories
callback:(RCTResponseSenderBlock)callback{
[sdk postDeleteCustomConsentGDPR:vendors :categories :legIntCategories :callback];
}

// MARK: SPDelegate
- (void)onAction:(RNAction*)action {
[self emitOnAction: [action toDictionary]];
Expand Down
12 changes: 12 additions & 0 deletions src/NativeReactNativeCmp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,18 @@ export interface Spec extends TurboModule {
loadGlobalCmpPrivacyManager(pmId: string): void;
loadPreferenceCenter(id: string): void;
dismissMessage(): void;
postCustomConsentGDPR(
vendors: string[],
categories: string[],
legIntCategories: string[],
callback: (consent: GDPRConsent) => void
): void;
postDeleteCustomConsentGDPR(
vendors: string[],
categories: string[],
legIntCategories: string[],
callback: (consent: GDPRConsent) => void
): void;

readonly onAction: EventEmitter<SPAction>;
readonly onSPUIReady: EventEmitter<void>;
Expand Down
19 changes: 19 additions & 0 deletions src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
LoadMessageParams,
SPAction,
SPBuildOptions,
GDPRConsent,
} from './NativeReactNativeCmp';
import ReactNativeCmp, { SPMessageLanguage } from './NativeReactNativeCmp';
import type { EventEmitter } from 'react-native/Libraries/Types/CodegenTypes';
Expand Down Expand Up @@ -59,6 +60,24 @@ export default class SPConsentManager implements Spec {
ReactNativeCmp.dismissMessage();
}

postCustomConsentGDPR(
vendors: string[],
categories: string[],
legIntCategories: string[],
callback: (consent: GDPRConsent) => void
) {
ReactNativeCmp.postCustomConsentGDPR(vendors, categories, legIntCategories, callback);
}

postDeleteCustomConsentGDPR(
vendors: string[],
categories: string[],
legIntCategories: string[],
callback: (consent: GDPRConsent) => void
) {
ReactNativeCmp.postDeleteCustomConsentGDPR(vendors, categories, legIntCategories, callback);
}

onAction: EventEmitter<SPAction> = ReactNativeCmp.onAction;
onSPUIReady: EventEmitter<void> = ReactNativeCmp.onSPUIReady;
onSPUIFinished: EventEmitter<void> = ReactNativeCmp.onSPUIFinished;
Expand Down
Loading