const { openInAppNotification, closeInAppNotification, notifications } =
useInAppNotificationsState();
In-App Notifications
The Stream Chat SDK for React Native provides a built-in way to display in-app notifications to users using a Toaster or their own custom implementation. This feature enhances user experience by allowing users to get feedback for certain actions without leaving the app.
In app notifications
In this cookbook, we will show you how to use the built-in toaster to display in-app notifications.
Store
The Stream Chat SDK for React Native provides a built-in store to display in-app notifications that manages the state of the notifications. The store is used in the useInAppNotificationsState
hook and is exposed from the SDK.
The hook returns the following methods:
openInAppNotification
: Opens a new notification. This can be used to display a notification for a specific action.closeInAppNotification
: Closes a notification. Calling this method will remove the notification from the store.notifications
: A list of all the notifications. This is a read-only list of the notifications that are currently displayed.
This hook can be easily used to display a notification for a specific action in the form of a Toaster.
Creating a Custom Toaster
To create a custom toaster, you can use the useInAppNotificationsState
hook to display a notification for a specific action.
An example of a custom toaster is the following:
import {
Dimensions,
StyleSheet,
Text,
TouchableOpacity,
View,
} from "react-native";
import Animated, {
Easing,
SlideInDown,
SlideOutDown,
} from "react-native-reanimated";
import {
SafeAreaView,
useSafeAreaInsets,
} from "react-native-safe-area-context";
import { useInAppNotificationsState, useTheme } from "stream-chat-react-native";
import type { Notification } from "stream-chat";
const { width } = Dimensions.get("window");
const severityIconMap: Record<Notification["severity"], string> = {
error: "❌",
success: "✅",
warning: "⚠️",
info: "ℹ️",
};
export const Toast = () => {
const { closeInAppNotification, notifications } =
useInAppNotificationsState();
const { top } = useSafeAreaInsets();
const {
theme: {
colors: { overlay, white_smoke },
},
} = useTheme();
return (
<SafeAreaView style={[styles.container, { top }]} pointerEvents="box-none">
{notifications.map((notification) => (
<Animated.View
key={notification.id}
entering={SlideInDown.easing(Easing.bezierFn(0.25, 0.1, 0.25, 1.0))}
exiting={SlideOutDown}
style={[styles.toast, { backgroundColor: overlay }]}
>
<View style={[styles.icon, { backgroundColor: overlay }]}>
<Text style={[styles.iconText, { color: white_smoke }]}>
{severityIconMap[notification.severity]}
</Text>
</View>
<View style={styles.content}>
<Text style={[styles.message, { color: white_smoke }]}>
{notification.message}
</Text>
</View>
<TouchableOpacity
onPress={() => closeInAppNotification(notification.id)}
>
<Text style={[styles.close, { color: white_smoke }]}>✕</Text>
</TouchableOpacity>
</Animated.View>
))}
</SafeAreaView>
);
};
const styles = StyleSheet.create({
container: {
position: "absolute",
right: 16,
left: 16,
alignItems: "flex-end",
},
toast: {
width: width * 0.9,
borderRadius: 12,
padding: 12,
marginBottom: 8,
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
shadowColor: "#000",
shadowOpacity: 0.2,
shadowRadius: 4,
elevation: 5,
},
content: {
flex: 1,
marginHorizontal: 8,
},
message: {
fontSize: 14,
fontWeight: "600",
},
close: {
fontSize: 16,
},
icon: {
width: 20,
height: 20,
borderRadius: 12,
justifyContent: "center",
alignItems: "center",
},
iconText: {
fontWeight: "bold",
includeFontPadding: false,
},
warning: {
backgroundColor: "yellow",
},
});
This example uses the Notification
type from the stream-chat
package to display the notification.
Triggering a notification
You can trigger the notification by calling the openInAppNotification
method with the notification object.
openInAppNotification({
id: "1",
message: "This is a test notification",
severity: "info",
created_at: Date.now(),
origin: {
emitter: "test_emitter",
id: "test_id",
},
});
Configuring the Toaster
The Toast
component should be added in the highest hierarchy of the app where you want to display the notifications.
<Toast />
Closing a Toaster
The toaster will automatically close after a certain amount of time. You can also close the toaster manually by calling the closeInAppNotification
method.
Client side notifications
To listen to client side notifications, you can use the useClientNotifications
hook exposed from the SDK.
const { notifications } = useClientNotifications();
This hook returns the following methods:
notifications
: A list of all the notifications. This is a read-only list of the notifications that are currently displayed.
Depending on the difference in the notifications list you can trigger the toaster to display a notification.
In our Sample app, we created a useClientNotificationsHandler
hook that listens to the client side notifications and triggers the toaster to display a notification.
This is how it looks like:
import type { Notification } from "stream-chat";
import {
useClientNotifications,
useInAppNotificationsState,
} from "stream-chat-react-native";
import { useEffect, useMemo, useRef } from "react";
export const usePreviousNotifications = (notifications: Notification[]) => {
const prevNotifications = useRef<Notification[]>(notifications);
const difference = useMemo(() => {
const prevIds = new Set(
prevNotifications.current.map((notification) => notification.id),
);
const newIds = new Set(
notifications.map((notification) => notification.id),
);
return {
added: notifications.filter(
(notification) => !prevIds.has(notification.id),
),
removed: prevNotifications.current.filter(
(notification) => !newIds.has(notification.id),
),
};
}, [notifications]);
prevNotifications.current = notifications;
return difference;
};
/**
* This hook is used to open and close the notifications when the notifications are added or removed.
* @returns {void}
*/
export const useClientNotificationsHandler = () => {
const { notifications } = useClientNotifications();
const { openInAppNotification, closeInAppNotification } =
useInAppNotificationsState();
const { added, removed } = usePreviousNotifications(notifications);
useEffect(() => {
added.forEach(openInAppNotification);
removed.forEach((notification) => closeInAppNotification(notification.id));
}, [added, closeInAppNotification, openInAppNotification, removed]);
};