Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get Safari working with FCM now. #9891

Merged
merged 7 commits into from
Nov 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 26 additions & 14 deletions packages/commonwealth/client/public/firebase-messaging-sw.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,23 @@
// typescript package JUST for our service worker which I feel is a waste of time
// since our service worker is just one file and less than 100 lines of code

/**
* Safari doesn't work with console.log messages from serviceWorker context
* so the only way to debug it is to do a postMessage to the client and then
* the client then logs the message.
*/
function log(message) {
self.clients.matchAll().then((clients) => {
clients.forEach((client) => client.postMessage({ type: 'log', message }));
});
}

log('Within firebase-messaging-sw.js');

// Listen for 'push' events from the push service
self.addEventListener('push', function (event) {
if (!event.data) {
console.warn('Push notification lacks data. Ignoring');
log('Push notification lacks data. Ignoring');
return;
}

Expand All @@ -29,10 +42,7 @@ self.addEventListener('push', function (event) {
// from Knock. Note that this is different from what we see in their logs
// and there are different properties here including event_data.notification
// which is not actually shown in the Knock console.
console.log(
'Received event data from Knock: ',
JSON.stringify(event_data, null, 2),
);
log('Received event data from Knock: ' + JSON.stringify(event_data, null, 2));

// we MUST handle body, title, and url computation here... for all workflow
// message types including comment-created,
Expand Down Expand Up @@ -69,25 +79,27 @@ self.addEventListener('push', function (event) {

// Listen for 'notificationclick' events to handle notification interactions
self.addEventListener('notificationclick', function (event) {
log('Got notification click');
event.notification.close();

// Extract the data from the notification
const { url } = event.notification.data;

// // Perform an action when the notification is clicked, like opening a URL
log('Going to load URL ' + url);

// Perform an action when the notification is clicked, like opening a URL
event.waitUntil(
clients.matchAll({ type: 'window' }).then((windowClients) => {
self.clients.matchAll({ type: 'window' }).then((windowClients) => {
// Check if there is at least one client (browser tab)
for (let i = 0; i < windowClients.length; i++) {
const client = windowClients[i];
// If the app is already open in a tab, focus it
if (client.url === url && 'focus' in client) {
return client.focus();
}
const windowClient = windowClients[i];
windowClient.navigate(url).then(() => windowClient.focus());
return;
}
// If no clients are found, open a new one
if (clients.openWindow) {
return clients.openWindow(url);
if (self.clients.openWindow) {
log('Opening URL in new window: ' + url);
return self.clients.openWindow(url);
}
}),
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { BrowserType } from 'helpers/browser';

const SAFARI_USES_FCM = true;

/**
* Compute the channel for Knock notifications. Firebase cloud messaging or
* Apple.
Expand All @@ -9,7 +11,7 @@ export function computeChannelTypeFromBrowserType(
): 'FCM' | 'APNS' | undefined {
switch (browserType) {
case 'safari':
return 'APNS';
return SAFARI_USES_FCM ? 'FCM' : 'APNS';
case 'chrome':
return 'FCM';
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,19 @@ const app = initializeApp(PUBLIC_FIREBASE_CONFIG);
// Initialize Firebase Cloud Messaging and get a reference to the service
const messaging = getMessaging(app);

if (navigator.serviceWorker) {
console.log('Registering message listener for service worker.');
navigator.serviceWorker.onmessage = (event) => {
if (event.data.type === 'log') {
console.log('Service Worker:', event.data.message);
} else {
console.warn('Ignoring message: ', event);
}
};
} else {
console.log('No service worker');
}

export async function getFirebaseMessagingToken() {
const reg = await navigator.serviceWorker.register(
'/firebase-messaging-sw.js',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ export function useRegisterPushNotificationSubscriptionCallback() {

const permission = await Notification.requestPermission();
if (permission === 'granted') {
console.log('Notification permission granted.');
console.log(
'Notification permission granted for channelType: ' + channelType,
);
const token = await getFirebaseMessagingToken();
await registerClientRegistrationToken.mutateAsync({
id: user.id,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import { getBrowserType } from 'helpers/browser';
import useAppStatus from 'hooks/useAppStatus';

const SAFARI_ENABLED = true;

export const useSupportsPushNotifications = () => {
const { isAddedToHomeScreen } = useAppStatus();

const browserType = getBrowserType();

if (browserType === 'safari' && isAddedToHomeScreen) {
// Safari only works if we've added it as a PWA
return true;
return SAFARI_ENABLED;
}

return browserType === 'chrome';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@ export function useUnregisterPushNotificationSubscriptionCallback() {

const permission = await Notification.requestPermission();
if (permission === 'granted') {
console.log('Notification permission granted.');
console.log(
'Notification permission granted for channelType: ' + channelType,
);
const token = await getFirebaseMessagingToken();
await unregisterClientRegistrationToken.mutateAsync({
id: user.id,
Expand Down
Loading