Skip to content

Commit 86ce1d2

Browse files
committed
Angular PWA Course
1 parent 6e6698f commit 86ce1d2

10 files changed

+174
-8
lines changed

gen-vapid-keys.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
2+
3+
console.log("Generating VAPID keys ...");
4+
5+
6+
const webpush = require('web-push');
7+
8+
9+
// VAPID keys should only be generated only once.
10+
const vapidKeys = webpush.generateVAPIDKeys();
11+
12+
13+
console.log("VAPID Public Key: ", vapidKeys.publicKey);
14+
15+
16+
console.log("VAPID Private Key: ", vapidKeys.privateKey);
17+
18+
19+
20+
/*
21+
22+
VAPID key pair: Voluntary Application Server Identification
23+
24+
publicKey: "BIvC8I6yoFc-DZNgFTANsy9cae80mjWzTym7aB5zY45vBVZQK9VureFHvoh5ijW8EKG2I-g1YaN5rcKe_5AYrvM",
25+
26+
privateKey: "Xox-dZU4rnLLkRlE5EzblVUrI3LbYj3a719F0a87UWI"
27+
28+
*/
29+
30+
31+
32+

package.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
"server": "./node_modules/.bin/ts-node ./server/server.ts",
99
"build": "ng build",
1010
"start:prod": "ng build --prod && cd dist && http-server -c-1 -P http://localhost:9000 .",
11+
"gen-vapid-keys": "node ./gen-vapid-keys.js",
1112
"test": "ng test",
1213
"lint": "ng lint",
1314
"e2e": "ng e2e"
@@ -22,20 +23,21 @@
2223
"@angular/http": "^5.1.2",
2324
"@angular/platform-browser": "^5.1.2",
2425
"@angular/platform-browser-dynamic": "^5.1.2",
26+
"@angular/platform-server": "^5.1.2",
2527
"@angular/router": "^5.1.2",
2628
"@angular/service-worker": "^5.1.2",
27-
"core-js": "^2.4.1",
28-
"rxjs": "^5.5.2",
29-
"zone.js": "^0.8.14",
3029
"@types/express": "^4.0.36",
3130
"@types/lodash": "^4.14.70",
3231
"body-parser": "^1.17.2",
3332
"command-line-args": "^4.0.6",
3433
"cookie-parser": "^1.4.3",
34+
"core-js": "^2.4.1",
3535
"http-server": "^0.10.0",
3636
"moment": "^2.18.1",
3737
"nodemon": "^1.11.0",
38-
"@angular/platform-server": "^5.1.2"
38+
"rxjs": "^5.5.2",
39+
"web-push": "^3.2.5",
40+
"zone.js": "^0.8.14"
3941
},
4042
"devDependencies": {
4143
"@angular/cli": "^1.6.0",

server/add-push-subscriber.route.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
2+
3+
4+
import {USER_SUBSCRIPTIONS} from "./in-memory-db";
5+
6+
export function addPushSubscriber(req, res) {
7+
8+
const sub = req.body;
9+
10+
console.log('Received Subscription on the server: ', sub);
11+
12+
USER_SUBSCRIPTIONS.push(sub);
13+
14+
res.status(200).json({message: "Subscription added successfully."});
15+
}
16+

server/in-memory-db.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
3+
4+
export let USER_SUBSCRIPTIONS = [];
5+

server/send-newsletter.route.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
2+
import {USER_SUBSCRIPTIONS} from "./in-memory-db";
3+
4+
const webpush = require('web-push');
5+
6+
7+
export function sendNewsletter(req, res) {
8+
9+
console.log('All Newsletter subscriptions', JSON.stringify(USER_SUBSCRIPTIONS));
10+
11+
Promise.all(USER_SUBSCRIPTIONS.map(sub => webpush.sendNotification(sub, 'Newsletter Available ...')))
12+
.then(() => res.sendStatus(200))
13+
.catch(err => {
14+
console.error("Error sending notification, reason: ", err);
15+
res.sendStatus(500);
16+
});
17+
}
18+

server/server.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,22 @@
22
import * as express from 'express';
33
import {Application} from "express";
44
import {readAllLessons} from "./read-all-lessons.route";
5+
import {addPushSubscriber} from "./add-push-subscriber.route";
6+
import {sendNewsletter} from "./send-newsletter.route";
57
const bodyParser = require('body-parser');
68

9+
const webpush = require('web-push');
10+
11+
const vapidKeys = {
12+
publicKey: "BIvC8I6yoFc-DZNgFTANsy9cae80mjWzTym7aB5zY45vBVZQK9VureFHvoh5ijW8EKG2I-g1YaN5rcKe_5AYrvM",
13+
privateKey: "Xox-dZU4rnLLkRlE5EzblVUrI3LbYj3a719F0a87UWI"
14+
};
15+
16+
webpush.setVapidDetails(
17+
'mailto:example@yourdomain.org',
18+
vapidKeys.publicKey,
19+
vapidKeys.privateKey
20+
);
721

822

923
const app: Application = express();
@@ -16,6 +30,13 @@ app.use(bodyParser.json());
1630
app.route('/api/lessons')
1731
.get(readAllLessons);
1832

33+
app.route('/api/notifications')
34+
.post(addPushSubscriber);
35+
36+
app.route('/api/newsletter')
37+
.post(sendNewsletter);
38+
39+
1940

2041
// launch an HTTP Server
2142
const httpServer = app.listen(9000, () => {

src/app/app.module.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import 'rxjs/add/operator/first';
2121
import 'rxjs/add/observable/of';
2222
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
2323
import {AppRoutingModule} from "./app-routing.module";
24+
import {NewsletterService} from "./services/newsletter.service";
2425

2526

2627

@@ -36,11 +37,12 @@ import {AppRoutingModule} from "./app-routing.module";
3637
BrowserAnimationsModule,
3738
AppRoutingModule,
3839
ReactiveFormsModule,
39-
ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production })
40+
ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production }),
4041

