Skip to content

Commit e92ce86

Browse files
erwanlpfruddish
andauthored
feat: expo plugin (#155)
* feat: expo plugin * fix: export usage * chore: use correct wording for Australia Co-authored-by: Uddish Verma <[email protected]> --------- Co-authored-by: Uddish Verma <[email protected]>
1 parent 8935005 commit e92ce86

File tree

6 files changed

+405
-4
lines changed

6 files changed

+405
-4
lines changed

README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,52 @@ See the [example app](https://github.com/intercom/intercom-react-native/blob/mai
427427

428428
___
429429

430+
### Expo
431+
432+
If you are using Expo, you can use the built-in plugin.
433+
434+
After installing this npm package, add the [config plugin](https://docs.expo.io/guides/config-plugins/) to the [`plugins`](https://docs.expo.io/versions/latest/config/app/#plugins) array of your `app.json` or `app.config.js`:
435+
436+
```json
437+
{
438+
"expo": {
439+
"plugins": ["@config-plugins/intercom-react-native"]
440+
}
441+
}
442+
```
443+
444+
The plugin provides props for extra customization. Every time you change the props or plugins, you'll need to rebuild (and `prebuild`) the native app. If no extra properties are added, defaults will be used.
445+
446+
- `appId` (_string_): App ID from Intercom.
447+
- `androidApiKey` (_string_): Android API Key from Intercom.
448+
- `iosApiKey` (_string_): iOS API Key from Intercom.
449+
- `intercomRegion` (_string_): Region for Intercom `US`, `EU`, `AU`. Optional. Defaults to `US`.
450+
451+
```json
452+
{
453+
"expo": {
454+
"plugins": [
455+
[
456+
"@config-plugins/intercom-react-native",
457+
{
458+
"appId": "abc123",
459+
"androidApiKey": "android_sdk-abc123",
460+
"iosApiKey": "ios_sdk-abc123",
461+
"intercomRegion": "EU" // Europe
462+
}
463+
]
464+
]
465+
}
466+
}
467+
```
468+
469+
Next, rebuild your app as described in the ["Adding custom native code"](https://docs.expo.io/workflow/customizing/) guide.
470+
471+
#### Limitations
472+
473+
- **No push notifications support**: Intercom push notifications currently aren't supported by this config plugin extension. This will be added in the future.
474+
475+
430476
## Methods
431477

432478
## Import

app.plugin.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('./lib/commonjs/expo-plugins');

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
"access": "public"
5454
},
5555
"devDependencies": {
56+
"@expo/config-plugins": "^7.8.4",
5657
"@react-native-community/eslint-config": "^2.0.0",
5758
"@types/jest": "^26.0.0",
5859
"@types/mocha": "^8.2.2",

src/expo-plugins/@types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export type IntercomRegion = 'US' | 'EU' | 'AU';
2+
3+
export type IntercomPluginProps = {
4+
iosApiKey: string;
5+
androidApiKey: string;
6+
appId: string;
7+
intercomRegion?: IntercomRegion;
8+
};

src/expo-plugins/index.ts

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import {
2+
ConfigPlugin,
3+
createRunOncePlugin,
4+
withAppDelegate,
5+
AndroidConfig,
6+
withMainApplication,
7+
withAndroidManifest,
8+
withInfoPlist,
9+
} from '@expo/config-plugins';
10+
import {
11+
addImports,
12+
appendContentsInsideDeclarationBlock,
13+
} from '@expo/config-plugins/build/android/codeMod';
14+
import {
15+
addObjcImports,
16+
insertContentsInsideObjcFunctionBlock,
17+
} from '@expo/config-plugins/build/ios/codeMod';
18+
import type { IntercomPluginProps, IntercomRegion } from './@types';
19+
import packageJson from '../../package.json';
20+
21+
const mainApplication: ConfigPlugin<IntercomPluginProps> = (_config, props) =>
22+
withMainApplication(_config, (config) => {
23+
let stringContents = config.modResults.contents;
24+
stringContents = addImports(
25+
stringContents,
26+
['com.intercom.reactnative.IntercomModule;'],
27+
false
28+
);
29+
stringContents = appendContentsInsideDeclarationBlock(
30+
stringContents,
31+
'onCreate',
32+
`IntercomModule.initialize(this, "${props.androidApiKey}", "${props.appId}");`
33+
);
34+
config.modResults.contents = stringContents;
35+
return config;
36+
});
37+
38+
const androidManifest: ConfigPlugin<IntercomPluginProps> = (
39+
_config,
40+
{ intercomRegion = 'US' }
41+
) => {
42+
let newConfig = AndroidConfig.Permissions.withPermissions(
43+
_config,
44+
[
45+
'android.permission.READ_EXTERNAL_STORAGE',
46+
'android.permission.VIBRATE',
47+
].filter(Boolean)
48+
);
49+
50+
newConfig = withAndroidManifest(newConfig, (config) => {
51+
const currentMainApplication =
52+
AndroidConfig.Manifest.getMainApplicationOrThrow(config.modResults);
53+
const androidRegionMapper: Record<IntercomRegion, string> = {
54+
US: '@integer/intercom_server_region_us',
55+
EU: '@integer/intercom_server_region_eu',
56+
AU: '@integer/intercom_server_region_aus',
57+
};
58+
59+
AndroidConfig.Manifest.addMetaDataItemToMainApplication(
60+
currentMainApplication,
61+
'io.intercom.android.sdk.server.region',
62+
androidRegionMapper[intercomRegion]
63+
);
64+
65+
return config;
66+
});
67+
68+
return newConfig;
69+
};
70+
71+
const withIntercomAndroid: ConfigPlugin<IntercomPluginProps> = (
72+
config,
73+
props
74+
) => {
75+
let newConfig = config;
76+
newConfig = mainApplication(newConfig, props);
77+
newConfig = androidManifest(newConfig, props);
78+
return newConfig;
79+
};
80+
81+
const appDelegate: ConfigPlugin<IntercomPluginProps> = (_config, props) =>
82+
withAppDelegate(_config, (config) => {
83+
let stringContents = config.modResults.contents;
84+
stringContents = addObjcImports(stringContents, ['<IntercomModule.h>']);
85+
stringContents = insertContentsInsideObjcFunctionBlock(
86+
stringContents,
87+
'application didFinishLaunchingWithOptions:',
88+
`[IntercomModule initialize:@"${props.iosApiKey}" withAppId:@"${props.appId}"];`,
89+
{ position: 'tailBeforeLastReturn' }
90+
);
91+
config.modResults.contents = stringContents;
92+
return config;
93+
});
94+
95+
const infoPlist: ConfigPlugin<IntercomPluginProps> = (
96+
_config,
97+
{ intercomRegion }
98+
) => {
99+
const newConfig = withInfoPlist(_config, (config) => {
100+
if (intercomRegion) {
101+
config.modResults.IntercomRegion = intercomRegion;
102+
}
103+
104+
return config;
105+
});
106+
107+
return newConfig;
108+
};
109+
const withIntercomIOS: ConfigPlugin<IntercomPluginProps> = (config, props) => {
110+
let newConfig = appDelegate(config, props);
111+
newConfig = infoPlist(newConfig, props);
112+
return newConfig;
113+
};
114+
115+
const withIntercomReactNative: ConfigPlugin<IntercomPluginProps> = (
116+
config,
117+
props
118+
) => {
119+
let newConfig = config;
120+
newConfig = withIntercomAndroid(newConfig, props);
121+
newConfig = withIntercomIOS(newConfig, props);
122+
return newConfig;
123+
};
124+
125+
const pkg = {
126+
// Prevent this plugin from being run more than once.
127+
// This pattern enables users to safely migrate off of this
128+
// out-of-tree `@config-plugins/intercom-react-native` to a future
129+
// upstream plugin in `intercom-react-native`
130+
name: packageJson.name,
131+
// Indicates that this plugin is dangerously linked to a module,
132+
// and might not work with the latest version of that module.
133+
version: packageJson.version,
134+
};
135+
136+
export default createRunOncePlugin(
137+
withIntercomReactNative,
138+
pkg.name,
139+
pkg.version
140+
);

0 commit comments

Comments
 (0)