Notifications: Close when read, replace same-chat instead of counter (#1413)

This commit is contained in:
Alexander Zinchuk 2021-08-23 03:38:36 +03:00
parent 1f36d97607
commit d291016123
3 changed files with 46 additions and 17 deletions

View File

@ -4,7 +4,7 @@ import { ApiUpdate, MAIN_THREAD_ID } from '../../../api/types';
import { ARCHIVED_FOLDER_ID, MAX_ACTIVE_PINNED_CHATS } from '../../../config';
import { pick } from '../../../util/iteratees';
import { showNewMessageNotification } from '../../../util/notifications';
import { closeMessageNotifications, showNewMessageNotification } from '../../../util/notifications';
import { updateAppBadge } from '../../../util/appBadge';
import {
updateChat,
@ -42,6 +42,13 @@ addReducer('apiUpdate', (global, actions, update: ApiUpdate) => {
setGlobal(newGlobal);
runThrottledForUpdateAppBadge(() => updateAppBadge(selectCountNotMutedUnread(getGlobal())));
if (update.chat.id) {
closeMessageNotifications({
chatId: update.chat.id,
lastReadInboxMessageId: update.chat.lastReadInboxMessageId,
});
}
break;
}

View File

@ -31,6 +31,11 @@ type NotificationData = {
icon?: string;
};
type CloseNotificationData = {
lastReadInboxMessageId?: number;
chatId: number;
};
let lastSyncAt = new Date().valueOf();
const shownNotifications = new Set();
const clickBuffer: Record<string, NotificationData> = {};
@ -87,14 +92,15 @@ async function playNotificationSound(id: number) {
});
}
async function showNotification({
function showNotification({
chatId,
messageId,
body,
title,
icon,
}: NotificationData) {
const tag = String(chatId || 0);
const isFirstBatch = new Date().valueOf() - lastSyncAt < 1000;
const tag = String(isFirstBatch ? 0 : chatId || 0);
const options: NotificationOptions = {
body,
data: {
@ -107,21 +113,29 @@ async function showNotification({
tag,
vibrate: [200, 100, 200],
};
const notifications = await self.registration.getNotifications({ tag });
if (notifications.length > 0) {
const current = notifications[0];
const count = current.data.count + 1;
options.data.count = count;
options.data.messageId = current.data.messageId;
options.body = `You have ${count} new messages`;
current.close();
}
return Promise.all([
playNotificationSound(messageId || chatId || 0),
self.registration.showNotification(title, options),
]);
}
async function closeNotifications({
chatId,
lastReadInboxMessageId,
}: CloseNotificationData) {
const notifications = await self.registration.getNotifications();
const lastMessageId = lastReadInboxMessageId || Number.MAX_VALUE;
notifications.forEach((notification) => {
if (
notification.tag === '0'
|| (notification.data.chatId === chatId && notification.data.messageId <= lastMessageId)
) {
notification.close();
}
});
}
export function handlePush(e: PushEvent) {
if (DEBUG) {
// eslint-disable-next-line no-console
@ -223,16 +237,16 @@ export function handleClientMessage(e: ExtendableMessageEvent) {
}
}
if (e.data.type === 'newMessageNotification') {
// Do not show notifications right after sync (when browser is opened)
// To avoid stale notifications
if (new Date().valueOf() - lastSyncAt < 3000) return;
// store messageId for already shown notification
const notification: NotificationData = e.data.payload;
// mark this notification as shown if it was handled locally
shownNotifications.add(notification.messageId);
e.waitUntil(showNotification(notification));
}
if (e.data.type === 'closeMessageNotifications') {
e.waitUntil(closeNotifications(e.data.payload));
}
}
self.onsync = () => {

View File

@ -3,7 +3,7 @@ import {
ApiChat, ApiMediaFormat, ApiMessage, ApiUser,
} from '../api/types';
import { renderActionMessageText } from '../components/common/helpers/renderActionMessageText';
import { DEBUG } from '../config';
import { DEBUG, IS_TEST } from '../config';
import { getDispatch, getGlobal, setGlobal } from '../lib/teact/teactn';
import {
getChatAvatarHash,
@ -378,6 +378,14 @@ export async function showNewMessageNotification({
}
}
export function closeMessageNotifications(payload: { chatId: number; lastReadInboxMessageId?: number }) {
if (IS_TEST || !navigator.serviceWorker.controller) return;
navigator.serviceWorker.controller.postMessage({
type: 'closeMessageNotifications',
payload,
});
}
// Notify service worker that client is fully loaded
export function notifyClientReady() {
if (!navigator.serviceWorker.controller) return;