Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/develop' into sync-upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
batsis committed Oct 25, 2024
2 parents 34f5ff5 + c3b465b commit f59fc2d
Show file tree
Hide file tree
Showing 19 changed files with 3,884 additions and 2,419 deletions.
100 changes: 82 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,34 +6,89 @@ It allows Expo-based apps to integrate with the Marketing Cloud SDK.

## Installation

To install the package use your prefered package manager:
To install the package use your preferred package manager:

```bash
npm install @allboatsrise/expo-marketingcloudsdk expo-notifications
npm install @allboatsrise/expo-marketingcloudsdk expo-notifications zod
```
or
```bash
yarn add @allboatsrise/expo-marketingcloudsdk expo-notifications
yarn add @allboatsrise/expo-marketingcloudsdk expo-notifications zod
```

## Plugin setup
#### [View parameters](#plugin-parameters)

Add package to `plugins` in `app.js`/`app.config.js`.
Add package to `plugins` in `app.js`/`app.config.js` with minimal configuration.

```javascript
expo: {
...
plugins: [
```json
"expo": {
"plugins": [
[
'@allboatsrise/expo-marketingcloudsdk', {
appId: << MARKETING_CLOUD_APP_ID >>,
accessToken: << MARKETING_CLOUD_ACCESS_TOKEN >>,
serverUrl: << MARKETING_CLOUD_SERVER_URL >>,
"@allboatsrise/expo-marketingcloudsdk", {
"appId": "<< MARKETING_CLOUD_APP_ID >>",
"accessToken": "<< MARKETING_CLOUD_ACCESS_TOKEN >>",
"serverUrl": "<< MARKETING_CLOUD_SERVER_URL >>",
}
],
'expo-notifications',
]
"expo-notifications"
]
}
```

Sample initialization of notifications in the app

```typescript
import * as Notifications from 'expo-notifications'
import * as MarketingCloud from '@allboatsrise/expo-marketingcloudsdk'

// ensure push notifications appear regardless whether app is active or not
Notifications.setNotificationHandler({
handleNotification: async (_notification) => {
return {
shouldShowAlert: true,
shouldPlaySound: true,
shouldSetBadge: true,
}
},
})

