Chat: Fix muted state (#5752)

This commit is contained in:
zubiden 2025-03-30 15:46:13 +02:00 committed by Alexander Zinchuk
parent e494152ea0
commit 4b2bee8a2f
8 changed files with 57 additions and 23 deletions

View File

@ -1923,18 +1923,20 @@ const Composer: FC<OwnProps & StateProps> = ({
{!hasText && ( {!hasText && (
<> <>
{isChannel && ( {isChannel && (
<Button <Transition className="composer-action-button" name="reveal" activeKey={Number(isSilentPosting)}>
round <Button
faded round
className="composer-action-button" faded
color="translucent" className="composer-action-button"
onClick={handleToggleSilentPosting} color="translucent"
ariaLabel={lang( onClick={handleToggleSilentPosting}
isSilentPosting ? 'AriaComposerSilentPostingDisable' : 'AriaComposerSilentPostingEnable', ariaLabel={lang(
)} isSilentPosting ? 'AriaComposerSilentPostingDisable' : 'AriaComposerSilentPostingEnable',
> )}
<Icon name={isSilentPosting ? 'mute' : 'unmute'} /> >
</Button> <Icon name={isSilentPosting ? 'mute' : 'unmute'} />
</Button>
</Transition>
)} )}
{withScheduledButton && ( {withScheduledButton && (
<Button <Button

View File

@ -281,6 +281,7 @@ const Chat: FC<OwnProps & StateProps> = ({
isSavedDialog, isSavedDialog,
currentUserId, currentUserId,
isPreview, isPreview,
topics,
}); });
const isIntersecting = useIsIntersecting(ref, chat ? observeIntersection : undefined); const isIntersecting = useIsIntersecting(ref, chat ? observeIntersection : undefined);

View File

@ -75,6 +75,7 @@ import {
replaceChatListIds, replaceChatListIds,
replaceChatListLoadingParameters, replaceChatListLoadingParameters,
replaceMessages, replaceMessages,
replaceNotifyExceptions,
replaceSimilarChannels, replaceSimilarChannels,
replaceThreadParam, replaceThreadParam,
replaceUserStatuses, replaceUserStatuses,
@ -2928,6 +2929,7 @@ async function loadChats(
const offsetId = params.nextOffsetId; const offsetId = params.nextOffsetId;
const isFirstBatch = !shouldIgnorePagination && !offsetPeer && !offsetDate && !offsetId; const isFirstBatch = !shouldIgnorePagination && !offsetPeer && !offsetDate && !offsetId;
const shouldReplaceStaleState = listType === 'active' && isFirstBatch;
const result = listType === 'saved' ? await callApi('fetchSavedChats', { const result = listType === 'saved' ? await callApi('fetchSavedChats', {
limit: CHAT_LIST_LOAD_SLICE, limit: CHAT_LIST_LOAD_SLICE,
@ -2960,16 +2962,21 @@ async function loadChats(
global = updateChats(global, newChats); global = updateChats(global, newChats);
if (isFirstBatch) { if (isFirstBatch) {
global = replaceChatListIds(global, listType, chatIds); global = replaceChatListIds(global, listType, chatIds);
global = replaceUserStatuses(global, result.userStatusesById);
} else { } else {
global = addChatListIds(global, listType, chatIds); global = addChatListIds(global, listType, chatIds);
}
if (shouldReplaceStaleState) {
global = replaceUserStatuses(global, result.userStatusesById);
global = replaceNotifyExceptions(global, result.notifyExceptionById);
} else {
global = addUserStatuses(global, result.userStatusesById); global = addUserStatuses(global, result.userStatusesById);
global = addNotifyExceptions(global, result.notifyExceptionById);
} }
global = updateChatListSecondaryInfo(global, listType, result); global = updateChatListSecondaryInfo(global, listType, result);
global = replaceMessages(global, result.messages); global = replaceMessages(global, result.messages);
global = updateChatsLastMessageId(global, result.lastMessageByChatId, listType); global = updateChatsLastMessageId(global, result.lastMessageByChatId, listType);
global = addNotifyExceptions(global, result.notifyExceptionById);
if (!shouldIgnorePagination) { if (!shouldIgnorePagination) {
global = replaceChatListLoadingParameters( global = replaceChatListLoadingParameters(

View File

@ -544,8 +544,15 @@ addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
const chat = selectChat(global, chatId); const chat = selectChat(global, chatId);
const currentThreadInfo = selectThreadInfo(global, chatId, threadId); const currentThreadInfo = selectThreadInfo(global, chatId, threadId);
if (chat?.isForum && threadInfo.lastReadInboxMessageId !== currentThreadInfo?.lastReadInboxMessageId) { const topic = selectTopic(global, chatId, threadId);
actions.loadTopicById({ chatId, topicId: Number(threadId) }); if (chat?.isForum) {
if (!topic || topic.lastMessageId !== currentThreadInfo?.lastReadInboxMessageId) {
actions.loadTopicById({ chatId, topicId: Number(threadId) });
} else {
global = updateTopic(global, chatId, Number(threadId), {
unreadCount: 0,
});
}
} }
// Update reply thread last read message id if already read in main thread // Update reply thread last read message id if already read in main thread

View File

@ -498,7 +498,7 @@ function reduceChats<T extends GlobalState>(global: T): GlobalState['chats'] {
similarChannelsById: {}, similarChannelsById: {},
similarBotsById: {}, similarBotsById: {},
isFullyLoaded: {}, isFullyLoaded: {},
notifyExceptionById: {}, notifyExceptionById: pickTruthy(global.chats.notifyExceptionById, idsToSave),
loadingParameters: INITIAL_GLOBAL_STATE.chats.loadingParameters, loadingParameters: INITIAL_GLOBAL_STATE.chats.loadingParameters,
byId: pickTruthy(global.chats.byId, idsToSave), byId: pickTruthy(global.chats.byId, idsToSave),
fullInfoById: pickTruthy(global.chats.fullInfoById, idsToSave), fullInfoById: pickTruthy(global.chats.fullInfoById, idsToSave),
@ -659,7 +659,7 @@ function omitLocalMedia(message: ApiMessage): ApiMessage {
function reduceSettings<T extends GlobalState>(global: T): GlobalState['settings'] { function reduceSettings<T extends GlobalState>(global: T): GlobalState['settings'] {
const { const {
byKey, themes, performance, botVerificationShownPeerIds, miniAppsCachedPosition, miniAppsCachedSize, byKey, themes, performance, botVerificationShownPeerIds, miniAppsCachedPosition, miniAppsCachedSize, notifyDefaults,
} = global.settings; } = global.settings;
return { return {
@ -670,6 +670,7 @@ function reduceSettings<T extends GlobalState>(global: T): GlobalState['settings
botVerificationShownPeerIds, botVerificationShownPeerIds,
miniAppsCachedPosition, miniAppsCachedPosition,
miniAppsCachedSize, miniAppsCachedSize,
notifyDefaults,
}; };
} }

View File

@ -53,6 +53,18 @@ export function addNotifyExceptions<T extends GlobalState>(
}; };
} }
export function replaceNotifyExceptions<T extends GlobalState>(
global: T, notifyExceptionById: Record<string, ApiPeerNotifySettings>,
): T {
return {
...global,
chats: {
...global.chats,
notifyExceptionById,
},
};
}
export function addNotifyException<T extends GlobalState>( export function addNotifyException<T extends GlobalState>(
global: T, id: string, notifyException: ApiPeerNotifySettings, global: T, id: string, notifyException: ApiPeerNotifySettings,
): T { ): T {

View File

@ -1,8 +1,8 @@
import { useMemo } from '../lib/teact/teact'; import { useMemo } from '../lib/teact/teact';
import { getActions } from '../global'; import { getActions } from '../global';
import type { ApiChat, ApiTopic, ApiUser } from '../api/types';
import type { MenuItemContextAction } from '../components/ui/ListItem'; import type { MenuItemContextAction } from '../components/ui/ListItem';
import { type ApiChat, type ApiUser } from '../api/types';
import { SERVICE_NOTIFICATIONS_USER_ID } from '../config'; import { SERVICE_NOTIFICATIONS_USER_ID } from '../config';
import { import {
@ -23,6 +23,7 @@ const useChatContextActions = ({
isSavedDialog, isSavedDialog,
currentUserId, currentUserId,
isPreview, isPreview,
topics,
handleDelete, handleDelete,
handleMute, handleMute,
handleChatFolderChange, handleChatFolderChange,
@ -37,6 +38,7 @@ const useChatContextActions = ({
isSavedDialog?: boolean; isSavedDialog?: boolean;
currentUserId?: string; currentUserId?: string;
isPreview?: boolean; isPreview?: boolean;
topics?: Record<number, ApiTopic>;
handleDelete?: NoneToVoidFunction; handleDelete?: NoneToVoidFunction;
handleMute?: NoneToVoidFunction; handleMute?: NoneToVoidFunction;
handleChatFolderChange: NoneToVoidFunction; handleChatFolderChange: NoneToVoidFunction;
@ -149,8 +151,9 @@ const useChatContextActions = ({
return compact([actionOpenInNewTab, actionPin, actionAddToFolder, actionMute]) as MenuItemContextAction[]; return compact([actionOpenInNewTab, actionPin, actionAddToFolder, actionMute]) as MenuItemContextAction[];
} }
const actionMaskAsRead = (chat.unreadCount || chat.hasUnreadMark) const actionMaskAsRead = (
? { title: lang('MarkAsRead'), icon: 'readchats', handler: () => markChatMessagesRead({ id: chat.id }) } chat.unreadCount || chat.hasUnreadMark || Object.values(topics || {}).some(({ unreadCount }) => unreadCount)
) ? { title: lang('MarkAsRead'), icon: 'readchats', handler: () => markChatMessagesRead({ id: chat.id }) }
: undefined; : undefined;
const actionMarkAsUnread = !(chat.unreadCount || chat.hasUnreadMark) && !chat.isForum const actionMarkAsUnread = !(chat.unreadCount || chat.hasUnreadMark) && !chat.isForum
? { title: lang('MarkAsUnread'), icon: 'unread', handler: () => markChatUnread({ id: chat.id }) } ? { title: lang('MarkAsUnread'), icon: 'unread', handler: () => markChatUnread({ id: chat.id }) }
@ -181,7 +184,7 @@ const useChatContextActions = ({
}, [ }, [
chat, user, canChangeFolder, lang, handleChatFolderChange, isPinned, isInSearch, isMuted, currentUserId, chat, user, canChangeFolder, lang, handleChatFolderChange, isPinned, isInSearch, isMuted, currentUserId,
handleDelete, handleMute, handleReport, folderId, isSelf, isServiceNotifications, isSavedDialog, deleteTitle, handleDelete, handleMute, handleReport, folderId, isSelf, isServiceNotifications, isSavedDialog, deleteTitle,
isPreview, isPreview, topics,
]); ]);
}; };

View File

@ -278,9 +278,10 @@ function checkIfShouldNotify(chat: ApiChat, message: Partial<ApiMessage>) {
const topic = selectTopicFromMessage(global, message as ApiMessage); const topic = selectTopicFromMessage(global, message as ApiMessage);
const topicMutedUntil = topic?.notifySettings.mutedUntil; const topicMutedUntil = topic?.notifySettings.mutedUntil;
const isMuted = topicMutedUntil === undefined ? isChatMuted : topicMutedUntil > getServerTime(); const isMuted = topicMutedUntil === undefined ? isChatMuted : topicMutedUntil > getServerTime();
const shouldIgnoreMute = message.isMentioned;
const shouldNotifyAboutMessage = message.content?.action?.type !== 'phoneCall'; const shouldNotifyAboutMessage = message.content?.action?.type !== 'phoneCall';
if (isMuted || !shouldNotifyAboutMessage if ((isMuted && !shouldIgnoreMute) || !shouldNotifyAboutMessage
|| chat.isNotJoined || !chat.isListed || selectIsChatWithSelf(global, chat.id)) { || chat.isNotJoined || !chat.isListed || selectIsChatWithSelf(global, chat.id)) {
return false; return false;
} }