diff --git a/README.md b/README.md index acb5b46..5a90523 100644 --- a/README.md +++ b/README.md @@ -123,7 +123,12 @@ const YourApp = () => { ## Contributing -See the [contributing guide](CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow. +See the [contributing guide](docs/CONTRIBUTING.md) to learn how to contribute to the repository and the development workflow. + +## Integration with Sourcepoint SDK + +See the [Sourcepoint SDK documentation](./docs/SOURCEPOINT.md) for information on integrating the Contentpass SDK with the Sourcepoint SDK. + ## License diff --git a/CODE_OF_CONDUCT.md b/docs/CODE_OF_CONDUCT.md similarity index 100% rename from CODE_OF_CONDUCT.md rename to docs/CODE_OF_CONDUCT.md diff --git a/CONTRIBUTING.md b/docs/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to docs/CONTRIBUTING.md diff --git a/docs/SOURCEPOINT_SDK_INTEGRATION.md b/docs/SOURCEPOINT_SDK_INTEGRATION.md new file mode 100644 index 0000000..a3afa5e --- /dev/null +++ b/docs/SOURCEPOINT_SDK_INTEGRATION.md @@ -0,0 +1,126 @@ +# Integration with Sourcepoint SDK +The Contentpass SDK can be seamlessly integrated with the Sourcepoint SDK to manage consent. Since the Sourcepoint SDK +is a separate React Native package, you must install it independently. For detailed guidance, refer to the +[Sourcepoint SDK documentation](https://github.com/SourcePointUSA/react-native-sourcepoint-cmp). + + +## Setting Up Sourcepoint +To use the Sourcepoint SDK, follow these steps: + +1. Create a Sourcepoint Account: Set up a Sourcepoint account and create a property with the type APP. +2. Configure the Property, ensure the property includes: + - Campaigns + - Partition Sets + - Scenarios (with the `acps` parameter — see [here](https://docs.contentpass.net/docs/onboarding/cmp/sourcepoint#4-sourcepoint-contentpass-integration) for details) + - Messages + +For more information, consult the [Sourcepoint documentation](https://docs.sourcepoint.com/hc/en-us). + +A newly created property should resemble the following example: +![sourcepoint-property-example.png](./assets/sourcepoint-property-example.png) + +### Adding a Custom Action to Sourcepoint Messages +To integrate the Sourcepoint SDK with the Contentpass SDK, you must define a custom action in Sourcepoint Messages: +1. Navigate to `Messages` > `GDPR Messages` > `Web / Webview (TCF)`. +2. Edit the relevant message and add a custom action to the login button, such as `cp('login')`. +3. This custom action will authenticate users through the Contentpass SDK. + +Example configuration: +![sourcepoint-button-config.png](./assets/sourcepoint-button-config.png) + +## Code Integration +After setting up Sourcepoint, install its SDK package. Use the following code to integrate Contentpass and Sourcepoint +SDKs in your app. + +### Important note +After creating a property in Sourcepoint, you must wait approximately 15 minutes before it becomes available for use +with the `@sourcepoint/react-native-cmp package`. This delay allows the configuration to propagate. + +### Implementation +Install the Sourcepoint SDK package, then use the following code to integrate the two SDKs in your app: + +```jsx +import { useEffect, useRef, useState } from 'react'; +import { SPConsentManager } from '@sourcepoint/react-native-cmp'; +import { ContentpassStateType, useContentpassSdk } from '@contentpass/react-native-contentpass'; + +const sourcePointConfig = { + accountId: 'SOURCEPOINT_ACCOUNT_ID', + propertyId: 'SOURCEPOINT_PROPERTY_ID', + propertyName: 'SOURCEPOINT_PROPERTY_NAME', +}; + +const setupSourcepoint = (hasValidSubscription) => { + const { accountId, propertyName, propertyId } = sourcePointConfig; + const spConsentManager = new SPConsentManager(); + + spConsentManager.build(accountId, propertyId, propertyName, { + gdpr: { + targetingParams: { + acps: hasValidSubscription ? 'true' : 'false', + }, + }, + }); + + return spConsentManager; +}; + +const App = () => { + const [authResult, setAuthResult] = useState(); + const contentpassSdk = useContentpassSdk(); + const spConsentManager = useRef(); + + useEffect(() => { + const onContentpassStateChange = (state) => { + setAuthResult(state); + }; + + contentpassSdk.registerObserver(onContentpassStateChange); + + return () => { + contentpassSdk.unregisterObserver(onContentpassStateChange); + }; + }, [contentpassSdk]); + + useEffect(() => { + // wait for the authResult to be set before setting up Sourcepoint + if (!authResult || authResult.state === ContentpassStateType.INITIALISING) { + return; + } + + spConsentManager.current = setupSourcepoint( + authResult?.hasValidSubscription ?? false + ); + + spConsentManager.current?.onAction((action) => { + if (action.customActionId === "cp('login')") { + contentpassSdk.authenticate() + } + }); + + spConsentManager.current?.loadMessage(); + + return () => { + spConsentManager.current?.dispose(); + }; + }, [authResult, contentpassSdk]); + + return ( + // Your app content + ) +} +``` + +## Troubleshooting +While integrating the Contentpass SDK with Sourcepoint SDK (version `0.3.0`), you may encounter known issues. Pull requests +addressing these issues have been submitted in the [Sourcepoint GitHub repository](https://github.com/SourcePointUSA/react-native-sourcepoint-cmp): +- [PR #10](https://github.com/SourcePointUSA/react-native-sourcepoint-cmp/pull/10) +- [PR #11](https://github.com/SourcePointUSA/react-native-sourcepoint-cmp/pull/11) + +### Temporary Fixes +If these fixes are not yet available in the latest version of the SDK, you can patch node_modules in your project using +tools like `yarn patch` or similar. The patch files can be found here: +- Patch for [PR #10](https://github.com/SourcePointUSA/react-native-sourcepoint-cmp/pull/10): [@sourcepoint-react-native-cmp-npm-0.3.0-2434c31dc9.patch](./sourcepoint-patches/@sourcepoint-react-native-cmp-npm-0.3.0-2434c31dc9.patch) +- Patch for [PR #11](https://github.com/SourcePointUSA/react-native-sourcepoint-cmp/pull/11): (https://github.com/SourcePointUSA/react-native-sourcepoint-cmp/pull/11): [@sourcepoint-react-native-cmp-patch-34fca36663.patch](./sourcepoint-patches/@sourcepoint-react-native-cmp-patch-34fca36663.patch) + +We hope these issues will be resolved in the next release of the Sourcepoint SDK, eliminating the need for manual patches. diff --git a/docs/assets/sourcepoint-button-config.png b/docs/assets/sourcepoint-button-config.png new file mode 100644 index 0000000..92c1b2a Binary files /dev/null and b/docs/assets/sourcepoint-button-config.png differ diff --git a/docs/assets/sourcepoint-property-example.png b/docs/assets/sourcepoint-property-example.png new file mode 100644 index 0000000..9db55d1 Binary files /dev/null and b/docs/assets/sourcepoint-property-example.png differ diff --git a/docs/sourcepoint-patches/@sourcepoint-react-native-cmp-npm-0.3.0-2434c31dc9.patch b/docs/sourcepoint-patches/@sourcepoint-react-native-cmp-npm-0.3.0-2434c31dc9.patch new file mode 100644 index 0000000..b916e3c --- /dev/null +++ b/docs/sourcepoint-patches/@sourcepoint-react-native-cmp-npm-0.3.0-2434c31dc9.patch @@ -0,0 +1,78 @@ +diff --git a/android/src/main/java/com/sourcepoint/reactnativecmp/RNSourcepointCmpModule.kt b/android/src/main/java/com/sourcepoint/reactnativecmp/RNSourcepointCmpModule.kt +index deb04fb18dbd8adffcb225801ad35b3154a3c7ad..4d372d921cbae6163179435feb9c821e3c37134b 100644 +--- a/android/src/main/java/com/sourcepoint/reactnativecmp/RNSourcepointCmpModule.kt ++++ b/android/src/main/java/com/sourcepoint/reactnativecmp/RNSourcepointCmpModule.kt +@@ -119,6 +119,7 @@ class RNSourcepointCmpModule internal constructor(context: ReactApplicationConte + override fun onAction(view: View, consentAction: ConsentAction): ConsentAction { + sendEvent(SDKEvent.onAction, createMap().apply { + putString("actionType", RNSourcepointActionType.from(consentAction.actionType).name) ++ putString("customActionId", consentAction.customActionId ?: "") + }) + return consentAction + } +diff --git a/ios/RNSourcepointCmp.swift b/ios/RNSourcepointCmp.swift +index 556b56618c847ad8aeaf9cdc680813cc26b732a9..ba7d707232d72fe5038ec05fb6fdb8e27216e4ee 100644 +--- a/ios/RNSourcepointCmp.swift ++++ b/ios/RNSourcepointCmp.swift +@@ -69,7 +69,10 @@ extension RNSourcepointCmp: SPDelegate { + func onAction(_ action: SPAction, from controller: UIViewController) { + RNSourcepointCmp.shared?.sendEvent( + withName: "onAction", +- body: ["actionType": RNSourcepointActionType(from: action.type).rawValue] ++ body: [ ++ "actionType": RNSourcepointActionType(from: action.type).rawValue, ++ "customActionId": action.customActionId ?? "", ++ ] + ) + } + +diff --git a/lib/typescript/src/index.d.ts b/lib/typescript/src/index.d.ts +index 1602516717becd5ec0a8e5036ad2d821110af96f..67bb9e5153b7489c5ead4dcd056b502b71bc2a7d 100644 +--- a/lib/typescript/src/index.d.ts ++++ b/lib/typescript/src/index.d.ts +@@ -12,6 +12,7 @@ export declare class SPConsentManager implements Spec { + loadUSNatPrivacyManager(pmId: string): void; + onAction(callback: (body: { + actionType: SPActionType; ++ customActionId: string; + }) => void): void; + onSPUIReady(callback: () => void): void; + onSPUIFinished(callback: () => void): void; +diff --git a/lib/typescript/src/types.d.ts b/lib/typescript/src/types.d.ts +index a15f7a06c3e0635168987a44a33009ff42bbd31c..a035a72af14f32adf0ff88f959eb8c517ffaea47 100644 +--- a/lib/typescript/src/types.d.ts ++++ b/lib/typescript/src/types.d.ts +@@ -86,6 +86,7 @@ export interface Spec extends TurboModule { + loadUSNatPrivacyManager(pmId: string): void; + onAction(callback: (body: { + actionType: SPActionType; ++ customActionId: string; + }) => void): void; + onSPUIReady(callback: () => void): void; + onSPUIFinished(callback: () => void): void; +diff --git a/src/index.ts b/src/index.ts +index b3e76b15572c56f1a4e54068b90243d6dd028e18..a03d87fea4a93edb6bf904c99d32b029b840bade 100644 +--- a/src/index.ts ++++ b/src/index.ts +@@ -67,7 +67,7 @@ export class SPConsentManager implements Spec { + RNSourcepointCmp.loadUSNatPrivacyManager(pmId); + } + +- onAction(callback: (body: { actionType: SPActionType }) => void): void { ++ onAction(callback: (body: { actionType: SPActionType, customActionId: string }) => void): void { + this.emitter.removeAllListeners('onAction'); + this.emitter.addListener('onAction', callback); + } +diff --git a/src/types.ts b/src/types.ts +index 26ac3d8162c0534af98e2a20d237856195fe5a10..4257aff5ed128988c7d3fba60545672966162b20 100644 +--- a/src/types.ts ++++ b/src/types.ts +@@ -113,7 +113,7 @@ export interface Spec extends TurboModule { + loadGDPRPrivacyManager(pmId: string): void; + loadUSNatPrivacyManager(pmId: string): void; + +- onAction(callback: (body: { actionType: SPActionType }) => void): void; ++ onAction(callback: (body: { actionType: SPActionType, customActionId: string }) => void): void; + onSPUIReady(callback: () => void): void; + onSPUIFinished(callback: () => void): void; + onFinished(callback: () => void): void; diff --git a/docs/sourcepoint-patches/@sourcepoint-react-native-cmp-patch-34fca36663.patch b/docs/sourcepoint-patches/@sourcepoint-react-native-cmp-patch-34fca36663.patch new file mode 100644 index 0000000..28b1936 --- /dev/null +++ b/docs/sourcepoint-patches/@sourcepoint-react-native-cmp-patch-34fca36663.patch @@ -0,0 +1,13 @@ +diff --git a/android/src/main/java/com/sourcepoint/reactnativecmp/RNSourcepointCmpTypes.kt b/android/src/main/java/com/sourcepoint/reactnativecmp/RNSourcepointCmpTypes.kt +index bb8c6c37adc3d7980c7e92268b98b9d97ef8de40..3d67b3a3f44743c4542a1966e66e1af3ea93c555 100644 +--- a/android/src/main/java/com/sourcepoint/reactnativecmp/RNSourcepointCmpTypes.kt ++++ b/android/src/main/java/com/sourcepoint/reactnativecmp/RNSourcepointCmpTypes.kt +@@ -16,7 +16,7 @@ data class SPCampaign( + val rawTargetingParam: ReadableMap?, + val supportLegacyUSPString: Boolean + ) { +- val targetingParams = rawTargetingParam?.toHashMap()?.map { TargetingParam(it.key, it.toString()) } ?: emptyList() ++ val targetingParams = rawTargetingParam?.toHashMap()?.map { TargetingParam(it.key, it.value.toString()) } ?: emptyList() + } + + data class SPCampaigns( diff --git a/package.json b/package.json index 9b2c393..463e184 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "android", "ios", "cpp", + "docs", "*.podspec", "!ios/build", "!android/build",