Skip to content

Instantly share code, notes, and snippets.

@klclee
Last active October 6, 2024 13:35
Show Gist options
  • Save klclee/1173fe60ba88b4719df8c76626f69bfd to your computer and use it in GitHub Desktop.
Save klclee/1173fe60ba88b4719df8c76626f69bfd to your computer and use it in GitHub Desktop.
Ably setup.md

Serverside:

I set up 2 endpoints to test:

  • '/ably/register'
export const register = wrap(
  ApiHandler(async (event: any) => {
    const session = useSession();
    const sessionUser = sessionData(session, Config.SESSION_ENCRYPT_KEY);
    if (!sessionUser) return sendResponse(401, { message: 'Unauthorized' });

    try {
      const ably = new Ably.Rest({
        key: 'ROOTKEY'
      });
      logger.info('ablyToken', { clientId: sessionUser.username });
      const tokenDetails = await ably.auth.requestToken({ clientId: sessionUser.username });

      logger.info('ablyToken', tokenDetails);
      return sendResponse(200, tokenDetails);
    } catch (e: any) {
      logger.error('error generating ably token', {
        name: e.name,
        message: e.message,
        stack: e.stack,
      });
      return sendResponse(500, { message: 'Error generating token' });
    }
  }));
  • typical serverside reply:
{
    "token": "TOKEN",
    "keyName": "KEY",
    "issued": 1728206050720,
    "expires": 1728209650720,
    "capability": "{\"[*]*\":[\"publish\",\"push-admin\",\"push-subscribe\"]}",
    "clientId": "opener"
}
  • /ably/noti
export const pushNotification = wrap(
  ApiHandler(async (event: any) => {
    const session = useSession();
    const sessionUser = sessionData(session, Config.SESSION_ENCRYPT_KEY);
    if (!sessionUser) return sendResponse(401, { message: 'Unauthorized' });

    try {
      const ably = new Ably.Rest({
        key: 'ROOT KEY',
      });
      logger.info('ably: noti for', {clientId: sessionUser.username})
      await ably.push.admin.publish(
        {
          clientId: sessionUser.username,
        },
        {
          notification: {
            title: 'Hello from Ably!',
            body: 'Example push notification from Ably.',
          },
          data: {
            bob: 'hi',
            sam: 'ho'
          }
        }
      );
    }catch (e: any) {
      logger.error('error generating ably token', {
        name: e.name,
        message: e.message,
        stack: e.stack,
      });
      return sendResponse(500, { message: 'Error sending' });
    }
  }));

Client Side (React)

import React, { useEffect, useState } from 'react';
import Ably, { RealtimeClient } from 'ably';
import Push from 'ably/push';
import { AblyProvider } from 'ably/react';
import { useAuthContext } from 'src/auth/context';
import { serviceEndpoints } from 'src/config-global';

import { register } from 'register-service-worker'

type Props = {
  children: React.ReactNode;
};
export default function AblyHelper({ children }: Props) {
  const { jwt, authenticated } = useAuthContext();
  const [ablyClient, setAblyClient] = useState<RealtimeClient | undefined>(undefined);


  const act = async () => {
    return new Ably.Realtime({
      authUrl: `${serviceEndpoints.userApi}/ably/register`,
      authHeaders: { Authorization: `Bearer ${jwt}` },
      // pushServiceWorkerUrl: '/ably/service-worker-v3.js',
      plugins: { Push },
    });
  };

  const handleServiceWorkerMessage = (event: any) => {
    console.log('react noti', event)
    if (event.data.type === 'ably') {
      console.log('Received notification in react:', event);
      // Now you can access the notification object and use it in your app
      // For example, navigate to a specific page based on the notification data
    }
  };


  useEffect(() => {
    if (jwt) {
      /*
        NOTE: to when changing service worker. Change the file name to a new one and change the path below.
       */
      register('/ably/service-worker-v1.js', {
        registrationOptions: { scope: '/ably/' },
        registered (registration) {
          console.log('Service worker has been registered.')
          act()
            .then((client) => {
              console.log('ably client', client);
              setAblyClient(client);
              client.connection.once('connected', () => {
                client.push.activate().then(res => {
                  console.info('worker started');
                }).catch(console.error)
              });
            })
            .catch(console.error);
        },
        error (error) {
          console.error('Error during service worker registration:', error)
        }
      })

      navigator.serviceWorker.addEventListener('message', handleServiceWorkerMessage);
    }

    return () => {
      navigator.serviceWorker.removeEventListener('message', handleServiceWorkerMessage);
    };
  }, [jwt]);

  return (
    <>
      {authenticated ? (
        <>
          {ablyClient && (
            <AblyProvider client={ablyClient}>{children}</AblyProvider>
          )}
        </>
      ): children}
    </>
  );
}
  • server worker
self.addEventListener("push", (event) => {
  const { notification } = event.data.json();
  console.log('notification', notification);
  self.registration.showNotification(notification.title, notification);

  event.waitUntil(
    clients.matchAll({ type: "window", includeUncontrolled: true })
    .then(function(clientList) {
      console.log('matching clients',clientList)
      for (var i = 0; i < clientList.length; i++) {
        clientList[i].postMessage({ type: 'ably', data: notification });
      }
    }))
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment