Push notifications: Fixes for not joined chats (#1099)

This commit is contained in:
Alexander Zinchuk 2021-05-14 01:35:28 +03:00
parent f964517a25
commit c94163803e
2 changed files with 57 additions and 17 deletions

View File

@ -74,11 +74,17 @@ function getNotificationData(data: PushData): NotificationData {
}
function showNotification({
chatId, messageId, body, title,
chatId,
messageId,
body,
title,
}: NotificationData) {
return self.registration.showNotification(title, {
body,
data: { chatId, messageId },
data: {
chatId,
messageId,
},
icon: 'icon-192x192.png',
badge: 'icon-192x192.png',
vibrate: [200, 100, 200],
@ -110,8 +116,11 @@ export function handlePush(e: PushEvent) {
e.waitUntil(showNotification(notification));
}
function focusChatMessage(client: WindowClient, data: { chatId?: number; messageId?: number }) {
const { chatId, messageId } = data;
async function focusChatMessage(client: WindowClient, data: { chatId?: number; messageId?: number }) {
const {
chatId,
messageId,
} = data;
if (!chatId) return;
client.postMessage({
type: 'focusMessage',
@ -120,6 +129,15 @@ function focusChatMessage(client: WindowClient, data: { chatId?: number; message
messageId,
},
});
// Catch "focus not allowed" DOM Exceptions
try {
await client.focus();
} catch (error) {
if (DEBUG) {
// eslint-disable-next-line no-console
console.warn('[SW] ', error);
}
}
}
export function handleNotificationClick(e: NotificationEvent) {
@ -129,10 +147,10 @@ export function handleNotificationClick(e: NotificationEvent) {
const notifyClients = async () => {
const clients = await self.clients.matchAll({ type: 'window' }) as WindowClient[];
const clientsInScope = clients.filter((client) => client.url === self.registration.scope);
await Promise.all(clientsInScope.map(async (client) => {
await client.focus();
e.waitUntil(Promise.all(clientsInScope.map((client) => {
clickBuffer[client.id] = data;
}));
return focusChatMessage(client, data);
})));
if (!self.clients.openWindow || clientsInScope.length > 0) return undefined;
// If there is no opened client we need to open one and wait until it is fully loaded
@ -147,12 +165,16 @@ export function handleNotificationClick(e: NotificationEvent) {
}
export function handleClientMessage(e: ExtendableMessageEvent) {
if (DEBUG) {
// eslint-disable-next-line no-console
console.log('[SW] New message from client', e);
}
if (!e.data) return;
const source = e.source as WindowClient;
if (e.data.type === 'clientReady') {
// focus on chat message when client is fully ready
if (clickBuffer[source.id]) {
focusChatMessage(source, clickBuffer[source.id]);
e.waitUntil(focusChatMessage(source, clickBuffer[source.id]));
delete clickBuffer[source.id];
}
}

View File

@ -2,7 +2,7 @@ import { callApi } from '../api/gramjs';
import { ApiChat, ApiMessage } from '../api/types';
import { renderActionMessageText } from '../components/common/helpers/renderActionMessageText';
import { DEBUG } from '../config';
import { getDispatch, getGlobal } from '../lib/teact/teactn';
import { getDispatch, getGlobal, setGlobal } from '../lib/teact/teactn';
import {
getChatTitle,
getMessageAction,
@ -12,6 +12,7 @@ import {
isActionMessage,
isChatChannel,
} from '../modules/helpers';
import { replaceSettings } from '../modules/reducers';
import { selectChatMessage, selectUser } from '../modules/selectors';
import { IS_SERVICE_WORKER_SUPPORTED } from './environment';
@ -122,9 +123,22 @@ export async function unsubscribe() {
await unsubscribeFromPush(subscription);
}
// Load notification settings from the api
async function loadNotificationsSettings() {
const result = await callApi('loadNotificationsSettings');
if (!result) return;
setGlobal(replaceSettings(getGlobal(), result));
}
export async function subscribe() {
await requestPermission();
if (!checkIfPushSupported()) return;
loadNotificationsSettings();
if (!checkIfPushSupported()) {
// Ask for notification permissions only if service worker notifications are not supported
// As pushManager.subscribe automatically triggers permission popup
await requestPermission();
return;
}
const serviceWorkerRegistration = await navigator.serviceWorker.ready;
let subscription = await serviceWorkerRegistration.pushManager.getSubscription();
if (!checkIfShouldResubscribe(subscription)) return;
@ -157,17 +171,22 @@ export async function subscribe() {
// and / or gcm_user_visible_only
// eslint-disable-next-line no-console
console.log('[PUSH] Unable to subscribe to push.', error);
// Request permissions and fall back to local notifications
// if pushManager.subscribe was aborted due to invalid VAPID key.
if (error.code === DOMException.ABORT_ERR) {
await requestPermission();
}
}
}
}
async function checkIfShouldNotify(chat: ApiChat, isActive: boolean) {
if (chat.isMuted) return false;
function checkIfShouldNotify(chat: ApiChat, isActive: boolean) {
if (chat.isMuted || chat.isNotJoined) return false;
// Dont show notification for active chat if client has focus
if (isActive && document.hasFocus()) return false;
await getDispatch().loadNotificationsSettings();
const global = getGlobal();
switch (chat.type) {
case 'chatTypePrivate':
@ -226,7 +245,7 @@ function getNotificationContent(chat: ApiChat, message: ApiMessage) {
};
}
export async function showNewMessageNotification({
export function showNewMessageNotification({
chat,
message,
isActiveChat,
@ -234,8 +253,7 @@ export async function showNewMessageNotification({
if (!checkIfNotificationsSupported()) return;
if (!message.id) return;
const shouldNotify = await checkIfShouldNotify(chat, isActiveChat);
if (!shouldNotify) return;
if (!checkIfShouldNotify(chat, isActiveChat)) return;
const {
title,