Hightlight browser tab on incoming messages
This commit is contained in:
parent
c4e79fa9d6
commit
833cb1d6d3
BIN
public/favicon-unread-32x32.png
Normal file
BIN
public/favicon-unread-32x32.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 853 B |
@ -1,15 +1,21 @@
|
|||||||
import React, { FC, useEffect, memo } from '../../lib/teact/teact';
|
import React, { FC, useEffect, memo } from '../../lib/teact/teact';
|
||||||
import { withGlobal } from '../../lib/teact/teactn';
|
import { getGlobal, withGlobal } from '../../lib/teact/teactn';
|
||||||
|
|
||||||
import { GlobalActions } from '../../global/types';
|
import { GlobalActions } from '../../global/types';
|
||||||
|
|
||||||
import '../../modules/actions/all';
|
import '../../modules/actions/all';
|
||||||
import { ANIMATION_END_DELAY, DEBUG } from '../../config';
|
import { ANIMATION_END_DELAY, DEBUG } from '../../config';
|
||||||
import { pick } from '../../util/iteratees';
|
import { pick } from '../../util/iteratees';
|
||||||
import { selectIsForwardModalOpen, selectIsMediaViewerOpen, selectIsRightColumnShown } from '../../modules/selectors';
|
import {
|
||||||
|
selectCountNotMutedUnread,
|
||||||
|
selectIsForwardModalOpen,
|
||||||
|
selectIsMediaViewerOpen,
|
||||||
|
selectIsRightColumnShown,
|
||||||
|
} from '../../modules/selectors';
|
||||||
import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
|
import { dispatchHeavyAnimationEvent } from '../../hooks/useHeavyAnimationCheck';
|
||||||
import useShowTransition from '../../hooks/useShowTransition';
|
|
||||||
import buildClassName from '../../util/buildClassName';
|
import buildClassName from '../../util/buildClassName';
|
||||||
|
import useShowTransition from '../../hooks/useShowTransition';
|
||||||
|
import useBackgroundMode from '../../hooks/useBackgroundMode';
|
||||||
|
|
||||||
import LeftColumn from '../left/LeftColumn';
|
import LeftColumn from '../left/LeftColumn';
|
||||||
import MiddleColumn from '../middle/MiddleColumn';
|
import MiddleColumn from '../middle/MiddleColumn';
|
||||||
@ -34,9 +40,12 @@ type StateProps = {
|
|||||||
|
|
||||||
type DispatchProps = Pick<GlobalActions, 'loadAnimatedEmojis'>;
|
type DispatchProps = Pick<GlobalActions, 'loadAnimatedEmojis'>;
|
||||||
|
|
||||||
|
const APP_NAME = 'Telegram';
|
||||||
const ANIMATION_DURATION = 350;
|
const ANIMATION_DURATION = 350;
|
||||||
|
const NOTIFICATION_INTERVAL = 1000;
|
||||||
|
|
||||||
let timeout: number | undefined;
|
let rightColumnAnimationTimeout: number | undefined;
|
||||||
|
let notificationInterval: number | undefined;
|
||||||
|
|
||||||
let DEBUG_isLogged = false;
|
let DEBUG_isLogged = false;
|
||||||
|
|
||||||
@ -90,18 +99,43 @@ const Main: FC<StateProps & DispatchProps> = ({
|
|||||||
document.body.classList.add('animating-right-column');
|
document.body.classList.add('animating-right-column');
|
||||||
dispatchHeavyAnimationEvent(ANIMATION_DURATION + ANIMATION_END_DELAY);
|
dispatchHeavyAnimationEvent(ANIMATION_DURATION + ANIMATION_END_DELAY);
|
||||||
|
|
||||||
if (timeout) {
|
if (rightColumnAnimationTimeout) {
|
||||||
clearTimeout(timeout);
|
clearTimeout(rightColumnAnimationTimeout);
|
||||||
timeout = undefined;
|
rightColumnAnimationTimeout = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
timeout = window.setTimeout(() => {
|
rightColumnAnimationTimeout = window.setTimeout(() => {
|
||||||
document.body.classList.remove('animating-right-column');
|
document.body.classList.remove('animating-right-column');
|
||||||
timeout = undefined;
|
rightColumnAnimationTimeout = undefined;
|
||||||
}, ANIMATION_DURATION + ANIMATION_END_DELAY);
|
}, ANIMATION_DURATION + ANIMATION_END_DELAY);
|
||||||
}
|
}
|
||||||
}, [animationLevel, isRightColumnShown]);
|
}, [animationLevel, isRightColumnShown]);
|
||||||
|
|
||||||
|
useBackgroundMode(() => {
|
||||||
|
const initialUnread = selectCountNotMutedUnread(getGlobal());
|
||||||
|
let index = 0;
|
||||||
|
|
||||||
|
notificationInterval = window.setInterval(() => {
|
||||||
|
if (index % 2 === 0) {
|
||||||
|
const newUnread = selectCountNotMutedUnread(getGlobal()) - initialUnread;
|
||||||
|
if (newUnread > 0) {
|
||||||
|
document.title = `${newUnread} notification${newUnread > 1 ? 's' : ''}`;
|
||||||
|
updateIcon(true);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
document.title = APP_NAME;
|
||||||
|
updateIcon(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}, NOTIFICATION_INTERVAL);
|
||||||
|
}, () => {
|
||||||
|
clearInterval(notificationInterval);
|
||||||
|
notificationInterval = undefined;
|
||||||
|
document.title = APP_NAME;
|
||||||
|
updateIcon(false);
|
||||||
|
});
|
||||||
|
|
||||||
function stopEvent(e: React.MouseEvent<HTMLDivElement, MouseEvent>) {
|
function stopEvent(e: React.MouseEvent<HTMLDivElement, MouseEvent>) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
@ -120,6 +154,19 @@ const Main: FC<StateProps & DispatchProps> = ({
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function updateIcon(asUnread: boolean) {
|
||||||
|
document.querySelectorAll<HTMLLinkElement>('link[rel="icon"]')
|
||||||
|
.forEach((link) => {
|
||||||
|
if (asUnread) {
|
||||||
|
if (!link.href.includes('favicon-unread')) {
|
||||||
|
link.href = link.href.replace('favicon', 'favicon-unread');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
link.href = link.href.replace('favicon-unread', 'favicon');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export default memo(withGlobal(
|
export default memo(withGlobal(
|
||||||
(global): StateProps => ({
|
(global): StateProps => ({
|
||||||
animationLevel: global.settings.byKey.animationLevel,
|
animationLevel: global.settings.byKey.animationLevel,
|
||||||
|
|||||||
@ -150,3 +150,22 @@ export function selectChatByUsername(global: GlobalState, username: string) {
|
|||||||
(chat) => chat.username && chat.username.toLowerCase() === usernameLowered,
|
(chat) => chat.username && chat.username.toLowerCase() === usernameLowered,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selectCountNotMutedUnread(global: GlobalState) {
|
||||||
|
const activeChatIds = global.chats.listIds.active;
|
||||||
|
if (!activeChatIds) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const chats = global.chats.byId;
|
||||||
|
|
||||||
|
return activeChatIds.reduce((acc, chatId) => {
|
||||||
|
const chat = chats[chatId];
|
||||||
|
|
||||||
|
if (chat && chat.unreadCount && !chat.isMuted) {
|
||||||
|
return acc + chat.unreadCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user