Inbox Notifications

SuprSend SDK for React Native applications for integrating inbox functionality.

This SDK exposes custom hooks to provide inbox functionality in react native applications.

Installing

npm install @suprsend/react-headless
yarn add @suprsend/react-headless

Integrating

Following are the Components and hooks available in SuprSend Inbox Headless:

  1. SuprSendProvider
  2. useBell
  3. useNotifications
  4. useEvent

SuprSend Provider

Enclose your app component in SuprSendProvider like below

import { SuprSendProvider } from "@suprsend/react-headless";

function App() {
  return (
    <SuprSendProvider
      workspaceKey="<workspace_key>"
      workspaceSecret="<workspace_secret>"
      subscriberId="<subscriber_id>"
      distinctId="<distinct_id>"
    >
      <YourAppCode />
    </SuprSendProvider>
  );
}

useBell hook

This hook provides unSeenCount, markAllSeen which is related to the Bell icon in the inbox

unSeenCount: Use this variable to show the unseen notification count anywhere in your application.
markAllSeen: Used to function to mark seen for all notifications. Call this method on clicking the bell icon so that it will reset the notification count to 0 and all notifications which were not seen till then will be marked seen. (Seen event is different from individual notification clicked event)

import { useBell } from "@suprsend/react-headless";

function Bell() {
  const { unSeenCount, markAllSeen } = useBell();
  return <p onClick={() => markAllSeen()}>{unSeenCount}</p>;
}

useNotifications hook

This hook provides a notifications list, unSeenCount, markClicked, and markAllSeen.

notifications: List of all notifications. This array can be looped and notifications can be displayed.
unSeenCount: Use this variable to show the unseen notification count anywhere in your application.
markClicked: Method used to mark a notification as clicked. Pass notification id which is clicked as the first param.

import { useNotifications } from "@suprsend/react-headless";

function Notifications() {
  const { notifications } = useNotifications();

  return (
    <div>
      <h3>Notifications</h3>
      {notifications.map((notification) => {
        return (
          <NotificationItem
            notification={notification}
            key={notification.n_id}
  					markClicked={markClicked}
          />
        );
      })}
    </div>
  );
}


function NotificationItem({ notification, markClicked }) {
  const message = notification.message;
  const created = new Date(notification.created_on).toDateString();
  
  return (
    <div
      onClick={() => {
        markClicked(notification.n_id);
      }}
      style={{
        backgroundColor: "lightgray",
        margin: 2,
        borderRadius: 5,
        padding: 4,
        cursor: "pointer",
      }}
    >
      <div style={{ display: "flex" }}>
        <p>{message.header}</p>
        {!notification.seen_on && <p style={{ color: "green" }}>*</p>}
      </div>
      <div>
        <p>{message.text}</p>
      </div>
      <div>
        <p style={{ fontSize: "12px" }}>{created}</p>
      </div>
    </div>
  );
}

useEvent hook

This hook is an event emitter, that takes arguments event type, and callback function(gets called when event type event happens). Must be called anywhere inside SuprSendProvider

Handled Events:

  1. new_notification: Called when the new notification occurs and can be used to show toast in your application.
import { useEvent } from "@suprsend/react-headless";

function Home() {
  useEvent("new_notification", (newNotification) => {
    console.log("new notification data: ", newNotification);
    alert("You have new notifications");
  });

  return <p>Home</p>;
}

Check the example implementation: https://codesandbox.io/s/suprsend-react-headless-example-qkxobd?file=/src/App.tsx

Generating Subscriber_id

Use the below function in your server-side code to generate a unique unguessable subscriber_id using your distinct_id and inbox-secret (picked from the Inbox Vendor Integration page).

  • subscriber_id is unique to each distinct_id and should be generated for each user.
  • Inbox Secret is the Shared Secret key available in your Inbox vendor page. This key is unique to your workspace and should not be shared with anyone for security purposes
import base64
import hashlib
import hmac

def hmac_rawurlsafe_base64_string(distinct_id: str, secret: str):
    digest = hmac.HMAC(secret.encode(), msg=distinct_id.encode(), digestmod=hashlib.sha256).digest()
    encoded = base64.urlsafe_b64encode(digest).decode()
    return encoded.rstrip("=")
package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
)

func HmacRawURLSafeBase64String(message, secret string) string {
	hash := hmac.New(sha256.New, []byte(secret))
	hash.Write([]byte(message))
	return base64.RawURLEncoding.EncodeToString(hash.Sum(nil))
}
import crypto from "crypto";

function hmac_rawurlsafe_base64_string(distinct_id, secret) {
  const hash = crypto
    .createHmac("sha256", secret)
    .update(distinct_id)
    .digest("base64");
  return hash.trimEnd("=");
}
package test;

import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;

public class TestHmacGeneratation {
	public static void main(String[] args) throws Exception {
		String distinct_id = "b8278572-2929-4af6-be2b-cdc2bc1f6256";
		String secret = "IG-J8Wvf7M-w4ll13h53NJAMQQNHdUqFTSJ2JVAZl0s";
		TestHmacGeneratation instance = new TestHmacGeneratation();
		String output = instance.hmacRawURLSafeBase64String(distinct_id, secret);
		System.out.println(output);
		// prints dHBWYF4oV190o4j-e3eYxB-SCkeHnoaiofe8EmGk9JQ
	}
	
	private String hmacRawURLSafeBase64String(String distinctId, String secret) throws InvalidKeyException, NoSuchAlgorithmException {
		Mac sha256mac = getSha256macInstance(secret);
		byte[] macData = sha256mac.doFinal(distinctId.getBytes(StandardCharsets.UTF_8));
		String hmacString = Base64.getUrlEncoder().withoutPadding().encodeToString(macData);
		return hmacString;
	}

	private Mac getSha256macInstance(String secret) throws NoSuchAlgorithmException, InvalidKeyException {
		final byte[] secretBytes = secret.getBytes(StandardCharsets.UTF_8);
		SecretKeySpec keySpec = new SecretKeySpec(secretBytes, "HmacSHA256");
		Mac sha256mac;
		try {
			sha256mac = Mac.getInstance("HmacSHA256");
			sha256mac.init(keySpec);
		} catch (NoSuchAlgorithmException e) {
			throw e;
		} catch (InvalidKeyException e) {
			throw e;
		}
		return sha256mac;
	}
}
CREATE EXTENSION "pgcryto";

CREATE OR REPLACE FUNCTION hmac_rawurlsafe_base64_string(distinct_id VARCHAR, secret VARCHAR) RETURNS VARCHAR
LANGUAGE PLPGSQL AS $func$
DECLARE
    hmac_string VARCHAR;
BEGIN
    hmac_string = encode(hmac(distinct_id::TEXT, secret, 'SHA256'), 'base64');
    hmac_string = replace(replace(hmac_string, '+', '-'), '/', '_'); 
    hmac_string = RTRIM(hmac_string, '='); 
    RETURN hmac_string;
END;
$func$;

Why Subscriber id is needed?

subscriber_id is raw-URL-safe-base64 encoded string of SHA-256 HMAC of your distinct_id. Its purpose is to prevent unauthorized access to Inbox service by just spoofing distinct_id. To generate the subscriber_id with respect to a distinct_id, use the inbox secret (from Vendor page) and generation function provided above. It is imperative that the inbox secret is stored safely on your server side and not exposed to client-side code.

🚧

Note

The subscriber_id must be generated by server-side code (not in client app)


Even after setting up the inbox, if you are not able to see notifications then cross-check if your subscriber_id mentioned is exactly correct by opening the subscriber's tab in the Suprsend dashboard and searching for distinct_id like below. In most cases mismatch of subscriber_id with distinct_id causes notifications not getting displayed.