diff --git a/.gitignore b/.gitignore
index 0839c11..1ea85be 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,5 @@
node_modules/
.vscode
-src/browser-notif.js
+src/*.js
src/Test.ts
example/
\ No newline at end of file
diff --git a/README.md b/README.md
index 41e6705..5bede03 100755
--- a/README.md
+++ b/README.md
@@ -24,32 +24,26 @@ npm install browser-notif --save
import BrowserNotif from './BrowserNotif'
// If you want to explicitly call request permission. Usually this is only called once.
-BrowserNotif.requestPermission(p => console.log(p))
+BrowserNotif.requestPermission().then(p => console.log(p))
// Create instance
-let notif1 = new BrowserNotif()
-
-notif1
- .notify('First Notif', 'Hi there! Nice to meet you.', (n) => {
- console.log('First Notif fired!', n)
- })
- .click(() => {
- window.open('https://www.ipanardian.com')
- })
-
-// With options
-let notif2 = new BrowserNotif({
- icon: 'icon.png',
+let notif1 = new BrowserNotif({
+ icon: 'logo.png',
lang: 'en-US',
- timeout: 10 // How long notif will appear in seconds
-})
+ timeout: 10 // How long notif will be appear in seconds
+})
-notif2
- .notify('Second Notif', 'Typescript has released new version, chek it out!', (n) => {
- console.log('Second Notif fired!', n)
+notif1
+ .notify('First Notif', 'Hi there! Nice to meet you.', {
+ click() {
+ window.open('//ipanardian.com')
+ },
+ error() {
+ //On error
+ }
})
- .click(() => {
- window.open('https://www.typescriptlang.org')
+ .then(() => {
+ //Do something
})
//close notif pragmatically
@@ -59,34 +53,36 @@ notif1.close()
### Javascript
In Javascript BrowserNotif use UMD module pattern and Polyfill for ```Object.assign```.
```js
-BrowserNotif.default.requestPermission(function(p) {
- console.log(p)
-})
+BrowserNotif.default.requestPermission().then(p => console.log(p))
var notif = new BrowserNotif.default({icon: 'icon.png'})
notif
- .notify('First Notif', 'Hi there! Nice to meet you.', function(n) {
- console.log('First Notif fired!', n)
+ .notify('First Notif', 'Hi there! Nice to meet you.', {
+ click: function() {
+ window.open('//ipanardian.com')
+ }
})
- .click(function() {
- window.open('https://www.ipanardian.com')
+ .then(function() {
+ //Do something
})
```
## Notification On Mobile Devices
Notification on mobile devices is using ```Service Worker```. A service worker is an event-driven worker registered against an origin and a path. Service worker runs in the background and only run over HTTPS.
-> Put file 'sw.js' on root directory
+> Put file 'sw.min.js' on root directory of application
```js
-var notif = new BrowserNotif.default({icon: 'icon.png'})
+var notif = new BrowserNotif({icon: 'icon.png', serviceWorkerPath: 'sw.min.js'})
notif
- .clickOnServiceWorker(function(){
- clients.openWindow('//ipanardian.com')
- })
- .notify('Notif from Ipan Ardian', 'Hi there! Nice to meet you.', function(n) {
- console.log(n)
+ .notify('First Notif', 'Hi there! Nice to meet you.', {
+ .clickOnServiceWorker(clients => {
+ clients.openWindow('//ipanardian.com')
+ })
+ })
+ .then(() => {
+ //Do something
})
```
![gif](http://i.giphy.com/l3vRfm7aebpZjQHf2.gif)
@@ -107,7 +103,7 @@ Check 'dist' folder.
- BrowserNotif.js
- BrowserNotif.min.js
- BrowserNotif.min.js.map
-- sw.js
+- sw.min.js
## Browser compatibility
If browser not support Notification API then native alert will be triggered.
diff --git a/src/BrowserNotif.ts b/src/BrowserNotif.ts
index ba5f2a8..160dbbc 100644
--- a/src/BrowserNotif.ts
+++ b/src/BrowserNotif.ts
@@ -11,60 +11,7 @@
///
///
-/**
- * Interface for BrowserNotif configuration
- *
- * dir?: NotificationDirection;
- * lang?: string;
- * body?: string;
- * tag?: string;
- * image?: string;
- * icon?: string;
- * badge?: string;
- * sound?: string;
- * vibrate?: number | number[],
- * timestamp?: number,
- * renotify?: boolean;
- * silent?: boolean;
- * requireInteraction?: boolean;
- * data?: any;
- * actions?: NotificationAction[]
- * timeout?: number;
- * serviceWorkerPath?: string; Default : sw.js
- */
-interface BrowserNotifOptions extends NotificationOptions {
- [key: string]: any
- timeout?: number
- serviceWorkerPath?: string
-}
-
-/**
- * Interface for BrowserNotif
- */
-interface BrowserNotifInterface {
- notify(title: string, body: string, callback: (notif: Notification) => void): BrowserNotif
- click(callback: () => void): BrowserNotif
- close(): void
- error(callback: () => void): BrowserNotif
- isMobile(): boolean
-}
-
-/**
- * Interface for Permission
- */
-interface PermissionInterface {
- Default: string
- Granted: string,
- Denied: string
-}
-
-/**
- * Interface for Data
- */
-interface Data {
- [key: string]: any
- clickOnServiceWorker?: string
-}
+import {BrowserNotifOptions, BrowserNotifInterface, BrowserNotifEvent, BrowserNotifData, PermissionInterface} from './Interface'
export default class BrowserNotif implements BrowserNotifInterface
{
@@ -94,9 +41,9 @@ export default class BrowserNotif implements BrowserNotifInterface
/**
* Arbitrary data
- * @type {Data}
+ * @type {BrowserNotifData}
*/
- protected data: Data = {}
+ protected data: BrowserNotifData = {}
/**
* How long notification will appear in second. Set to 0 to make always visible
@@ -166,14 +113,15 @@ export default class BrowserNotif implements BrowserNotifInterface
/**
* Register serviceWorker and Get request permission
- * @param {string} callback
+ * @param Promise
*/
- public static requestPermission(callback: (permission: NotificationPermission) => void): void {
- Notification.requestPermission((permission: NotificationPermission) => {
- if (typeof callback === 'function') {
- callback.call(this, permission)
- }
- });
+ public static requestPermission(): Promise {
+ return new Promise((resolve, reject) => {
+ Notification.requestPermission().then((permission: NotificationPermission) => {
+ resolve(permission)
+ })
+ .catch(err => reject(err))
+ })
}
/**
@@ -182,7 +130,7 @@ export default class BrowserNotif implements BrowserNotifInterface
*/
protected _registerServiceWorker(): void {
if ('serviceWorker' in navigator) {
- navigator.serviceWorker.register(this.options.serviceWorkerPath || 'sw.js').then(serviceWorkerRegistration => {
+ navigator.serviceWorker.register(this.options.serviceWorkerPath || 'sw.min.js').then(serviceWorkerRegistration => {
console.log('Service Worker is ready :', serviceWorkerRegistration)
})
.catch(e => console.warn('BrowserNotif: ', e))
@@ -192,73 +140,87 @@ export default class BrowserNotif implements BrowserNotifInterface
/**
* Show notification from serviceWorker
* This is an experimental technology!
- * @param {()} callback
+ * @return Promise
*/
- protected _showNotifServiceWorker(callback?: () => void): void {
- if ('serviceWorker' in navigator) {
- navigator.serviceWorker.ready.then(registration => {
- if (!this.notifOptions.tag) {
- this.notifOptions.tag = 'browserNotif_'+ Math.random().toString().substr(3, 10)
- }
- if (Object.keys(this.data).length > 0) {
- this.notifOptions.data = JSON.stringify(this.data)
- }
- registration.showNotification(this.title, this.notifOptions).then(() => {
- callback.call(this)
+ protected _showNotifServiceWorker(): Promise {
+ return new Promise((resolve, reject) => {
+ if ('serviceWorker' in navigator) {
+ navigator.serviceWorker.ready.then(registration => {
+ if (!this.notifOptions.tag) {
+ this.notifOptions.tag = 'browserNotif_'+ Math.random().toString().substr(3, 10)
+ }
+ if (Object.keys(this.data).length > 0) {
+ this.notifOptions.data = JSON.stringify(this.data)
+ }
+ registration.showNotification(this.title, this.notifOptions).then((notificationEvent) => {
+ resolve(notificationEvent)
+ })
})
- })
- .catch(e => console.error('BrowserNotif: ', e))
- }
+ .catch(e => {
+ throw new Error('BrowserNotif: '+ e)
+ })
+ }
+ else {
+ throw new Error('BrowserNotif: serviceWorker not available')
+ }
+ })
}
/**
* Get notification object from serviceWorker
- * @param {Notification} callback
+ * @return Promise
*/
- protected _getNotifServiceWorker(callback: (notification: Notification) => void): void {
- if ('serviceWorker' in navigator) {
- navigator.serviceWorker.ready.then(registration => {
- registration.getNotifications({tag: this.notifOptions.tag}).then(notifications => {
- if (notifications.length > 0) {
- callback.call(this, notifications[0])
- }
- })
- })
- .catch(e => console.error('BrowserNotif: ', e))
- }
+ protected _getNotifServiceWorker(): Promise {
+ return new Promise((resolve, reject) => {
+ if ('serviceWorker' in navigator) {
+ navigator.serviceWorker.ready.then(registration => {
+ registration.getNotifications({tag: this.notifOptions.tag}).then(notifications => {
+ if (notifications.length > 0) {
+ resolve(notifications[0])
+ }
+ else {
+ reject('BrowserNotif: Notification not found')
+ }
+ })
+ })
+ .catch(e => {
+ reject('BrowserNotif: '+ e)
+ })
+ }
+ })
}
/**
- * Trigger notify
+ * Create notify
* @param {string} title
* @param {string} body
- * @param {Event} callback
- * @return {BrowserNotif}
+ * @param {BrowserNotifEvent} notifEvent
+ * @return Promise
*/
- public notify(title: string, body: string, callback?: (notif: Notification) => void): BrowserNotif {
- if (!BrowserNotif.isSupported()) {
- alert(`${title}\n\n${body}`)
- return this
- }
- this._validateTitle(title)
-
- this.title = title;
- this.notifOptions.body = body;
- if (this.Permission.Granted === Notification.permission) {
- this._notify(callback);
- }
- else if (this.Permission.Denied !== Notification.permission) {
- BrowserNotif.requestPermission(permission => {
- if (this.Permission.Granted === permission) {
- this._notify(callback);
- }
- });
- }
- else {
- console.warn('User denied the notification permission')
- }
-
- return this
+ public notify(title: string, body: string, notifEvent?: BrowserNotifEvent): Promise {
+ return new Promise((resolve, reject) => {
+ if (!BrowserNotif.isSupported()) {
+ alert(`${title}\n\n${body}`)
+ resolve()
+ }
+ this._validateTitle(title)
+
+ this.title = title;
+ this.notifOptions.body = body;
+ if (this.Permission.Granted === Notification.permission) {
+ this._notify(notifEvent).then(() => resolve())
+ }
+ else if (this.Permission.Denied !== Notification.permission) {
+ BrowserNotif.requestPermission().then(permission => {
+ if (this.Permission.Granted === permission) {
+ this._notify(notifEvent).then(() => resolve())
+ }
+ });
+ }
+ else {
+ reject('User denied the notification permission')
+ }
+ })
}
/**
@@ -276,31 +238,43 @@ export default class BrowserNotif implements BrowserNotifInterface
/**
* Create an instance of Notification API
- * @param {Notification} callback
+ * @param {BrowserNotifEvent} notifEvent
+ * @return {Promise}
*/
- protected _notify(callback?: (notif: Notification) => void): void {
- if (this.isMobile()) {
- this._registerServiceWorker()
- this._showNotifServiceWorker(() => {
- this._getNotifServiceWorker(notification => {
- this.notification = notification
- if (typeof callback === 'function') {
- callback.call(this, this.notification)
- }
+ protected _notify(notifEvent?: BrowserNotifEvent): Promise {
+ return new Promise((resolve, reject) => {
+ if (this.isMobile()) {
+ Promise.resolve().then(() => {
+ this._registerServiceWorker()
+ this._prepareClickOnServiceWorker.apply(this, [notifEvent])
+ return this._showNotifServiceWorker()
+
+ })
+ .then((notificationEvent) => {
+ this._getNotifServiceWorker().then(notification => {
+ this.notification = notification
+ resolve()
+ })
+ })
+ .catch(err => {
+ reject(err)
})
- })
- }
- else {
- if (this.notification instanceof Notification) {
- this.notification.close()
}
- this.notification = new Notification(this.title, this.notifOptions)
- this._closeNotification()
- if (typeof callback === 'function') {
- callback.call(this, this.notification)
+ else {
+ Promise.resolve().then(() => {
+ if (this.notification instanceof Notification) {
+ this.notification.close()
+ }
+ this.notification = new Notification(this.title, this.notifOptions)
+ this._prepareNotifEvent.apply(this, [notifEvent])
+ this._closeNotification()
+ resolve()
+ })
+ .catch(err => {
+ reject(err)
+ })
}
- }
-
+ })
}
/**
@@ -311,32 +285,35 @@ export default class BrowserNotif implements BrowserNotifInterface
setTimeout(this.notification.close.bind(this.notification), this.timeout * 1e3);
}
}
-
+
/**
- * Click event on Notification
- * @param {} callback
- * @return {BrowserNotif}
+ * Prepare for Notification Event
+ * @param {BrowserNotifEvent} notifEvent
*/
- public click(callback: () => void): BrowserNotif {
- if (typeof callback === 'function' && this.notification instanceof Notification) {
- this.notification.onclick = () => {
- this.notification.close()
- callback.call(this);
+ protected _prepareNotifEvent(notifEvent?: BrowserNotifEvent): void {
+ if (typeof notifEvent != 'undefined' && this.notification instanceof Notification) {
+ if (typeof notifEvent.click == 'function') {
+ this.notification.onclick = () => {
+ this.notification.close()
+ notifEvent.click.call(this)
+ }
+ }
+ if (typeof notifEvent.error == 'function') {
+ this.notification.onerror = () => {
+ notifEvent.error.call(this)
+ }
}
}
- return this
}
/**
- * Click event on serviceWorker Notification
- * @param {} callback
- * @return {BrowserNotif}
+ * Prepare for click event on serviceWorker Notification
+ * @param {BrowserNotifEvent} notifEvent
*/
- public clickOnServiceWorker(callback: () => void): BrowserNotif {
- if (typeof callback === 'function') {
- this.data.clickOnServiceWorker = callback.toString()
+ protected _prepareClickOnServiceWorker(notifEvent?: BrowserNotifEvent): void {
+ if (typeof notifEvent != 'undefined' && typeof notifEvent.clickOnServiceWorker == 'function') {
+ this.data.clickOnServiceWorker = notifEvent.clickOnServiceWorker.toString()
}
- return this
}
/**
@@ -348,20 +325,6 @@ export default class BrowserNotif implements BrowserNotifInterface
}
}
- /**
- * Error of Notification
- * @param {} callback
- * @return {BrowserNotif}
- */
- public error(callback: () => void): BrowserNotif {
- if (typeof callback === 'function' && this.notification instanceof Notification) {
- this.notification.onerror = () => {
- callback.call(this);
- }
- }
- return this
- }
-
/**
* Detect mobile device
* @return {boolean}
diff --git a/src/Interface.ts b/src/Interface.ts
new file mode 100644
index 0000000..2c84516
--- /dev/null
+++ b/src/Interface.ts
@@ -0,0 +1,81 @@
+/**
+ * Interface for BrowserNotif configuration
+ *
+ * dir?: NotificationDirection;
+ * lang?: string;
+ * body?: string;
+ * tag?: string;
+ * image?: string;
+ * icon?: string;
+ * badge?: string;
+ * sound?: string;
+ * vibrate?: number | number[],
+ * timestamp?: number,
+ * renotify?: boolean;
+ * silent?: boolean;
+ * requireInteraction?: boolean;
+ * data?: any;
+ * actions?: NotificationAction[]
+ * timeout?: number;
+ * serviceWorkerPath?: string; Default : sw.js
+ */
+export interface BrowserNotifOptions extends NotificationOptions {
+ [key: string]: any
+ timeout?: number
+ serviceWorkerPath?: string
+}
+
+/**
+ * Interface for BrowserNotif
+ */
+export interface BrowserNotifInterface {
+ notify(title: string, body: string, notifEvent?: BrowserNotifEvent): Promise
+ close(): void
+ isMobile(): boolean
+}
+
+/**
+ * Interface for BrowserNotifEvent
+ */
+export interface BrowserNotifEvent {
+ click?(): void
+ clickOnServiceWorker?(clients: Clients): void
+ error?(): void
+}
+
+/**
+ * Interface for Permission
+ */
+export interface PermissionInterface {
+ Default: string
+ Granted: string
+ Denied: string
+}
+
+/**
+ * Interface for NotifData
+ */
+export interface BrowserNotifData {
+ [key: string]: any
+ clickOnServiceWorker?: string
+}
+
+/**
+ * The Clients interface of the Service Workers API
+ */
+export interface Clients {
+ get(id: string): Promise
+ matchAll(options: Object): Promise
+ openWindow(url: string): void
+ claim(): Promise
+}
+
+/**
+ * The Client interface of the Service Workers API
+ */
+export interface Client {
+ readonly frameType: string
+ readonly id: string
+ readonly url: string
+ postMessage(message: string, transfer?: Object): void
+}
\ No newline at end of file
diff --git a/src/sw.ts b/src/sw.ts
index 9e32bf9..a79c00b 100644
--- a/src/sw.ts
+++ b/src/sw.ts
@@ -27,7 +27,7 @@ self.onnotificationclick = function(event) {
}
try {
if (data !== null && data.clickOnServiceWorker !== undefined) {
- Function("(" +data['clickOnServiceWorker']+ ")()")()
+ Function("(" +data['clickOnServiceWorker']+ ")(clients)")()
}
} catch (error) {
throw new Error('BrowserNotif: Error clickOnServiceWorker '+ error)