From 4027dfd106ee3c5b38d7e3f95e9ad5b9a2eb2508 Mon Sep 17 00:00:00 2001 From: David East Date: Thu, 6 Sep 2018 11:36:25 -0600 Subject: [PATCH 1/8] docs(functions): Document AngularFireFunctions Don't merge until `@angular/fire` rename occurs. --- docs/functions/functions.md | 72 +++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 docs/functions/functions.md diff --git a/docs/functions/functions.md b/docs/functions/functions.md new file mode 100644 index 000000000..eaf817edb --- /dev/null +++ b/docs/functions/functions.md @@ -0,0 +1,72 @@ +# AngularFireFunctions + +> The Cloud Functions for Firebase client SDKs let you call functions directly from a Firebase app. To call a function from your app in this way, write and deploy an HTTPS Callable function in Cloud Functions, and then add client logic to call the function from your app. + +### Import the `NgModule` + +Cloud Functions for AngularFire is contained in the `@angular/fire/functions` module namespace. Import the `AngularFireFunctionsModule` in your `NgModule`. This sets up the `AngularFireFunction` service for dependency injection. + +```ts +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; +import { AppComponent } from './app.component'; +import { AngularFireModule } from '@angular/fire'; +import { AngularFireFunctionsModule } from '@angular/fire/functions'; +import { environment } from '../environments/environment'; + +@NgModule({ + imports: [ + BrowserModule, + AngularFireModule.initializeApp(environment.firebase), + AngularFireFunctionsModule + ], + declarations: [ AppComponent ], + bootstrap: [ AppComponent ] +}) +export class AppModule {} +``` + +### Injecting the AngularFireFunctions service + +Once the `AngularFireFunctionsModule` is registered you can inject the `AngularFireFunctions` service. + +```ts +import { Component } from '@angular/core'; +import { AngularFireFunctions } from '@angular/fire/functions'; + +@Component({ + selector: 'app-component', + template: `` +}) +export class AppComponent { + constructor(private fns: AngularFireFunctions { } +} +``` + +### Creating a callable function + +AngularFireFunctions is super easy. You create a function on the server side and then "call" it by its name with the client library. + +| method | | +| ---------|--------------------| +| `httpCallable(name: string): (data: T) ` | Creates a callable function based on a function name. Returns a function that can create the observable of the http call. | +```ts + +import { Component } from '@angular/core'; +import { AngularFireStorage } from '@angular/fire/functions'; + +@Component({ + selector: 'app-root', + template: ` + { data$ } | async + ` +}) +export class AppComponent { + constructor(private fns: AngularFireFunctions) { + const callable = fns.httpsCallable('my-fn-name'); + this.data$ = callable({ name: 'some-data' }); + } +} +``` + +Notice that calling `httpsCallable()` does not initiate the request. It creates a reusable function that is called to initiate the request. From 5a68f24f9dab42fb6bf4647a78b94bd7f05e91ff Mon Sep 17 00:00:00 2001 From: David East Date: Thu, 6 Sep 2018 11:37:50 -0600 Subject: [PATCH 2/8] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 8bb269210..87b780185 100644 --- a/README.md +++ b/README.md @@ -92,8 +92,10 @@ Firebase offers two cloud-based, client-accessible database solutions that suppo ### Universal - [Server-side Rendering with Universal](docs/server-side-rendering.md) -### Deploy to Firebase Hosting +### Functions +- [Getting started with Callable Functions](docs/functions/functions.md) +### Deploy to Firebase Hosting - [Deploying AngularFire to Firebase Hosting](docs/deploying-angularfire-to-firebase.md) ### Ionic From ca6f4d4cba40576c1c4ac90c5a1dd02c87d9e7c2 Mon Sep 17 00:00:00 2001 From: David East Date: Thu, 6 Sep 2018 12:47:33 -0600 Subject: [PATCH 3/8] Create messaging.md --- docs/messaging/messaging.md | 196 ++++++++++++++++++++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 docs/messaging/messaging.md diff --git a/docs/messaging/messaging.md b/docs/messaging/messaging.md new file mode 100644 index 000000000..363da3239 --- /dev/null +++ b/docs/messaging/messaging.md @@ -0,0 +1,196 @@ +# AngularFireMessaging + +> The FCM JavaScript API lets you receive notification messages in web apps running in browsers that support the Push API. support. + +### AngularFireMessaging is not compatible with the Angular Service Worker + +If you are using the Angular Service Worker, you are not currently able to use AngularFireMessaging. If you'd like this feature please file an issue in [either repository](https://github.com/angular/angular/tree/master/packages/service-worker). Your alternatives are to use [WorkboxJS](https://developers.google.com/web/tools/workbox/) or just simply use the Firebase Messaging Service Worker, which is detailed below. + +### Import the `NgModule` + +Cloud Storage for AngularFire is contained in the `@angular/fire/messaging` module namespace. Import the `AngularFireMessagingModule` in your `NgModule`. This sets up the `AngularFireMessaging` service for dependency injection. + +```ts +import { BrowserModule } from '@angular/platform-browser'; +import { NgModule } from '@angular/core'; +import { AppComponent } from './app.component'; +import { AngularFireModule } from '@angular/fire'; +import { AngularFireStorageModule } from '@angular/fire/messaging'; +import { environment } from '../environments/environment'; + +@NgModule({ + imports: [ + BrowserModule, + AngularFireModule.initializeApp(environment.firebase), + AngularFireMessagingModule + ], + declarations: [ AppComponent ], + bootstrap: [ AppComponent ] +}) +export class AppModule {} +``` + +### Setting up the Firebase Messaging Service Worker + +There are two parts 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). + +You can either use the `firebase-messaging-sw.js` file provided in the docs or you can set your own Service Worker to import that script. Make sure to set up your `.angular-cli.json` file to copy over the Service Worker file: + +```json + "assets": [ + "assets", + "favicon.ico", + "firebase-messaging-sw.js", + "manifest.json" + ], +``` + +### Requesting permission + +Once you have the Firebase Messaging Service Worker setup and installed, you need to request permission to send a user notifications. While the browser will popup a UI for you, it is highly recommend to ask the user for permission with a custom UI and only ask when it makes sense. If you blindly ask for permission you have an extremely high chance of getting denied. + +```ts +import { Component } from '@angular/core'; +import { AngularFireMessaging } from '@angular/fire/messaging'; + +@Component({ + selector: 'app-root', + template: ` + + ` +}) +export class AppComponent { + constructor(private afMessaging: AngularFireMessaging) { } + requestPermission() { + this.afMessaging.requestPermission + .subscribe( + () => { console.log('Permission granted!'); }, + (error) => { console.error(error); }, + ); + } +} +``` + +Once you have the permission of the user, you need their token. You can do this with the `getToken` observable or the `tokenChanges` observable. The `tokenChanges` observable listens for token refreshes whereas the `getToken` observable is a one-time call. + +```ts +import { Component } from '@angular/core'; +import { AngularFireMessaging } from '@angular/fire/messaging'; +import { mergeMapTo } from 'rxjs/operators'; + +@Component({ + selector: 'app-root', + template: ` + + ` +}) +export class AppComponent { + constructor(private afMessaging: AngularFireMessaging) { } + requestPermission() { + this.afMessaging.requestPermission + .pipe(mergeMapTo(this.afMessaging.tokenChanges)) + .subscribe( + (token) => { console.log('Permission granted! Save to the server!', token); }, + (error) => { console.error(error); }, + ); + } +} +``` + +Once you have a user's token, you need to save it to the server in order to send them notifications in response to events. Let's say ou want to send a push each time a user sends a chat message. Once a user grants permission, you can send the token to the Realtime Database or Cloud Firestore and associate it with a unique id, like a Firebase Auth UID. You can then create a Cloud Function trigger that looks up the user's token when a chat message is created. + +### Shortcutting token requests + +An easier way of requesting permission and getting tokens is with the `requestToken` observable. It combines the two steps above into one observable. + +```ts +import { Component } from '@angular/core'; +import { AngularFireMessaging } from '@angular/fire/messaging'; + +@Component({ + selector: 'app-root', + template: ` + + ` +}) +export class AppComponent { + constructor(private afMessaging: AngularFireMessaging) { } + requestPermission() { + this.afMessaging.requestToken + .subscribe( + (token) => { console.log('Permission granted! Save to the server!', token); }, + (error) => { console.error(error); }, + ); + } +} +``` + +The `requestToken` observable uses the `tokenChanges` observable to listen to refreshes. + +### Deleting tokens + +Need to delete a user's token? Not a problem. + +```ts +import { Component } from '@angular/core'; +import { AngularFireMessaging } from '@angular/fire/messaging'; +import { mergeMap } from 'rxjs/operators'; + +@Component({ + selector: 'app-root', + template: ` + + ` +}) +export class AppComponent { + constructor(private afMessaging: AngularFireMessaging) { } + deleteToken() { + this.afMessaging.getToken + .pipe(mergeMap(token => this.afMessaging.deleteToken(token))) + .subscribe( + (token) => { console.log('Deleted!'); }, + ); + } +} +``` + +The code above requests the current user's token and passes it to the `deleteToken()` observable. + +### Subscribing to foreground messages + +Once you have a user's token and they are subscribed, you can listen to messages in the foreground. The Firebase Messaging Service Worker handles background push notifications. + +```ts +import { Component } from '@angular/core'; +import { AngularFireMessaging } from '@angular/fire/messaging'; + +@Component({ + selector: 'app-root', + template: ` + + ` +}) +export class AppComponent { + constructor(private afMessaging: AngularFireMessaging) { } + listen() { + this.afMessaging.messages + .subscribe((message) => { console.log(message); }); + } +} +``` + +### Sending notifications + +[Sending a notification](https://firebase.google.com/docs/cloud-messaging/js/first-message) requires a call to a server. You can do this directly with an HTTP call or you can even build a Cloud Function to do this in response to an event. A Cloud Function trigger is ideal because you have trusted access to the database and can securely look up tokens to send to the right user. If you want to send push notifications via HTTP requests you'll need to secure the API call. This is usually done with a Firebase Auth UID. On the server you can verify the UID with the Firebase Admin SDK and allow access to get a user's push id. + +The [Firebase Admin SDK has helper functions for sending notifications](https://firebase.google.com/docs/cloud-messaging/admin/send-messages) to the user and subscribing them to topics, which [simplifies sending grouped messages](https://firebase.google.com/docs/cloud-messaging/admin/manage-topic-subscriptions). From 78feef7079c920b49ae471db375cebfd2a9847db Mon Sep 17 00:00:00 2001 From: David East Date: Thu, 6 Sep 2018 12:48:25 -0600 Subject: [PATCH 4/8] Update functions.md --- docs/functions/functions.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/functions/functions.md b/docs/functions/functions.md index eaf817edb..cdd709251 100644 --- a/docs/functions/functions.md +++ b/docs/functions/functions.md @@ -39,7 +39,7 @@ import { AngularFireFunctions } from '@angular/fire/functions'; template: `` }) export class AppComponent { - constructor(private fns: AngularFireFunctions { } + constructor(private fns: AngularFireFunctions { }) } ``` From dfc6e847570248040ca58aa7f5d04e2e3cd18f1b Mon Sep 17 00:00:00 2001 From: David East Date: Thu, 6 Sep 2018 12:53:10 -0600 Subject: [PATCH 5/8] Update README.md --- README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 87b780185..d313504b1 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,11 @@ Status: Release candidate - **Realtime bindings** - Synchronize data in realtime. - **Authentication** - Log users in with a variety of providers and monitor authentication state in realtime. - **Offline Data** - Store data offline automatically with AngularFirestore. +- **Server-side Render** - Generate static HTML to boost perceived performance or create static sites. - **ngrx friendly** - Integrate with ngrx using AngularFire's action based APIs. +- **Manage binary data** - Upload, download, and delete binary files like images, videos, and other blobs. +- **Call server code** - Directly call serverless Cloud Functions with user context automatically passed. +- **Push notifications** - Register and listen for push notifications #### Quick links [Contributing](CONTRIBUTING.md) @@ -92,7 +96,10 @@ Firebase offers two cloud-based, client-accessible database solutions that suppo ### Universal - [Server-side Rendering with Universal](docs/server-side-rendering.md) -### Functions +### Send push notifications +- [Getting started with Firebase Messaging](docs/messaging/messaging.md) + +### Directly call Cloud Functions - [Getting started with Callable Functions](docs/functions/functions.md) ### Deploy to Firebase Hosting From 6f001940ee54a5180b0668f6a3f3ed1cc9693f8a Mon Sep 17 00:00:00 2001 From: David East Date: Thu, 6 Sep 2018 12:58:00 -0600 Subject: [PATCH 6/8] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d313504b1..bb5ee1f36 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Status: Release candidate - **Manage binary data** - Upload, download, and delete binary files like images, videos, and other blobs. - **Call server code** - Directly call serverless Cloud Functions with user context automatically passed. - **Push notifications** - Register and listen for push notifications +- **Modular** - Include only what's needed. No AngularFire package is above 3kb with most under 2kb (gzipped). #### Quick links [Contributing](CONTRIBUTING.md) From 9c8e5f3630cc335f323b4e5d5278334ca72524e5 Mon Sep 17 00:00:00 2001 From: David East Date: Thu, 6 Sep 2018 13:24:51 -0600 Subject: [PATCH 7/8] Update messaging.md --- docs/messaging/messaging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/messaging/messaging.md b/docs/messaging/messaging.md index 363da3239..f5b126e3b 100644 --- a/docs/messaging/messaging.md +++ b/docs/messaging/messaging.md @@ -1,6 +1,6 @@ # AngularFireMessaging -> The FCM JavaScript API lets you receive notification messages in web apps running in browsers that support the Push API. support. +> The FCM JavaScript API lets you receive notification messages in web apps running in browsers that support the Push API. ### AngularFireMessaging is not compatible with the Angular Service Worker From 62d6a001febeaff07afc113ca79ec5b345c5ea0e Mon Sep 17 00:00:00 2001 From: James Daniels Date: Thu, 6 Sep 2018 14:02:38 -0700 Subject: [PATCH 8/8] Minor changes * Fix two syntax errors * Note about callable returning an Observable --- docs/functions/functions.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/functions/functions.md b/docs/functions/functions.md index cdd709251..19aeeaf30 100644 --- a/docs/functions/functions.md +++ b/docs/functions/functions.md @@ -39,7 +39,7 @@ import { AngularFireFunctions } from '@angular/fire/functions'; template: `` }) export class AppComponent { - constructor(private fns: AngularFireFunctions { }) + constructor(private fns: AngularFireFunctions) { } } ``` @@ -57,9 +57,7 @@ import { AngularFireStorage } from '@angular/fire/functions'; @Component({ selector: 'app-root', - template: ` - { data$ } | async - ` + template: `{ data$ | async }` }) export class AppComponent { constructor(private fns: AngularFireFunctions) { @@ -69,4 +67,4 @@ export class AppComponent { } ``` -Notice that calling `httpsCallable()` does not initiate the request. It creates a reusable function that is called to initiate the request. +Notice that calling `httpsCallable()` does not initiate the request. It creates a function, which when called creates an Observable, subscribe or convert it to a Promise to initiate the request.