MagicBell-React
MagicBell-React is a set of React components to build a notification center for your site powered by magicbell.io.
- Full TypeScript support
- Supports preact
- Requires React 16.8+
- Compatible with any browser that supports
Proxy
Quick Start
npm i @magicbell/magicbell-react
yarn add @magicbell/magicbell-react
import React from 'react';
import ReactDOM from 'react-dom';
import MagicBell, { FloatingNotificationCenter } from '@magicbell/magicbell-react';
ReactDOM.render(
<MagicBell apiKey={MAGICBELL_API_KEY} userEmail="john@example.com">
{(props) => <FloatingNotificationCenter height={300} {...props} />}
</MagicBell>,
document.body,
);
Check the storybook to explore all components.
Notifications page demo.
Table of Contents
Introduction
This package is built using MobX. The notification store and its items are MobX observables. To use this package you don't need to know anything about MobX. However, if you build a component, make it observe changes to the observable objects we expose. This is as simple as this:
export default function MyComponent() {}
import { observer } from 'mobx-react-lite';
function MyComponent() {}
export default observer(MyComponent);
The overhead of observer itself is negligible. You can read more about how to observe MobX objects at the official docs of mobx-react.
Once you make your custom component observe changes, it will be updated automatically.
MagicBell
The MagicBell
component is the default export of this package and is the root component for building a widget. It initializes a connection to magicbell.io, renders a bell icon with the number of unseen notifications and keeps the widget updated in real time.
Demo
These are all the properties accepted by this component.
Property | Type | Description |
---|
apiKey | string | The API key of your magicbell.io project |
userEmail | string | The email of the user you want to show notifications for |
userKey | string | The HMAC for the user. It is recommended to enable HMAC authentication but not required |
children | ({ isOpen, toggle, launcherRef }) => JSX.Element | The children function to render a list of notifications for the user |
theme | IMagicBellTheme | An optional object containing custom color values for the widget, see Custom Themes |
BellIcon | JSX.Element | An optional react element to be displayed instead of the default bell icon |
defaultIsOpen | boolean | An optional flag to set the default visibility state of the element returned by the children function. It is false by default. |
Children function
This component expects a children function. This is how you render whatever you want to based on the state of the MagicBell
.
You can use the notification center from this package (see NotificationCenter
):
import React from 'react';
import ReactDOM from 'react-dom';
import MagicBell, { NotificationCenter } from '@magicbell/magicbell-react';
ReactDOM.render(
<MagicBell apiKey={MAGICBELL_API_KEY} userEmail="john@example.com">
{() => <NotificationCenter height={300} />}
</MagicBell>,
document.body,
);
or use your own:
import React from 'react';
import ReactDOM from 'react-dom';
import MagicBell from '@magicbell/magicbell-react';
ReactDOM.render(
<MagicBell apiKey={MAGICBELL_API_KEY} userEmail="john@example.com">
{({ launcherRef, isOpen, toggle }) => (
<MyOwnNotificationCenter toggle={toggle} isOpen={isOpen} launcherRef={launcherRef} />
)}
</MagicBell>,
document.body,
);
The MagicBell
component does not render the component returned by the children function by default, only the bell is rendered. When the bell is clicked, the child component is toggled. This behaviour can be changed using the defaultIsOpen
flag.
As shown above, the children function gets a function to manually toggle the notification center. You can access the notifications
store through MagicBellContext
.
import { useContext } from 'react';
const { notifications } = useContext(MagicBellContext);
MagicBellProvider
The MagicBellProvider
component is the main component for building a custom notification center. It initializes a connection to magicbell.io, creates a MagicBellContext
and keeps the list of notifications updated in real time.
This component wraps the children you provide in a MagicBellContext.Provider
, so you can safely wrap your entire app in this component. Nothing else will be rendered.
import React from 'react';
import ReactDOM from 'react-dom';
import { MagicBellProvider } from '@magicbell/magicbell-react';
ReactDOM.render(
<MagicBellProvider apiKey={MAGICBELL_API_KEY} userEmail="john@example.com">
<App />
</MagicBellProvider>,
document.body,
);
MagicBellProvider
creates a React context object, so now you can access the MagicBellContext
anywhere in your application.
These are all the properties accepted by this component.
Property | Type | Description |
---|
apiKey | string | The API key of your magicbell.io project |
userEmail | string | The email of the user you want to show notifications for |
userKey | string | The HMAC for the user. It is recommended to enable HMAC authentication but not required |
children | JSX.Element | The children to be wrapped in a MagicBellContext.Provider |
theme | IMagicBellTheme | An optional object containing custom color values for the widget, see Custom Themes |
NotificationCenter
The NotificationCenter
component renders a header, a footer and an infinite scroll list of notifications.
These are all the properties accepted by this component.
Property | Type | Description |
---|
height | number | Height in pixels of the infinite scroll list |
onAllRead | () => void | An optional callback function invoked when the "Mark All Read" button is clicked |
onNotificationClick | (notification) => void | An optional callback function invoked when a notification is clicked |
FloatingNotificationCenter
This component renders a NotificationCenter
component in a tooltip. The tooltip is created with popper.
These are all the properties accepted by this component.
Property | Type | Description |
---|
isOpen | boolean | Whether the notification center is visible or not |
toggle | () => void | Toggle the notification center |
launcherRef | React.RefObject | React ref pointing to the element that toggles the notification center |
placement | string | Position of the notification center relative to the launcher. It can be one of these: "auto", "auto-start", "auto-end", "top", "bottom", "right", "left", "top-start", "top-end", "bottom-start", "bottom-end", "right-start", "right-end", "left-start", "left-end". |
NotificationList
The NotificationList
component renders an infinite scroll list of notifications. When the user scrolls to the bottom the next page of notifications are fetched and appended to the current array of notifications. By default it renders a ClickableNotification
component for each item in the notifications store.
These are all the properties accepted by this component.
Property | Type | Description |
---|
height | number | Height in pixels of the infinite scroll list |
onItemClick | (notification) => void | An optional callback function invoked when a notification is clicked |
ListItem | ({ notification, onItemClick }) => JSX.Element | An optional custom component to use for each item in the list |
If the height property is not provided, then the window scroll will be used.
Example: notification center with a custom list item.
ClickableNotification
This component renders the title and content of a notification.
These are all the properties accepted by this component.
Property | Type | Description |
---|
notification | Notification | The notification object |
onClick | (notification) => void | An optional callback function invoked when the component is clicked |
IMPORTANT: When a notification is clicked, the notification is marked as read. If you implement your own component, you might also want to mark the notification as read manually. E.g.:
import React from 'react';
import { observer } from 'mobx-react-lite';
function CustomNotification({ notification, onClick }) {
const handleClick = () => {
notification.markAsRead();
onClick(notification);
};
return <div onClick={handleClick}>{notification.title}</div>;
}
export default observer(CustomNotification);
NotificationContent
This component compiles the notification's content
using the Liquid template language and renders the result. The output is sanitized to prevent XSS attacks.
These are all the properties accepted by this component.
Property | Type | Description |
---|
notification | Notification | The notification object |
useMagicBellEvent
This a hook to listen to realtime events.
import { useMagicBellEvent } from '@magicbell/magicbell-react';
useMagicBellEvent('notifications.new', showPushNotification);
This is a list of events you can listen to:
Event name | Description |
---|
* | Any event |
notifications.new | A new notification for the authenticated user was created |
notifications.read | A notification was marked as read |
notifications.read.all | All notifications were marked as read |
notifications.unread | A notification was marked as unread |
notifications.delete | A notification was deleted |
MagicBellContext
This is a React context object which contains the store of notifications and the theme, so you can access these values wherever you need them.
import React, { useContext } from 'react';
import { MagicBellContext } from '@magicbell/magicbell-react';
function MyComponent() {
const { theme, notifications } = useContext(MagicBellContext);
return <p>You have {notifications.total} notifications</p>;
}
Custom Themes
Is is possible to customize the text color, font size and border radius of some elements by providing to the MagicBell
component a theme
property. This is going to be deep merged with the default theme.
This is the definition of the default theme:
{
icon: {
borderColor: '#3498F4',
width: '24px',
},
header: {
backgroundColor: '#3498F4',
backgroundOpacity: 1,
borderRadius: '8px',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif',
fontSize: '14px',
textColor: 'white',
textAlign: 'left',
textTransform: 'none',
},
footer: {
backgroundColor: '#3498F4',
backgroundOpacity: 1,
borderRadius: '8px',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif',
fontSize: '14px',
textColor: 'white',
textAlign: 'right',
textTransform: 'none',
},
unseenBadge: {
backgroundColor: '#DF4759',
backgroundOpacity: 1,
borderRadius: '2px',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif',
fontSize: '14px',
textColor: 'white',
textAlign: 'left',
textTransform: 'none',
},
notification: {
default: {
backgroundColor: 'white',
backgroundOpacity: 1,
borderRadius: '8px',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif',
fontSize: '14px',
textColor: '#3A424D',
textAlign: 'left',
textTransform: 'none',
},
unread: {
backgroundColor: '#D9E2EF',
backgroundOpacity: 0.2,
borderRadius: '8px',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif',
fontSize: '14px',
textColor: '#3A424D',
textAlign: 'left',
textTransform: 'none',
},
unseen: {
backgroundColor: '#D9E2EF',
backgroundOpacity: 0.05,
borderRadius: '8px',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif',
fontSize: '14px',
textColor: '#3A424D',
textAlign: 'left',
textTransform: 'none',
},
},
}
You can override any attribute of this theme. Colors can be expressed in HEX, HSL, HSV or RGB(A).
import React from 'react';
import ReactDOM from 'react-dom';
import MagicBell, { NotificationCenter } from '@magicbell/magicbell-react';
const customTheme = {
icon: {
borderColor: 'rgba(160, 30, 120, 0.5)',
},
header: {
borderRadius: '2px',
},
footer: {
borderRadius: '2px',
},
};
ReactDOM.render(
<MagicBell apiKey={MAGICBELL_API_KEY} userEmail="john@example.com" theme={customTheme}>
{() => <NotificationCenter height="300" />}
</MagicBell>,
document.body,
);
The notification model
The Notification
class can be imported from @magicbell/magicbell-react
. It implements this interface.
interface INotification {
id: string;
title: string;
content: string | null;
category: string | null;
actionUrl: string;
metaData: any;
readAt: number | null;
seenAt: number | null;
sentAt: number;
isRead: boolean;
isSeen: boolean;
seenAtDate: Dayjs | null;
sentAtDate: Dayjs;
readAtDate: Dayjs | null;
fetch: () => Promise;
markAsRead: () => Promise;
markAsUnread: () => Promise;
delete: () => Promise;
}
All attributes are MobX observables.
seenAtDate
A date representation of the seenAt
attribute. It returns an immutable instance of Dayjs. Dayjs exposes an API similar to moment.js.
notification.seenAtDate.format('DD/MM/YYYY');
notification.seenAtDate.fromNow();
notification.seenAtDate.to('2021-01-01');
notification.seenAtDate.add(2, 'day');
readAtDate
A date representation of the readAt
attribute. It returns an immutable instance of Dayjs.
sentAtDate
A date representation of the sentAt
attribute. It returns an immutable instance of Dayjs.
fetch
Fetches the notification from the magicbell.io server. All fetched attributes are assigned to the current object.
markAsRead
This method makes a POST request to the read notification API endpoint of magicbell.io. It sets the readAt
attribute as well.
markAsUnread
This method makes a POST request to the unread notification API endpoint of magicbell.io. It sets the readAt
attribute to null
as well.
delete
This method makes a DELETE request to the notification API endpoint of magicbell.io. If the notification belongs to a store, it will remove itself from the store.
The notification store
The NotificationStore
class implements this interface:
interface INotificationStore {
unseenCount: number;
total: number;
perPage: number;
totalPages: number;
currentPage: number;
items: Notification[];
length: number;
isEmpty: boolean;
hasNextPage: boolean;
at: (number) => Notification | null;
map: (fn) => any[];
fetch: (params) => Promise;
fetchNextPage: () => Promise;
create: (data) => Promise<Notification>;
markAllAsRead: () => Promise;
markAllAsSeen: () => Promise;
remove: () => void;
}
All attributes are MobX observables.
TIP: You can import this class from @magicbell/magicbell-react
. However, you may want to access he notifications store through the MagicBellContext
, instead of creating a new instance in your app.
length
Number of notifications in the items
array.
at
Get a notificaiton from the items
array, specified by index.
map
Creates an array of values by running each notification in items
array thru iteratee. The iteratee is invoked with three arguments: (notification, index, itemsArray)
.
fetch
Fetch notifications from the magicbell server. The pagination data is also updated. All provided parameters are included in the request.
The response is appended to the current array of notifications, so it can be used as the view model for an infinite scroll list.
fetchNextPage
This method is simply wrapping the fetch
method, sending as a parameter the next page of notifications.
create
Create a new notification.
It is equivalent to creating a Notification
instance with some attributes, saving the notification to the server, and adding it to the array of items
after being successfully created.
markAllAsRead
Makes a POST request to the read notifications API endpoint. It also marks all notifications in the collection as read.
markAllAsSeen
Makes a POST request to the seen notifications API endpoint. It also sets the unseenCount
to 0 and marks all notifications in the collection as seen.
remove
Removes the given notification from the items
array. It does not make any request to the server. If you want to delete a notification, use the delete
method of the notification object instead.