Skip to content

Instantly share code, notes, and snippets.

@ovidb
Created April 29, 2024 14:02
Show Gist options
  • Save ovidb/40384ce371e0a696873ad0cbcde0bbd7 to your computer and use it in GitHub Desktop.
Save ovidb/40384ce371e0a696873ad0cbcde0bbd7 to your computer and use it in GitHub Desktop.
Intercom Plugin with Notifications for both iOS and Android
...
plugins: [
[
'./plugins/withIntercom',
{
appId: Env.INTERCOM_APP_ID,
androidApiKey: Env.INTERCOM_AND_API_KEY,
iosApiKey: Env.INTERCOM_IOS_API_KEY,
},
],
]
...
const {
AndroidConfig,
ConfigPlugin,
withAndroidManifest,
withAppDelegate,
withInfoPlist,
withMainApplication,
createRunOncePlugin,
withDangerousMod,
} = require('@expo/config-plugins')
const {
addImports,
appendContentsInsideDeclarationBlock,
} = require('@expo/config-plugins/build/android/codeMod')
const {
addObjcImports,
insertContentsInsideObjcFunctionBlock,
} = require('@expo/config-plugins/build/ios/codeMod')
const packageJson = require('../package.json')
const path = require('path')
const { promises: fs } = require('fs')
const withIntercomReactNative = createRunOncePlugin(
(config, props) => {
let newConfig = config
newConfig = withIntercomAndroid(newConfig, props)
newConfig = withIntercomIOS(newConfig, props)
return newConfig
},
'withIntercomReactNative',
packageJson.version,
)
function withIntercomAndroid(config, props) {
let newConfig = config
newConfig = mainApplication(newConfig, props)
newConfig = androidManifest(newConfig, props)
newConfig = withIntercomMainNotificationService(newConfig)
return newConfig
}
function withIntercomIOS(config, props) {
let newConfig = appDelegate(config, props)
newConfig = infoPlist(newConfig, props)
return newConfig
}
function mainApplication(config, props) {
return withMainApplication(config, config => {
let stringContents = config.modResults.contents
stringContents = addImports(
stringContents,
['com.intercom.reactnative.IntercomModule;'],
false,
)
stringContents = appendContentsInsideDeclarationBlock(
stringContents,
'onCreate',
`IntercomModule.initialize(this, "${props.androidApiKey}", "${props.appId}");`,
)
config.modResults.contents = stringContents
return config
})
}
function withRegionsManifest(newConfig, intercomRegion) {
return withAndroidManifest(newConfig, config => {
const currentMainApplication =
AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults)
const androidRegionMapper = {
US: '@integer/intercom_server_region_us',
EU: '@integer/intercom_server_region_eu',
AU: '@integer/intercom_server_region_aus',
}
AndroidConfig.Manifest.addMetaDataItemToMainApplication(
currentMainApplication,
'io.intercom.android.sdk.server.region',
androidRegionMapper[intercomRegion],
)
return config
})
}
function withIntercomMainNotificationServiceManifest(newConfig) {
return withAndroidManifest(newConfig, config => {
config.modResults.manifest.$ = config.modResults.manifest.$ = {
...config.modResults.manifest.$,
'xmlns:tools': 'http://schemas.android.com/tools',
}
if (config.modResults.manifest.application?.[0]) {
const serviceExists =
config.modResults.manifest.application[0].service?.some(
service => service.$['android:name'] === '.MainNotificationService',
)
if (!serviceExists) {
config.modResults.manifest.application[0].service = [
...(config.modResults.manifest.application[0].service ?? []),
{
$: {
'android:name': '.MainNotificationService',
'android:exported': 'false',
},
'intent-filter': [
{
$: {},
action: [
{
$: {
'android:name': 'com.google.firebase.MESSAGING_EVENT',
},
},
],
},
],
},
]
}
const receiverExists =
config.modResults.manifest.application[0].receiver?.some(
receiver =>
receiver.$['android:name'] ===
'com.intercom.reactnative.RNIntercomPushBroadcastReceiver',
)
if (!receiverExists) {
config.modResults.manifest.application[0].receiver = [
...(config.modResults.manifest.application[0].receiver ?? []),
{
$: {
'android:name':
'com.intercom.reactnative.RNIntercomPushBroadcastReceiver',
'tools:replace': 'android:exported',
'android:exported': 'true',
},
},
]
}
}
return config
})
}
function androidManifest(config, { intercomRegion = 'US' }) {
let newConfig = AndroidConfig.Permissions.withPermissions(
config,
[
'android.permission.READ_EXTERNAL_STORAGE',
'android.permission.VIBRATE',
].filter(Boolean),
)
newConfig = withRegionsManifest(newConfig, intercomRegion)
newConfig = withIntercomMainNotificationServiceManifest(newConfig)
return newConfig
}
function getMainNotificationService(packageName) {
return `package ${packageName};
import expo.modules.notifications.service.ExpoFirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import com.intercom.reactnative.IntercomModule;
public class MainNotificationService extends ExpoFirebaseMessagingService {
@Override
public void onNewToken(String refreshedToken) {
IntercomModule.sendTokenToIntercom(getApplication(), refreshedToken);
super.onNewToken(refreshedToken);
}
@Override
public void onMessageReceived(RemoteMessage remoteMessage) {
if (IntercomModule.isIntercomPush(remoteMessage)) {
IntercomModule.handleRemotePushMessage(getApplication(), remoteMessage);
} else {
super.onMessageReceived(remoteMessage);
}
}
}`
}
function getPackageRoot(projectRoot) {
return path.join(projectRoot, 'android', 'app', 'src', 'main', 'java')
}
function getCurrentPackageName(projectRoot, packageRoot) {
const mainApplication = AndroidConfig.Paths.getProjectFilePath(
projectRoot,
'MainApplication',
)
const packagePath = path.dirname(mainApplication)
const packagePathParts = path
.relative(packageRoot, packagePath)
.split(path.sep)
.filter(Boolean)
return packagePathParts.join('.')
}
async function saveFileAsync(path, content) {
return fs.writeFile(path, content, 'utf8')
}
async function createMainNotificationService(projectRoot) {
const packageRoot = getPackageRoot(projectRoot)
const packageName = getCurrentPackageName(projectRoot, packageRoot)
const filePath = path.join(
packageRoot,
`${packageName.split('.').join('/')}/MainNotificationService.java`,
)
try {
return await saveFileAsync(
filePath,
getMainNotificationService(packageName),
)
} catch (e) {
WarningAggregator.addWarningAndroid(
'withIntercom',
`Couldn't create MainNotificationService.java - ${e}.`,
)
}
}
function withIntercomMainNotificationService(config) {
return withDangerousMod(config, [
'android',
async config => {
await createMainNotificationService(config.modRequest.projectRoot)
return config
},
])
}
function appDelegate(config, props) {
return withAppDelegate(config, config => {
let stringContents = config.modResults.contents
stringContents = addObjcImports(stringContents, ['<IntercomModule.h>'])
stringContents = addObjcImports(stringContents, [
'<UserNotifications/UserNotifications.h>',
])
stringContents = insertContentsInsideObjcFunctionBlock(
stringContents,
'application didFinishLaunchingWithOptions:',
`[IntercomModule initialize:@"${props.iosApiKey}" withAppId:@"${props.appId}"];`,
{ position: 'tailBeforeLastReturn' },
)
stringContents = insertContentsInsideObjcFunctionBlock(
stringContents,
'application didRegisterForRemoteNotificationsWithDeviceToken:',
`[IntercomModule setDeviceToken:deviceToken];`,
{ position: 'tailBeforeLastReturn' },
)
config.modResults.contents = stringContents
return config
})
}
function infoPlist(config, { intercomRegion }) {
const newConfig = withInfoPlist(config, config => {
if (intercomRegion) {
config.modResults.IntercomRegion = intercomRegion
}
return config
})
return newConfig
}
module.exports = withIntercomReactNative
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment