Skip to main content

Integrating notifications overview

This section explores how to integrate notifications on your dApp, mobile app, crypto wallet or on any other frontend.

Push is an interoperable network which already have live integrations from Push Metamask snap, Unstoppable web / mobile app, Push dapp / mobile app / extension among other dapps, extensions and mobile apps. This means notifs are already received by wallets that are on those platforms as they have integrated Push protocol.

Integration process

Integrating notifications directly on your frontend is a critical component as it enables your users to have the best experience and massively improves re-engagement and Web3 UX for your protocol.

To get started, you will need to —

  • Ensure your frontend has a way for your users to opt in to your channel on your frontend.
  • Choose what notification fetching mechanism you want to integrate for notifications to your users (Pull based, Push based or bridge based) on your frontend.

Receiving notifications

Notifications can be received on your frontend using either active user interaction, also known as pull based method or showing notifications as soon as it's triggered requiring no user interaction, which is known as push based method or triggering additional logic when notification is sent to a user using bridge based method.

note

You will need to pick the best method according to your needs. Most of the time, it will be a combination of Pull and Push or all three for a more polished and re-engaging experience for your user.

Pull based method

This method enables users to see notifications when they actively interact with an object on your frontend and relies on fetching notifications using restful APIs. For example: when you click bell icon on youtube and it fetches your notifications.

Push based method

This method doesn't rely on user interaction, instead it shows users notifications as soon as the frontend receives it. This is useful particularly when a user is on your frontend and you show them notifications as they are received.

Bridge based method

This method is usually used when you want notifications to land on user's mobile home screen or through browser notification service.

Bridge based method allows triggering additional logic and are more of an extension to push based method as they enable routing to other Web2 notification services such as Firebase, Apple Push Notification Service or to telegram / twitter / or other social bots.

note

Bridge based delivery is especially useful if you want to deliver notifications to your mobile app or extension while pull based and push based mechanism works best inside your dapp or mobile app.

Pull method live implementation

Implementing pull based mechanism requires inclusion of fetch notifications API and then using your custom frontend component or UIWeb component of Push to display them.

customPropMinimized = 'true';
// DO NOT FORGET TO IMPORT LIBRARIES
// NOT NEEDED HERE SINCE PLAYGROUND IMPORTS INTERNALLY

// import { ethers } from ethers;
// import { PushAPI, CONSTANTS } from @pushprotocol/restapi;
// import { NotificationItem } from @pushprotocol/uiweb;

function App(props) {
  const [wallet, setWallet] = useState(
    '0xD8634C39BBFd4033c0d3289C4515275102423681'
  );
  const [notifItems, setNotifItems] = useState([]);

  const walletRef = useRef();

  useEffect(() => {
    if (walletRef.current) {
      walletRef.current.value = wallet;
    }
  }, [wallet]);

  const fetchNotification = async () => {
    const walletText = walletRef.current.value;

    // Demo only supports MetaMask (or other browser based wallets) and gets provider that injects as window.ethereum into each page
    const provider = new ethers.providers.Web3Provider(window.ethereum);

    // Switch to sepolia
    await provider.send('wallet_switchEthereumChain', [
      { chainId: '0xAA36A7' },
    ]);

    // Get provider
    await provider.send('eth_requestAccounts', []);

    // Grabbing signer from provider
    const signer = provider.getSigner();

    // Initialize user for push
    const userAlice = await PushAPI.initialize(signer, {
      env: CONSTANTS.ENV.STAGING,
    });

    // retrieve notifications for users
    const inboxNotifications = await userAlice.notification.list('INBOX', {
      account: `eip155:11155111:${wallet}`,
      limit: 5,
    });

    // set notifItems state so that react can render
    setNotifItems(inboxNotifications);
  };

  function NotificationInterface() {
    const inputStyle = {
      padding: '10px',
      margin: '10px 0',
      width: '100%',
      boxSizing: 'border-box',
    };

    const textareaStyle = {
      ...inputStyle,
      height: '100px',
      resize: 'vertical',
    };

    const buttonStyle = {
      padding: '10px 20px',
      backgroundColor: '#dd44b9',
      color: '#FFF',
      border: 'none',
      borderRadius: '5px',
      cursor: 'pointer',
      marginTop: '20px',
    };

    return (
      <div style={{ width: 'auto', margin: '20px auto' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between' }}>
          <div style={{ flex: 1 }}>
            <h2>
              Pull based mechanism for displaying notifcations on frontend
            </h2>
            <p />
            <label>
              Put any wallet address and click on fetch notifications to see the
              live results. Click to expand <b>Live Editor</b> tab to see the
              code and play with it. For this demo, You will need Metamask (or
              equivalent browser injected wallet), you will also need to sign a
              transaction to see the notifications.
            </label>
            <p />
            <label>Wallet address</label>
            <input
              type='text'
              placeholder='Enter wallet address'
              style={inputStyle}
              ref={walletRef}
              maxLength={80}
            />
          </div>
        </div>
        <button style={buttonStyle} onClick={fetchNotification}>
          Fetch Notifications
        </button>

        <p />
        <p />

        {notifItems.length > 0 ? (
          <h3>{`Notification Items for ${wallet}`}</h3>
        ) : (
          <></>
        )}

        {notifItems.map((notifItemSingular, idx) => {
          const {
            cta,
            title,
            message,
            app,
            icon,
            image,
            url,
            blockchain,
            notification,
          } = notifItemSingular;

          return (
            <NotificationItem
              key={idx} // any unique id
              notificationTitle={title}
              notificationBody={message}
              cta={cta}
              app={app}
              icon={icon}
              image={image}
              url={url}
              theme={'light'} // or can be dark
              chainName={blockchain}
              // chainName={blockchain as chainNameType} // if using Typescript
            />
          );
        })}
      </div>
    );
  }

  return (
    <>
      <NotificationInterface />
    </>
  );
}
LIVE PREVIEW

Push method live implementation

Implementing push based mechanism requires connecting to Push nodes via stream notifications API which will trigger notifications as they come. You can then use your custom frontend component or UIWeb component of Push to display them.

customPropMinimized = 'true';
// DO NOT FORGET TO IMPORT LIBRARIES
// NOT NEEDED HERE SINCE PLAYGROUND IMPORTS INTERNALLY

// import { ethers } from ethers;
// import { PushAPI, CONSTANTS } from @pushprotocol/restapi;
// import { NotificationItem } from @pushprotocol/uiweb;

function App(props) {
  const [wallet, setWallet] = useState(
    '0xD8634C39BBFd4033c0d3289C4515275102423681'
  );
  const [progressTexts, setProgressTexts] = useState([]);
  const [notifItems, setNotifItems] = useState([]);

  const triggerNotification = async () => {
    // Demo only supports MetaMask (or other browser based wallets) and gets provider that injects as window.ethereum into each page
    const provider = new ethers.providers.Web3Provider(window.ethereum);

    // Switch to sepolia
    await provider.send('wallet_switchEthereumChain', [
      { chainId: '0xAA36A7' },
    ]);

    // Get provider
    await provider.send('eth_requestAccounts', []);

    // Grabbing signer from provider
    const signer = provider.getSigner();

    // Initialize user for push
    const userAlice = await PushAPI.initialize(signer, {
      env: CONSTANTS.ENV.STAGING,
    });

    // establish connection to stream
    const stream = await userAlice.initStream([
      CONSTANTS.STREAM.CONNECT,
      CONSTANTS.STREAM.NOTIF,
    ]);

    // Listen for stream connection
    stream.on(CONSTANTS.STREAM.CONNECT, async (data) => {
      console.log('STREAM CONNECTED');
      let text = [
        'Stream Connected...',
        'Sending Simulated Notification...',
        'Wait for few moments for stream to capture notif and display...',
        'Waiting for you to sign notification payload...',
      ];
      setProgressTexts(text);
      await userAlice.channel.send([userAlice.account], {
        notification: {
          title: 'GM Builders!',
          body: `_Simulated notification_ listened by stream and rendered with **@UIWeb/NotificationItem** with latest timestamp - ${new Date().valueOf()} [timestamp: ${new Date().valueOf()}]`,
        },
        payload: {
          title: 'GM Builders!',
          body: `_Simulated notification_ listened by stream and rendered with **@UIWeb/NotificationItem** with latest timestamp - ${new Date().valueOf()} [timestamp: ${new Date().valueOf()}]`,
          cta: 'https://push.org',
          embed:
            'https://push.org/assets/images/cover-image-8485332aa8d3f031e142a1180c71b341.webp',
        },
      });
      text.push(
        'Message generated and sent. Waiting for stream to pick it up...'
      );
      setProgressTexts(text);
    });

    // Listen for notifications
    stream.on(CONSTANTS.STREAM.NOTIF, (item) => {
      let text = progressTexts;
      console.log(item);
      text.push('Notification Received...');
      text.push(JSON.stringify(item));
      setProgressTexts(text);

      // create notification item compatible with UIWeb/NotificationItem
      const compatibleNotifItem = {
        title: item.message.payload.title,
        message: item.message.payload.body,
        image: item.message.payload.embed,
        cta: item.message.payload.cta,
        icon: item.channel.icon,
        app: item.channel.name,
        url: item.channel.url,
        blockchain: item.source,
        notification: item.message.notification,
      };
      setNotifItems([compatibleNotifItem]);
    });

    // connect stream
    stream.connect();
  };

  function NotificationInterface() {
    const inputStyle = {
      padding: '10px',
      margin: '10px 0',
      width: '100%',
      boxSizing: 'border-box',
    };

    const textareaStyle = {
      ...inputStyle,
      height: '100px',
      resize: 'vertical',
    };

    const buttonStyle = {
      padding: '10px 20px',
      backgroundColor: '#dd44b9',
      color: '#FFF',
      border: 'none',
      borderRadius: '5px',
      cursor: 'pointer',
      marginTop: '20px',
    };

    return (
      <div style={{ width: 'auto', margin: '20px auto' }}>
        <div style={{ display: 'flex', justifyContent: 'space-between' }}>
          <div style={{ flex: 1 }}>
            <h2>
              Push based mechanism for displaying notifcations on frontend
            </h2>
            <p />
            <label>
              Put any wallet address and click on fetch notifications to see the
              live results. Click to expand <b>Live Editor</b> tab to see the
              code and play with it. For this demo, You will need Metamask (or
              equivalent browser injected wallet), you will also need to sign a
              transaction to see the notifications.
            </label>
            <p />
          </div>
        </div>
        <div>
          <hr />
          <h3>
            Progress (will show progress information once Trigger Notification
            is clicked)
          </h3>

          {progressTexts.map((text, idx) => {
            return (
              <>
                <span>{text}</span>
                <br />
              </>
            );
          })}
        </div>
        <hr />
        <button style={buttonStyle} onClick={triggerNotification}>
          Trigger Notification
        </button>

        <p />
        <p />

        {notifItems.length > 0 ? (
          <h3>{`Notification Items for ${wallet}`}</h3>
        ) : (
          <></>
        )}

        {notifItems.map((notifItemSingular, idx) => {
          const {
            cta,
            title,
            message,
            app,
            icon,
            image,
            url,
            blockchain,
            notification,
          } = notifItemSingular;

          return (
            <NotificationItem
              key={idx} // any unique id
              notificationTitle={title}
              notificationBody={message}
              cta={cta}
              app={app}
              icon={icon}
              image={image}
              url={url}
              theme={'light'} // or can be dark
              chainName={blockchain}
              // chainName={blockchain as chainNameType} // if using Typescript
            />
          );
        })}
      </div>
    );
  }

  return (
    <>
      <NotificationInterface />
    </>
  );
}
LIVE PREVIEW

Bridge method implementation

Bridge method is recommended when you want to do extra things when a notification is triggered to a wallet.

For example, you might want to route them to telegram bot or use Web2 notification services of Apple or Google to display it on mobile home screen (completely different process than displaying notifications inside your app) which requires following their rules. There are couple of options to implement them —

via Push delivery node

Delivery node is the recomended way to setup your mobile home screen push notifications or to connect to Push protocol notification network via your OS!!

Setup and bridge notifications via Push Delivery Node

via AWS SNS

Delivery node is a decentralized solution while AWS SNS route is centralized. It is recommended to use delivery node but you can use AWS SNS incase you prefer Web2.5 approach — Setup and implement AWS SNS