├── img
├── push2.gif
├── realtime-logo.jpg
├── chrome_webpush.png
├── firefox_webpush.png
└── realtime-large-logo.jpg
├── manifest.json
├── LICENSE
├── WebPushManager.js
├── index.js
├── README.md
├── service-worker.js
├── index.html
└── css
├── normalize.css
└── skeleton.css
/img/push2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realtime-framework/WebPushNotifications/HEAD/img/push2.gif
--------------------------------------------------------------------------------
/img/realtime-logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realtime-framework/WebPushNotifications/HEAD/img/realtime-logo.jpg
--------------------------------------------------------------------------------
/img/chrome_webpush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realtime-framework/WebPushNotifications/HEAD/img/chrome_webpush.png
--------------------------------------------------------------------------------
/img/firefox_webpush.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realtime-framework/WebPushNotifications/HEAD/img/firefox_webpush.png
--------------------------------------------------------------------------------
/img/realtime-large-logo.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/realtime-framework/WebPushNotifications/HEAD/img/realtime-large-logo.jpg
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Realtime Web Push Demo",
3 | "short_name": "Web Push Demo",
4 | "gcm_sender_id": "103953800507"
5 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Realtime Framework
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/WebPushManager.js:
--------------------------------------------------------------------------------
1 |
2 | var WebPushManager = function(){
3 | }
4 |
5 | WebPushManager.prototype.start = function(callback) {
6 | if ('serviceWorker' in navigator) {
7 | navigator.serviceWorker.register('./service-worker.js')
8 | .then(this.getRegistrationId(callback));
9 | } else {
10 | callback('Service workers aren\'t supported in this browser.', null);
11 | }
12 | }
13 |
14 | WebPushManager.prototype.getRegistrationId = function (callback) {
15 | navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
16 |
17 | var fb_messaging = firebase.messaging();
18 | fb_messaging.useServiceWorker(serviceWorkerRegistration);
19 |
20 | fb_messaging.requestPermission()
21 | .then(function() {
22 | console.log('Notification permission granted.');
23 |
24 | fb_messaging.getToken()
25 | .then(function(currentToken) {
26 | if (currentToken) {
27 | callback(null, currentToken);
28 | }
29 | })
30 | .catch(function(err) {
31 | callback(err)
32 | });
33 | })
34 | .catch(function(err) {
35 | console.log('Unable to get permission to notify. ', err);
36 | callback(err);
37 | });
38 | });
39 | }
40 |
41 | WebPushManager.prototype.forceNotification = function(message) {
42 | navigator.serviceWorker.ready.then(function(serviceWorkerRegistration) {
43 | serviceWorkerRegistration.active.postMessage(message);
44 | });
45 | }
46 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // Generate the user private channel
2 | var channel = generateUserChannel();
3 |
4 | $(document).ready(function() {
5 |
6 | // In this example we are using a demo Realtime application key without any security
7 | // so you should replace it with your own appkey and follow the guidelines
8 | // to configure it
9 | var RealtimeAppKey = "K4xqxB";
10 |
11 | // update the UI
12 | $('#curl').text('curl "http://ortc-developers-useast1-s0001.realtime.co/send" --data "AK=' + RealtimeAppKey + '&AT=SomeToken&C=' + channel + '&M=12345678_1-1_This is a web push notification sent using the Realtime REST API"');
13 | $('#channel').text(channel);
14 |
15 | // start Web Push Manager to obtain device id and register it with Realtime
16 | // a service worker will be launched in background to receive the incoming push notifications
17 | var webPushManager = new WebPushManager();
18 |
19 | webPushManager.start(function(error, registrationId){
20 | if (error) {
21 |
22 | if(error.message) {
23 | alert(error.message);
24 | } else {
25 | alert("Ooops! It seems this browser doesn't support Web Push Notifications :(");
26 | }
27 |
28 | $("#curl").html("Oops! Something went wrong. It seems your browser does not support Web Push Notifications.
Error:
" + error.message);
29 | $("#sendButton").text("No can do ... this browser doesn't support web push notifications");
30 | $("#sendButton").css("background-color","red");
31 | };
32 |
33 | // Create Realtime Messaging client
34 | client = RealtimeMessaging.createClient();
35 | client.setClusterUrl('https://ortc-developers.realtime.co/server/ssl/2.1/');
36 |
37 | client.onConnected = function (theClient) {
38 | // client is connected
39 |
40 | // subscribe users to their private channels
41 | theClient.subscribeWithNotifications(channel, true, registrationId,
42 | function (theClient, channel, msg) {
43 | // while you are browsing this page you'll be connected to Realtime
44 | // and receive messages directly in this callback
45 | console.log("Received a message from the Realtime server:", msg);
46 |
47 | // Since the service worker will only show a notification if the user
48 | // is not browsing your website you can force a push notification to be displayed.
49 | // For most use cases it would be better to change the website UI by showing a badge
50 | // or any other form of showing the user something changed instead
51 | // of showing a pop-up notification.
52 | // Also consider thar if the user has severals tabs opened it will see a notification for
53 | // each one ...
54 | webPushManager.forceNotification(msg);
55 | });
56 | };
57 |
58 | // Establish the connection
59 | client.connect(RealtimeAppKey, 'JustAnyRandomToken');
60 | });
61 | });
62 |
63 | // generate a GUID
64 | function S4() {
65 | return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
66 | }
67 |
68 | // generate the user private channel and save it at the local storage
69 | // so we always use the same channel for each user
70 | function generateUserChannel(){
71 | userChannel = localStorage.getItem("channel");
72 | if (userChannel == null || userChannel == "null"){
73 | guid = (S4() + S4() + "-" + S4() + "-4" + S4().substr(0,3) + "-" + S4() + "-" + S4() + S4() + S4()).toLowerCase();
74 | userChannel = 'channel-' + guid;
75 | localStorage.setItem("channel", userChannel);
76 | }
77 | return userChannel;
78 | }
79 |
80 | // send a message to the user private channel to trigger a push notification
81 | function send(){
82 | if (client) {
83 | client.send(channel, "This is a web push notification sent using the Realtime JavaScript SDK");
84 | };
85 | }
86 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Web Push Notifications with Realtime
2 | This project shows how to use the Web Push Notifications API in a website, allowing easy engagement with users that are currently not browsing the website. This project uses the Realtime Messaging JavaScript SDK and requires Chrome 50+ or Firefox 44+.
3 |
4 | ## Realtime + Web Push Notifications guide
5 |
6 | - Register to get your free Realtime Messaging application key at [https://accounts.realtime.co/signup/](https://accounts.realtime.co/signup/)
7 |
8 | - Create a Firebase Cloud Messaging project. [Follow this tutorial](http://messaging-public.realtime.co/documentation/starting-guide/mobilePushGCM.html).
9 |
10 | - Open the `index.html` file and replace the Firebase initialization code shown below with the configuration code you got in the previous step:
11 |
12 |
13 |
14 |
21 |
22 |
23 | - In the `index.js` file replace the Realtime demo application key (K4xqxB) with your own Realtime application key:
24 |
25 | var RealtimeAppKey = "K4xqxB";
26 |
27 | - Edit the `service-worker.js` file enter your Firebase Sender ID in the `messagingSenderId` property:
28 |
29 | firebase.initializeApp({
30 | 'messagingSenderId': '915139563807'
31 | });
32 |
33 | - Map a webserver to folder where you have cloned this repository, open http://localhost/index.html in your Chrome/Firefox browser and try it out. If it doesn't work as expected have a look at the limitations and troubleshooting sections below.
34 |
35 |
36 | ## Limitations
37 | 1. This will only work on Chrome 50+ and Firefox 44+
38 | 2. If you are not using localhost you must use the https protocol (it will work on localhost with http)
39 | 3. At least one Chrome/Firefox tab must be opened in order to receive push notifications
40 |
41 | ## Troubleshooting
42 |
43 | * If you get the following error message it means you have changed the `gcm_sender_id` in your manifest.json file. Please update your manifest and enter the exact value shown in the message:
44 |
45 | Messaging: Please change your web app manifest's 'gcm_sender_id' value to '103953800507' to use Firebase messaging. (messaging/incorrect-gcm-sender-id).
46 |
47 | ### Not receiving push notifications
48 | * Check that you are running the example from a webserver (e.g. http://localhost) and not from the file system (e.g. file:///C:/web/WebPushNotifications-master/index.html);
49 |
50 | * Check that you have entered the right Firebase configurations;
51 |
52 | * Don't forget to give permissions for the push notifications when your browser requests them;
53 |
54 | * Make sure your webserver is properly configured to serve the file manifest.json (check if there are no 404 errors in your browsers Developers Tool network tab). IIS users may need to add the MIME type;
55 |
56 | * If you're not using localhost make sure you are using the https protocol with a valid SSL certificate for the domain you are using;
57 |
58 | * Check if you have any other browser tab opened using the website you're testing. If you do, make sure that page has a Realtime connection established and is subscribing the push notification channel. Push notifications won't be displayed to users that are currently browsing the site that originated the push.
59 |
60 | ## Private channel vs Global channel
61 | If you want to control to which users you are sending each push you should use a private channel for each user. If you want to broadcast a push notification to all users you should use a global channel that every user subscribes.
62 |
63 | A mixed private/global channel strategy can also be used, it really depends on your use case.
64 |
65 | ## On-line example
66 | You can test the Realtime Web Push Notifications [here](https://framework.realtime.co/demo/web-push).
67 |
--------------------------------------------------------------------------------
/service-worker.js:
--------------------------------------------------------------------------------
1 | // Give the service worker access to Firebase Messaging.
2 | // Note that you can only use Firebase Messaging here, other Firebase libraries
3 | // are not available in the service worker.
4 | importScripts('https://www.gstatic.com/firebasejs/3.5.0/firebase-app.js');
5 | importScripts('https://www.gstatic.com/firebasejs/3.5.0/firebase-messaging.js');
6 |
7 | // Initialize the Firebase app in the service worker by passing in the
8 | // messagingSenderId.
9 | firebase.initializeApp({
10 | 'messagingSenderId': '915139563807'
11 | });
12 |
13 | // Retrieve an instance of Firebase Messaging so that it can handle background
14 | // messages.
15 | const fb_messaging = firebase.messaging();
16 |
17 | // Buffer to save multipart messages
18 | var messagesBuffer = {};
19 |
20 | // Gets the number of keys in a dictionary
21 | var countKeys = function (dic) {
22 | var count = 0;
23 | for (var i in dic) {
24 | count++;
25 | }
26 | return count;
27 | };
28 |
29 | // Parses the Realtime messages using multipart format
30 | var parseRealtimeMessage = function (message) {
31 | // Multi part
32 | var regexPattern = /^(\w[^_]*)_{1}(\d*)-{1}(\d*)_{1}([\s\S.]*)$/;
33 | var match = regexPattern.exec(message);
34 |
35 | var messageId = null;
36 | var messageCurrentPart = 1;
37 | var messageTotalPart = 1;
38 | var lastPart = false;
39 |
40 | if (match && match.length > 0) {
41 | if (match[1]) {
42 | messageId = match[1];
43 | }
44 | if (match[2]) {
45 | messageCurrentPart = match[2];
46 | }
47 | if (match[3]) {
48 | messageTotalPart = match[3];
49 | }
50 | if (match[4]) {
51 | message = match[4];
52 | }
53 | }
54 |
55 | if (messageId) {
56 | if (!messagesBuffer[messageId]) {
57 | messagesBuffer[messageId] = {};
58 | }
59 | messagesBuffer[messageId][messageCurrentPart] = message;
60 | if (countKeys(messagesBuffer[messageId]) == messageTotalPart) {
61 | lastPart = true;
62 | }
63 | }
64 | else {
65 | lastPart = true;
66 | }
67 |
68 | if (lastPart) {
69 | if (messageId) {
70 | message = "";
71 |
72 | // Aggregate all parts
73 | for (var i = 1; i <= messageTotalPart; i++) {
74 | message += messagesBuffer[messageId][i];
75 | delete messagesBuffer[messageId][i];
76 | }
77 |
78 | delete messagesBuffer[messageId];
79 | }
80 |
81 | return message;
82 | } else {
83 | // We don't have yet all parts, we need to wait ...
84 | return null;
85 | }
86 | }
87 |
88 | // Shows a notification
89 | function showNotification(message) {
90 | // In this example we are assuming the message is a simple string
91 | // containing the notification text. The target link of the notification
92 | // click is fixed, but in your use case you could send a JSON message with
93 | // a link property and use it in the click_url of the notification
94 |
95 | // The notification title
96 | const notificationTitle = 'Web Push Notification';
97 |
98 | // The notification properties
99 | const notificationOptions = {
100 | body: message,
101 | icon: 'img/realtime-logo.jpg',
102 | data: {
103 | click_url: '/index.html'
104 | },
105 | tag: Date.now()
106 | };
107 |
108 | return self.registration.showNotification(notificationTitle,
109 | notificationOptions);
110 | }
111 |
112 | // If you would like to customize notifications that are received in the
113 | // background (Web app is closed or not in browser focus) then you should
114 | // implement this optional method.
115 | fb_messaging.setBackgroundMessageHandler(function(payload) {
116 | console.log('Received background message ', payload);
117 |
118 | // Customize notification here
119 | if(payload.data && payload.data.M) {
120 | var message = parseRealtimeMessage(payload.data.M);
121 | return showNotification(message);
122 | }
123 | });
124 |
125 | // Forces a notification
126 | self.addEventListener('message', function (evt) {
127 | evt.waitUntil(showNotification(evt.data));
128 | });
129 |
130 | // The user has clicked on the notification ...
131 | self.addEventListener('notificationclick', function(event) {
132 | // Android doesn’t close the notification when you click on it
133 | // See: http://crbug.com/463146
134 | event.notification.close();
135 |
136 | if(event.notification.data && event.notification.data.click_url) {
137 | // gets the notitication click url
138 | var click_url = event.notification.data.click_url;
139 |
140 | // This looks to see if the current is already open and
141 | // focuses if it is
142 | event.waitUntil(clients.matchAll({
143 | type: "window"
144 | }).then(function(clientList) {
145 | for (var i = 0; i < clientList.length; i++) {
146 | var client = clientList[i];
147 | if (client.url == click_url && 'focus' in client)
148 | return client.focus();
149 | }
150 | if (clients.openWindow) {
151 | var url = click_url;
152 | return clients.openWindow(url);
153 | }
154 |
155 | }));
156 | }
157 | });
158 |
159 |
160 |
161 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
68 | Starting with Chrome 50 and Firefox 44 you are able to send push notifications to your website users, even when they are not browsing your website. 69 |
70 |71 | We have integrated this new feature into the Realtime Cloud Messaging Pub/Sub service to make your life easier. 72 |
73 | 74 |77 | You probably noticed that your browser asked if you allowed notifications from this website. 78 | If you accepted you are already subscribed to your own private channel to receive notifications. 79 |
80 | 81 |
82 | Your channel is: ...
83 |
86 | This was performed through the new subscribeWithNotifications method in the Realtime JavaScript SDK.
87 |
89 | Demo time! Clicking on the following button will send a new push notification to your private channel (only you'll see the push). 90 |
91 | 92 |93 | 94 |
95 | 96 |97 | You should see a Chrome or Firefox Push Notification pop-up like this near your system tray (the first push may take a little more time to be delivered due to the underlying Firebase Cloud Messaging backend configuration). 98 |
99 | 100 | 101 |
102 |
103 |
106 | or 107 |
108 | 109 |
110 |
111 |
114 | This example uses a private channel for each user so you can send pushes at will, without annoying other users. A typical use case in a website would be using a global channel and subscribing each user to that channel. This way you could broadcast a single push to all users simultaneously. It´s powerful but should be used wisely. 115 |
116 | 117 |
120 | 131 | No problem, if an instance of Chrome or Firefox is running users will see your notification pop-up inviting them back to your website. If Chrome or Firefox is not running the pop-up will be shown as soon as the browser is launched. 132 |
133 | 134 |135 | Let's try it. Copy the following curl command and navigate away from this website. Paste the command in your terminal window and send yourself a push notification. When you receive it click on the pop-up and you'll be redirected to the Realtime homepage. 136 |
137 | 138 |139 |
144 | The command above is simply sending a Realtime message to your private channel (the C parameter) using the Realtime REST API. The Realtime server will take care of broadcasting it to Firebase Cloud Messaging for delivery. 145 |
146 | 147 |149 | But the good news won't stop here. Taking advantage of the Realtime Cloud Messaging integration with Firebase Cloud Messaging for Android devices and Apple Notification Services for iOS/OSX devices, now you'll be able to send one single push to your entire user base in almost all platforms. Until now you could only do it with native mobile apps, now you can do it also for your website users. 150 |
151 | 152 | 153 |155 | You'll find this example source code and starting guide at https://github.com/realtime-framework/WebPushNotifications 156 |
157 | 158 |159 | Feel free to clone it and run it in your localhost or website. 160 |
161 | 162 |163 | Follow the guidelines and in less than one hour you'll be engaging with your website users. Go for it ... 164 |
165 | 166 |