Skip to content

Commit bd63d5a

Browse files
committed
add support for theme modes
1 parent b125dae commit bd63d5a

File tree

7 files changed

+141
-9
lines changed

7 files changed

+141
-9
lines changed

android/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,4 +70,5 @@ dependencies {
7070
implementation "com.facebook.react:react-native:+" // From node_modules
7171
implementation "com.google.firebase:firebase-messaging:${safeExtGet('firebaseMessagingVersion', '20.2.+')}"
7272
implementation 'io.intercom.android:intercom-sdk:17.1.0'
73+
implementation 'io.intercom.android:intercom-sdk-ui:17.1.0'
7374
}

android/src/main/java/com/intercom/reactnative/IntercomErrorCodes.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ public class IntercomErrorCodes {
1717
public static final String HIDE_INTERCOM = "206";
1818
public static final String SET_LAUNCHER_VISIBILITY = "208";
1919
public static final String SET_BOTTOM_PADDING = "209";
20+
public static final String SET_THEME_MODE = "210";
2021
public static final String HANDLE_PUSH_MESSAGE = "301";
2122
public static final String SEND_TOKEN_TO_INTERCOM = "302";
2223
public static final String FETCH_HELP_CENTER_COLLECTIONS = "901";

android/src/main/java/com/intercom/reactnative/IntercomModule.java

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
import io.intercom.android.sdk.helpcenter.sections.HelpCenterCollectionContent;
3939
import io.intercom.android.sdk.identity.Registration;
4040
import io.intercom.android.sdk.push.IntercomPushClient;
41+
import io.intercom.android.sdk.ui.theme.ThemeMode;
4142
import android.app.TaskStackBuilder;
4243

4344
@ReactModule(name = IntercomModule.NAME)
@@ -530,6 +531,42 @@ public void setBottomPadding(int paddingBottom, Promise promise) {
530531
}
531532
}
532533

