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

View File

@ -31,6 +31,11 @@ type NotificationData = {
icon?: string; icon?: string;
}; };
type CloseNotificationData = {
lastReadInboxMessageId?: number;
chatId: number;
};
let lastSyncAt = new Date().valueOf(); let lastSyncAt = new Date().valueOf();
const shownNotifications = new Set(); const shownNotifications = new Set();
const clickBuffer: Record<string, NotificationData> = {}; const clickBuffer: Record<string, NotificationData> = {};
@ -87,14 +92,15 @@ async function playNotificationSound(id: number) {
}); });
} }
async function showNotification({ function showNotification({
chatId, chatId,
messageId, messageId,
body, body,
title, title,
icon, icon,
}: NotificationData) { }: NotificationData) {
const tag = String(chatId || 0); const isFirstBatch = new Date().valueOf() - lastSyncAt < 1000;
const tag = String(isFirstBatch ? 0 : chatId || 0);
const options: NotificationOptions = { const options: NotificationOptions = {
body, body,
data: { data: {
@ -107,21 +113,29 @@ async function showNotification({
tag, tag,
vibrate: [200, 100, 200], 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([ return Promise.all([
playNotificationSound(messageId || chatId || 0), playNotificationSound(messageId || chatId || 0),
self.registration.showNotification(title, options), 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) { export function handlePush(e: PushEvent) {
if (DEBUG) { if (DEBUG) {
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
@ -223,16 +237,16 @@ export function handleClientMessage(e: ExtendableMessageEvent) {
} }
} }
if (e.data.type === 'newMessageNotification') { 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 // store messageId for already shown notification
const notification: NotificationData = e.data.payload; const notification: NotificationData = e.data.payload;
// mark this notification as shown if it was handled locally // mark this notification as shown if it was handled locally
shownNotifications.add(notification.messageId); shownNotifications.add(notification.messageId);
e.waitUntil(showNotification(notification)); e.waitUntil(showNotification(notification));
} }
if (e.data.type === 'closeMessageNotifications') {
e.waitUntil(closeNotifications(e.data.payload));
}
} }
self.onsync = () => { self.onsync = () => {

View File

@ -3,7 +3,7 @@ import {
ApiChat, ApiMediaFormat, ApiMessage, ApiUser, ApiChat, ApiMediaFormat, ApiMessage, ApiUser,
} from '../api/types'; } from '../api/types';
import { renderActionMessageText } from '../components/common/helpers/renderActionMessageText'; 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 { getDispatch, getGlobal, setGlobal } from '../lib/teact/teactn';
import { import {
getChatAvatarHash, 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 // Notify service worker that client is fully loaded
export function notifyClientReady() { export function notifyClientReady() {
if (!navigator.serviceWorker.controller) return; if (!navigator.serviceWorker.controller) return;