4142
],
4243
providers: [
43-
LessonsService
44+
LessonsService,
45+
NewsletterService
4446
],
4547
bootstrap: [AppComponent]
4648
})

src/app/lessons/lessons.component.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@
22
<div class="lessons-list-container v-h-center-block-parent" >
33

44
<div class="buttons">
5-
<button class="button button-danger" (click)="loadLessons()">Reload Lessons</button>
5+
6+
<button class="button button-primary" (click)="subscribeToNotifications()">Subscribe</button>
7+
8+
<button class="button button-danger" (click)="sendNewsletter()">Send</button>
9+
610
</div>
711

812
<h2>All Lessons - V8</h2>

src/app/lessons/lessons.component.ts

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import {Component, OnInit} from '@angular/core';
22
import {LessonsService} from "../services/lessons.service";
33
import {Observable} from "rxjs/Observable";
44
import {Lesson} from "../model/lesson";
5+
import {SwPush} from "@angular/service-worker";
6+
import {NewsletterService} from "../services/newsletter.service";
57

68
@Component({
79
selector: 'lessons',
@@ -13,7 +15,12 @@ export class LessonsComponent implements OnInit {
1315
lessons$: Observable<Lesson[]>;
1416
isLoggedIn$: Observable<boolean>;
1517

16-
constructor(private lessonsService: LessonsService) {
18+
readonly VAPID_PUBLIC_KEY = "BIvC8I6yoFc-DZNgFTANsy9cae80mjWzTym7aB5zY45vBVZQK9VureFHvoh5ijW8EKG2I-g1YaN5rcKe_5AYrvM";
19+
20+
constructor(
21+
private lessonsService: LessonsService,
22+
private swPush: SwPush,
23+
private newsletterService: NewsletterService) {
1724

1825
}
1926

@@ -26,4 +33,38 @@ export class LessonsComponent implements OnInit {
2633
this.lessons$ = this.lessonsService.loadAllLessons().catch(err => Observable.of([]));
2734
}
2835

36+
subscribeToNotifications() {
37+
38+
this.swPush.requestSubscription({
39+
serverPublicKey: this.VAPID_PUBLIC_KEY
40+
})
41+
.then(sub => {
42+
43+
console.log("Notification Subscription: ", sub);
44+
45+
// Passing subscription object to our backend
46+
this.newsletterService.addPushSubscriber(sub)
47+
.subscribe(
48+
() => console.log('Sent push subscription object to server: '),
49+
err => console.log('Could not send subscription object to server, reason: ', err)
50+
);
51+
})
52+
.catch(err => console.error("Could not subscribe to notifications", err));
53+
54+
}
55+
56+
57+
sendNewsletter() {
58+
59+
console.log("Sending Newsletter to all Subscribers ...");
60+
61+
this.newsletterService.send().subscribe();
62+
63+
64+
}
65+
66+
67+
68+
69+
2970
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
2+
3+
import {Injectable} from "@angular/core";
4+
import {HttpClient} from "@angular/common/http";
5+
6+
7+
8+
@Injectable()
9+
export class NewsletterService {
10+
11+
constructor(private http: HttpClient) {
12+
13+
}
14+
15+
addPushSubscriber(sub:any) {
16+
return this.http.post('/api/notifications', sub);
17+
}
18+
19+
send() {
20+
return this.http.post('/api/newsletter', null);
21+
}
22+
23+
}
24+
25+

0 commit comments

Comments
 (0)