More fixes for muted chats; PWA badge (#1133)

This commit is contained in:
Alexander Zinchuk 2021-06-03 21:22:49 +03:00
parent 9af2ce7d0b
commit ffe756414c
8 changed files with 62 additions and 9 deletions

View File

@ -100,3 +100,8 @@ interface HTMLElement {
webkitEnterFullscreen?: () => Promise<void>; webkitEnterFullscreen?: () => Promise<void>;
webkitRequestFullscreen?: () => Promise<void>; webkitRequestFullscreen?: () => Promise<void>;
} }
interface Navigator {
// PWA badging extensions https://w3c.github.io/badging/
setAppBadge?(count: number): Promise<void>;
}

View File

@ -11,6 +11,7 @@ import {
ApiTypingStatus, ApiTypingStatus,
MAIN_THREAD_ID, ApiUser, MAIN_THREAD_ID, ApiUser,
} from '../../api/types'; } from '../../api/types';
import { NotifyException, NotifySettings } from '../../types';
import { import {
MIN_SCREEN_WIDTH_FOR_STATIC_LEFT_COLUMN, MIN_SCREEN_WIDTH_FOR_STATIC_LEFT_COLUMN,
@ -27,6 +28,7 @@ import {
getMessageKey, getMessageKey,
getChatTitle, getChatTitle,
getSenderTitle, getSenderTitle,
selectIsChatMuted,
} from '../../modules/helpers'; } from '../../modules/helpers';
import { import {
selectChat, selectChat,
@ -43,6 +45,8 @@ import {
selectScheduledIds, selectScheduledIds,
selectIsInSelectMode, selectIsInSelectMode,
selectIsChatWithBot, selectIsChatWithBot,
selectNotifySettings,
selectNotifyExceptions,
} from '../../modules/selectors'; } from '../../modules/selectors';
import useEnsureMessage from '../../hooks/useEnsureMessage'; import useEnsureMessage from '../../hooks/useEnsureMessage';
import useWindowSize from '../../hooks/useWindowSize'; import useWindowSize from '../../hooks/useWindowSize';
@ -88,6 +92,8 @@ type StateProps = {
isChatWithSelf?: boolean; isChatWithSelf?: boolean;
isChatWithBot?: boolean; isChatWithBot?: boolean;
lastSyncTime?: number; lastSyncTime?: number;
notifySettings: NotifySettings;
notifyExceptions?: Record<number, NotifyException>;
}; };
type DispatchProps = Pick<GlobalActions, ( type DispatchProps = Pick<GlobalActions, (
@ -115,6 +121,8 @@ const MiddleHeader: FC<OwnProps & StateProps & DispatchProps> = ({
isChatWithSelf, isChatWithSelf,
isChatWithBot, isChatWithBot,
lastSyncTime, lastSyncTime,
notifySettings,
notifyExceptions,
openChatWithInfo, openChatWithInfo,
pinMessage, pinMessage,
focusMessage, focusMessage,
@ -211,7 +219,9 @@ const MiddleHeader: FC<OwnProps & StateProps & DispatchProps> = ({
} }
const count = currentChat.unreadCount || 0; const count = currentChat.unreadCount || 0;
if (count && (!currentChat.isMuted || currentChat.unreadMentionsCount)) { if (
count && (!selectIsChatMuted(currentChat, notifySettings, notifyExceptions) || currentChat.unreadMentionsCount)
) {
isActive = true; isActive = true;
} }
@ -226,7 +236,7 @@ const MiddleHeader: FC<OwnProps & StateProps & DispatchProps> = ({
isActive, isActive,
totalCount, totalCount,
}; };
}, [isLeftColumnHideable, chatsById]); }, [isLeftColumnHideable, chatsById, notifySettings, notifyExceptions]);
const canToolsCollideWithChatInfo = ( const canToolsCollideWithChatInfo = (
windowWidth >= MIN_SCREEN_WIDTH_FOR_STATIC_LEFT_COLUMN windowWidth >= MIN_SCREEN_WIDTH_FOR_STATIC_LEFT_COLUMN
@ -444,6 +454,8 @@ export default memo(withGlobal<OwnProps>(
isChatWithSelf: selectIsChatWithSelf(global, chatId), isChatWithSelf: selectIsChatWithSelf(global, chatId),
isChatWithBot: chat && selectIsChatWithBot(global, chat), isChatWithBot: chat && selectIsChatWithBot(global, chat),
lastSyncTime, lastSyncTime,
notifySettings: selectNotifySettings(global),
notifyExceptions: selectNotifyExceptions(global),
}; };
const messagesById = selectChatMessages(global, chatId); const messagesById = selectChatMessages(global, chatId);

View File

@ -15,6 +15,7 @@ import {
import { initApi, callApi } from '../../../api/gramjs'; import { initApi, callApi } from '../../../api/gramjs';
import { unsubscribe } from '../../../util/notifications'; import { unsubscribe } from '../../../util/notifications';
import * as cacheApi from '../../../util/cacheApi'; import * as cacheApi from '../../../util/cacheApi';
import { updateAppBadge } from '../../../util/appBadge';
addReducer('initApi', (global: GlobalState, actions) => { addReducer('initApi', (global: GlobalState, actions) => {
const sessionId = localStorage.getItem(GRAMJS_SESSION_ID_KEY) || undefined; const sessionId = localStorage.getItem(GRAMJS_SESSION_ID_KEY) || undefined;
@ -111,6 +112,7 @@ addReducer('signOut', () => {
addReducer('reset', () => { addReducer('reset', () => {
localStorage.removeItem(GRAMJS_SESSION_ID_KEY); localStorage.removeItem(GRAMJS_SESSION_ID_KEY);
updateAppBadge(0);
cacheApi.clear(MEDIA_CACHE_NAME); cacheApi.clear(MEDIA_CACHE_NAME);
cacheApi.clear(MEDIA_CACHE_NAME_AVATARS); cacheApi.clear(MEDIA_CACHE_NAME_AVATARS);

View File

@ -5,6 +5,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 { showNewMessageNotification } from '../../../util/notifications';
import { updateAppBadge } from '../../../util/appBadge';
import { import {
updateChat, updateChat,
replaceChatListIds, replaceChatListIds,
@ -17,12 +18,15 @@ import {
selectIsChatListed, selectIsChatListed,
selectChatListType, selectChatListType,
selectCurrentMessageList, selectCurrentMessageList,
selectCountNotMutedUnread,
} from '../../selectors'; } from '../../selectors';
import { throttle } from '../../../util/schedulers';
const TYPING_STATUS_CLEAR_DELAY = 6000; // 6 seconds const TYPING_STATUS_CLEAR_DELAY = 6000; // 6 seconds
// Enough to animate and mark as read in Message List // Enough to animate and mark as read in Message List
const CURRENT_CHAT_UNREAD_DELAY = 1000; const CURRENT_CHAT_UNREAD_DELAY = 1000;
const runThrottledForUpdateAppBadge = throttle((cb) => cb(), CURRENT_CHAT_UNREAD_DELAY, true);
addReducer('apiUpdate', (global, actions, update: ApiUpdate) => { addReducer('apiUpdate', (global, actions, update: ApiUpdate) => {
switch (update['@type']) { switch (update['@type']) {
@ -32,8 +36,11 @@ addReducer('apiUpdate', (global, actions, update: ApiUpdate) => {
actions.loadTopChats(); actions.loadTopChats();
} }
setGlobal(updateChat(global, update.id, update.chat, update.newProfilePhoto)); const newGlobal = updateChat(global, update.id, update.chat, update.newProfilePhoto);
setGlobal(newGlobal);
const unreadCount = selectCountNotMutedUnread(newGlobal);
runThrottledForUpdateAppBadge(() => updateAppBadge(unreadCount));
break; break;
} }
@ -125,6 +132,8 @@ addReducer('apiUpdate', (global, actions, update: ApiUpdate) => {
})); }));
} }
const unreadCount = selectCountNotMutedUnread(getGlobal());
updateAppBadge(unreadCount);
showNewMessageNotification({ chat, message, isActiveChat }); showNewMessageNotification({ chat, message, isActiveChat });
break; break;

View File

@ -377,7 +377,7 @@ export function getFolderUnreadDialogs(
const hasActiveDialogs = listedChats.some((chat) => ( const hasActiveDialogs = listedChats.some((chat) => (
chat.unreadMentionsCount chat.unreadMentionsCount
|| (!chat.isMuted && (chat.unreadCount || chat.hasUnreadMark)) || (!selectIsChatMuted(chat, notifySettings, notifyExceptions) && (chat.unreadCount || chat.hasUnreadMark))
)); ));
return { return {

View File

@ -2,10 +2,11 @@ import { ApiChat, MAIN_THREAD_ID } from '../../api/types';
import { GlobalState } from '../../global/types'; import { GlobalState } from '../../global/types';
import { import {
getPrivateChatUserId, isChatChannel, isChatPrivate, isHistoryClearMessage, isUserBot, isUserOnline, getPrivateChatUserId, isChatChannel, isChatPrivate, isHistoryClearMessage, isUserBot, isUserOnline, selectIsChatMuted,
} from '../helpers'; } from '../helpers';
import { selectUser } from './users'; import { selectUser } from './users';
import { ALL_FOLDER_ID, ARCHIVED_FOLDER_ID, MEMBERS_LOAD_SLICE } from '../../config'; import { ALL_FOLDER_ID, ARCHIVED_FOLDER_ID, MEMBERS_LOAD_SLICE } from '../../config';
import { selectNotifyExceptions, selectNotifySettings } from './settings';
export function selectChat(global: GlobalState, chatId: number): ApiChat | undefined { export function selectChat(global: GlobalState, chatId: number): ApiChat | undefined {
return global.chats.byId[chatId]; return global.chats.byId[chatId];
@ -157,7 +158,11 @@ export function selectCountNotMutedUnread(global: GlobalState) {
return activeChatIds.reduce((acc, chatId) => { return activeChatIds.reduce((acc, chatId) => {
const chat = chats[chatId]; const chat = chats[chatId];
if (chat && chat.unreadCount && !chat.isMuted) { if (
chat
&& chat.unreadCount
&& !selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global))
) {
return acc + chat.unreadCount; return acc + chat.unreadCount;
} }

14
src/util/appBadge.ts Normal file
View File

@ -0,0 +1,14 @@
import { DEBUG } from '../config';
export function updateAppBadge(unreadCount: number) {
if (typeof window.navigator.setAppBadge !== 'function') {
return;
}
window.navigator.setAppBadge(unreadCount).catch((err) => {
if (DEBUG) {
// eslint-disable-next-line no-console
console.error(err);
}
});
}

View File

@ -11,10 +11,13 @@ import {
getPrivateChatUserId, getPrivateChatUserId,
isActionMessage, isActionMessage,
isChatChannel, isChatChannel,
selectIsChatMuted,
} from '../modules/helpers'; } from '../modules/helpers';
import { getTranslation } from './langProvider'; import { getTranslation } from './langProvider';
import { replaceSettings } from '../modules/reducers'; import { replaceSettings } from '../modules/reducers';
import { selectChatMessage, selectUser } from '../modules/selectors'; import {
selectChatMessage, selectNotifyExceptions, selectNotifySettings, selectUser,
} from '../modules/selectors';
import { IS_SERVICE_WORKER_SUPPORTED } from './environment'; import { IS_SERVICE_WORKER_SUPPORTED } from './environment';
function getDeviceToken(subscription: PushSubscription) { function getDeviceToken(subscription: PushSubscription) {
@ -187,12 +190,15 @@ export async function subscribe() {
} }
function checkIfShouldNotify(chat: ApiChat, isActive: boolean) { function checkIfShouldNotify(chat: ApiChat, isActive: boolean) {
if (chat.isMuted || chat.isNotJoined) return false; const global = getGlobal();
if (selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global)) || chat.isNotJoined) {
return false;
}
// Dont show notification for active chat if client has focus // Dont show notification for active chat if client has focus
if (isActive && document.hasFocus()) return false; if (isActive && document.hasFocus()) return false;
const global = getGlobal();
switch (chat.type) { switch (chat.type) {
case 'chatTypePrivate': case 'chatTypePrivate':
case 'chatTypeSecret': case 'chatTypeSecret':