Notifications: Fix pinned message ignore mute (#6897)

This commit is contained in:
Alexander Zinchuk 2026-04-27 14:29:24 +02:00
parent d8f913b3a0
commit 881c09acdf
7 changed files with 58 additions and 20 deletions

View File

@ -1909,6 +1909,7 @@
"ActionPinnedTextYou" = "You pinned \"{text}\"";
"ActionPinnedNotFound" = "{from} pinned a message";
"ActionPinnedNotFoundYou" = "You pinned a message";
"PinnedMessagesNotifications" = "Pinned Messages";
"ActionPinnedMedia" = "{from} pinned {media}";
"ActionPinnedMediaYou" = "You pinned {media}";
"ActionPinnedMediaPhoto" = "a photo";

View File

@ -1,5 +1,3 @@
import type { ChangeEvent } from 'react';
import type { FC } from '../../../lib/teact/teact';
import { memo, useCallback, useEffect } from '../../../lib/teact/teact';
import { getActions, withGlobal } from '../../../global';
@ -26,22 +24,25 @@ type OwnProps = {
type StateProps = {
notifyDefaults?: Record<ApiNotifyPeerType, ApiPeerNotifySettings>;
hasContactJoinedNotifications: boolean;
shouldNotifyAboutPinnedMessages: boolean;
hasWebNotifications: boolean;
hasPushNotifications: boolean;
notificationSoundVolume: number;
};
const SettingsNotifications: FC<OwnProps & StateProps> = ({
const SettingsNotifications = ({
isActive,
onReset,
notifyDefaults,
hasContactJoinedNotifications,
shouldNotifyAboutPinnedMessages,
hasPushNotifications,
hasWebNotifications,
notificationSoundVolume,
}) => {
}: OwnProps & StateProps) => {
const {
loadNotificationSettings,
setSettingOption,
updateContactSignUpNotification,
updateNotificationSettings,
updateWebNotificationSettings,
@ -61,7 +62,7 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
const areUsersMuted = Boolean(notifyDefaults?.users?.mutedUntil);
const handleSettingsChange = useCallback((
e: ChangeEvent<HTMLInputElement>,
e: React.ChangeEvent<HTMLInputElement>,
peerType: ApiNotifyPeerType,
setting: 'mute' | 'showPreviews',
) => {
@ -73,52 +74,56 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
isMuted: setting === 'mute' ? !e.target.checked : currentIsMuted,
shouldShowPreviews: setting === 'showPreviews' ? e.target.checked : currentShouldShowPreviews,
});
}, [notifyDefaults]);
}, [notifyDefaults, updateNotificationSettings]);
const handleWebNotificationsChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
const handleWebNotificationsChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
const isEnabled = e.target.checked;
updateWebNotificationSettings({
hasWebNotifications: isEnabled,
...(!isEnabled && { hasPushNotifications: false }),
hasPushNotifications: isEnabled ? undefined : false,
});
}, [updateWebNotificationSettings]);
const handlePushNotificationsChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
const handlePushNotificationsChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
updateWebNotificationSettings({
hasPushNotifications: e.target.checked,
});
}, [updateWebNotificationSettings]);
const handlePrivateChatsNotificationsChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
const handlePrivateChatsNotificationsChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
handleSettingsChange(e, 'users', 'mute');
}, [handleSettingsChange]);
const handlePrivateChatsPreviewChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
const handlePrivateChatsPreviewChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
handleSettingsChange(e, 'users', 'showPreviews');
}, [handleSettingsChange]);
const handleGroupsNotificationsChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
const handleGroupsNotificationsChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
handleSettingsChange(e, 'groups', 'mute');
}, [handleSettingsChange]);
const handleGroupsPreviewChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
const handleGroupsPreviewChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
handleSettingsChange(e, 'groups', 'showPreviews');
}, [handleSettingsChange]);
const handleChannelsNotificationsChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
const handleChannelsNotificationsChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
handleSettingsChange(e, 'channels', 'mute');
}, [handleSettingsChange]);
const handleChannelsPreviewChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
const handleChannelsPreviewChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
handleSettingsChange(e, 'channels', 'showPreviews');
}, [handleSettingsChange]);
const handleContactNotificationChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
const handleContactNotificationChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
updateContactSignUpNotification({
isSilent: !e.target.checked,
});
}, [updateContactSignUpNotification]);
const handlePinnedMessagesNotificationChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
setSettingOption({ shouldNotifyAboutPinnedMessages: e.target.checked });
}, [setSettingOption]);
const handleVolumeChange = useCallback((volume: number) => {
updateWebNotificationSettings({
notificationSoundVolume: volume,
@ -233,6 +238,11 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
checked={hasContactJoinedNotifications}
onChange={handleContactNotificationChange}
/>
<Checkbox
label={lang('PinnedMessagesNotifications')}
checked={shouldNotifyAboutPinnedMessages}
onChange={handlePinnedMessagesNotificationChange}
/>
</div>
</div>
);
@ -242,6 +252,7 @@ export default memo(withGlobal<OwnProps>(
(global): Complete<StateProps> => {
return {
hasContactJoinedNotifications: Boolean(global.settings.byKey.hasContactJoinedNotifications),
shouldNotifyAboutPinnedMessages: global.settings.byKey.shouldNotifyAboutPinnedMessages,
hasWebNotifications: global.settings.byKey.hasWebNotifications,
hasPushNotifications: global.settings.byKey.hasPushNotifications,
notificationSoundVolume: global.settings.byKey.notificationSoundVolume,

View File

@ -1,5 +1,6 @@
import type {
ApiChat,
ApiMessage,
ApiNotifyPeerType,
ApiPeer,
ApiPeerNotifySettings,
@ -39,6 +40,20 @@ export function getShouldShowMessagePreview(
return Boolean(settings?.shouldShowPreviews);
}
export function getShouldIgnoreNotificationMute(
message: Partial<ApiMessage>,
shouldNotifyAboutPinnedMessages: boolean,
) {
if (!message.isMentioned) return false;
const isPinMessage = message.content?.action?.type === 'pinMessage';
if (isPinMessage) {
return shouldNotifyAboutPinnedMessages && !message.isSilent;
}
return true;
}
export function getChatNotifySettings(
chat: ApiChat,
notifyDefaults?: Record<ApiNotifyPeerType, ApiPeerNotifySettings>,

View File

@ -300,6 +300,7 @@ export const INITIAL_GLOBAL_STATE: GlobalState = {
autoLoadFileMaxSizeMb: 10,
hasWebNotifications: true,
hasPushNotifications: true,
shouldNotifyAboutPinnedMessages: true,
notificationSoundVolume: 5,
shouldSuggestStickers: true,
shouldSuggestCustomEmoji: true,

View File

@ -139,6 +139,7 @@ export interface AccountSettings {
hasWebNotifications: boolean;
hasPushNotifications: boolean;
hasContactJoinedNotifications?: boolean;
shouldNotifyAboutPinnedMessages: boolean;
notificationSoundVolume: number;
canAutoLoadPhotoFromContacts: boolean;
canAutoLoadPhotoInPrivateChats: boolean;

View File

@ -1543,6 +1543,7 @@ export interface LangPair {
'ActionFallbackSomeone': undefined;
'ActionUnsupported': undefined;
'ActionPinnedNotFoundYou': undefined;
'PinnedMessagesNotifications': undefined;
'ActionPinnedMediaPhoto': undefined;
'ActionPinnedMediaVideo': undefined;
'ActionPinnedMediaAudio': undefined;

View File

@ -13,7 +13,12 @@ import {
getMessageRecentReaction,
getUserFullName,
} from '../global/helpers';
import { getIsChatMuted, getIsChatSilent, getShouldShowMessagePreview } from '../global/helpers/notifications';
import {
getIsChatMuted,
getIsChatSilent,
getShouldIgnoreNotificationMute,
getShouldShowMessagePreview,
} from '../global/helpers/notifications';
import { getMessageSenderName } from '../global/helpers/peers';
import {
selectCurrentMessageList,
@ -291,10 +296,13 @@ function checkIfShouldNotify(chat: ApiChat, message: Partial<ApiMessage>) {
const topic = selectTopicFromMessage(global, message as ApiMessage);
const topicMutedUntil = topic?.notifySettings.mutedUntil;
const isMuted = topicMutedUntil === undefined ? isChatMuted : topicMutedUntil > getServerTime();
const shouldIgnoreMute = message.isMentioned;
const shouldNotifyAboutPinnedMessages = global.settings.byKey.shouldNotifyAboutPinnedMessages;
const shouldIgnoreMute = getShouldIgnoreNotificationMute(message, shouldNotifyAboutPinnedMessages);
const isPinMessage = message.content?.action?.type === 'pinMessage';
const shouldSkipPinnedMessageNotification = isPinMessage && !shouldNotifyAboutPinnedMessages;
const shouldNotifyAboutMessage = message.content?.action?.type !== 'phoneCall';
if ((isMuted && !shouldIgnoreMute) || !shouldNotifyAboutMessage
if (shouldSkipPinnedMessageNotification || (isMuted && !shouldIgnoreMute) || !shouldNotifyAboutMessage
|| chat.isNotJoined || !chat.isListed || selectIsChatWithSelf(global, chat.id)) {
return false;
}
@ -496,7 +504,7 @@ export async function notifyAboutMessage({
// Play sound when notification is displayed
notification.onshow = () => {
// TODO Update when reaction badges are implemented
if (isReaction || message.isSilent || IS_TAURI) return;
if (isSilent || isReaction || message.isSilent || IS_TAURI) return;
playNotifySoundDebounced(String(message.id) || chat.id);
};
}