export const App: React.FC = () => {
useEffect(() => {
let cleanup = () => {}

;(async () => {
// request push notifications permission on load
// ideally: show this elsewhere where it's more relevant instead of as soon as when the ap loads
let result = await Notifications.getPermissionsAsync()
if (!result.granted && result.canAskAgain) {
result = await Notifications.requestPermissionsAsync({
ios: {
allowAlert: true,
allowBadge: true,
allowSound: true,
},
})
}

if (!result.granted) return

const token = await Notifications.getDevicePushTokenAsync()

// let Marketing Cloud SDK the value of current push token
MarketingCloud.setSystemToken(token.data)

// In rare situations a push token may be changed by the push notification service while the app is running.
const subscription = Notifications.addPushTokenListener((newToken) => {
MarketingCloud.setSystemToken(newToken.data)
})
cleanup = () => subscription.remove()
})()

return () => cleanup()
}, [])

// remaining app logic...
}
```

Expand All @@ -51,7 +106,8 @@ expo: {
| `analyticsEnabled` | boolean | No | Sets the configuration flag that enables or disables Salesforce MarketingCloud Analytics services |
| `applicationControlsBadging` | boolean | No | Sets the configuration value which enables or disables application control over badging |
| `delayRegistrationUntilContactKeyIsSet` | boolean | No | Sets the configuration value which enables or disables application control over delaying SDK registration until a contact key is set |
| `markNotificationReadOnInboxNotificationOpen` | boolean | No | Sets the configuration value which enables or disables marking inbox notifications as read on open (Android only) |
| `markNotificationReadOnInboxNotificationOpen` | boolean | No | Sets the configuration value which enables or disables marking inbox notifications as read on open |
| `debug` | boolean | No | Enable logging debug messages |

# Usage

Expand Down Expand Up @@ -83,15 +139,17 @@ Various functions, their parameters, return values, and their specific purposes
| `getMessages` | None | `Promise<InboxMessage[]>` | Returns a promise that resolves to an array of `InboxMessage` objects representing the inbox messages. |
| `getReadMessageCount` | None | `Promise<number>` | Returns a promise that resolves to a number representing the total number of read inbox messages. |
| `getReadMessages` | None | `Promise<InboxMessage[]>` | Returns a promise that resolves to an array of `InboxMessage` objects representing the read inbox messages. |
| `trackMessageOpened` | `messageId`: string | Promise<boolean> | Returns a promise that resolves to true when inbox open event successfully triggered on message. |


## Add event listener
Available event listeners:

| Function | Parameters | Description |
| --- | --- | --- |
| `addLogListener` | `listener: (event: LogEventPayload) => void` | Adds a listener function to the `onLog` event, which is triggered when a new log event is generated. The function should take an argument of type `LogEventPayload`, which contains information about the log event. Returns a `Subscription` object that can be used to unsubscribe the listener. |
| `addInboxResponseListener` | `listener: (event: InboxResponsePayload) => void` | Adds a listener function to the `onInboxResponse` event, which is triggered when a new inbox response is received. The function should take an argument of type `InboxResponsePayload`, which contains information about the inbox response. Returns a `Subscription` object that can be used to unsubscribe the listener. |
| `addLogListener` | `listener: (event: LogEventPayload) => void` | Adds a listener function to the `onLog` event, which is triggered when a new log event is generated. |
| `addInboxResponseListener` | `listener: (event: InboxResponsePayload) => void` | Adds a listener function to the `onInboxResponse` event, which is triggered when a new inbox response is received. |
| `addRegistrationResponseSucceededListener` | `listener: (event: RegistrationResponseSucceededPayload) => void` | Adds a listener function to the `onRegistrationResponseSucceeded` event, which is triggered when SDK successfully registers with backend. |

```typescript
// listeners being used in a useEffect hook.
Expand All @@ -100,13 +158,19 @@ useEffect(() => {
const logSubscription = addLogListener((logEvent: LogEventPayload) => {
// Do something with logEvent
})
const inboxSubscription = addInboxResponseListener((inboxEvent: InboxMessage[]) => {

const inboxSubscription = addInboxResponseListener((inboxEvent: InboxResponsePayload) => {
// Do something with inboxEvent
})

const registrationSubscription = MarketingCloud.addRegistrationResponseSucceededListener((registrationEvent: RegistrationResponseSucceededPayload) => {
// Do something with registrationEvent
})

return () => {
logSubscription.remove()
inboxSubscription.remove()
registrationSubscription.remove()
}
}, [])
```
Expand Down
100 changes: 28 additions & 72 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -1,98 +1,54 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'maven-publish'

group = 'expo.modules.marketingcloudsdk'
version = '47.0.0'

buildscript {
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
if (expoModulesCorePlugin.exists()) {
apply from: expoModulesCorePlugin
applyKotlinExpoModulesCorePlugin()
}

// Simple helper that allows the root project to override versions declared by this library.
ext.safeExtGet = { prop, fallback ->
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}

// Ensures backward compatibility
ext.getKotlinVersion = {
if (ext.has("kotlinVersion")) {
ext.kotlinVersion()
} else {
ext.safeExtGet("kotlinVersion", "1.6.10")
version = '51.0.5'

def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
apply from: expoModulesCorePlugin
applyKotlinExpoModulesCorePlugin()
useCoreDependencies()
useExpoPublishing()

// If you want to use the managed Android SDK versions from expo-modules-core, set this to true.
// The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code.
// Most of the time, you may like to manage the Android SDK versions yourself.
def useManagedAndroidSdkVersions = false
if (useManagedAndroidSdkVersions) {
useDefaultAndroidSdkVersions()
} else {
buildscript {
// Simple helper that allows the root project to override versions declared by this library.
ext.safeExtGet = { prop, fallback ->
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}
}

repositories {
mavenCentral()
}

dependencies {
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getKotlinVersion()}")
}
}

// Creating sources with comments
task androidSourcesJar(type: Jar) {
classifier = 'sources'
from android.sourceSets.main.java.srcDirs
}

afterEvaluate {
publishing {
publications {
release(MavenPublication) {
from components.release
// Add additional sourcesJar to artifacts
artifact(androidSourcesJar)
}
}
repositories {
maven {
url = mavenLocal().url
}
project.android {
compileSdkVersion safeExtGet("compileSdkVersion", 34)
defaultConfig {
minSdkVersion safeExtGet("minSdkVersion", 21)
targetSdkVersion safeExtGet("targetSdkVersion", 34)
}
}
}

android {
compileSdkVersion safeExtGet("compileSdkVersion", 31)

compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}

kotlinOptions {
jvmTarget = JavaVersion.VERSION_11.majorVersion
freeCompilerArgs += [
"-Xjvm-default=all",
]
}

namespace "expo.modules.marketingcloudsdk"
defaultConfig {
minSdkVersion safeExtGet("minSdkVersion", 21)
targetSdkVersion safeExtGet("targetSdkVersion", 31)
versionCode 1
versionName "47.0.0"
versionName "51.0.5"
}
lintOptions {
abortOnError false
}
}

repositories {
mavenCentral()
maven { url "https://salesforce-marketingcloud.github.io/MarketingCloudSDK-Android/repository" }
}

dependencies {
implementation project(":expo-modules-core")
implementation project(":expo-notifications")
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
implementation 'com.facebook.react:react-native:+'
api "com.salesforce.marketingcloud:marketingcloudsdk:8.0.8"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.3"
api "com.salesforce.marketingcloud:marketingcloudsdk:8.2.0"
}
Original file line number Diff line number Diff line change
@@ -1,49 +1,58 @@
package expo.modules.marketingcloudsdk

import android.app.Activity
import android.app.Application
import android.content.Context
import android.os.Bundle
import android.util.Log
import com.salesforce.marketingcloud.MCLogListener
import com.salesforce.marketingcloud.MarketingCloudConfig
import com.salesforce.marketingcloud.MarketingCloudSdk
import com.salesforce.marketingcloud.notifications.NotificationCustomizationOptions
import com.salesforce.marketingcloud.sfmcsdk.BuildConfig
import com.salesforce.marketingcloud.sfmcsdk.SFMCSdk
import com.salesforce.marketingcloud.sfmcsdk.SFMCSdkModuleConfig
import com.salesforce.marketingcloud.sfmcsdk.components.logging.LogLevel
import com.salesforce.marketingcloud.sfmcsdk.components.logging.LogListener
import expo.modules.core.interfaces.ReactActivityLifecycleListener
import expo.modules.core.interfaces.ApplicationLifecycleListener

class ExpoMarketingCloudSdkReactActivityLifecycleListener(activityContext: Context) : ReactActivityLifecycleListener {
override fun onCreate(activity: Activity, savedInstanceState: Bundle?) {
class ExpoMarketingCloudSdkApplicationLifecycleListener : ApplicationLifecycleListener {
override fun onCreate(application: Application) {
// Initialize logging _before_ initializing the SDK to avoid losing valuable debugging information.
if(BuildConfig.DEBUG) {
if(getDebug(application)) {
SFMCSdk.setLogging(LogLevel.DEBUG, LogListener.AndroidLogger())
MarketingCloudSdk.setLogLevel(MCLogListener.VERBOSE)
MarketingCloudSdk.setLogListener(MCLogListener.AndroidLogListener())
SFMCSdk.requestSdk { sdk ->
sdk.mp { push ->
push.registrationManager.registerForRegistrationEvents {
// Log the registration on successful sends to the MC
Log.i("~#SFMC-expo", "Registration: $it")
}
}
}
}

// Configure Salesforce Marketing Cloud SDK
SFMCSdk.configure(activity, SFMCSdkModuleConfig.build {
SFMCSdk.configure(application, SFMCSdkModuleConfig.build {
pushModuleConfig = MarketingCloudConfig.builder().apply {
setApplicationId(getAppId(activity))
setAccessToken(getAccessToken(activity))
setAnalyticsEnabled(getAnalyticsEnabled(activity))
setMarketingCloudServerUrl(getServerUrl(activity))
setDelayRegistrationUntilContactKeyIsSet(getDelayRegistrationUntilContactKeyIsSet(activity))
if(getSenderId(activity) != "") setSenderId(getSenderId(activity))
setInboxEnabled(getInboxEnabled(activity))
setMarkMessageReadOnInboxNotificationOpen(getMarkMessageReadOnInboxNotificationOpen(activity))
setApplicationId(getAppId(application))
setAccessToken(getAccessToken(application))
setAnalyticsEnabled(getAnalyticsEnabled(application))
setMarketingCloudServerUrl(getServerUrl(application))
setDelayRegistrationUntilContactKeyIsSet(getDelayRegistrationUntilContactKeyIsSet(application))
if(getSenderId(application) != "") setSenderId(getSenderId(application))
setInboxEnabled(getInboxEnabled(application))
setMarkMessageReadOnInboxNotificationOpen(getMarkMessageReadOnInboxNotificationOpen(application))
setNotificationCustomizationOptions(
NotificationCustomizationOptions.create(R.drawable.notification_icon)
)
}.build(activity)
}.build(application)
}) { initStatus ->
// TODO handle initialization status
Log.e("SMFCSdk: initStatus", initStatus.toString())
}
}

private fun getDebug(context: Context): Boolean = context.resources.getString(R.string.expo_marketingcloudsdk_debug) == "true"
private fun getAppId(context: Context): String = context.resources.getString(R.string.expo_marketingcloudsdk_app_id)
private fun getAccessToken(context: Context): String = context.resources.getString(R.string.expo_marketingcloudsdk_access_token)
private fun getServerUrl(context: Context): String = context.resources.getString(R.string.expo_marketingcloudsdk_server_url)
Expand Down
Loading

0 comments on commit f59fc2d

Please sign in to comment.