534+
@ReactMethod
535+
public void setThemeMode(String themeMode, Promise promise) {
536+
try {
537+
ThemeMode themeModeEnum = null;
538+
539+
if (themeMode == null || themeMode.trim().isEmpty()) {
540+
promise.reject(IntercomErrorCodes.SET_THEME_MODE,
541+
"Theme mode cannot be null or empty. Use 'LIGHT', 'DARK', or 'SYSTEM'.");
542+
return;
543+
}
544+
545+
switch (themeMode) {
546+
case "SYSTEM":
547+
themeModeEnum = ThemeMode.SYSTEM;
548+
break;
549+
case "LIGHT":
550+
themeModeEnum = ThemeMode.LIGHT;
551+
break;
552+
case "DARK":
553+
themeModeEnum = ThemeMode.DARK;
554+
break;
555+
default:
556+
promise.reject(IntercomErrorCodes.SET_THEME_MODE,
557+
"Invalid theme mode: '" + themeMode + "'. Use 'LIGHT', 'DARK', or 'SYSTEM'.");
558+
return;
559+
}
560+
561+
Intercom.client().setThemeMode(themeModeEnum);
562+
promise.resolve(true);
563+
} catch (Exception err) {
564+
Log.e(NAME, "setThemeMode error:");
565+
Log.e(NAME, err.toString());
566+
promise.reject(IntercomErrorCodes.SET_THEME_MODE, "Error in setThemeMode: " + err.toString());
567+
}
568+
}
569+
533570
@ReactMethod
534571
public void setUserJwt(String jwt, Promise promise) {
535572
try {

example/ios/Podfile.lock

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ PODS:
55
- fmt (9.1.0)
66
- glog (0.3.5)
77
- Intercom (19.1.2)
8-
- intercom-react-native (8.7.0):
8+
- intercom-react-native (8.8.0):
99
- Intercom (~> 19.1.2)
1010
- React-Core
1111
- RCT-Folly (2024.01.01.00):
@@ -915,8 +915,6 @@ PODS:
915915
- react-native-config/App (= 1.5.7)
916916
- react-native-config/App (1.5.7):
917917
- React-Core
918-
- react-native-notifications (5.1.0):
919-
- React-Core
920918
- React-nativeconfig (0.74.0)
921919
- React-NativeModulesApple (0.74.0):
922920
- glog
@@ -1167,7 +1165,6 @@ DEPENDENCIES:
11671165
- React-logger (from `../node_modules/react-native/ReactCommon/logger`)
11681166
- React-Mapbuffer (from `../node_modules/react-native/ReactCommon`)
11691167
- react-native-config (from `../node_modules/react-native-config`)
1170-
- react-native-notifications (from `../node_modules/react-native-notifications`)
11711168
- React-nativeconfig (from `../node_modules/react-native/ReactCommon`)
11721169
- React-NativeModulesApple (from `../node_modules/react-native/ReactCommon/react/nativemodule/core/platform/ios`)
11731170
- React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`)
@@ -1261,8 +1258,6 @@ EXTERNAL SOURCES:
12611258
:path: "../node_modules/react-native/ReactCommon"
12621259
react-native-config:
12631260
:path: "../node_modules/react-native-config"
1264-
react-native-notifications:
1265-
:path: "../node_modules/react-native-notifications"
12661261
React-nativeconfig:
12671262
:path: "../node_modules/react-native/ReactCommon"
12681263
React-NativeModulesApple:
@@ -1319,14 +1314,14 @@ SPEC CHECKSUMS:
13191314
fmt: 4c2741a687cc09f0634a2e2c72a838b99f1ff120
13201315
glog: c5d68082e772fa1c511173d6b30a9de2c05a69a2
13211316
Intercom: 0643528ea72515fb47096ad38fe455780441881b
1322-
intercom-react-native: 13c59dd7882e6b60a1d23e4071210c1a828e5d98
1317+
intercom-react-native: 15a322d6032f91058563a02e049f4d7246aad1a0
13231318
RCT-Folly: 5f972de9f7d384c7d0e7380dd7da506228e568f5
13241319
RCTDeprecation: 3ca8b6c36bfb302e1895b72cfe7db0de0c92cd47
13251320
RCTRequired: 9fc183af555fd0c89a366c34c1ae70b7e03b1dc5
13261321
RCTTypeSafety: db1dd5ad1081a5e160d30bb29ef922693d5ac4b1
13271322
React: 8650d592d90b99097504b8dcfebab883972aed71
13281323
React-callinvoker: 6bb8b399ab8cec59e52458c3a592aa1fca130b68
1329-
React-Codegen: 253c0e9d0326adb08ee46e6941199ad47370038b
1324+
React-Codegen: fbad87d0dc7c5bc1536b25bc5cf2f19a1449e438
13301325
React-Core: ab1b60c382b7b79c374b68918f856826ec7f02a9
13311326
React-CoreModules: c5791800e490979b15b819e13ceaee42aa4a2672
13321327
React-cxxreact: 7a5de9c31527a3a36b02caa3540ab55080a6448a
@@ -1345,7 +1340,6 @@ SPEC CHECKSUMS:
13451340
React-logger: 5ae0978955199c132e71e8cf7797f619a6d17164
13461341
React-Mapbuffer: 3b85b3778e447cd1f06d353b8e967af50f272829
13471342
react-native-config: 963b5efabc864cf69412e54b5de49b6a23e4af03
1348-
react-native-notifications: 3bafa1237ae8a47569a84801f17d80242fe9f6a5
13491343
React-nativeconfig: 951ec32f632e81cbd7d40aebb3211313251c092e
13501344
React-NativeModulesApple: 612f931b1e79736f2d59353979042a424fb314c8
13511345
React-perflogger: 271f1111779fef70f9502d1d38da5132e5585230

example/src/App.tsx

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import Intercom, {
1818
type UserAttributes,
1919
Visibility,
2020
IntercomContent,
21+
ThemeMode,
2122
} from '@intercom/intercom-react-native';
2223

2324
import {
@@ -48,6 +49,7 @@ export default function App() {
4849
useState<boolean>(true);
4950
const [launcherVisibility, setLauncherVisibility] = useState<boolean>(false);
5051
const [user, setUser] = useState<UserAttributes>({ email: '' });
52+
const [currentTheme, setCurrentTheme] = useState<ThemeMode>(ThemeMode.SYSTEM);
5153

5254
const [conversationId, setConversationId] = useState<string | undefined>(
5355
CONVERSATION_ID
@@ -582,6 +584,54 @@ export default function App() {
582584
resetAttributes();
583585
}}
584586
/>
587+
<Button
588+
intercom_accessibilityLabel="set-theme-light"
589+
intercom_title={`Set Theme: Light ${
590+
currentTheme === 'LIGHT' ? '✓' : ''
591+
}`}
592+
intercom_onPress={() => {
593+
Intercom.setThemeMode(ThemeMode.LIGHT)
594+
.then(() => {
595+
setCurrentTheme(ThemeMode.LIGHT);
596+
})
597+
.catch((e) => {
598+
showErrorAlert(e);
599+
console.error(e);
600+
});
601+
}}
602+
/>
603+
<Button
604+
intercom_accessibilityLabel="set-theme-dark"
605+
intercom_title={`Set Theme: Dark ${
606+
currentTheme === 'DARK' ? '✓' : ''
607+
}`}
608+
intercom_onPress={() => {
609+
Intercom.setThemeMode(ThemeMode.DARK)
610+
.then(() => {
611+
setCurrentTheme(ThemeMode.DARK);
612+
})
613+
.catch((e) => {
614+
showErrorAlert(e);
615+
console.error(e);
616+
});
617+
}}
618+
/>
619+
<Button
620+
intercom_accessibilityLabel="set-theme-system"
621+
intercom_title={`Set Theme: System ${
622+
currentTheme === 'SYSTEM' ? '✓' : ''
623+
}`}
624+
intercom_onPress={() => {
625+
Intercom.setThemeMode(ThemeMode.SYSTEM)
626+
.then(() => {
627+
setCurrentTheme(ThemeMode.SYSTEM);
628+
})
629+
.catch((e) => {
630+
showErrorAlert(e);
631+
console.error(e);
632+
});
633+
}}
634+
/>
585635
<Button
586636
intercom_accessibilityLabel="logout"
587637
intercom_disabled={!loggedUser}

ios/IntercomModule.m

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,35 @@ - (NSData *)dataFromHexString:(NSString *)string {
365365
resolve(@(YES));
366366
};
367367

368+
RCT_EXPORT_METHOD(setThemeMode:(NSString *)themeMode
369+
resolver:(RCTPromiseResolveBlock)resolve
370+
rejecter:(RCTPromiseRejectBlock)reject) {
371+
@try {
372+
if (themeMode == nil || [themeMode isKindOfClass:[NSNull class]] || [themeMode length] == 0) {
373+
reject(@"SET_THEME_MODE", @"Theme mode cannot be null or empty. Use 'LIGHT', 'DARK', or 'SYSTEM'.", nil);
374+
return;
375+
}
376+
377+
ICMThemeOverride themeOverride;
378+
379+
if ([themeMode isEqualToString:@"LIGHT"]) {
380+
themeOverride = ICMThemeOverrideLight;
381+
} else if ([themeMode isEqualToString:@"DARK"]) {
382+
themeOverride = ICMThemeOverrideDark;
383+
} else if ([themeMode isEqualToString:@"SYSTEM"]) {
384+
themeOverride = ICMThemeOverrideSystem;
385+
} else {
386+
reject(@"SET_THEME_MODE", [NSString stringWithFormat:@"Invalid theme mode: '%@'. Use 'LIGHT', 'DARK', or 'SYSTEM'.", themeMode], nil);
387+
return;
388+
}
389+
390+
[Intercom setThemeOverride:themeOverride];
391+
resolve(@(YES));
392+
} @catch (NSException *exception) {
393+
reject(@"SET_THEME_MODE", @"Error in setThemeMode", [self exceptionToError:exception :@"SET_THEME_MODE" :@"setThemeMode"]);
394+
}
395+
};
396+
368397
- (NSError *)exceptionToError:(NSException *)exception :(NSString *)code :(NSString *)domain {
369398
NSMutableDictionary *info = [NSMutableDictionary dictionary];
370399
[info setValue:exception.name forKey:@"ExceptionName"];

src/index.tsx

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ export enum LogLevel {
2626

2727
type LogLevelType = keyof typeof LogLevel;
2828

29+
export enum ThemeMode {
30+
LIGHT = 'LIGHT',
31+
DARK = 'DARK',
32+
SYSTEM = 'SYSTEM',
33+
}
34+
35+
type ThemeModeType = keyof typeof ThemeMode;
36+
2937
export const IntercomEvents = {
3038
IntercomUnreadCountDidChange:
3139
IntercomEventEmitter.UNREAD_COUNT_CHANGE_NOTIFICATION,
@@ -290,6 +298,17 @@ export type IntercomType = {
290298
*/
291299
setLogLevel(logLevel: LogLevelType): Promise<boolean>;
292300

301+
/**
302+
* Sets the theme mode for the Intercom SDK.
303+
*
304+
* This allows you to override the server-provided theme setting for the current session only.
305+
* The theme mode controls whether the SDK displays in light mode, dark mode, or follows the system theme.
306+
* The theme selection will be reset when the app restarts.
307+
*
308+
* @param themeMode The theme mode to set (LIGHT, DARK, or SYSTEM).
309+
*/
310+
setThemeMode(themeMode: ThemeModeType): Promise<boolean>;
311+
293312
/**
294313
Sets a JWT token for the user, necessary for using the Messenger
295314
when Messenger Security is enforced. This is an improvement to Identity Verification.
@@ -358,6 +377,7 @@ const Intercom: IntercomType = {
358377

359378
sendTokenToIntercom: (token) => IntercomModule.sendTokenToIntercom(token),
360379
setLogLevel: (logLevel) => IntercomModule.setLogLevel(logLevel),
380+
setThemeMode: (themeMode) => IntercomModule.setThemeMode(themeMode),
361381
setUserJwt: (jwt) => IntercomModule.setUserJwt(jwt),
362382
addEventListener: (event, callback) => {
363383
event === IntercomEvents.IntercomUnreadCountDidChange &&

0 commit comments

Comments
 (0)