From e188653ada3f9921d2d9a853374d599160890d62 Mon Sep 17 00:00:00 2001 From: lukeweston1234 <45077571+lukeweston1234@users.noreply.github.com> Date: Thu, 2 May 2024 12:00:20 -0400 Subject: [PATCH 1/2] Update messaging.md --- docs/messaging.md | 194 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 185 insertions(+), 9 deletions(-) diff --git a/docs/messaging.md b/docs/messaging.md index a1203c55a..5582918cf 100644 --- a/docs/messaging.md +++ b/docs/messaging.md @@ -6,20 +6,196 @@ # Cloud Messaging -The FCM JavaScript API lets you receive notification messages in web apps running in browsers that support the Push API. +### Provide Messaging to your existing application -[Learn More](https://firebase.google.com/docs/cloud-messaging/) +``` +import { getMessaging, provideMessaging } from "@angular/fire/messaging"; -## Dependency Injection +bootstrapApplication(AppComponent, { + providers: [ + ..., + provideMessaging(() => getMessaging()) + ), + ], +}); +``` -YADA YADA YADA +# Create a Firebase Messaging Service Worker -## Firebase API +There are two parts to Firebase Messaging, a Service Worker and the DOM API. AngularFireMessaging allows you to request permission, get tokens, delete tokens, and subscribe to messages on the DOM side. To register to receive notifications you need to set up the Service Worker. [The official Firebase documentation for setting up the details exactly how to do that](https://firebase.google.com/docs/cloud-messaging/js/client). -Something something look at the offical docs +#### Create your firebase-messaging-sw.js file in your src/assets folder -## Convenience observables +*Note: When copying the below file, make sure your firebase version in your installation matches the version your are importing from below* -### Foo +It may be wise to use file replacements or environments here for different environments -bar baz \ No newline at end of file +``` +import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js"; +import { getMessaging } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-messaging-sw.js"; + +const firebaseApp = initializeApp({ + apiKey: "", + authDomain: "", + projectId: "", + storageBucket: "", + messagingSenderId: "", + appId: "", +}); + +const messaging = getMessaging(firebaseApp); +``` + +# Example messaging service + +``` +import { Injectable } from "@angular/core"; +import { Messaging, getToken, onMessage } from "@angular/fire/messaging"; +import { Observable, tap } from "rxjs"; + +@Injectable({ + providedIn: "root", +}) +export class FcmService { + constructor(private msg: Messaging){ + Notification.requestPermission().then( + (notificationPermissions: NotificationPermission) => { + if (notificationPermissions === "granted") { + console.log("Granted"); + } + if (notificationPermissions === "denied") { + console.log("Denied"); + } + }); + navigator.serviceWorker + .register("/assets/firebase-messaging-sw.js", { + type: "module", + }) + .then((serviceWorkerRegistration) => { + getToken(this.msg, { + vapidKey: `an optional key generated on Firebase for your fcm tokens`, + serviceWorkerRegistration: serviceWorkerRegistration, + }).then((x) => { + console.log('my fcm token', x); + // This is a good place to then store it on your database for each user + }); + }); + } + this.message$ = new Observable((sub) => onMessage(this.msg, (token) => + sub.next(token))).pipe( + tap((token) => { + console.log("My fcm token", token); + }) + ); + } + deleteToken(){ + // We can also delete fcm tokens, make sure to also update this on + // Your firestore db if you are storing them as well + + await deleteToken(this.msg); + } +``` + +# Testing and Sending Notifications + +Firebase will allow you to send a test notification under Engage > Messaging > New Campaign > Notifications. Here you can click send test message. Additionally, you can send them programmatically through Firebase cloud functions. + +Here is a barebones Node example + +``` +export const sendTestMessage = onRequest(async (_, res) => { + try { + const message = { + notification: { + title: "Test Title", + body: "Test Body", + }, + token: "your token here, you can store these and retreive as you please", + }; + await admin.messaging().send(message); + res.sendStatus(200); + } catch (error) { + console.error(error); + res.sendStatus(500); + } +}); +``` + +Here is a Node example that listens for a new comment, then sends a notification, and also adds it to a cache on Firebase so users can click through them. + +``` +exports.onPostReply = + onDocumentCreated("comments/{commentId}", async (event) => { + if (!event) throw new Error("No event found for document creation"); + const snapshot = event.data; + if (!snapshot) { + throw new Error("No data associated with the event"); + } + const data = snapshot.data(); + if (!data.postId) { + throw new Error("No post ID found"); + } + const postRef = await firestore.collection("posts").doc(data.postId).get(); + const postData = postRef.data(); + if (!postData) { + throw new Error("No postData found"); + } + // userUid will be the post author's id. + const {userUid} = postData; + if (!userUid) { + throw new Error( + "Could not find the userUid for the post author for post reply" + ); + } + const messageForNotification = { + title: "You have a new reply on your post", + body: "", + }; + await createNotificationAndCache(messageForNotification, userUid); + }); + + // If you want to cache notifications a number of times, abstracting this + // to a function can bring a lot of value. + +interface NotificationProps { + title: string; + body: string; +} + +async function createNotificationAndCache( + notificationProps: NotificationProps, userAuthUid: string) { + const userRef = await firestore.collection("users").where("authUid", "==", + userAuthUid).get(); + const userData = userRef.docs[0].data(); + + const promises: Promise[] = []; + // This sample application has seperate fcm tokens for web and mobile + if (userData.mobileToken) { + const message = { + notification: notificationProps, + token: userData.mobileToken, + }; + const promise = admin.messaging().send(message); + promises.push(promise); + } + if (userData.webToken) { + const message = { + notification: notificationProps, + token: userData.webToken, + }; + const promise = admin.messaging().send(message); + promises.push(promise); + } + + const notificationCacheValue = { + userAuthUid: userAuthUid, + tokenTitle: notificationProps.title, + tokenBody: notificationProps.body, + isActive: true, // This determines whether a notification has been seen + }; + + promises.push( + firestore.collection("notificationCache").add(notificationCacheValue)); + + await Promise.all(promises); +} ``` From 2951a4919af3db49fd2df4f534a0b8805dc68e71 Mon Sep 17 00:00:00 2001 From: lukeweston1234 <45077571+lukeweston1234@users.noreply.github.com> Date: Thu, 2 May 2024 12:09:31 -0400 Subject: [PATCH 2/2] Update messaging.md --- docs/messaging.md | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/docs/messaging.md b/docs/messaging.md index 5582918cf..fe5aa3742 100644 --- a/docs/messaging.md +++ b/docs/messaging.md @@ -6,7 +6,9 @@ # Cloud Messaging -### Provide Messaging to your existing application +Firebase FCM allows you to register devices with unique FCM tokens, that you can later programtically send notifications to using Firebase Cloud Functions. It is up to the application to update these tokens in Firebase if you want to use them in other layers of your application, i.e send a notification to all administrators, etc. In that case, you would likely want to store your fcm tokens on your user collection, or a sub collection or another collection with different permissions. + +# Provide Messaging to your existing application ``` import { getMessaging, provideMessaging } from "@angular/fire/messaging"; @@ -22,7 +24,7 @@ bootstrapApplication(AppComponent, { # Create a Firebase Messaging Service Worker -There are two parts to Firebase Messaging, a Service Worker and the DOM API. AngularFireMessaging allows you to request permission, get tokens, delete tokens, and subscribe to messages on the DOM side. To register to receive notifications you need to set up the Service Worker. [The official Firebase documentation for setting up the details exactly how to do that](https://firebase.google.com/docs/cloud-messaging/js/client). +There are two parts to Firebase Messaging, a Service Worker and the DOM API. Angular Fire Messaging allows you to request permission, get tokens, delete tokens, and subscribe to messages on the DOM side. To register to receive notifications you need to set up the Service Worker. [The official Firebase documentation for setting up the details exactly how to do that](https://firebase.google.com/docs/cloud-messaging/js/client). #### Create your firebase-messaging-sw.js file in your src/assets folder @@ -31,6 +33,8 @@ There are two parts to Firebase Messaging, a Service Worker and the DOM API. Ang It may be wise to use file replacements or environments here for different environments ``` +// This sample application is using 9.22, make sure you are importing the same version + import { initializeApp } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-app.js"; import { getMessaging } from "https://www.gstatic.com/firebasejs/9.22.0/firebase-messaging-sw.js"; @@ -50,7 +54,7 @@ const messaging = getMessaging(firebaseApp); ``` import { Injectable } from "@angular/core"; -import { Messaging, getToken, onMessage } from "@angular/fire/messaging"; +import { Messaging, getToken, onMessage, deleteToken } from "@angular/fire/messaging"; import { Observable, tap } from "rxjs"; @Injectable({ @@ -81,26 +85,24 @@ export class FcmService { }); }); } - this.message$ = new Observable((sub) => onMessage(this.msg, (token) => - sub.next(token))).pipe( - tap((token) => { - console.log("My fcm token", token); + this.message$ = new Observable((sub) => onMessage(this.msg, (msg) => + sub.next(msg))).pipe( + tap((msg) => { + console.log("My Firebase Cloud Message", msg); }) ); } deleteToken(){ - // We can also delete fcm tokens, make sure to also update this on - // Your firestore db if you are storing them as well - - await deleteToken(this.msg); + // We can also delete fcm tokens, make sure to also update this on your firestore db if you are storing them as well + await deleteToken(this.msg); } ``` # Testing and Sending Notifications -Firebase will allow you to send a test notification under Engage > Messaging > New Campaign > Notifications. Here you can click send test message. Additionally, you can send them programmatically through Firebase cloud functions. +Firebase will allow you to send a test notification under Engage > Messaging > New Campaign > Notifications. Here you can click send a test message. Additionally, you can send them programmatically through Firebase cloud functions. -Here is a barebones Node example +Here is a barebones Node example: ``` export const sendTestMessage = onRequest(async (_, res) => { @@ -121,7 +123,7 @@ export const sendTestMessage = onRequest(async (_, res) => { }); ``` -Here is a Node example that listens for a new comment, then sends a notification, and also adds it to a cache on Firebase so users can click through them. +Here is a Node example that listens for a new comment on a collection, then sends a notification, and also adds it to a cache on Firebase so users can click through them. ``` exports.onPostReply =