Customising Feed

Customising tenant

By default tenantId prop is set to default tenant. If you use multi-tenant architecture and want to fetch inbox notifications of other tenant, you can pass tenant_id prop.

<Inbox tenantId="suprsend" />
<SuprSendFeedProvider tenantId="suprsend">
  ....
</SuprSendFeedProvider>

If you are passing tenant_id in feed, make sure to pass scope key while creating userToken else 403 error will be thrown due to scope mismatching.

Adding tabs

You can pass stores prop to add multiple tabs in feed. For each tab you can write your own logic to get notifications based on tags, notification categories and notification status like read, archived. Read more about it here.

<Inbox stores={[{"storeId": "All", "label": "all"}, {"storeId": "Archived", "label": "archived", "query": {"archived": true}}]} />
<SuprSendFeedProvider stores={[{"storeId": "All", "label": "all"}, {"storeId": "Archived", "label": "archived", "query": {"archived": true}}]}>
  ....
</SuprSendFeedProvider>

Customise page size

You can change number of notifications to fetch per page to any integer value. Default value is 20 and maximum allowed page size is 100.

<Inbox pageSize={25} />
<SuprSendFeedProvider pageSize={25}>
  ....
</SuprSendFeedProvider>

Disable pagination

By default, feed supports infinite scrolling type pagination to get older notifications when you scroll through notifications list. If you want to remove infinite scroll ie., stop getting previous page notifications, you can set pagination=false.

<Inbox pagination={false} />
<NotificationFeed pagination={false} />

Enable dark mode

<Inbox themeType="dark" / "light" />
<NotificationFeed themeType="dark" / "light" />
Light ThemeDark Theme

Custom notification card click handler

Clicking on notification card will open the link if Action URL field is present in triggered inbox template. If you want to override this with your custom callback function you can pass notificationClickHandler.

<Inbox
  notificationClickHandler={(notificationData: IRemoteNotification) => {
    console.log('notification clicked', notificationData.n_id)
  }}
/>
<NotificationFeed
  notificationClickHandler={(notificationData: IRemoteNotification) => {
    console.log('notification clicked', notificationData.n_id)
  }}
/>

Custom action button click handlers

Clicking on action buttons in notification card will open link if you provide url on action button fields in triggered inbox template. If you want to override this with your custom action button callback functions you can pass primaryActionClickHandler and secondaryActionClickHandler.

<Inbox
  primaryActionClickHandler={(notificationData: IRemoteNotification) => {
    console.log('primary action button clicked', notificationData.n_id)
  }}
  secondaryActionClickHandler={(notificationData: IRemoteNotification) => {
    console.log('secondary action button clicked', notificationData.n_id)
  }}
/>
<NotificationFeed
  primaryActionClickHandler={(notificationData: IRemoteNotification) => {
    console.log('primary action button clicked', notificationData.n_id)
  }}
  secondaryActionClickHandler={(notificationData: IRemoteNotification) => {
    console.log('secondary action button clicked', notificationData.n_id)
  }}
/>

Popover position

Popover component is wrapper around NotificationFeed component and will be opened on click of bell icon. By default popover is shown at bottom ie., the inbox notifications popup list will be shown at bottom of the bell icon.

Example: Feed needs to be shown at bottom of left sidebar above profile icon, in that case on click of bell icon you would want to show notifications popover at right side.

<Inbox popperPosition="top" / "bottom" / "left" / "right" />

Custom bell component

For Bell, you can customise either css styles of existing bell component or replace existing bell with your own bell component. To replace existing bell component with your own component use bellComponent prop.

<Inbox bellComponent={() => <p>MyBell</p>} />

Custom badge component

This element shows the number of unseen notifications on Bell.You can customise either css styles of existing badge component or replace existing badge with your own badge component. To replace existing badge component with your own component use badgeComponent prop.

<Inbox badgeComponent={(count: number) => <p>{count}</p>} />

Custom header component

You can add custom component on right side of Header, this will replace Mark all as read text and add any JSX you pass as component in that place. You can even add custom icons like setting or preferences in that JSX and navigate user to that pages.

<Inbox
  headerRightComponent={({ markAllRead: ()=>void, closeInboxPopover: ()=>void }) => <p onClick={markAllRead}>Custom Mark All as Read</p>} 
/>
<NotificationFeed
  headerRightComponent={({ markAllRead: ()=>void, closeInboxPopover: ()=>void }) => <p onClick={markAllRead}>Custom Mark All as Read</p>} 
/>

Custom notification card component

You can customise either css styles of existing notification card component or replace existing card with your own card component. To replace existing component use notificationComponent prop.

<Inbox
  notificationComponent={(config: CustomNotificationCardProps) => (
    <p onClick={() => config.markAsRead(config.notificationData.n_id)}>
      My Notification card
    </p>
  )}
/>
<NotificationFeed
  notificationComponent={(config: CustomNotificationCardProps) => (
    <p onClick={() => config.markAsRead(config.notificationData.n_id)}>
      My Notification card
    </p>
  )}
/>
interface CustomNotificationCardProps {
  notificationData: IRemoteNotification;
  markAsRead: (e?: Event) => Promise<ApiResponse> | undefined;
  markAsUnread: (e?: Event) => Promise<ApiResponse> | undefined;
  markAsArchived: (e?: Event) => Promise<ApiResponse> | undefined;
  markAsInteracted: (e?: Event) => Promise<ApiResponse> | undefined;
}

BodyMarkdown component

We support markdown language in body field in inbox template. So while designing your own notification card component if you want to use our inbuilt body component which includes support for markdown language to save some time you could use BodyMarkdown component.

<Inbox
  notificationComponent={(config: CustomNotificationCardProps) => (
    <div onClick={() => config.markAsRead(config.notificationData.n_id)}>
      <p>My Notification card</p>
      <BodyMarkdown body={config.notificationData.message.text} />
    </div>
  )}
/>
interface BodyMarkdownProps {
  body: string;
  handleActionClick?: HandleActionClick; // for links, this callback will be executed on click
  style?: NotificationCardBodyTextThemeProps;
}

interface NotificationCardBodyTextThemeProps extends React.CSSProperties {
  color?: string;
  blockquoteColor?: string;
  tableBorderColor?: string;
  linkColor?: string;
}

type HandleActionClick = (
  e: React.MouseEvent<HTMLSpanElement, MouseEvent>,
  clickData: ClickHandlerPayload
) => void;


Custom loader component

Existing loader icon which is displayed while getting notifications from SuprSend server, can be changed.

<Inbox loaderComponent={() => (<p>Loading...</p>)} />
<NotificationFeed loaderComponent={() => (<p>Loading...</p>)} />

Custom no notifications component

When no notifications are present then we show empty component. This component can be overridden by using noNotificationsComponent prop.

<Inbox noNotificationsComponent={() => (<p>No Notifications Yet</p>)} />
<NotificationFeed noNotificationsComponent={() => (<p>No Notifications Yet.</p>)} />

Customising CSS styles

If you don't want to replace existing components, you have an option to change styles of them.

// typedef for Inbox drop-in component
interface ITheme extends INotificationFeedTheme {
  bell?: IconThemeProps;
  badge?: BadgeThemeProps;
}

interface IconThemeProps {
  height?: number | string;
  width?: number | string;
  color?: string;
}

interface BadgeThemeProps extends React.CSSProperties {
  backgroundColor?: string;
  color?: string;
}

// typedef for NotificationFeed drop-in component
interface INotificationFeedTheme {
  header?: IHeaderTheme;
  tabs?: TabsThemeProps;
  notificationsContainer?: INotificationsContainerTheme;
  notification?: INotificationCardTheme;
}

interface IHeaderTheme {
  container?: React.CSSProperties;
  headerText?: React.CSSProperties;
  markAllReadText?: React.CSSProperties;
}

interface TabsThemeProps {
  color?: string;
  unselectedColor?: string;
  bottomColor?: string;
  badgeColor?: string;
  badgeText?: string;
}

interface INotificationsContainerTheme {
  container?: React.CSSProperties;
  noNotificationsText?: React.CSSProperties;
  noNotificationsSubtext?: React.CSSProperties;
  loader?: LoaderThemeProps;
}

interface INotificationCardTheme {
  container?: NotificationCardContainerThemeProps;
  pinnedIcon?: IconThemeProps;
  pinnedText?: React.CSSProperties;
  headerText?: React.CSSProperties;
  bodyText?: NotificationCardBodyTextThemeProps;
  unseenDot?: React.CSSProperties;
  avatar?: React.CSSProperties;
  createdOnText?: React.CSSProperties;
  subtext?: React.CSSProperties;
  expiresText?: NotificationCardExpriesTextThemeProps;
  actions?: Array<{
    container?: NotificationCardActionButtonViewThemeProps;
    text?: React.CSSProperties;
  }>;
  actionsMenuIcon?: CardActionMenuIconThemeProps;
  actionsMenu?: React.CSSProperties;
  actionsMenuItem?: CardActionsMenuItem;
  actionsMenuItemIcon?: IconThemeProps;
  actionsMenuItemText?: React.CSSProperties;
}

interface LoaderThemeProps {
  color?: string;
}

interface NotificationCardContainerThemeProps extends React.CSSProperties {
  borderBottom?: string | number;
  readBackgroundColor?: string;
  unreadBackgroundColor?: string;
  hoverBackgroundColor?: string;
}

interface NotificationCardBodyTextThemeProps extends React.CSSProperties {
  color?: string;
  blockquoteColor?: string;
  tableBorderColor?: string;
  linkColor?: string;
}

interface NotificationCardExpriesTextThemeProps extends React.CSSProperties {
  expiringBackgroundColor?: string;
  expiringColor?: string;
}

interface NotificationCardActionButtonViewThemeProps extends React.CSSProperties {
  hoverBackgroundColor?: string;
}

interface CardActionMenuIconThemeProps extends IconThemeProps {
  hoverBackgroundColor?: string;
}

interface CardActionsMenuItem extends React.CSSProperties {
  hoverBackgroundColor?: string;
}


const darkColors = {
  primary: '#2E70E8',
  primaryText: '#EFEFEF',
  secondaryText: '#CBD5E1',
  border: '#3A4A61',
  main: '#1D2635',
  error: '#F97066',
};

const darkInboxStyles = {
  bell: { color: '#fff' },
  badge: { backgroundColor: darkColors.primary },
  ...darkNotificationsFeedStyles
};

const darkNotificationsFeedStyles={
  header: {
    container: {
      backgroundColor: darkColors.main,
      borderBottom: `0.5px solid ${darkColors.border}`,
      boxShadow: '0 0 5px 0 rgba(0, 0, 0, 0.5)',
    },
    headerText: { color: darkColors.primaryText },
    markAllReadText: { color: darkColors.primary },
  },
  tabs: {
    color: darkColors.primaryText,
    unselectedColor: darkColors.secondaryText + 'D9',
    bottomColor: darkColors.primary,
    badgeColor: 'rgba(100, 116, 139, 0.5)',
    badgeText: darkColors.primaryText,
  },
  notificationsContainer: {
    container: {
      backgroundColor: darkColors.main,
      borderColor: darkColors.border,
    },
    noNotificationsText: {
      color: darkColors.primaryText,
    },
    noNotificationsSubtext: {
      color: darkColors.secondaryText,
    },
    loader: { color: darkColors.primary },
  },
  notification: {
    container: {
      borderBottom: `1px solid ${darkColors.border}`,
      readBackgroundColor: darkColors.main,
      unreadBackgroundColor: '#273244',
      hoverBackgroundColor: '#2D3A4D',
    },
    pinnedText: {
      color: darkColors?.secondaryText,
    },
    headerText: { color: darkColors.primaryText },
    bodyText: {
      color: darkColors.secondaryText,
      blockquoteColor: 'rgba(100, 116, 139, 0.5)',
    },
    unseenDot: { backgroundColor: darkColors.primary },
    createdOnText: { color: darkColors.secondaryText },
    subtext: { color: '#94a3b8' },
    actions: [
      { container: { backgroundColor: darkColors.primary } },
      {
        container: {
          borderColor: darkColors.border,
          backgroundColor: 'transparent',
          hoverBackgroundColor: darkColors.main,
        },
        text: { color: darkColors.secondaryText },
      },
    ],
    expiresText: {
      backgroundColor: 'rgba(100, 116, 139, 0.5)',
      color: darkColors.secondaryText,
      expiringBackgroundColor: 'rgba(217, 45, 32, 0.15)',
      expiringColor: darkColors.error,
    },
    actionsMenuIcon: {
      color: darkColors.secondaryText,
      hoverBackgroundColor: 'rgba(100, 116, 139, 0.5)',
    },
    actionsMenu: {
      backgroundColor: darkColors.main,
      borderColor: darkColors.border,
    },
    actionsMenuItem: { hoverBackgroundColor: 'rgba(100, 116, 139, 0.2)' },
    actionsMenuItemIcon: { color: darkColors.secondaryText },
    actionsMenuItemText: {
      color: darkColors.secondaryText,
    },
  }
}