Skip to content

Web push with ccs-chat-notifications-client

The /push entry points of ccs-chat-notifications-client register a browser for web push, so a user is notified when your app is closed. They use only the browser’s service worker and Push API and talk only to the Chat API. For the task view, see Web push.

Install the package as described on the package page. The push entry points need no peer dependency. The React hook needs react.

import { ChatPushClient } from '@central-code-station/ccs-chat-notifications-client/push';
import { usePushRegistration } from '@central-code-station/ccs-chat-notifications-client/push/react';

Copy the template from the package into your static root as push-sw.js. Serve it from the root of your site as JavaScript.

Terminal window
cp node_modules/@central-code-station/ccs-chat-notifications-client/push-sw-template.js public/push-sw.js

On push it shows a notification from { notification: { title, body }, data }. On notificationclick it closes the notification, focuses an open window of your app, or opens /. It does not navigate to the conversation. data carries tenant_id, application_id, conversation_id and message_id for a deep link you add yourself. See Push payloads. ChatPushClient registers the file for you, so you never call serviceWorker.register yourself.

The first permission prompt needs a real user gesture, so call register() from a click handler.

push.ts
import { ChatPushClient } from '@central-code-station/ccs-chat-notifications-client/push';
import { context } from './fe-guide-context';
// Push devices belong to the tenant, so there is no applicationId.
const push = new ChatPushClient({
chatApiUrl: context.chatApiUrl,
tenantId: context.tenantId,
getAccessToken: context.getAccessToken,
// serviceWorkerUrl: '/push-sw.js', // the default
});
const enable = document.querySelector<HTMLButtonElement>('#enable-notifications');
const disable = document.querySelector<HTMLButtonElement>('#disable-notifications');
if (!push.isSupported()) enable?.remove(); // no service worker or Push API here
// The first permission prompt needs a real click. Do not call register() on page load.
enable?.addEventListener('click', async () => {
const result = await push.register(); // never rejects
if (!result.success) console.warn('Web push is not available', result.error);
});
disable?.addEventListener('click', () => void push.unregister());
Member Description
isSupported() Whether this browser supports web push at all. Check it before you show the button.
register() Registers the service worker, fetches Chat’s public key, subscribes with the Push API and sends the subscription to Chat. Safe to call again. Never rejects.
unregister() Unsubscribes this browser and removes it from Chat. Works even if register() did not run in this session. Never rejects.

Both methods return { success, error? } and log failures to the console as [ChatPushClient]. register() calls the public key operation and Register a device. unregister() calls Unregister a device. Devices belong to the tenant, so there is no applicationId and one registration covers all its applications.

Field Required Description
chatApiUrl yes The Chat API base URL.
tenantId yes The tenant whose notifications this browser receives.
getAccessToken yes Returns the user’s Chat token, sync or async. Read again on every register() and unregister().
serviceWorkerUrl no Path of the service worker you host. Default /push-sw.js.

usePushRegistration(options) takes the same options and returns { isSupported, status, register, unregister }. status is 'idle', 'registering', 'unregistering', 'registered' or 'error'.

notifications-toggle.tsx
import { usePushRegistration } from '@central-code-station/ccs-chat-notifications-client/push/react';
import { context } from './fe-guide-context';
export function NotificationsToggle() {
const { isSupported, status, register, unregister } = usePushRegistration({
chatApiUrl: context.chatApiUrl,
tenantId: context.tenantId,
getAccessToken: context.getAccessToken,
});
if (!isSupported) return null;
const busy = status === 'registering' || status === 'unregistering';
// status starts as 'idle' and only tracks calls made from this hook.
return (
<button disabled={busy} onClick={() => (status === 'registered' ? unregister() : register())}>
{status === 'registered' ? 'Turn off notifications' : 'Turn on notifications'}
{status === 'error' ? ' (not available)' : ''}
</button>
);
}
  • It does not detect an existing subscription. status starts at 'idle' and tracks only calls made through the hook.
  • It does not register native mobile devices, and it does not run in React Native.
  • It has no /push/angular entry point. There is no reactive state to bridge into a Signal, only two async actions, so inject ChatPushClient into your own @Injectable() service.
  • It does not choose who is notified. Chat sends web push only to offline members. See Push notifications.