This repository has been archived by the owner on Oct 31, 2021. It is now read-only.
/
Notify.ts
126 lines (100 loc) 路 3.32 KB
/
Notify.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
import {Toast} from '@overlays/Toast';
import {uid} from '@utils/uid';
import {settings} from './Settings';
export type NotificationPayload = {
title: string;
body?: string;
image?: string;
};
export type ResolveNotification = 'click' | 'close' | string | null;
// Currently active notifications
const pendingRequests = new Map<string, (s: ResolveNotification) => void>();
// Wait until service worker is initialized
void navigator.serviceWorker.ready.then(() => {
// Listen to resolved notifications
navigator.serviceWorker.addEventListener('message', ev => {
const {type, tag, action} = ev.data;
if (type === 'notify-reply') {
const resolver = pendingRequests.get(tag);
if (resolver) {
resolver(action);
}
}
});
});
// Internal notification-request function
// TODO: What about actions on chromium-based browsers?
const requestNotification = (options: NotificationPayload, interaction = false): string | null => {
// Check if notifications are enabled
if (settings.notifications.turnedOn !== true) {
return null;
}
// Check if document has to be visible
if (settings.notifications.hideIfAppIsVisible &&
document.visibilityState === 'visible') {
return null;
}
// Check if notifications are actually allowed
if (Notification.permission !== 'granted') {
settings.notifications.turnedOn = false;
Toast.instance.show({
text: 'Notifications are disabled by your browser.',
body: 'Go to Settings > Notifications to turn them on'
});
return null;
}
const {controller} = navigator.serviceWorker;
if (!controller) {
return null;
}
const tag = uid('notify');
// Safari "polyfill"...
if (window.safari) {
const {title, body} = options;
const notifications = new Notification(title, {
badge: '/favicon.ico',
icon: '/favicon.ico',
requireInteraction: interaction,
renotify: true,
body
});
const resolve = (type: ResolveNotification): void => {
const item = pendingRequests.get(tag);
if (item) {
item(type);
}
};
notifications.onerror = () => resolve('close');
notifications.onclose = () => resolve('close');
notifications.onclick = () => resolve('click');
} else {
controller.postMessage({
type: 'notify',
data: {interaction, ...options},
tag
});
}
return tag;
};
/**
* Shows a notification and expects a response from the user
* @param options
*/
export const showNotification = async (options: NotificationPayload): Promise<ResolveNotification> => {
return new Promise<ResolveNotification>(resolve => {
const tag = requestNotification(options, true);
if (tag) {
pendingRequests.set(tag, resolve);
} else {
resolve(null);
}
});
};
/**
* Shows a notification without reacting to any user-interaction, returns boolean
* whether the notification got displayed.
* @param options
*/
export const pushNotification = (options: NotificationPayload): boolean => {
return requestNotification(options, false) !== null;
};