Notification: Fix sound playback conditions (#5734)
This commit is contained in:
parent
37eb65344c
commit
a65d6e0697
@ -24,13 +24,14 @@ import type {
|
||||
} from '../../types';
|
||||
|
||||
import { pick, pickTruthy } from '../../../util/iteratees';
|
||||
import { getServerTime, getServerTimeOffset } from '../../../util/serverTime';
|
||||
import { getServerTimeOffset } from '../../../util/serverTime';
|
||||
import { addPhotoToLocalDb, addUserToLocalDb } from '../helpers/localDb';
|
||||
import { serializeBytes } from '../helpers/misc';
|
||||
import {
|
||||
buildApiBotVerification, buildApiFormattedText, buildApiPhoto, buildApiUsernames, buildAvatarPhotoId,
|
||||
} from './common';
|
||||
import { omitVirtualClassFields } from './helpers';
|
||||
import { buildApiPeerNotifySettings } from './misc';
|
||||
import {
|
||||
buildApiEmojiStatus,
|
||||
buildApiPeerColor,
|
||||
@ -119,10 +120,8 @@ export function buildApiChatFromDialog(
|
||||
): ApiChat {
|
||||
const {
|
||||
peer, folderId, unreadMark, unreadCount, unreadMentionsCount, unreadReactionsCount,
|
||||
notifySettings: { silent, muteUntil },
|
||||
readOutboxMaxId, readInboxMaxId, draft, viewForumAsMessages,
|
||||
} = dialog;
|
||||
const isMuted = silent || (typeof muteUntil === 'number' && getServerTime() < muteUntil);
|
||||
|
||||
return {
|
||||
id: getApiChatIdFromMtpPeer(peer),
|
||||
@ -134,8 +133,6 @@ export function buildApiChatFromDialog(
|
||||
unreadCount,
|
||||
unreadMentionsCount,
|
||||
unreadReactionsCount,
|
||||
isMuted,
|
||||
muteUntil,
|
||||
...(unreadMark && { hasUnreadMark: true }),
|
||||
...(draft instanceof GramJs.DraftMessage && { draftDate: draft.date }),
|
||||
...(viewForumAsMessages && { isForumAsMessages: true }),
|
||||
@ -579,9 +576,7 @@ export function buildApiTopic(forumTopic: GramJs.TypeForumTopic): ApiTopic | und
|
||||
unreadMentionsCount,
|
||||
unreadReactionsCount,
|
||||
fromId,
|
||||
notifySettings: {
|
||||
silent, muteUntil,
|
||||
},
|
||||
notifySettings,
|
||||
} = forumTopic;
|
||||
|
||||
return {
|
||||
@ -600,8 +595,7 @@ export function buildApiTopic(forumTopic: GramJs.TypeForumTopic): ApiTopic | und
|
||||
unreadMentionsCount,
|
||||
unreadReactionsCount,
|
||||
fromId: getApiChatIdFromMtpPeer(fromId),
|
||||
isMuted: silent || (typeof muteUntil === 'number' ? getServerTime() < muteUntil : undefined),
|
||||
muteUntil,
|
||||
notifySettings: buildApiPeerNotifySettings(notifySettings),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -163,6 +163,7 @@ export function buildApiMessageFromNotification(
|
||||
chatId: SERVICE_NOTIFICATIONS_USER_ID,
|
||||
date: notification.inboxDate || currentDate,
|
||||
content,
|
||||
isInvertedMedia: notification.invertMedia,
|
||||
isOutgoing: false,
|
||||
};
|
||||
}
|
||||
|
||||
@ -8,6 +8,7 @@ import type {
|
||||
ApiLanguage,
|
||||
ApiOldLangString,
|
||||
ApiPeerColors,
|
||||
ApiPeerNotifySettings,
|
||||
ApiPrivacyKey,
|
||||
ApiSession,
|
||||
ApiTimezone,
|
||||
@ -21,7 +22,6 @@ import { numberToHexColor } from '../../../util/colors';
|
||||
import {
|
||||
buildCollectionByCallback, omit, omitUndefined, pick,
|
||||
} from '../../../util/iteratees';
|
||||
import { getServerTime } from '../../../util/serverTime';
|
||||
import { addUserToLocalDb } from '../helpers/localDb';
|
||||
import { omitVirtualClassFields } from './helpers';
|
||||
import { buildApiDocument, buildMessageTextContent } from './messageContent';
|
||||
@ -106,40 +106,20 @@ export function buildPrivacyKey(key: GramJs.TypePrivacyKey): ApiPrivacyKey | und
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function buildApiNotifyException(
|
||||
notifySettings: GramJs.TypePeerNotifySettings, peer: GramJs.TypePeer,
|
||||
) {
|
||||
export function buildApiPeerNotifySettings(
|
||||
notifySettings: GramJs.TypePeerNotifySettings,
|
||||
): ApiPeerNotifySettings {
|
||||
const {
|
||||
silent, muteUntil, showPreviews, otherSound,
|
||||
} = notifySettings;
|
||||
|
||||
const hasSound = Boolean(otherSound && !(otherSound instanceof GramJs.NotificationSoundNone));
|
||||
const hasSound = !(otherSound instanceof GramJs.NotificationSoundNone);
|
||||
|
||||
return {
|
||||
chatId: getApiChatIdFromMtpPeer(peer),
|
||||
isMuted: silent || (typeof muteUntil === 'number' && getServerTime() < muteUntil),
|
||||
...(!hasSound && { isSilent: true }),
|
||||
...(showPreviews !== undefined && { shouldShowPreviews: Boolean(showPreviews) }),
|
||||
muteUntil,
|
||||
};
|
||||
}
|
||||
|
||||
export function buildApiNotifyExceptionTopic(
|
||||
notifySettings: GramJs.TypePeerNotifySettings, peer: GramJs.TypePeer, topicId: number,
|
||||
) {
|
||||
const {
|
||||
silent, muteUntil, showPreviews, otherSound,
|
||||
} = notifySettings;
|
||||
|
||||
const hasSound = Boolean(otherSound && !(otherSound instanceof GramJs.NotificationSoundNone));
|
||||
|
||||
return {
|
||||
chatId: getApiChatIdFromMtpPeer(peer),
|
||||
topicId,
|
||||
isMuted: silent || (typeof muteUntil === 'number' && getServerTime() < muteUntil),
|
||||
...(!hasSound && { isSilent: true }),
|
||||
...(showPreviews !== undefined && { shouldShowPreviews: Boolean(showPreviews) }),
|
||||
muteUntil,
|
||||
hasSound,
|
||||
isSilentPosting: silent,
|
||||
mutedUntil: muteUntil,
|
||||
shouldShowPreviews: showPreviews,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@ -14,6 +14,7 @@ import type {
|
||||
ApiMessage,
|
||||
ApiMissingInvitedUser,
|
||||
ApiPeer,
|
||||
ApiPeerNotifySettings,
|
||||
ApiPhoto,
|
||||
ApiTopic,
|
||||
ApiUser,
|
||||
@ -31,7 +32,7 @@ import {
|
||||
SERVICE_NOTIFICATIONS_USER_ID,
|
||||
TOPICS_SLICE,
|
||||
} from '../../../config';
|
||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||
import { buildCollectionByKey, omitUndefined } from '../../../util/iteratees';
|
||||
import {
|
||||
buildApiChatBotCommands,
|
||||
buildApiChatFolder,
|
||||
@ -52,6 +53,7 @@ import {
|
||||
} from '../apiBuilders/chats';
|
||||
import { buildApiBotVerification, buildApiPhoto } from '../apiBuilders/common';
|
||||
import { buildApiMessage, buildMessageDraft } from '../apiBuilders/messages';
|
||||
import { buildApiPeerNotifySettings } from '../apiBuilders/misc';
|
||||
import { buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
||||
import { buildStickerSet } from '../apiBuilders/symbols';
|
||||
import { buildApiUser, buildApiUserStatuses } from '../apiBuilders/users';
|
||||
@ -96,6 +98,7 @@ type ChatListData = {
|
||||
orderedPinnedIds: string[] | undefined;
|
||||
totalChatCount: number;
|
||||
messages: ApiMessage[];
|
||||
notifyExceptionById: Record<string, ApiPeerNotifySettings>;
|
||||
lastMessageByChatId: Record<string, number>;
|
||||
nextOffsetId?: number;
|
||||
nextOffsetPeerId?: string;
|
||||
@ -150,6 +153,7 @@ export async function fetchChats({
|
||||
|
||||
const chats: ApiChat[] = [];
|
||||
const draftsById: Record<string, ApiDraft> = {};
|
||||
const notifyExceptionById: Record<string, ApiPeerNotifySettings> = {};
|
||||
|
||||
const dialogs = (resultPinned?.dialogs || []).concat(result.dialogs);
|
||||
|
||||
@ -186,7 +190,12 @@ export async function fetchChats({
|
||||
|
||||
chats.push(chat);
|
||||
|
||||
scheduleMutedChatUpdate(chat.id, chat.muteUntil, sendApiUpdate);
|
||||
const notifySettings = buildApiPeerNotifySettings(dialog.notifySettings);
|
||||
if (Object.values(omitUndefined(notifySettings)).length) {
|
||||
notifyExceptionById[chat.id] = notifySettings;
|
||||
|
||||
scheduleMutedChatUpdate(chat.id, notifySettings.mutedUntil, sendApiUpdate);
|
||||
}
|
||||
|
||||
if (withPinned && dialog.pinned) {
|
||||
orderedPinnedIds.push(chat.id);
|
||||
@ -229,6 +238,7 @@ export async function fetchChats({
|
||||
totalChatCount,
|
||||
lastMessageByChatId,
|
||||
messages,
|
||||
notifyExceptionById,
|
||||
nextOffsetId,
|
||||
nextOffsetPeerId,
|
||||
nextOffsetDate,
|
||||
@ -327,6 +337,7 @@ export async function fetchSavedChats({
|
||||
lastMessageByChatId,
|
||||
messages,
|
||||
draftsById: {},
|
||||
notifyExceptionById: {},
|
||||
nextOffsetId,
|
||||
nextOffsetPeerId,
|
||||
nextOffsetDate,
|
||||
@ -474,7 +485,9 @@ export async function requestChatUpdate({
|
||||
|
||||
applyState(result.state);
|
||||
|
||||
scheduleMutedChatUpdate(chatUpdate.id, chatUpdate.muteUntil, sendApiUpdate);
|
||||
const notifySettings = buildApiPeerNotifySettings(dialog.notifySettings);
|
||||
|
||||
scheduleMutedChatUpdate(chatUpdate.id, notifySettings.mutedUntil, sendApiUpdate);
|
||||
}
|
||||
|
||||
export function saveDraft({
|
||||
@ -722,25 +735,27 @@ async function getFullChannelInfo(
|
||||
};
|
||||
}
|
||||
|
||||
export async function updateChatMutedState({
|
||||
chat, isMuted, muteUntil = 0,
|
||||
export function updateChatMutedState({
|
||||
chat, isMuted, mutedUntil = 0,
|
||||
}: {
|
||||
chat: ApiChat; isMuted: boolean; muteUntil?: number;
|
||||
chat: ApiChat; isMuted?: boolean; mutedUntil?: number;
|
||||
}) {
|
||||
if (isMuted && !muteUntil) {
|
||||
muteUntil = MAX_INT_32;
|
||||
if (isMuted && !mutedUntil) {
|
||||
mutedUntil = MAX_INT_32;
|
||||
}
|
||||
await invokeRequest(new GramJs.account.UpdateNotifySettings({
|
||||
invokeRequest(new GramJs.account.UpdateNotifySettings({
|
||||
peer: new GramJs.InputNotifyPeer({
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
}),
|
||||
settings: new GramJs.InputPeerNotifySettings({ muteUntil }),
|
||||
settings: new GramJs.InputPeerNotifySettings({ muteUntil: mutedUntil }),
|
||||
}));
|
||||
|
||||
sendApiUpdate({
|
||||
'@type': 'updateNotifyExceptions',
|
||||
'@type': 'updateChatNotifySettings',
|
||||
chatId: chat.id,
|
||||
isMuted,
|
||||
settings: {
|
||||
mutedUntil,
|
||||
},
|
||||
});
|
||||
|
||||
void requestChatUpdate({
|
||||
@ -749,27 +764,29 @@ export async function updateChatMutedState({
|
||||
});
|
||||
}
|
||||
|
||||
export async function updateTopicMutedState({
|
||||
chat, topicId, isMuted, muteUntil = 0,
|
||||
export function updateTopicMutedState({
|
||||
chat, topicId, isMuted, mutedUntil = 0,
|
||||
}: {
|
||||
chat: ApiChat; topicId: number; isMuted: boolean; muteUntil?: number;
|
||||
chat: ApiChat; topicId: number; isMuted?: boolean; mutedUntil?: number;
|
||||
}) {
|
||||
if (isMuted && !muteUntil) {
|
||||
muteUntil = MAX_INT_32;
|
||||
if (isMuted && !mutedUntil) {
|
||||
mutedUntil = MAX_INT_32;
|
||||
}
|
||||
await invokeRequest(new GramJs.account.UpdateNotifySettings({
|
||||
invokeRequest(new GramJs.account.UpdateNotifySettings({
|
||||
peer: new GramJs.InputNotifyForumTopic({
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
topMsgId: topicId,
|
||||
}),
|
||||
settings: new GramJs.InputPeerNotifySettings({ muteUntil }),
|
||||
settings: new GramJs.InputPeerNotifySettings({ muteUntil: mutedUntil }),
|
||||
}));
|
||||
|
||||
sendApiUpdate({
|
||||
'@type': 'updateTopicNotifyExceptions',
|
||||
'@type': 'updateTopicNotifySettings',
|
||||
chatId: chat.id,
|
||||
topicId,
|
||||
isMuted,
|
||||
settings: {
|
||||
mutedUntil,
|
||||
},
|
||||
});
|
||||
|
||||
// TODO[forums] Request forum topic thread update
|
||||
|
||||
@ -1834,11 +1834,14 @@ export async function reportSponsoredMessage({
|
||||
|
||||
export async function readAllMentions({
|
||||
chat,
|
||||
threadId,
|
||||
}: {
|
||||
chat: ApiChat;
|
||||
threadId?: ThreadId;
|
||||
}) {
|
||||
const result = await invokeRequest(new GramJs.messages.ReadMentions({
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
topMsgId: threadId ? Number(threadId) : undefined,
|
||||
}));
|
||||
|
||||
if (!result) return;
|
||||
@ -1846,17 +1849,20 @@ export async function readAllMentions({
|
||||
processAffectedHistory(chat, result);
|
||||
|
||||
if (result.offset) {
|
||||
await readAllMentions({ chat });
|
||||
await readAllMentions({ chat, threadId });
|
||||
}
|
||||
}
|
||||
|
||||
export async function readAllReactions({
|
||||
chat,
|
||||
threadId,
|
||||
}: {
|
||||
chat: ApiChat;
|
||||
threadId?: ThreadId;
|
||||
}) {
|
||||
const result = await invokeRequest(new GramJs.messages.ReadReactions({
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
topMsgId: threadId ? Number(threadId) : undefined,
|
||||
}));
|
||||
|
||||
if (!result) return;
|
||||
@ -1864,14 +1870,15 @@ export async function readAllReactions({
|
||||
processAffectedHistory(chat, result);
|
||||
|
||||
if (result.offset) {
|
||||
await readAllReactions({ chat });
|
||||
await readAllReactions({ chat, threadId });
|
||||
}
|
||||
}
|
||||
|
||||
export async function fetchUnreadMentions({
|
||||
chat, ...pagination
|
||||
chat, threadId, ...pagination
|
||||
}: {
|
||||
chat: ApiChat;
|
||||
threadId?: ThreadId;
|
||||
offsetId?: number;
|
||||
addOffset?: number;
|
||||
maxId?: number;
|
||||
@ -1879,6 +1886,7 @@ export async function fetchUnreadMentions({
|
||||
}) {
|
||||
const result = await invokeRequest(new GramJs.messages.GetUnreadMentions({
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
topMsgId: threadId ? Number(threadId) : undefined,
|
||||
limit: MENTION_UNREAD_SLICE,
|
||||
...pagination,
|
||||
}));
|
||||
@ -1899,9 +1907,10 @@ export async function fetchUnreadMentions({
|
||||
}
|
||||
|
||||
export async function fetchUnreadReactions({
|
||||
chat, ...pagination
|
||||
chat, threadId, ...pagination
|
||||
}: {
|
||||
chat: ApiChat;
|
||||
threadId?: ThreadId;
|
||||
offsetId?: number;
|
||||
addOffset?: number;
|
||||
maxId?: number;
|
||||
@ -1909,6 +1918,7 @@ export async function fetchUnreadReactions({
|
||||
}) {
|
||||
const result = await invokeRequest(new GramJs.messages.GetUnreadReactions({
|
||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||
topMsgId: threadId ? Number(threadId) : undefined,
|
||||
limit: REACTION_UNREAD_SLICE,
|
||||
...pagination,
|
||||
}));
|
||||
|
||||
@ -8,7 +8,8 @@ import type {
|
||||
ApiConfig,
|
||||
ApiInputPrivacyRules,
|
||||
ApiLanguage,
|
||||
ApiNotifyException,
|
||||
ApiNotifyPeerType,
|
||||
ApiPeerNotifySettings,
|
||||
ApiPhoto,
|
||||
ApiPrivacyKey,
|
||||
ApiUser,
|
||||
@ -21,15 +22,14 @@ import {
|
||||
MAX_INT_32,
|
||||
} from '../../../config';
|
||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||
import { getServerTime } from '../../../util/serverTime';
|
||||
import { buildAppConfig } from '../apiBuilders/appConfig';
|
||||
import { buildApiPhoto, buildPrivacyRules } from '../apiBuilders/common';
|
||||
import {
|
||||
buildApiConfig,
|
||||
buildApiCountryList,
|
||||
buildApiLanguage,
|
||||
buildApiNotifyException,
|
||||
buildApiPeerColors,
|
||||
buildApiPeerNotifySettings,
|
||||
buildApiSession,
|
||||
buildApiTimezone,
|
||||
buildApiWallpaper,
|
||||
@ -323,20 +323,26 @@ export async function fetchNotificationExceptions() {
|
||||
return acc;
|
||||
}
|
||||
|
||||
acc.push(buildApiNotifyException(update.notifySettings, update.peer.peer));
|
||||
const peerId = getApiChatIdFromMtpPeer(update.peer.peer);
|
||||
|
||||
acc[peerId] = buildApiPeerNotifySettings(update.notifySettings);
|
||||
|
||||
return acc;
|
||||
}, [] as ApiNotifyException[]);
|
||||
}, {} as Record<string, ApiPeerNotifySettings>);
|
||||
}
|
||||
|
||||
export async function fetchNotificationSettings() {
|
||||
export async function fetchContactSignUpSetting() {
|
||||
const hasContactJoinedNotifications = await invokeRequest(new GramJs.account.GetContactSignUpNotification());
|
||||
|
||||
return hasContactJoinedNotifications;
|
||||
}
|
||||
|
||||
export async function fetchNotifyDefaultSettings() {
|
||||
const [
|
||||
isMutedContactSignUpNotification,
|
||||
privateContactNotificationsSettings,
|
||||
groupNotificationsSettings,
|
||||
broadcastNotificationsSettings,
|
||||
usersSettings,
|
||||
groupsSettings,
|
||||
channelsSettings,
|
||||
] = await Promise.all([
|
||||
invokeRequest(new GramJs.account.GetContactSignUpNotification()),
|
||||
invokeRequest(new GramJs.account.GetNotifySettings({
|
||||
peer: new GramJs.InputNotifyUsers(),
|
||||
})),
|
||||
@ -348,37 +354,14 @@ export async function fetchNotificationSettings() {
|
||||
})),
|
||||
]);
|
||||
|
||||
if (!privateContactNotificationsSettings || !groupNotificationsSettings || !broadcastNotificationsSettings) {
|
||||
return false;
|
||||
if (!usersSettings || !groupsSettings || !channelsSettings) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const {
|
||||
silent: privateSilent, muteUntil: privateMuteUntil, showPreviews: privateShowPreviews,
|
||||
} = privateContactNotificationsSettings;
|
||||
const {
|
||||
silent: groupSilent, muteUntil: groupMuteUntil, showPreviews: groupShowPreviews,
|
||||
} = groupNotificationsSettings;
|
||||
const {
|
||||
silent: broadcastSilent, muteUntil: broadcastMuteUntil, showPreviews: broadcastShowPreviews,
|
||||
} = broadcastNotificationsSettings;
|
||||
|
||||
return {
|
||||
hasContactJoinedNotifications: !isMutedContactSignUpNotification,
|
||||
hasPrivateChatsNotifications: !(
|
||||
privateSilent
|
||||
|| (typeof privateMuteUntil === 'number' && getServerTime() < privateMuteUntil)
|
||||
),
|
||||
hasPrivateChatsMessagePreview: privateShowPreviews,
|
||||
hasGroupNotifications: !(
|
||||
groupSilent || (typeof groupMuteUntil === 'number'
|
||||
&& getServerTime() < groupMuteUntil)
|
||||
),
|
||||
hasGroupMessagePreview: groupShowPreviews,
|
||||
hasBroadcastNotifications: !(
|
||||
broadcastSilent || (typeof broadcastMuteUntil === 'number'
|
||||
&& getServerTime() < broadcastMuteUntil)
|
||||
),
|
||||
hasBroadcastMessagePreview: broadcastShowPreviews,
|
||||
users: buildApiPeerNotifySettings(usersSettings),
|
||||
groups: buildApiPeerNotifySettings(groupsSettings),
|
||||
channels: buildApiPeerNotifySettings(channelsSettings),
|
||||
};
|
||||
}
|
||||
|
||||
@ -386,17 +369,17 @@ export function updateContactSignUpNotification(isSilent: boolean) {
|
||||
return invokeRequest(new GramJs.account.SetContactSignUpNotification({ silent: isSilent }));
|
||||
}
|
||||
|
||||
export function updateNotificationSettings(peerType: 'contact' | 'group' | 'broadcast', {
|
||||
isSilent,
|
||||
export function updateNotificationSettings(peerType: ApiNotifyPeerType, {
|
||||
isMuted,
|
||||
shouldShowPreviews,
|
||||
}: {
|
||||
isSilent?: boolean;
|
||||
isMuted?: boolean;
|
||||
shouldShowPreviews?: boolean;
|
||||
}) {
|
||||
let peer: GramJs.TypeInputNotifyPeer;
|
||||
if (peerType === 'contact') {
|
||||
if (peerType === 'users') {
|
||||
peer = new GramJs.InputNotifyUsers();
|
||||
} else if (peerType === 'group') {
|
||||
} else if (peerType === 'groups') {
|
||||
peer = new GramJs.InputNotifyChats();
|
||||
} else {
|
||||
peer = new GramJs.InputNotifyBroadcasts();
|
||||
@ -404,8 +387,7 @@ export function updateNotificationSettings(peerType: 'contact' | 'group' | 'broa
|
||||
|
||||
const settings = {
|
||||
showPreviews: shouldShowPreviews,
|
||||
silent: isSilent,
|
||||
muteUntil: isSilent ? MAX_INT_32 : 0,
|
||||
muteUntil: isMuted ? MAX_INT_32 : 0,
|
||||
};
|
||||
|
||||
return invokeRequest(new GramJs.account.UpdateNotifySettings({
|
||||
|
||||
@ -3,7 +3,7 @@ import type { OnApiUpdate } from '../types';
|
||||
import { MAX_INT_32 } from '../../config';
|
||||
import { getServerTime } from '../../util/serverTime';
|
||||
|
||||
type UnmuteQueueItem = { chatId: string; topicId?: number; muteUntil: number };
|
||||
type UnmuteQueueItem = { chatId: string; topicId?: number; mutedUntil: number };
|
||||
const unmuteTimers = new Map<string, any>();
|
||||
const unmuteQueue: Array<UnmuteQueueItem> = [];
|
||||
const scheduleUnmute = (item: UnmuteQueueItem, onUpdate: NoneToVoidFunction) => {
|
||||
@ -12,9 +12,9 @@ const scheduleUnmute = (item: UnmuteQueueItem, onUpdate: NoneToVoidFunction) =>
|
||||
clearTimeout(unmuteTimers.get(id));
|
||||
unmuteTimers.delete(id);
|
||||
}
|
||||
if (item.muteUntil === MAX_INT_32 || item.muteUntil <= getServerTime()) return;
|
||||
if (item.mutedUntil === MAX_INT_32 || item.mutedUntil <= getServerTime()) return;
|
||||
unmuteQueue.push(item);
|
||||
unmuteQueue.sort((a, b) => b.muteUntil - a.muteUntil);
|
||||
unmuteQueue.sort((a, b) => b.mutedUntil - a.mutedUntil);
|
||||
const next = unmuteQueue.pop();
|
||||
if (!next) return;
|
||||
const timer = setTimeout(() => {
|
||||
@ -23,30 +23,34 @@ const scheduleUnmute = (item: UnmuteQueueItem, onUpdate: NoneToVoidFunction) =>
|
||||
const afterNext = unmuteQueue.pop();
|
||||
if (afterNext) scheduleUnmute(afterNext, onUpdate);
|
||||
}
|
||||
}, (item.muteUntil - getServerTime()) * 1000);
|
||||
}, (item.mutedUntil - getServerTime()) * 1000);
|
||||
unmuteTimers.set(id, timer);
|
||||
};
|
||||
|
||||
export function scheduleMutedChatUpdate(chatId: string, muteUntil = 0, onUpdate: OnApiUpdate) {
|
||||
export function scheduleMutedChatUpdate(chatId: string, mutedUntil = 0, onUpdate: OnApiUpdate) {
|
||||
scheduleUnmute({
|
||||
chatId,
|
||||
muteUntil,
|
||||
mutedUntil,
|
||||
}, () => onUpdate({
|
||||
'@type': 'updateNotifyExceptions',
|
||||
'@type': 'updateChatNotifySettings',
|
||||
chatId,
|
||||
isMuted: false,
|
||||
settings: {
|
||||
mutedUntil: 0,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
export function scheduleMutedTopicUpdate(chatId: string, topicId: number, muteUntil = 0, onUpdate: OnApiUpdate) {
|
||||
export function scheduleMutedTopicUpdate(chatId: string, topicId: number, mutedUntil = 0, onUpdate: OnApiUpdate) {
|
||||
scheduleUnmute({
|
||||
chatId,
|
||||
topicId,
|
||||
muteUntil,
|
||||
mutedUntil,
|
||||
}, () => onUpdate({
|
||||
'@type': 'updateTopicNotifyExceptions',
|
||||
'@type': 'updateTopicNotifySettings',
|
||||
chatId,
|
||||
topicId,
|
||||
isMuted: false,
|
||||
settings: {
|
||||
mutedUntil: 0,
|
||||
},
|
||||
}));
|
||||
}
|
||||
|
||||
@ -47,8 +47,7 @@ import {
|
||||
buildMessageDraft,
|
||||
} from '../apiBuilders/messages';
|
||||
import {
|
||||
buildApiNotifyException,
|
||||
buildApiNotifyExceptionTopic,
|
||||
buildApiPeerNotifySettings,
|
||||
buildLangStrings,
|
||||
buildPrivacyKey,
|
||||
} from '../apiBuilders/misc';
|
||||
@ -631,28 +630,6 @@ export function updater(update: Update) {
|
||||
messageIds: update.messages,
|
||||
isPinned: update.pinned,
|
||||
});
|
||||
} else if (
|
||||
update instanceof GramJs.UpdateNotifySettings
|
||||
&& update.peer instanceof GramJs.NotifyPeer
|
||||
) {
|
||||
const payload = buildApiNotifyException(update.notifySettings, update.peer.peer);
|
||||
scheduleMutedChatUpdate(payload.chatId, payload.muteUntil, sendApiUpdate);
|
||||
sendApiUpdate({
|
||||
'@type': 'updateNotifyExceptions',
|
||||
...payload,
|
||||
});
|
||||
} else if (
|
||||
update instanceof GramJs.UpdateNotifySettings
|
||||
&& update.peer instanceof GramJs.NotifyForumTopic
|
||||
) {
|
||||
const payload = buildApiNotifyExceptionTopic(
|
||||
update.notifySettings, update.peer.peer, update.peer.topMsgId,
|
||||
);
|
||||
scheduleMutedTopicUpdate(payload.chatId, payload.topicId, payload.muteUntil, sendApiUpdate);
|
||||
sendApiUpdate({
|
||||
'@type': 'updateTopicNotifyExceptions',
|
||||
...payload,
|
||||
});
|
||||
} else if (
|
||||
update instanceof GramJs.UpdateUserTyping
|
||||
|| update instanceof GramJs.UpdateChatUserTyping
|
||||
@ -837,18 +814,41 @@ export function updater(update: Update) {
|
||||
// Settings
|
||||
} else if (update instanceof GramJs.UpdateNotifySettings) {
|
||||
const {
|
||||
notifySettings: {
|
||||
showPreviews, silent, muteUntil,
|
||||
},
|
||||
peer: { className },
|
||||
notifySettings,
|
||||
peer: notifyPeer,
|
||||
} = update;
|
||||
const className = notifyPeer.className;
|
||||
const settings = buildApiPeerNotifySettings(notifySettings);
|
||||
|
||||
if (notifyPeer instanceof GramJs.NotifyPeer) {
|
||||
const peerId = getApiChatIdFromMtpPeer(notifyPeer.peer);
|
||||
scheduleMutedChatUpdate(peerId, settings.mutedUntil, sendApiUpdate);
|
||||
sendApiUpdate({
|
||||
'@type': 'updateChatNotifySettings',
|
||||
chatId: peerId,
|
||||
settings,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (notifyPeer instanceof GramJs.NotifyForumTopic) {
|
||||
const peerId = getApiChatIdFromMtpPeer(notifyPeer.peer);
|
||||
scheduleMutedTopicUpdate(peerId, notifyPeer.topMsgId, settings.mutedUntil, sendApiUpdate);
|
||||
sendApiUpdate({
|
||||
'@type': 'updateTopicNotifySettings',
|
||||
chatId: peerId,
|
||||
topicId: notifyPeer.topMsgId,
|
||||
settings,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const peerType = className === 'NotifyUsers'
|
||||
? 'contact'
|
||||
? 'users'
|
||||
: (className === 'NotifyChats'
|
||||
? 'group'
|
||||
? 'groups'
|
||||
: (className === 'NotifyBroadcasts'
|
||||
? 'broadcast'
|
||||
? 'channels'
|
||||
: undefined
|
||||
)
|
||||
);
|
||||
@ -858,11 +858,9 @@ export function updater(update: Update) {
|
||||
}
|
||||
|
||||
sendApiUpdate({
|
||||
'@type': 'updateNotifySettings',
|
||||
'@type': 'updateDefaultNotifySettings',
|
||||
peerType,
|
||||
isSilent: Boolean(silent
|
||||
|| (typeof muteUntil === 'number' && Date.now() + getServerTimeOffset() * 1000 < muteUntil * 1000)),
|
||||
shouldShowPreviews: Boolean(showPreviews),
|
||||
settings,
|
||||
});
|
||||
} else if (update instanceof GramJs.UpdatePeerBlocked) {
|
||||
sendApiUpdate({
|
||||
|
||||
@ -2,7 +2,7 @@ import type { ApiBotCommand } from './bots';
|
||||
import type {
|
||||
ApiChatReactions, ApiFormattedText, ApiInputMessageReplyInfo, ApiPhoto, ApiStickerSet,
|
||||
} from './messages';
|
||||
import type { ApiBotVerification, ApiChatInviteImporter } from './misc';
|
||||
import type { ApiBotVerification, ApiChatInviteImporter, ApiPeerNotifySettings } from './misc';
|
||||
import type {
|
||||
ApiEmojiStatusType, ApiFakeType, ApiUser, ApiUsername,
|
||||
} from './users';
|
||||
@ -27,8 +27,6 @@ export interface ApiChat {
|
||||
unreadMentionsCount?: number;
|
||||
unreadReactionsCount?: number;
|
||||
isVerified?: true;
|
||||
isMuted?: boolean;
|
||||
muteUntil?: number;
|
||||
areSignaturesShown?: boolean;
|
||||
areProfilesShown?: boolean;
|
||||
hasPrivateLink?: boolean;
|
||||
@ -262,8 +260,7 @@ export interface ApiTopic {
|
||||
unreadMentionsCount: number;
|
||||
unreadReactionsCount: number;
|
||||
fromId: string;
|
||||
isMuted?: boolean;
|
||||
muteUntil?: number;
|
||||
notifySettings: ApiPeerNotifySettings;
|
||||
}
|
||||
|
||||
export interface ApiChatlistInviteNew {
|
||||
|
||||
@ -106,13 +106,6 @@ export interface ApiSessionData {
|
||||
isTest?: true;
|
||||
}
|
||||
|
||||
export type ApiNotifyException = {
|
||||
chatId: string;
|
||||
isMuted: boolean;
|
||||
isSilent?: boolean;
|
||||
shouldShowPreviews?: boolean;
|
||||
};
|
||||
|
||||
export type ApiNotification = {
|
||||
localId: string;
|
||||
containerSelector?: string;
|
||||
@ -352,3 +345,12 @@ export type ApiLimitTypeWithModal = Exclude<ApiLimitType, (
|
||||
export type ApiLimitTypeForPromo = Exclude<ApiLimitType,
|
||||
'uploadMaxFileparts' | 'chatlistInvites' | 'chatlistJoined' | 'savedDialogsPinned'
|
||||
>;
|
||||
|
||||
export type ApiPeerNotifySettings = {
|
||||
mutedUntil?: number;
|
||||
hasSound?: boolean;
|
||||
isSilentPosting?: boolean;
|
||||
shouldShowPreviews?: boolean;
|
||||
};
|
||||
|
||||
export type ApiNotifyPeerType = 'users' | 'groups' | 'channels';
|
||||
|
||||
@ -35,7 +35,11 @@ import type {
|
||||
BoughtPaidMedia,
|
||||
} from './messages';
|
||||
import type {
|
||||
ApiEmojiInteraction, ApiError, ApiNotifyException, ApiSessionData,
|
||||
ApiEmojiInteraction,
|
||||
ApiError,
|
||||
ApiNotifyPeerType,
|
||||
ApiPeerNotifySettings,
|
||||
ApiSessionData,
|
||||
} from './misc';
|
||||
import type { ApiStarsAmount } from './payments';
|
||||
import type { ApiPrivacyKey, LangPackStringValue, PrivacyVisibility } from './settings';
|
||||
@ -501,21 +505,24 @@ export type ApiUpdateTwoFaError = {
|
||||
messageKey: RegularLangFnParameters;
|
||||
};
|
||||
|
||||
export type ApiUpdateNotifySettings = {
|
||||
'@type': 'updateNotifySettings';
|
||||
peerType: 'contact' | 'group' | 'broadcast';
|
||||
isSilent: boolean;
|
||||
shouldShowPreviews: boolean;
|
||||
export type ApiUpdateDefaultNotifySettings = {
|
||||
'@type': 'updateDefaultNotifySettings';
|
||||
peerType: ApiNotifyPeerType;
|
||||
settings: Partial<ApiPeerNotifySettings>;
|
||||
};
|
||||
|
||||
export type ApiUpdateNotifyExceptions = {
|
||||
'@type': 'updateNotifyExceptions';
|
||||
} & ApiNotifyException;
|
||||
export type ApiUpdatePeerNotifySettings = {
|
||||
'@type': 'updateChatNotifySettings';
|
||||
chatId: string;
|
||||
settings: Partial<ApiPeerNotifySettings>;
|
||||
};
|
||||
|
||||
export type ApiUpdateTopicNotifyExceptions = {
|
||||
'@type': 'updateTopicNotifyExceptions';
|
||||
export type ApiUpdateTopicNotifySettings = {
|
||||
'@type': 'updateTopicNotifySettings';
|
||||
chatId: string;
|
||||
topicId: number;
|
||||
} & ApiNotifyException;
|
||||
settings: Partial<ApiPeerNotifySettings>;
|
||||
};
|
||||
|
||||
export type ApiUpdateTwoFaStateWaitCode = {
|
||||
'@type': 'updateTwoFaStateWaitCode';
|
||||
@ -823,14 +830,14 @@ export type ApiUpdate = (
|
||||
ApiUpdateScheduledMessageSendSucceeded | ApiUpdateScheduledMessage | ApiUpdateStarPaymentStateCompleted |
|
||||
ApiUpdateDeleteScheduledMessages | ApiUpdateResetMessages | ApiUpdateMessageTranslations |
|
||||
ApiUpdateTwoFaError | ApiUpdateTwoFaStateWaitCode | ApiUpdateWebViewResultSent |
|
||||
ApiUpdateNotifySettings | ApiUpdateNotifyExceptions | ApiUpdatePeerBlocked | ApiUpdatePrivacy |
|
||||
ApiUpdateDefaultNotifySettings | ApiUpdatePeerNotifySettings | ApiUpdatePeerBlocked | ApiUpdatePrivacy |
|
||||
ApiUpdateServerTimeOffset | ApiUpdateMessageReactions | ApiUpdateSavedReactionTags |
|
||||
ApiUpdateGroupCallParticipants | ApiUpdateGroupCallConnection | ApiUpdateGroupCall | ApiUpdateGroupCallStreams |
|
||||
ApiUpdateGroupCallConnectionState | ApiUpdateGroupCallLeavePresentation | ApiUpdateGroupCallChatId |
|
||||
ApiUpdatePendingJoinRequests | ApiUpdatePaymentVerificationNeeded | ApiUpdatePaymentStateCompleted |
|
||||
ApiUpdatePhoneCall | ApiUpdatePhoneCallSignalingData | ApiUpdatePhoneCallMediaState |
|
||||
ApiUpdatePhoneCallConnectionState | ApiUpdateBotMenuButton | ApiUpdateTranscribedAudio | ApiUpdateUserEmojiStatus |
|
||||
ApiUpdateMessageExtendedMedia | ApiUpdateConfig | ApiUpdateTopicNotifyExceptions | ApiUpdatePinnedTopic |
|
||||
ApiUpdateMessageExtendedMedia | ApiUpdateConfig | ApiUpdateTopicNotifySettings | ApiUpdatePinnedTopic |
|
||||
ApiUpdatePinnedTopicsOrder | ApiUpdateTopic | ApiUpdateTopics | ApiUpdateRecentEmojiStatuses |
|
||||
ApiUpdateRecentReactions | ApiUpdateStory | ApiUpdateReadStories | ApiUpdateDeleteStory | ApiUpdateSentStoryReaction |
|
||||
ApiRequestReconnectApi | ApiRequestSync | ApiUpdateFetchingDifference | ApiUpdateChannelMessages |
|
||||
|
||||
@ -22,15 +22,15 @@ import {
|
||||
getHasAdminRight,
|
||||
isChatChannel,
|
||||
isUserRightBanned,
|
||||
selectIsChatMuted,
|
||||
} from '../../../global/helpers';
|
||||
import { getIsChatMuted } from '../../../global/helpers/notifications';
|
||||
import {
|
||||
selectBotAppPermissions,
|
||||
selectChat,
|
||||
selectChatFullInfo,
|
||||
selectCurrentMessageList,
|
||||
selectNotifyExceptions,
|
||||
selectNotifySettings,
|
||||
selectNotifyDefaults,
|
||||
selectNotifyException,
|
||||
selectTopicLink,
|
||||
selectUser,
|
||||
selectUserFullInfo,
|
||||
@ -419,7 +419,7 @@ const ChatExtra: FC<OwnProps & StateProps> = ({
|
||||
<Switcher
|
||||
id="group-notifications"
|
||||
label={userId ? 'Toggle User Notifications' : 'Toggle Chat Notifications'}
|
||||
checked={isMuted}
|
||||
checked={!isMuted}
|
||||
inactive
|
||||
/>
|
||||
</ListItem>
|
||||
@ -487,7 +487,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const user = chatOrUserId ? selectUser(global, chatOrUserId) : undefined;
|
||||
const botAppPermissions = chatOrUserId ? selectBotAppPermissions(global, chatOrUserId) : undefined;
|
||||
const isForum = chat?.isForum;
|
||||
const isMuted = chat && selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global));
|
||||
const isMuted = chat && getIsChatMuted(chat, selectNotifyDefaults(global), selectNotifyException(global, chat.id));
|
||||
const { threadId } = selectCurrentMessageList(global) || {};
|
||||
const topicId = isForum && threadId ? Number(threadId) : undefined;
|
||||
|
||||
|
||||
@ -51,16 +51,16 @@ const MuteChatModal: FC<OwnProps> = ({
|
||||
], [lang]);
|
||||
|
||||
const handleSubmit = useCallback(() => {
|
||||
let muteUntil: number;
|
||||
let mutedUntil: number;
|
||||
if (muteUntilOption === MuteDuration.Forever) {
|
||||
muteUntil = MAX_INT_32;
|
||||
mutedUntil = MAX_INT_32;
|
||||
} else {
|
||||
muteUntil = Math.floor(Date.now() / 1000) + Number(muteUntilOption);
|
||||
mutedUntil = Math.floor(Date.now() / 1000) + Number(muteUntilOption);
|
||||
}
|
||||
if (topicId) {
|
||||
updateTopicMutedState({ chatId, topicId, muteUntil });
|
||||
updateTopicMutedState({ chatId, topicId, mutedUntil });
|
||||
} else {
|
||||
updateChatMutedState({ chatId, muteUntil });
|
||||
updateChatMutedState({ chatId, mutedUntil });
|
||||
}
|
||||
onClose();
|
||||
}, [chatId, muteUntilOption, onClose, topicId]);
|
||||
|
||||
@ -23,8 +23,8 @@ import {
|
||||
groupStatefulContent,
|
||||
isUserId,
|
||||
isUserOnline,
|
||||
selectIsChatMuted,
|
||||
} from '../../../global/helpers';
|
||||
import { getIsChatMuted } from '../../../global/helpers/notifications';
|
||||
import {
|
||||
selectCanAnimateInterface,
|
||||
selectChat,
|
||||
@ -35,8 +35,8 @@ import {
|
||||
selectDraft,
|
||||
selectIsForumPanelClosed,
|
||||
selectIsForumPanelOpen,
|
||||
selectNotifyExceptions,
|
||||
selectNotifySettings,
|
||||
selectNotifyDefaults,
|
||||
selectNotifyException,
|
||||
selectOutgoingStatus,
|
||||
selectPeer,
|
||||
selectPeerStory,
|
||||
@ -470,7 +470,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
return {
|
||||
chat,
|
||||
isMuted: selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global)),
|
||||
isMuted: getIsChatMuted(chat, selectNotifyDefaults(global), selectNotifyException(global, chat.id)),
|
||||
lastMessageSender,
|
||||
draft: selectDraft(global, chatId, MAIN_THREAD_ID),
|
||||
isSelected,
|
||||
|
||||
@ -6,6 +6,7 @@ import type { ApiChat, ApiTopic } from '../../../api/types';
|
||||
import type { Signal } from '../../../util/signals';
|
||||
|
||||
import buildClassName from '../../../util/buildClassName';
|
||||
import { getServerTime } from '../../../util/serverTime';
|
||||
import { isSignal } from '../../../util/signals';
|
||||
import { formatIntegerCompact } from '../../../util/textFormat';
|
||||
import { extractCurrentThemeParams } from '../../../util/themeStyle';
|
||||
@ -62,20 +63,29 @@ const ChatBadge: FC<OwnProps> = ({
|
||||
isForum && topics ? Object.values(topics).filter(({ unreadCount }) => unreadCount) : undefined
|
||||
), [topics, isForum]);
|
||||
|
||||
const unreadCount = useMemo(() => (
|
||||
isForum
|
||||
// If we have unmuted topics, display the count of those. Otherwise, display the count of all topics.
|
||||
? ((isMuted && topicsWithUnread?.filter((acc) => acc.isMuted === false).length)
|
||||
|| topicsWithUnread?.length)
|
||||
: (topic || chat).unreadCount
|
||||
), [chat, topic, topicsWithUnread, isForum, isMuted]);
|
||||
const unreadCount = useMemo(() => {
|
||||
if (!isForum) {
|
||||
return (topic || chat).unreadCount;
|
||||
}
|
||||
|
||||
const shouldBeMuted = useMemo(() => {
|
||||
const hasUnmutedUnreadTopics = topics
|
||||
&& Object.values(topics).some((acc) => !acc.isMuted && acc.unreadCount);
|
||||
return topicsWithUnread?.length;
|
||||
}, [chat, topic, topicsWithUnread, isForum]);
|
||||
|
||||
return isMuted || (topics && !hasUnmutedUnreadTopics);
|
||||
}, [topics, isMuted]);
|
||||
const shouldBeUnMuted = useMemo(() => {
|
||||
if (!isForum) {
|
||||
return !isMuted || topic?.notifySettings.mutedUntil === 0;
|
||||
}
|
||||
|
||||
if (isMuted) {
|
||||
return topicsWithUnread?.some((acc) => acc.notifySettings.mutedUntil === 0);
|
||||
}
|
||||
|
||||
const isEveryUnreadMuted = topicsWithUnread?.every((acc) => (
|
||||
acc.notifySettings.mutedUntil && acc.notifySettings.mutedUntil > getServerTime()
|
||||
));
|
||||
|
||||
return !isEveryUnreadMuted;
|
||||
}, [isForum, isMuted, topicsWithUnread, topic?.notifySettings.mutedUntil]);
|
||||
|
||||
const hasUnreadMark = topic ? false : chat.hasUnreadMark;
|
||||
|
||||
@ -91,7 +101,7 @@ const ChatBadge: FC<OwnProps> = ({
|
||||
const isUnread = Boolean((unreadCount || hasUnreadMark) && !isSavedDialog);
|
||||
const className = buildClassName(
|
||||
'ChatBadge',
|
||||
shouldBeMuted && 'muted',
|
||||
!shouldBeUnMuted && 'muted',
|
||||
!isUnread && isPinned && 'pinned',
|
||||
isUnread && 'unread',
|
||||
);
|
||||
@ -109,7 +119,7 @@ const ChatBadge: FC<OwnProps> = ({
|
||||
|
||||
function renderContent() {
|
||||
const unreadReactionsElement = unreadReactionsCount && (
|
||||
<div className={buildClassName('ChatBadge reaction', shouldBeMuted && 'muted')}>
|
||||
<div className={buildClassName('ChatBadge reaction', !shouldBeUnMuted && 'muted')}>
|
||||
<Icon name="heart" />
|
||||
</div>
|
||||
);
|
||||
@ -121,7 +131,7 @@ const ChatBadge: FC<OwnProps> = ({
|
||||
);
|
||||
|
||||
const unopenedTopicElement = isTopicUnopened && (
|
||||
<div className={buildClassName('ChatBadge unopened', shouldBeMuted && 'muted')} />
|
||||
<div className={buildClassName('ChatBadge unopened', !shouldBeUnMuted && 'muted')} />
|
||||
);
|
||||
|
||||
const unreadCountElement = (hasUnreadMark || unreadCount) ? (
|
||||
|
||||
@ -10,6 +10,7 @@ import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
||||
import type { ChatAnimationTypes } from './hooks';
|
||||
|
||||
import { groupStatefulContent } from '../../../global/helpers';
|
||||
import { getIsChatMuted } from '../../../global/helpers/notifications';
|
||||
import {
|
||||
selectCanAnimateInterface,
|
||||
selectCanDeleteTopic,
|
||||
@ -17,6 +18,8 @@ import {
|
||||
selectChatMessage,
|
||||
selectCurrentMessageList,
|
||||
selectDraft,
|
||||
selectNotifyDefaults,
|
||||
selectNotifyException,
|
||||
selectOutgoingStatus,
|
||||
selectPeerStory,
|
||||
selectSender,
|
||||
@ -57,6 +60,7 @@ type OwnProps = {
|
||||
|
||||
type StateProps = {
|
||||
chat: ApiChat;
|
||||
isChatMuted?: boolean;
|
||||
canDelete?: boolean;
|
||||
lastMessage?: ApiMessage;
|
||||
lastMessageStory?: ApiTypeStory;
|
||||
@ -75,6 +79,7 @@ const Topic: FC<OwnProps & StateProps> = ({
|
||||
isSelected,
|
||||
chatId,
|
||||
chat,
|
||||
isChatMuted,
|
||||
style,
|
||||
lastMessage,
|
||||
lastMessageStory,
|
||||
@ -106,9 +111,9 @@ const Topic: FC<OwnProps & StateProps> = ({
|
||||
const [shouldRenderMuteModal, markRenderMuteModal, unmarkRenderMuteModal] = useFlag();
|
||||
|
||||
const {
|
||||
isPinned, isClosed,
|
||||
isPinned, isClosed, notifySettings,
|
||||
} = topic;
|
||||
const isMuted = topic.isMuted || (topic.isMuted === undefined && chat.isMuted);
|
||||
const isMuted = Boolean(notifySettings.mutedUntil || (notifySettings.mutedUntil === undefined && isChatMuted));
|
||||
|
||||
const handleOpenDeleteModal = useLastCallback(() => {
|
||||
markRenderDeleteModal();
|
||||
@ -154,6 +159,7 @@ const Topic: FC<OwnProps & StateProps> = ({
|
||||
const contextActions = useTopicContextActions({
|
||||
topic,
|
||||
chat,
|
||||
isChatMuted,
|
||||
wasOpened: wasTopicOpened,
|
||||
canDelete,
|
||||
handleDelete: handleOpenDeleteModal,
|
||||
@ -181,7 +187,7 @@ const Topic: FC<OwnProps & StateProps> = ({
|
||||
<TopicIcon topic={topic} className={styles.topicIcon} observeIntersection={observeIntersection} />
|
||||
<h3 dir="auto" className="fullName">{renderText(topic.title)}</h3>
|
||||
</div>
|
||||
{topic.isMuted && <Icon name="muted" />}
|
||||
{Boolean(notifySettings.mutedUntil) && <Icon name="muted" />}
|
||||
<div className="separator" />
|
||||
{isClosed && (
|
||||
<Icon name="lock-badge" className={styles.closedIcon} />
|
||||
@ -247,11 +253,16 @@ export default memo(withGlobal<OwnProps>(
|
||||
const storyData = lastMessage?.content.storyData;
|
||||
const lastMessageStory = storyData && selectPeerStory(global, storyData.peerId, storyData.id);
|
||||
|
||||
const isChatMuted = chat && getIsChatMuted(
|
||||
chat, selectNotifyDefaults(global), selectNotifyException(global, chat.id),
|
||||
);
|
||||
|
||||
return {
|
||||
chat,
|
||||
lastMessage,
|
||||
lastMessageSender,
|
||||
typingStatus,
|
||||
isChatMuted,
|
||||
canDelete: selectCanDeleteTopic(global, chatId, topic.id),
|
||||
withInterfaceAnimations: selectCanAnimateInterface(global),
|
||||
draft,
|
||||
|
||||
@ -13,6 +13,7 @@ import useOldLang from '../../../../hooks/useOldLang';
|
||||
export default function useTopicContextActions({
|
||||
topic,
|
||||
chat,
|
||||
isChatMuted,
|
||||
wasOpened,
|
||||
canDelete,
|
||||
handleDelete,
|
||||
@ -20,6 +21,7 @@ export default function useTopicContextActions({
|
||||
}: {
|
||||
topic: ApiTopic;
|
||||
chat: ApiChat;
|
||||
isChatMuted?: boolean;
|
||||
wasOpened?: boolean;
|
||||
canDelete?: boolean;
|
||||
handleDelete?: NoneToVoidFunction;
|
||||
@ -29,7 +31,7 @@ export default function useTopicContextActions({
|
||||
|
||||
return useMemo(() => {
|
||||
const {
|
||||
isPinned, isMuted, isClosed, id: topicId,
|
||||
isPinned, notifySettings, isClosed, id: topicId,
|
||||
} = topic;
|
||||
|
||||
const chatId = chat.id;
|
||||
@ -75,7 +77,7 @@ export default function useTopicContextActions({
|
||||
handler: () => toggleTopicPinned({ chatId, topicId, isPinned: true }),
|
||||
}) : undefined;
|
||||
|
||||
const actionMute = ((chat.isMuted && isMuted !== false) || isMuted === true)
|
||||
const actionMute = ((isChatMuted && notifySettings.mutedUntil === undefined) || notifySettings.mutedUntil)
|
||||
? {
|
||||
title: lang('ChatList.Unmute'),
|
||||
icon: 'unmute',
|
||||
@ -114,5 +116,5 @@ export default function useTopicContextActions({
|
||||
actionCloseTopic,
|
||||
actionDelete,
|
||||
]) as MenuItemContextAction[];
|
||||
}, [topic, chat, wasOpened, lang, canDelete, handleDelete, handleMute]);
|
||||
}, [topic, chat, isChatMuted, wasOpened, lang, canDelete, handleDelete, handleMute]);
|
||||
}
|
||||
|
||||
@ -5,10 +5,10 @@ import { getActions, withGlobal } from '../../../global';
|
||||
import type { ApiChat, ApiUser } from '../../../api/types';
|
||||
import { StoryViewerOrigin } from '../../../types';
|
||||
|
||||
import { isUserId, selectIsChatMuted } from '../../../global/helpers';
|
||||
import { isUserId } from '../../../global/helpers';
|
||||
import { getIsChatMuted } from '../../../global/helpers/notifications';
|
||||
import {
|
||||
selectChat, selectIsChatPinned, selectNotifyExceptions,
|
||||
selectNotifySettings, selectUser,
|
||||
selectChat, selectIsChatPinned, selectNotifyDefaults, selectNotifyException, selectUser,
|
||||
} from '../../../global/selectors';
|
||||
import { extractCurrentThemeParams } from '../../../util/themeStyle';
|
||||
|
||||
@ -156,9 +156,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const chat = selectChat(global, chatId);
|
||||
const user = selectUser(global, chatId);
|
||||
const isPinned = selectIsChatPinned(global, chatId);
|
||||
const isMuted = chat
|
||||
? selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global))
|
||||
: undefined;
|
||||
const isMuted = chat && getIsChatMuted(chat, selectNotifyDefaults(global), selectNotifyException(global, chat.id));
|
||||
|
||||
return {
|
||||
chat,
|
||||
|
||||
@ -3,6 +3,8 @@ import type { FC } from '../../../lib/teact/teact';
|
||||
import React, { memo, useCallback, useEffect } from '../../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../../global';
|
||||
|
||||
import type { ApiNotifyPeerType, ApiPeerNotifySettings } from '../../../api/types';
|
||||
|
||||
import {
|
||||
checkIfNotificationsSupported,
|
||||
checkIfOfflinePushFailed,
|
||||
@ -22,12 +24,7 @@ type OwnProps = {
|
||||
};
|
||||
|
||||
type StateProps = {
|
||||
hasPrivateChatsNotifications: boolean;
|
||||
hasPrivateChatsMessagePreview: boolean;
|
||||
hasGroupNotifications: boolean;
|
||||
hasGroupMessagePreview: boolean;
|
||||
hasBroadcastNotifications: boolean;
|
||||
hasBroadcastMessagePreview: boolean;
|
||||
notifyDefaults?: Record<ApiNotifyPeerType, ApiPeerNotifySettings>;
|
||||
hasContactJoinedNotifications: boolean;
|
||||
hasWebNotifications: boolean;
|
||||
hasPushNotifications: boolean;
|
||||
@ -37,12 +34,7 @@ type StateProps = {
|
||||
const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
isActive,
|
||||
onReset,
|
||||
hasPrivateChatsNotifications,
|
||||
hasPrivateChatsMessagePreview,
|
||||
hasGroupNotifications,
|
||||
hasGroupMessagePreview,
|
||||
hasBroadcastNotifications,
|
||||
hasBroadcastMessagePreview,
|
||||
notifyDefaults,
|
||||
hasContactJoinedNotifications,
|
||||
hasPushNotifications,
|
||||
hasWebNotifications,
|
||||
@ -66,27 +58,18 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
|
||||
const handleSettingsChange = useCallback((
|
||||
e: ChangeEvent<HTMLInputElement>,
|
||||
peerType: 'contact' | 'group' | 'broadcast',
|
||||
setting: 'silent' | 'showPreviews',
|
||||
peerType: ApiNotifyPeerType,
|
||||
setting: 'mute' | 'showPreviews',
|
||||
) => {
|
||||
const currentIsSilent = peerType === 'contact'
|
||||
? !hasPrivateChatsNotifications
|
||||
: !(peerType === 'group' ? hasGroupNotifications : hasBroadcastNotifications);
|
||||
const currentShouldShowPreviews = peerType === 'contact'
|
||||
? hasPrivateChatsMessagePreview
|
||||
: (peerType === 'group' ? hasGroupMessagePreview : hasBroadcastMessagePreview);
|
||||
const currentIsMuted = Boolean(notifyDefaults?.[peerType]?.mutedUntil);
|
||||
const currentShouldShowPreviews = Boolean(notifyDefaults?.[peerType]?.shouldShowPreviews);
|
||||
|
||||
updateNotificationSettings({
|
||||
peerType,
|
||||
...(setting === 'silent' && { isSilent: !e.target.checked, shouldShowPreviews: currentShouldShowPreviews }),
|
||||
...(setting === 'showPreviews' && { shouldShowPreviews: e.target.checked, isSilent: currentIsSilent }),
|
||||
isMuted: setting === 'mute' ? !e.target.checked : currentIsMuted,
|
||||
shouldShowPreviews: setting === 'showPreviews' ? e.target.checked : currentShouldShowPreviews,
|
||||
});
|
||||
}, [
|
||||
hasBroadcastMessagePreview, hasBroadcastNotifications,
|
||||
hasGroupMessagePreview, hasGroupNotifications,
|
||||
hasPrivateChatsMessagePreview, hasPrivateChatsNotifications,
|
||||
updateNotificationSettings,
|
||||
]);
|
||||
}, [notifyDefaults]);
|
||||
|
||||
const handleWebNotificationsChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
const isEnabled = e.target.checked;
|
||||
@ -103,27 +86,27 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
}, [updateWebNotificationSettings]);
|
||||
|
||||
const handlePrivateChatsNotificationsChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
handleSettingsChange(e, 'contact', 'silent');
|
||||
handleSettingsChange(e, 'users', 'mute');
|
||||
}, [handleSettingsChange]);
|
||||
|
||||
const handlePrivateChatsPreviewChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
handleSettingsChange(e, 'contact', 'showPreviews');
|
||||
handleSettingsChange(e, 'users', 'showPreviews');
|
||||
}, [handleSettingsChange]);
|
||||
|
||||
const handleGroupsNotificationsChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
handleSettingsChange(e, 'group', 'silent');
|
||||
handleSettingsChange(e, 'groups', 'mute');
|
||||
}, [handleSettingsChange]);
|
||||
|
||||
const handleGroupsPreviewChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
handleSettingsChange(e, 'group', 'showPreviews');
|
||||
handleSettingsChange(e, 'groups', 'showPreviews');
|
||||
}, [handleSettingsChange]);
|
||||
|
||||
const handleChannelsNotificationsChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
handleSettingsChange(e, 'broadcast', 'silent');
|
||||
handleSettingsChange(e, 'channels', 'mute');
|
||||
}, [handleSettingsChange]);
|
||||
|
||||
const handleChannelsPreviewChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
handleSettingsChange(e, 'broadcast', 'showPreviews');
|
||||
handleSettingsChange(e, 'channels', 'showPreviews');
|
||||
}, [handleSettingsChange]);
|
||||
|
||||
const handleContactNotificationChange = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
@ -186,17 +169,17 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
|
||||
<Checkbox
|
||||
label={lang('NotificationsForPrivateChats')}
|
||||
subLabel={lang(hasPrivateChatsNotifications
|
||||
subLabel={lang(notifyDefaults?.users?.mutedUntil
|
||||
? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
||||
checked={hasPrivateChatsNotifications}
|
||||
checked={Boolean(notifyDefaults?.users?.mutedUntil)}
|
||||
onChange={handlePrivateChatsNotificationsChange}
|
||||
/>
|
||||
<Checkbox
|
||||
label={lang('MessagePreview')}
|
||||
disabled={!hasPrivateChatsNotifications}
|
||||
subLabel={lang(hasPrivateChatsMessagePreview
|
||||
disabled={!notifyDefaults?.users?.mutedUntil}
|
||||
subLabel={lang(notifyDefaults?.users?.shouldShowPreviews
|
||||
? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
||||
checked={hasPrivateChatsMessagePreview}
|
||||
checked={Boolean(notifyDefaults?.users?.shouldShowPreviews)}
|
||||
onChange={handlePrivateChatsPreviewChange}
|
||||
/>
|
||||
</div>
|
||||
@ -206,15 +189,17 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
|
||||
<Checkbox
|
||||
label={lang('NotificationsForGroups')}
|
||||
subLabel={lang(hasGroupNotifications ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
||||
checked={hasGroupNotifications}
|
||||
subLabel={lang(notifyDefaults?.groups?.mutedUntil
|
||||
? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
||||
checked={Boolean(notifyDefaults?.groups?.mutedUntil)}
|
||||
onChange={handleGroupsNotificationsChange}
|
||||
/>
|
||||
<Checkbox
|
||||
label={lang('MessagePreview')}
|
||||
disabled={!hasGroupNotifications}
|
||||
subLabel={lang(hasGroupMessagePreview ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
||||
checked={hasGroupMessagePreview}
|
||||
disabled={!notifyDefaults?.groups?.mutedUntil}
|
||||
subLabel={lang(notifyDefaults?.groups?.shouldShowPreviews
|
||||
? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
||||
checked={Boolean(notifyDefaults?.groups?.shouldShowPreviews)}
|
||||
onChange={handleGroupsPreviewChange}
|
||||
/>
|
||||
</div>
|
||||
@ -224,15 +209,17 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
|
||||
<Checkbox
|
||||
label={lang('NotificationsForChannels')}
|
||||
subLabel={lang(hasBroadcastNotifications ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
||||
checked={hasBroadcastNotifications}
|
||||
subLabel={lang(notifyDefaults?.channels?.mutedUntil
|
||||
? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
||||
checked={Boolean(notifyDefaults?.channels?.mutedUntil)}
|
||||
onChange={handleChannelsNotificationsChange}
|
||||
/>
|
||||
<Checkbox
|
||||
label={lang('MessagePreview')}
|
||||
disabled={!hasBroadcastNotifications}
|
||||
subLabel={lang(hasBroadcastMessagePreview ? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
||||
checked={hasBroadcastMessagePreview}
|
||||
disabled={!notifyDefaults?.channels?.mutedUntil}
|
||||
subLabel={lang(notifyDefaults?.channels?.shouldShowPreviews
|
||||
? 'UserInfoNotificationsEnabled' : 'UserInfoNotificationsDisabled')}
|
||||
checked={Boolean(notifyDefaults?.channels?.shouldShowPreviews)}
|
||||
onChange={handleChannelsPreviewChange}
|
||||
/>
|
||||
</div>
|
||||
@ -253,12 +240,6 @@ const SettingsNotifications: FC<OwnProps & StateProps> = ({
|
||||
export default memo(withGlobal<OwnProps>(
|
||||
(global): StateProps => {
|
||||
return {
|
||||
hasPrivateChatsNotifications: Boolean(global.settings.byKey.hasPrivateChatsNotifications),
|
||||
hasPrivateChatsMessagePreview: Boolean(global.settings.byKey.hasPrivateChatsMessagePreview),
|
||||
hasGroupNotifications: Boolean(global.settings.byKey.hasGroupNotifications),
|
||||
hasGroupMessagePreview: Boolean(global.settings.byKey.hasGroupMessagePreview),
|
||||
hasBroadcastNotifications: Boolean(global.settings.byKey.hasBroadcastNotifications),
|
||||
hasBroadcastMessagePreview: Boolean(global.settings.byKey.hasBroadcastMessagePreview),
|
||||
hasContactJoinedNotifications: Boolean(global.settings.byKey.hasContactJoinedNotifications),
|
||||
hasWebNotifications: global.settings.byKey.hasWebNotifications,
|
||||
hasPushNotifications: global.settings.byKey.hasPushNotifications,
|
||||
|
||||
@ -2,7 +2,7 @@ import type { FC } from '../../lib/teact/teact';
|
||||
import React, { memo, useEffect, useRef } from '../../lib/teact/teact';
|
||||
import { getActions, withGlobal } from '../../global';
|
||||
|
||||
import type { MessageListType } from '../../types';
|
||||
import type { MessageListType, ThreadId } from '../../types';
|
||||
import { MAIN_THREAD_ID } from '../../api/types';
|
||||
|
||||
import { selectChat, selectCurrentMessageList, selectCurrentMiddleSearch } from '../../global/selectors';
|
||||
@ -24,6 +24,7 @@ type OwnProps = {
|
||||
type StateProps = {
|
||||
chatId?: string;
|
||||
messageListType?: MessageListType;
|
||||
threadId?: ThreadId;
|
||||
unreadCount?: number;
|
||||
unreadReactions?: number[];
|
||||
unreadMentions?: number[];
|
||||
@ -38,6 +39,7 @@ const FloatingActionButtons: FC<OwnProps & StateProps> = ({
|
||||
canPost,
|
||||
messageListType,
|
||||
chatId,
|
||||
threadId,
|
||||
unreadCount,
|
||||
unreadReactions,
|
||||
unreadMentions,
|
||||
@ -56,6 +58,16 @@ const FloatingActionButtons: FC<OwnProps & StateProps> = ({
|
||||
const hasUnreadReactions = Boolean(reactionsCount);
|
||||
const hasUnreadMentions = Boolean(mentionsCount);
|
||||
|
||||
const handleReadAllReactions = useLastCallback(() => {
|
||||
if (!chatId) return;
|
||||
readAllReactions({ chatId, threadId });
|
||||
});
|
||||
|
||||
const handleReadAllMentions = useLastCallback(() => {
|
||||
if (!chatId) return;
|
||||
readAllMentions({ chatId, threadId });
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (hasUnreadReactions && chatId && !unreadReactions?.length) {
|
||||
fetchUnreadReactions({ chatId });
|
||||
@ -120,7 +132,7 @@ const FloatingActionButtons: FC<OwnProps & StateProps> = ({
|
||||
icon="heart-outline"
|
||||
ariaLabelLang="AccDescrReactionMentionDown"
|
||||
onClick={focusNextReaction}
|
||||
onReadAll={readAllReactions}
|
||||
onReadAll={handleReadAllReactions}
|
||||
unreadCount={reactionsCount}
|
||||
className={buildClassName(
|
||||
styles.reactions,
|
||||
@ -133,7 +145,7 @@ const FloatingActionButtons: FC<OwnProps & StateProps> = ({
|
||||
icon="mention"
|
||||
ariaLabelLang="AccDescrMentionDown"
|
||||
onClick={focusNextMention}
|
||||
onReadAll={readAllMentions}
|
||||
onReadAll={handleReadAllMentions}
|
||||
unreadCount={mentionsCount}
|
||||
className={!hasUnreadMentions && styles.hidden}
|
||||
/>
|
||||
@ -166,6 +178,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
return {
|
||||
messageListType,
|
||||
chatId,
|
||||
threadId,
|
||||
reactionsCount: shouldShowCount ? chat.unreadReactionsCount : undefined,
|
||||
unreadReactions: shouldShowCount ? chat.unreadReactions : undefined,
|
||||
unreadMentions: shouldShowCount ? chat.unreadMentions : undefined,
|
||||
|
||||
@ -20,8 +20,8 @@ import {
|
||||
isSystemBot,
|
||||
isUserId,
|
||||
isUserRightBanned,
|
||||
selectIsChatMuted,
|
||||
} from '../../global/helpers';
|
||||
import { getIsChatMuted } from '../../global/helpers/notifications';
|
||||
import {
|
||||
selectBot,
|
||||
selectCanGift,
|
||||
@ -32,8 +32,8 @@ import {
|
||||
selectCurrentMessageList,
|
||||
selectIsChatWithSelf,
|
||||
selectIsRightColumnShown,
|
||||
selectNotifyExceptions,
|
||||
selectNotifySettings,
|
||||
selectNotifyDefaults,
|
||||
selectNotifyException,
|
||||
selectTabState,
|
||||
selectTopic,
|
||||
selectUser,
|
||||
@ -759,7 +759,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
|
||||
return {
|
||||
chat,
|
||||
isMuted: selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global)),
|
||||
isMuted: getIsChatMuted(chat, selectNotifyDefaults(global), selectNotifyException(global, chat.id)),
|
||||
isPrivate,
|
||||
isTopic: chat?.isForum && !isMainThread,
|
||||
isForum: chat?.isForum,
|
||||
|
||||
@ -86,7 +86,7 @@ export default function useMessageObservers(
|
||||
}
|
||||
|
||||
if (mentionIds.length) {
|
||||
markMentionsRead({ messageIds: mentionIds });
|
||||
markMentionsRead({ chatId, messageIds: mentionIds });
|
||||
}
|
||||
|
||||
if (reactionIds.length) {
|
||||
|
||||
@ -227,9 +227,9 @@ const ActionMessage = ({
|
||||
}
|
||||
|
||||
if (message.hasUnreadMention) {
|
||||
markMentionsRead({ messageIds: [id] });
|
||||
markMentionsRead({ chatId, messageIds: [id] });
|
||||
}
|
||||
}, [hasUnreadReaction, id, animateUnreadReaction, message.hasUnreadMention]);
|
||||
}, [hasUnreadReaction, chatId, id, animateUnreadReaction, message.hasUnreadMention]);
|
||||
|
||||
useEffect(() => {
|
||||
if (action.type !== 'giftPremium') return;
|
||||
|
||||
@ -869,9 +869,9 @@ const Message: FC<OwnProps & StateProps> = ({
|
||||
}
|
||||
|
||||
if (unreadMentionIds.length) {
|
||||
markMentionsRead({ messageIds: unreadMentionIds });
|
||||
markMentionsRead({ chatId, messageIds: unreadMentionIds });
|
||||
}
|
||||
}, [hasUnreadReaction, album, messageId, animateUnreadReaction, message.hasUnreadMention]);
|
||||
}, [hasUnreadReaction, album, chatId, messageId, animateUnreadReaction, message.hasUnreadMention]);
|
||||
|
||||
const albumLayout = useMemo(() => {
|
||||
return isAlbum
|
||||
|
||||
@ -9,11 +9,12 @@ import type { ApiPhoto, ApiUser } from '../../../api/types';
|
||||
import { ManagementProgress } from '../../../types';
|
||||
|
||||
import { SERVICE_NOTIFICATIONS_USER_ID } from '../../../config';
|
||||
import { isUserBot, selectIsChatMuted } from '../../../global/helpers';
|
||||
import { isUserBot } from '../../../global/helpers';
|
||||
import { getIsChatMuted } from '../../../global/helpers/notifications';
|
||||
import {
|
||||
selectChat,
|
||||
selectNotifyExceptions,
|
||||
selectNotifySettings,
|
||||
selectNotifyDefaults,
|
||||
selectNotifyException,
|
||||
selectTabState,
|
||||
selectUser,
|
||||
selectUserFullInfo,
|
||||
@ -296,7 +297,7 @@ export default memo(withGlobal<OwnProps>(
|
||||
const chat = selectChat(global, userId);
|
||||
const userFullInfo = selectUserFullInfo(global, userId);
|
||||
const { progress } = selectTabState(global).management;
|
||||
const isMuted = chat && selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global));
|
||||
const isMuted = chat && getIsChatMuted(chat, selectNotifyDefaults(global), selectNotifyException(global, chat.id));
|
||||
const personalPhoto = userFullInfo?.personalPhoto;
|
||||
const notPersonalPhoto = userFullInfo?.profilePhoto || userFullInfo?.fallbackPhoto;
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import type {
|
||||
ApiChat, ApiChatFolder, ApiChatlistExportedInvite,
|
||||
ApiChatMember, ApiError, ApiMissingInvitedUser,
|
||||
ApiTopic,
|
||||
} from '../../../api/types';
|
||||
import type { RequiredGlobalActions } from '../../index';
|
||||
import type {
|
||||
@ -60,6 +61,7 @@ import {
|
||||
addChatMembers,
|
||||
addChats,
|
||||
addMessages,
|
||||
addNotifyExceptions,
|
||||
addSimilarBots,
|
||||
addUsers,
|
||||
addUserStatuses,
|
||||
@ -221,7 +223,7 @@ addActionHandler('openChat', (global, actions, payload): ActionReturnType => {
|
||||
const chat = selectChat(global, id);
|
||||
|
||||
if (chat?.hasUnreadMark) {
|
||||
actions.toggleChatUnread({ id });
|
||||
actions.markChatRead({ id });
|
||||
}
|
||||
|
||||
const isChatOnlySummary = !selectChatLastMessageId(global, id);
|
||||
@ -623,32 +625,26 @@ addActionHandler('requestSavedDialogUpdate', async (global, actions, payload): P
|
||||
});
|
||||
|
||||
addActionHandler('updateChatMutedState', (global, actions, payload): ActionReturnType => {
|
||||
const { chatId, muteUntil = 0 } = payload;
|
||||
const { chatId, isMuted, mutedUntil } = payload;
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isMuted = payload.isMuted ?? muteUntil > 0;
|
||||
|
||||
global = updateChat(global, chatId, { isMuted });
|
||||
setGlobal(global);
|
||||
void callApi('updateChatMutedState', { chat, isMuted, muteUntil });
|
||||
void callApi('updateChatMutedState', { chat, isMuted, mutedUntil });
|
||||
});
|
||||
|
||||
addActionHandler('updateTopicMutedState', (global, actions, payload): ActionReturnType => {
|
||||
const { chatId, topicId, muteUntil = 0 } = payload;
|
||||
const {
|
||||
chatId, topicId, isMuted, mutedUntil,
|
||||
} = payload;
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat) {
|
||||
return;
|
||||
}
|
||||
|
||||
const isMuted = payload.isMuted ?? muteUntil > 0;
|
||||
|
||||
global = updateTopic(global, chatId, topicId, { isMuted });
|
||||
setGlobal(global);
|
||||
void callApi('updateTopicMutedState', {
|
||||
chat, topicId, isMuted, muteUntil,
|
||||
chat, topicId, isMuted, mutedUntil,
|
||||
});
|
||||
});
|
||||
|
||||
@ -1155,17 +1151,47 @@ addActionHandler('deleteChatFolder', async (global, actions, payload): Promise<v
|
||||
}
|
||||
});
|
||||
|
||||
addActionHandler('toggleChatUnread', (global, actions, payload): ActionReturnType => {
|
||||
addActionHandler('markChatUnread', (global, actions, payload): ActionReturnType => {
|
||||
const { id } = payload;
|
||||
const chat = selectChat(global, id);
|
||||
if (chat) {
|
||||
if (chat.unreadCount) {
|
||||
void callApi('markMessageListRead', { chat, threadId: MAIN_THREAD_ID });
|
||||
} else {
|
||||
void callApi('toggleDialogUnread', {
|
||||
chat,
|
||||
hasUnreadMark: !chat.hasUnreadMark,
|
||||
});
|
||||
if (!chat) return;
|
||||
void callApi('toggleDialogUnread', {
|
||||
chat,
|
||||
hasUnreadMark: !chat.hasUnreadMark,
|
||||
});
|
||||
});
|
||||
|
||||
addActionHandler('markChatRead', async (global, actions, payload): Promise<void> => {
|
||||
const { id } = payload;
|
||||
const chat = selectChat(global, id);
|
||||
if (!chat) return;
|
||||
if (!chat.isForum) {
|
||||
await callApi('markMessageListRead', { chat, threadId: MAIN_THREAD_ID });
|
||||
actions.readAllMentions({ chatId: id });
|
||||
actions.readAllReactions({ chatId: id });
|
||||
return;
|
||||
}
|
||||
|
||||
let hasMoreTopics = true;
|
||||
let lastTopic: ApiTopic | undefined;
|
||||
let processedCount = 0;
|
||||
|
||||
while (hasMoreTopics) {
|
||||
const result = await callApi('fetchTopics', {
|
||||
chat, offsetDate: lastTopic?.date, offsetTopicId: lastTopic?.id, offsetId: lastTopic?.lastMessageId, limit: 100,
|
||||
});
|
||||
|
||||
if (!result?.topics?.length) return;
|
||||
|
||||
result.topics.forEach((topic) => {
|
||||
if (!topic.unreadCount && !topic.unreadMentionsCount && !topic.unreadReactionsCount) return;
|
||||
actions.markTopicRead({ chatId: id, topicId: topic.id });
|
||||
});
|
||||
|
||||
lastTopic = result.topics[result.topics.length - 1];
|
||||
processedCount += result.topics.length;
|
||||
if (result.count <= processedCount) {
|
||||
hasMoreTopics = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -1185,6 +1211,8 @@ addActionHandler('markTopicRead', (global, actions, payload): ActionReturnType =
|
||||
threadId: topicId,
|
||||
maxId: lastTopicMessageId,
|
||||
});
|
||||
actions.readAllMentions({ chatId, threadId: topicId });
|
||||
actions.readAllReactions({ chatId, threadId: topicId });
|
||||
|
||||
global = getGlobal();
|
||||
global = updateTopic(global, chatId, topicId, {
|
||||
@ -2910,6 +2938,7 @@ async function loadChats(
|
||||
global = updateChatListSecondaryInfo(global, listType, result);
|
||||
global = replaceMessages(global, result.messages);
|
||||
global = updateChatsLastMessageId(global, result.lastMessageByChatId, listType);
|
||||
global = addNotifyExceptions(global, result.notifyExceptionById);
|
||||
|
||||
if (!shouldIgnorePagination) {
|
||||
global = replaceChatListLoadingParameters(
|
||||
|
||||
@ -1818,12 +1818,11 @@ async function fetchUnreadMentions<T extends GlobalState>(global: T, chatId: str
|
||||
}
|
||||
|
||||
addActionHandler('markMentionsRead', (global, actions, payload): ActionReturnType => {
|
||||
const { messageIds, tabId = getCurrentTabId() } = payload;
|
||||
|
||||
const chat = selectCurrentChat(global, tabId);
|
||||
const { chatId, messageIds, tabId = getCurrentTabId() } = payload;
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat) return;
|
||||
|
||||
global = removeUnreadMentions(global, chat.id, chat, messageIds, true);
|
||||
global = removeUnreadMentions(global, chatId, chat, messageIds, true);
|
||||
setGlobal(global);
|
||||
|
||||
actions.markMessagesRead({ messageIds, tabId });
|
||||
@ -1848,17 +1847,22 @@ addActionHandler('focusNextMention', async (global, actions, payload): Promise<v
|
||||
});
|
||||
|
||||
addActionHandler('readAllMentions', (global, actions, payload): ActionReturnType => {
|
||||
const { tabId = getCurrentTabId() } = payload || {};
|
||||
const { chatId, threadId = MAIN_THREAD_ID } = payload;
|
||||
|
||||
const chat = selectCurrentChat(global, tabId);
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat) return undefined;
|
||||
|
||||
callApi('readAllMentions', { chat });
|
||||
callApi('readAllMentions', { chat, threadId: threadId === MAIN_THREAD_ID ? undefined : threadId });
|
||||
|
||||
return updateChat(global, chat.id, {
|
||||
unreadMentionsCount: undefined,
|
||||
unreadMentions: undefined,
|
||||
});
|
||||
if (threadId === MAIN_THREAD_ID) {
|
||||
return updateChat(global, chat.id, {
|
||||
unreadMentionsCount: undefined,
|
||||
unreadMentions: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
// TODO[Forums]: Support mentions in threads
|
||||
return undefined;
|
||||
});
|
||||
|
||||
addActionHandler('openUrl', (global, actions, payload): ActionReturnType => {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { ApiError, ApiReaction, ApiReactionEmoji } from '../../../api/types';
|
||||
import type { ActionReturnType } from '../../types';
|
||||
import { ApiMediaFormat } from '../../../api/types';
|
||||
import { ApiMediaFormat, MAIN_THREAD_ID } from '../../../api/types';
|
||||
|
||||
import { GENERAL_REFETCH_INTERVAL } from '../../../config';
|
||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||
@ -557,16 +557,21 @@ addActionHandler('focusNextReaction', (global, actions, payload): ActionReturnTy
|
||||
});
|
||||
|
||||
addActionHandler('readAllReactions', (global, actions, payload): ActionReturnType => {
|
||||
const { tabId = getCurrentTabId() } = payload || {};
|
||||
const chat = selectCurrentChat(global, tabId);
|
||||
const { chatId, threadId = MAIN_THREAD_ID } = payload;
|
||||
const chat = selectChat(global, chatId);
|
||||
if (!chat) return undefined;
|
||||
|
||||
callApi('readAllReactions', { chat });
|
||||
callApi('readAllReactions', { chat, threadId: threadId === MAIN_THREAD_ID ? undefined : threadId });
|
||||
|
||||
return updateUnreadReactions(global, chat.id, {
|
||||
unreadReactionsCount: undefined,
|
||||
unreadReactions: undefined,
|
||||
});
|
||||
if (threadId === MAIN_THREAD_ID) {
|
||||
return updateUnreadReactions(global, chat.id, {
|
||||
unreadReactionsCount: undefined,
|
||||
unreadReactions: undefined,
|
||||
});
|
||||
}
|
||||
|
||||
// TODO[Forums]: Support unread reactions in threads
|
||||
return undefined;
|
||||
});
|
||||
|
||||
addActionHandler('loadTopReactions', async (global): Promise<void> => {
|
||||
|
||||
@ -5,7 +5,7 @@ import {
|
||||
UPLOADING_WALLPAPER_SLUG,
|
||||
} from '../../../types';
|
||||
|
||||
import { APP_CONFIG_REFETCH_INTERVAL, COUNTRIES_WITH_12H_TIME_FORMAT } from '../../../config';
|
||||
import { APP_CONFIG_REFETCH_INTERVAL, COUNTRIES_WITH_12H_TIME_FORMAT, MAX_INT_32 } from '../../../config';
|
||||
import { getCurrentTabId } from '../../../util/establishMultitabRole';
|
||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||
import { requestPermission, subscribe, unsubscribe } from '../../../util/notifications';
|
||||
@ -16,9 +16,9 @@ import { callApi } from '../../../api/gramjs';
|
||||
import { buildApiInputPrivacyRules } from '../../helpers';
|
||||
import { addActionHandler, getGlobal, setGlobal } from '../../index';
|
||||
import {
|
||||
addBlockedUser, addNotifyExceptions, deletePeerPhoto,
|
||||
addBlockedUser, addNotifyException, addNotifyExceptions, deletePeerPhoto,
|
||||
removeBlockedUser, replaceSettings, updateChat,
|
||||
updateNotifySettings, updateUser, updateUserFullInfo,
|
||||
updateUser, updateUserFullInfo,
|
||||
} from '../../reducers';
|
||||
import { updateTabState } from '../../reducers/tabs';
|
||||
import {
|
||||
@ -306,26 +306,40 @@ addActionHandler('loadNotificationExceptions', async (global): Promise<void> =>
|
||||
});
|
||||
|
||||
addActionHandler('loadNotificationSettings', async (global): Promise<void> => {
|
||||
const result = await callApi('fetchNotificationSettings');
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
const [signUpNotification, notifyDefaults] = await Promise.all([
|
||||
callApi('fetchContactSignUpSetting'),
|
||||
callApi('fetchNotifyDefaultSettings'),
|
||||
]);
|
||||
|
||||
if (!notifyDefaults) return;
|
||||
|
||||
global = getGlobal();
|
||||
global = replaceSettings(global, result);
|
||||
global = replaceSettings(global, {
|
||||
hasContactJoinedNotifications: signUpNotification,
|
||||
});
|
||||
global = {
|
||||
...global,
|
||||
settings: {
|
||||
...global.settings,
|
||||
notifyDefaults,
|
||||
},
|
||||
};
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
addActionHandler('updateNotificationSettings', async (global, actions, payload): Promise<void> => {
|
||||
const { peerType, isSilent, shouldShowPreviews } = payload!;
|
||||
const { peerType, isMuted, shouldShowPreviews } = payload!;
|
||||
|
||||
const result = await callApi('updateNotificationSettings', peerType, { isSilent, shouldShowPreviews });
|
||||
const result = await callApi('updateNotificationSettings', peerType, { isMuted, shouldShowPreviews });
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
global = getGlobal();
|
||||
global = updateNotifySettings(global, peerType, isSilent, shouldShowPreviews);
|
||||
global = addNotifyException(global, peerType, {
|
||||
mutedUntil: isMuted ? MAX_INT_32 : undefined,
|
||||
shouldShowPreviews,
|
||||
});
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
@ -582,12 +596,11 @@ addActionHandler('loadCountryList', async (global, actions, payload): Promise<vo
|
||||
setGlobal(global);
|
||||
});
|
||||
|
||||
addActionHandler('ensureTimeFormat', async (global, actions, payload): Promise<void> => {
|
||||
const { tabId = getCurrentTabId() } = payload || {};
|
||||
addActionHandler('ensureTimeFormat', async (global, actions): Promise<void> => {
|
||||
if (global.authNearestCountry) {
|
||||
const timeFormat = COUNTRIES_WITH_12H_TIME_FORMAT
|
||||
.has(global.authNearestCountry.toUpperCase()) ? '12h' : '24h';
|
||||
actions.setSettingOption({ timeFormat, tabId });
|
||||
actions.setSettingOption({ timeFormat });
|
||||
setTimeFormat(timeFormat);
|
||||
}
|
||||
|
||||
@ -598,7 +611,7 @@ addActionHandler('ensureTimeFormat', async (global, actions, payload): Promise<v
|
||||
const nearestCountryCode = await callApi('fetchNearestCountry');
|
||||
if (nearestCountryCode) {
|
||||
const timeFormat = COUNTRIES_WITH_12H_TIME_FORMAT.has(nearestCountryCode.toUpperCase()) ? '12h' : '24h';
|
||||
actions.setSettingOption({ timeFormat, tabId });
|
||||
actions.setSettingOption({ timeFormat });
|
||||
setTimeFormat(timeFormat);
|
||||
}
|
||||
});
|
||||
|
||||
@ -2,37 +2,33 @@ import type { ActionReturnType } from '../../types';
|
||||
|
||||
import { addActionHandler, setGlobal } from '../../index';
|
||||
import {
|
||||
addNotifyException, updateChat, updateNotifySettings,
|
||||
addNotifyException,
|
||||
updateNotifyDefaults,
|
||||
updateTopic,
|
||||
} from '../../reducers';
|
||||
|
||||
addActionHandler('apiUpdate', (global, actions, update): ActionReturnType => {
|
||||
switch (update['@type']) {
|
||||
case 'updateNotifySettings': {
|
||||
return updateNotifySettings(global, update.peerType, update.isSilent, update.shouldShowPreviews);
|
||||
case 'updateDefaultNotifySettings': {
|
||||
return updateNotifyDefaults(global, update.peerType, update.settings);
|
||||
}
|
||||
|
||||
case 'updateNotifyExceptions': {
|
||||
case 'updateChatNotifySettings': {
|
||||
const {
|
||||
chatId, isMuted, isSilent, shouldShowPreviews,
|
||||
chatId, settings,
|
||||
} = update;
|
||||
const chat = global.chats.byId[chatId];
|
||||
|
||||
if (chat) {
|
||||
global = updateChat(global, chatId, { isMuted });
|
||||
}
|
||||
|
||||
global = addNotifyException(global, chatId, { isMuted, isSilent, shouldShowPreviews });
|
||||
global = addNotifyException(global, chatId, settings);
|
||||
setGlobal(global);
|
||||
break;
|
||||
}
|
||||
|
||||
case 'updateTopicNotifyExceptions': {
|
||||
case 'updateTopicNotifySettings': {
|
||||
const {
|
||||
chatId, topicId, isMuted,
|
||||
chatId, topicId, settings,
|
||||
} = update;
|
||||
|
||||
global = updateTopic(global, chatId, topicId, { isMuted });
|
||||
global = updateTopic(global, chatId, topicId, { notifySettings: settings });
|
||||
|
||||
setGlobal(global);
|
||||
break;
|
||||
|
||||
@ -24,7 +24,10 @@ import { replaceSettings } from '../../reducers';
|
||||
import { updateTabState } from '../../reducers/tabs';
|
||||
import {
|
||||
selectCanAnimateInterface,
|
||||
selectNotifySettings, selectPerformanceSettings, selectTabState, selectTheme,
|
||||
selectPerformanceSettings,
|
||||
selectSettingsKeys,
|
||||
selectTabState,
|
||||
selectTheme,
|
||||
} from '../../selectors';
|
||||
|
||||
const HISTORY_ANIMATION_DURATION = 450;
|
||||
@ -100,7 +103,7 @@ addActionHandler('initShared', (): ActionReturnType => {
|
||||
});
|
||||
|
||||
addActionHandler('initMain', (global): ActionReturnType => {
|
||||
const { hasWebNotifications, hasPushNotifications } = selectNotifySettings(global);
|
||||
const { hasWebNotifications, hasPushNotifications } = selectSettingsKeys(global);
|
||||
if (hasWebNotifications && hasPushNotifications) {
|
||||
// Most of the browsers only show the notifications permission prompt after the first user gesture.
|
||||
const events = ['click', 'keypress'];
|
||||
|
||||
@ -300,6 +300,10 @@ function unsafeMigrateCache(cached: GlobalState, initialState: GlobalState) {
|
||||
|
||||
cached.cacheVersion = 2;
|
||||
}
|
||||
|
||||
if (!cached.chats.notifyExceptionById) {
|
||||
cached.chats.notifyExceptionById = initialState.chats.notifyExceptionById;
|
||||
}
|
||||
}
|
||||
|
||||
function updateCache(force?: boolean) {
|
||||
@ -494,6 +498,7 @@ function reduceChats<T extends GlobalState>(global: T): GlobalState['chats'] {
|
||||
similarChannelsById: {},
|
||||
similarBotsById: {},
|
||||
isFullyLoaded: {},
|
||||
notifyExceptionById: {},
|
||||
loadingParameters: INITIAL_GLOBAL_STATE.chats.loadingParameters,
|
||||
byId: pickTruthy(global.chats.byId, idsToSave),
|
||||
fullInfoById: pickTruthy(global.chats.fullInfoById, idsToSave),
|
||||
@ -662,7 +667,6 @@ function reduceSettings<T extends GlobalState>(global: T): GlobalState['settings
|
||||
themes,
|
||||
performance,
|
||||
privacy: {},
|
||||
notifyExceptions: {},
|
||||
botVerificationShownPeerIds,
|
||||
miniAppsCachedPosition,
|
||||
miniAppsCachedSize,
|
||||
|
||||
@ -13,7 +13,7 @@ import type {
|
||||
} from '../../api/types';
|
||||
import type { OldLangFn } from '../../hooks/useOldLang';
|
||||
import type {
|
||||
CustomPeer, NotifyException, NotifySettings, ThreadId,
|
||||
CustomPeer, ThreadId,
|
||||
} from '../../types';
|
||||
import type { LangFn } from '../../util/localization';
|
||||
import { MAIN_THREAD_ID } from '../../api/types';
|
||||
@ -306,40 +306,6 @@ export function isChatArchived(chat: ApiChat) {
|
||||
return chat.folderId === ARCHIVED_FOLDER_ID;
|
||||
}
|
||||
|
||||
export function selectIsChatMuted(
|
||||
chat: ApiChat, notifySettings: NotifySettings, notifyExceptions: Record<string, NotifyException> = {},
|
||||
) {
|
||||
// If this chat is in exceptions they take precedence
|
||||
if (notifyExceptions[chat.id] && notifyExceptions[chat.id].isMuted !== undefined) {
|
||||
return notifyExceptions[chat.id].isMuted;
|
||||
}
|
||||
|
||||
return (
|
||||
chat.isMuted
|
||||
|| (isUserId(chat.id) && !notifySettings.hasPrivateChatsNotifications)
|
||||
|| (isChatChannel(chat) && !notifySettings.hasBroadcastNotifications)
|
||||
|| (isChatGroup(chat) && !notifySettings.hasGroupNotifications)
|
||||
);
|
||||
}
|
||||
|
||||
export function selectShouldShowMessagePreview(
|
||||
chat: ApiChat, notifySettings: NotifySettings, notifyExceptions: Record<string, NotifyException> = {},
|
||||
) {
|
||||
const {
|
||||
hasPrivateChatsMessagePreview = true,
|
||||
hasBroadcastMessagePreview = true,
|
||||
hasGroupMessagePreview = true,
|
||||
} = notifySettings;
|
||||
// If this chat is in exceptions they take precedence
|
||||
if (notifyExceptions[chat.id] && notifyExceptions[chat.id].shouldShowPreviews !== undefined) {
|
||||
return notifyExceptions[chat.id].shouldShowPreviews;
|
||||
}
|
||||
|
||||
return (isUserId(chat.id) && hasPrivateChatsMessagePreview)
|
||||
|| (isChatChannel(chat) && hasBroadcastMessagePreview)
|
||||
|| (isChatGroup(chat) && hasGroupMessagePreview);
|
||||
}
|
||||
|
||||
export function getCanDeleteChat(chat: ApiChat) {
|
||||
return isChatBasicGroup(chat) || ((isChatSuperGroup(chat) || isChatChannel(chat)) && chat.isCreator);
|
||||
}
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
import type { ApiInputPrivacyRules, BotsPrivacyType, PrivacyVisibility } from '../../api/types';
|
||||
import type {
|
||||
ApiInputPrivacyRules,
|
||||
BotsPrivacyType,
|
||||
PrivacyVisibility,
|
||||
} from '../../api/types';
|
||||
import type { GlobalState } from '../types';
|
||||
|
||||
import { partition } from '../../util/iteratees';
|
||||
|
||||
65
src/global/helpers/notifications.ts
Normal file
65
src/global/helpers/notifications.ts
Normal file
@ -0,0 +1,65 @@
|
||||
import type {
|
||||
ApiChat,
|
||||
ApiNotifyPeerType,
|
||||
ApiPeer,
|
||||
ApiPeerNotifySettings,
|
||||
} from '../../api/types';
|
||||
|
||||
import { omitUndefined } from '../../util/iteratees';
|
||||
import { getServerTime } from '../../util/serverTime';
|
||||
import { isChatChannel, isUserId } from './chats';
|
||||
|
||||
export function getIsChatMuted(
|
||||
chat: ApiChat,
|
||||
notifyDefaults?: Record<ApiNotifyPeerType, ApiPeerNotifySettings>,
|
||||
notifyException?: ApiPeerNotifySettings,
|
||||
) {
|
||||
const settings = getChatNotifySettings(chat, notifyDefaults, notifyException);
|
||||
if (!settings?.mutedUntil) return false;
|
||||
return getServerTime() < settings.mutedUntil;
|
||||
}
|
||||
|
||||
export function getIsChatSilent(
|
||||
chat: ApiChat,
|
||||
notifyDefaults?: Record<ApiNotifyPeerType, ApiPeerNotifySettings>,
|
||||
notifyException?: ApiPeerNotifySettings,
|
||||
) {
|
||||
const settings = getChatNotifySettings(chat, notifyDefaults, notifyException);
|
||||
if (!settings) return false;
|
||||
return !settings.hasSound;
|
||||
}
|
||||
|
||||
export function getShouldShowMessagePreview(
|
||||
chat: ApiChat,
|
||||
notifyDefaults?: Record<ApiNotifyPeerType, ApiPeerNotifySettings>,
|
||||
notifyException?: ApiPeerNotifySettings,
|
||||
) {
|
||||
const settings = getChatNotifySettings(chat, notifyDefaults, notifyException);
|
||||
return Boolean(settings?.shouldShowPreviews);
|
||||
}
|
||||
|
||||
export function getChatNotifySettings(
|
||||
chat: ApiChat,
|
||||
notifyDefaults?: Record<ApiNotifyPeerType, ApiPeerNotifySettings>,
|
||||
notifyException?: ApiPeerNotifySettings,
|
||||
): ApiPeerNotifySettings | undefined {
|
||||
const defaults = notifyDefaults?.[getNotificationPeerType(chat)];
|
||||
|
||||
if (!notifyException && !defaults) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
...defaults,
|
||||
...(notifyException && omitUndefined(notifyException)),
|
||||
};
|
||||
}
|
||||
|
||||
export function getNotificationPeerType(peer: ApiPeer): ApiNotifyPeerType {
|
||||
if (isUserId(peer.id)) {
|
||||
return 'users';
|
||||
}
|
||||
|
||||
const chat = peer as ApiChat;
|
||||
return isChatChannel(chat) ? 'channels' : 'groups';
|
||||
}
|
||||
@ -122,6 +122,7 @@ export const INITIAL_GLOBAL_STATE: GlobalState = {
|
||||
similarChannelsById: {},
|
||||
similarBotsById: {},
|
||||
topicsInfoById: {},
|
||||
notifyExceptionById: {},
|
||||
loadingParameters: {
|
||||
active: {},
|
||||
archived: {},
|
||||
@ -297,7 +298,6 @@ export const INITIAL_GLOBAL_STATE: GlobalState = {
|
||||
},
|
||||
performance: INITIAL_PERFORMANCE_STATE_MAX,
|
||||
privacy: {},
|
||||
notifyExceptions: {},
|
||||
botVerificationShownPeerIds: [],
|
||||
},
|
||||
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import type { ApiNotifyException } from '../../api/types';
|
||||
import type { ApiNotifyPeerType, ApiPeerNotifySettings } from '../../api/types';
|
||||
import type {
|
||||
ISettings, IThemeSettings, NotifyException,
|
||||
ISettings, IThemeSettings,
|
||||
ThemeKey,
|
||||
} from '../../types';
|
||||
import type { GlobalState } from '../types';
|
||||
@ -39,52 +39,51 @@ export function replaceThemeSettings<T extends GlobalState>(
|
||||
}
|
||||
|
||||
export function addNotifyExceptions<T extends GlobalState>(
|
||||
global: T, notifyExceptions: ApiNotifyException[],
|
||||
): T {
|
||||
notifyExceptions.forEach((notifyException) => {
|
||||
const { chatId, ...exceptionData } = notifyException;
|
||||
global = addNotifyException(global, chatId, exceptionData);
|
||||
});
|
||||
|
||||
return global;
|
||||
}
|
||||
|
||||
export function addNotifyException<T extends GlobalState>(
|
||||
global: T, id: string, notifyException: NotifyException,
|
||||
global: T, notifyExceptionById: Record<string, ApiPeerNotifySettings>,
|
||||
): T {
|
||||
return {
|
||||
...global,
|
||||
settings: {
|
||||
...global.settings,
|
||||
notifyExceptions: {
|
||||
...global.settings.notifyExceptions,
|
||||
chats: {
|
||||
...global.chats,
|
||||
notifyExceptionById: {
|
||||
...global.chats.notifyExceptionById,
|
||||
...notifyExceptionById,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function addNotifyException<T extends GlobalState>(
|
||||
global: T, id: string, notifyException: ApiPeerNotifySettings,
|
||||
): T {
|
||||
return {
|
||||
...global,
|
||||
chats: {
|
||||
...global.chats,
|
||||
notifyExceptionById: {
|
||||
...global.chats.notifyExceptionById,
|
||||
[id]: notifyException,
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line consistent-return
|
||||
export function updateNotifySettings<T extends GlobalState>(
|
||||
global: T, peerType: 'contact' | 'group' | 'broadcast', isSilent?: boolean, shouldShowPreviews?: boolean,
|
||||
export function updateNotifyDefaults<T extends GlobalState>(
|
||||
global: T, peerType: ApiNotifyPeerType, settings: Partial<ApiPeerNotifySettings>,
|
||||
): T {
|
||||
switch (peerType) {
|
||||
case 'contact':
|
||||
return replaceSettings(global, {
|
||||
...(typeof isSilent !== 'undefined' && { hasPrivateChatsNotifications: !isSilent }),
|
||||
...(typeof shouldShowPreviews !== 'undefined' && { hasPrivateChatsMessagePreview: shouldShowPreviews }),
|
||||
});
|
||||
case 'group':
|
||||
return replaceSettings(global, {
|
||||
...(typeof isSilent !== 'undefined' && { hasGroupNotifications: !isSilent }),
|
||||
...(typeof shouldShowPreviews !== 'undefined' && { hasGroupMessagePreview: shouldShowPreviews }),
|
||||
});
|
||||
case 'broadcast':
|
||||
return replaceSettings(global, {
|
||||
...(typeof isSilent !== 'undefined' && { hasBroadcastNotifications: !isSilent }),
|
||||
...(typeof shouldShowPreviews !== 'undefined' && { hasBroadcastMessagePreview: shouldShowPreviews }),
|
||||
});
|
||||
}
|
||||
return {
|
||||
...global,
|
||||
settings: {
|
||||
...global.settings,
|
||||
notifyDefaults: {
|
||||
...global.settings.notifyDefaults,
|
||||
[peerType]: {
|
||||
...global.settings.notifyDefaults?.[peerType],
|
||||
...settings,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function addBlockedUser<T extends GlobalState>(global: T, contactId: string): T {
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
import type { GlobalState } from '../types';
|
||||
|
||||
export function selectNotifySettings<T extends GlobalState>(global: T) {
|
||||
return global.settings.byKey;
|
||||
export function selectNotifyDefaults<T extends GlobalState>(global: T) {
|
||||
return global.settings.notifyDefaults;
|
||||
}
|
||||
|
||||
export function selectNotifyExceptions<T extends GlobalState>(global: T) {
|
||||
return global.settings.notifyExceptions;
|
||||
export function selectNotifyException<T extends GlobalState>(global: T, chatId: string) {
|
||||
return global.chats.notifyExceptionById?.[chatId];
|
||||
}
|
||||
|
||||
export function selectLanguageCode<T extends GlobalState>(global: T) {
|
||||
@ -13,7 +13,7 @@ export function selectLanguageCode<T extends GlobalState>(global: T) {
|
||||
}
|
||||
|
||||
export function selectCanSetPasscode<T extends GlobalState>(global: T) {
|
||||
return global.authRememberMe && global.isCacheApiSupported;
|
||||
return global.authRememberMe;
|
||||
}
|
||||
|
||||
export function selectTranslationLanguage<T extends GlobalState>(global: T) {
|
||||
@ -27,3 +27,7 @@ export function selectNewNoncontactPeersRequirePremium<T extends GlobalState>(gl
|
||||
export function selectShouldHideReadMarks<T extends GlobalState>(global: T) {
|
||||
return global.settings.byKey.shouldHideReadMarks;
|
||||
}
|
||||
|
||||
export function selectSettingsKeys<T extends GlobalState>(global: T) {
|
||||
return global.settings.byKey;
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ import type {
|
||||
ApiMessageSearchContext,
|
||||
ApiNewPoll,
|
||||
ApiNotification,
|
||||
ApiNotifyPeerType,
|
||||
ApiPaymentStatus,
|
||||
ApiPhoto,
|
||||
ApiPremiumSection,
|
||||
@ -220,8 +221,8 @@ export interface ActionPayloads {
|
||||
isSilent: boolean;
|
||||
};
|
||||
updateNotificationSettings: {
|
||||
peerType: 'contact' | 'group' | 'broadcast';
|
||||
isSilent?: boolean;
|
||||
peerType: ApiNotifyPeerType;
|
||||
isMuted?: boolean;
|
||||
shouldShowPreviews?: boolean;
|
||||
};
|
||||
|
||||
@ -255,7 +256,7 @@ export interface ActionPayloads {
|
||||
loadCountryList: {
|
||||
langCode?: string;
|
||||
};
|
||||
ensureTimeFormat: WithTabId | undefined;
|
||||
ensureTimeFormat: undefined;
|
||||
|
||||
// misc
|
||||
loadWebPagePreview: {
|
||||
@ -365,7 +366,8 @@ export interface ActionPayloads {
|
||||
toggleChatArchived: {
|
||||
id: string;
|
||||
};
|
||||
toggleChatUnread: { id: string };
|
||||
markChatUnread: { id: string };
|
||||
markChatRead: { id: string };
|
||||
loadChatFolders: undefined;
|
||||
loadRecommendedChatFolders: undefined;
|
||||
editChatFolder: {
|
||||
@ -1047,7 +1049,7 @@ export interface ActionPayloads {
|
||||
updateChatMutedState: {
|
||||
chatId: string;
|
||||
isMuted?: boolean;
|
||||
muteUntil?: number;
|
||||
mutedUntil?: number;
|
||||
};
|
||||
|
||||
updateChat: {
|
||||
@ -1313,9 +1315,16 @@ export interface ActionPayloads {
|
||||
} & WithTabId;
|
||||
focusNextReaction: WithTabId | undefined;
|
||||
focusNextMention: WithTabId | undefined;
|
||||
readAllReactions: WithTabId | undefined;
|
||||
readAllMentions: WithTabId | undefined;
|
||||
readAllReactions: {
|
||||
chatId: string;
|
||||
threadId?: ThreadId;
|
||||
};
|
||||
readAllMentions: {
|
||||
chatId: string;
|
||||
threadId?: ThreadId;
|
||||
};
|
||||
markMentionsRead: {
|
||||
chatId: string;
|
||||
messageIds: number[];
|
||||
} & WithTabId;
|
||||
copyMessageLink: {
|
||||
@ -2505,7 +2514,7 @@ export interface ActionPayloads {
|
||||
chatId: string;
|
||||
topicId: number;
|
||||
isMuted?: boolean;
|
||||
muteUntil?: number;
|
||||
mutedUntil?: number;
|
||||
};
|
||||
setViewForumAsMessages: {
|
||||
chatId: string;
|
||||
|
||||
@ -15,8 +15,10 @@ import type {
|
||||
ApiGroupCall,
|
||||
ApiLanguage,
|
||||
ApiMessage,
|
||||
ApiNotifyPeerType,
|
||||
ApiPaidReactionPrivacyType,
|
||||
ApiPeerColors,
|
||||
ApiPeerNotifySettings,
|
||||
ApiPeerPhotos,
|
||||
ApiPeerStories,
|
||||
ApiPhoneCall,
|
||||
@ -54,7 +56,6 @@ import type {
|
||||
EmojiKeywords,
|
||||
ISettings,
|
||||
IThemeSettings,
|
||||
NotifyException,
|
||||
PerformanceType,
|
||||
Point,
|
||||
ServiceNotification,
|
||||
@ -221,6 +222,7 @@ export type GlobalState = {
|
||||
similarChannelIds?: string[];
|
||||
count?: number;
|
||||
}>>;
|
||||
notifyExceptionById: Record<string, ApiPeerNotifySettings>;
|
||||
|
||||
similarBotsById: Record<string, SimilarBotsInfo>;
|
||||
};
|
||||
@ -411,7 +413,7 @@ export type GlobalState = {
|
||||
loadedWallpapers?: ApiWallpaper[];
|
||||
themes: Partial<Record<ThemeKey, IThemeSettings>>;
|
||||
privacy: Partial<Record<ApiPrivacyKey, ApiPrivacySettings>>;
|
||||
notifyExceptions?: Record<number, NotifyException>;
|
||||
notifyDefaults?: Record<ApiNotifyPeerType, ApiPeerNotifySettings>;
|
||||
lastPremiumBandwithNotificationDate?: number;
|
||||
paidReactionPrivacy?: ApiPaidReactionPrivacyType;
|
||||
languages?: ApiLanguage[];
|
||||
|
||||
@ -79,7 +79,8 @@ const useChatContextActions = ({
|
||||
toggleSavedDialogPinned,
|
||||
updateChatMutedState,
|
||||
toggleChatArchived,
|
||||
toggleChatUnread,
|
||||
markChatRead,
|
||||
markChatUnread,
|
||||
openChatInNewTab,
|
||||
} = getActions();
|
||||
|
||||
@ -149,10 +150,10 @@ const useChatContextActions = ({
|
||||
}
|
||||
|
||||
const actionMaskAsRead = (chat.unreadCount || chat.hasUnreadMark)
|
||||
? { title: lang('MarkAsRead'), icon: 'readchats', handler: () => toggleChatUnread({ id: chat.id }) }
|
||||
? { title: lang('MarkAsRead'), icon: 'readchats', handler: () => markChatRead({ id: chat.id }) }
|
||||
: undefined;
|
||||
const actionMarkAsUnread = !(chat.unreadCount || chat.hasUnreadMark) && !chat.isForum
|
||||
? { title: lang('MarkAsUnread'), icon: 'unread', handler: () => toggleChatUnread({ id: chat.id }) }
|
||||
? { title: lang('MarkAsUnread'), icon: 'unread', handler: () => markChatUnread({ id: chat.id }) }
|
||||
: undefined;
|
||||
|
||||
const actionArchive = isChatArchived(chat)
|
||||
|
||||
@ -76,19 +76,6 @@ export interface IThemeSettings {
|
||||
isBlurred?: boolean;
|
||||
}
|
||||
|
||||
export type NotifySettings = {
|
||||
hasPrivateChatsNotifications?: boolean;
|
||||
hasPrivateChatsMessagePreview?: boolean;
|
||||
hasGroupNotifications?: boolean;
|
||||
hasGroupMessagePreview?: boolean;
|
||||
hasBroadcastNotifications?: boolean;
|
||||
hasBroadcastMessagePreview?: boolean;
|
||||
hasContactJoinedNotifications?: boolean;
|
||||
hasWebNotifications: boolean;
|
||||
hasPushNotifications: boolean;
|
||||
notificationSoundVolume: number;
|
||||
};
|
||||
|
||||
export type LangCode = (
|
||||
'en' | 'ar' | 'be' | 'ca' | 'nl' | 'fr' | 'de' | 'id' | 'it' | 'ko' | 'ms' | 'fa' | 'pl' | 'pt-br' | 'ru' | 'es'
|
||||
| 'tr' | 'uk' | 'uz'
|
||||
@ -96,7 +83,7 @@ export type LangCode = (
|
||||
|
||||
export type TimeFormat = '24h' | '12h';
|
||||
|
||||
export interface ISettings extends NotifySettings, Record<string, any> {
|
||||
export interface ISettings {
|
||||
theme: ThemeKey;
|
||||
shouldUseSystemTheme: boolean;
|
||||
messageTextSize: number;
|
||||
@ -139,6 +126,10 @@ export interface ISettings extends NotifySettings, Record<string, any> {
|
||||
shouldDebugExportedSenders?: boolean;
|
||||
shouldWarnAboutSvg?: boolean;
|
||||
shouldSkipWebAppCloseConfirmation: boolean;
|
||||
hasContactJoinedNotifications?: boolean;
|
||||
hasWebNotifications: boolean;
|
||||
hasPushNotifications: boolean;
|
||||
notificationSoundVolume: number;
|
||||
}
|
||||
|
||||
export type IAnchorPosition = {
|
||||
@ -461,12 +452,6 @@ export enum ManagementScreens {
|
||||
|
||||
export type ManagementType = 'user' | 'group' | 'channel' | 'bot';
|
||||
|
||||
export type NotifyException = {
|
||||
isMuted: boolean;
|
||||
isSilent?: boolean;
|
||||
shouldShowPreviews?: boolean;
|
||||
};
|
||||
|
||||
export type EmojiKeywords = {
|
||||
isLoading?: boolean;
|
||||
version?: number;
|
||||
|
||||
@ -3,20 +3,18 @@ import { addCallback } from '../lib/teact/teactn';
|
||||
import { addActionHandler, getGlobal } from '../global';
|
||||
|
||||
import type {
|
||||
ApiChat, ApiChatFolder, ApiUser,
|
||||
ApiChat, ApiChatFolder, ApiNotifyPeerType, ApiPeerNotifySettings, ApiUser,
|
||||
} from '../api/types';
|
||||
import type { GlobalState } from '../global/types';
|
||||
import type { NotifyException, NotifySettings } from '../types';
|
||||
import type { CallbackManager } from './callbacks';
|
||||
|
||||
import {
|
||||
ALL_FOLDER_ID, ARCHIVED_FOLDER_ID, DEBUG, SAVED_FOLDER_ID, SERVICE_NOTIFICATIONS_USER_ID,
|
||||
} from '../config';
|
||||
import { selectIsChatMuted } from '../global/helpers';
|
||||
import { getIsChatMuted } from '../global/helpers/notifications';
|
||||
import {
|
||||
selectChatLastMessage,
|
||||
selectNotifyExceptions,
|
||||
selectNotifySettings,
|
||||
selectNotifyDefaults,
|
||||
selectTabState,
|
||||
selectTopics,
|
||||
} from '../global/selectors';
|
||||
@ -79,8 +77,8 @@ let prevGlobal: {
|
||||
chatsById: Record<string, ApiChat>;
|
||||
foldersById: Record<string, ApiChatFolder>;
|
||||
usersById: Record<string, ApiUser>;
|
||||
notifySettings: NotifySettings;
|
||||
notifyExceptions?: Record<number, NotifyException>;
|
||||
notifyDefaults?: Record<ApiNotifyPeerType, ApiPeerNotifySettings>;
|
||||
notifyExceptions?: Record<number, ApiPeerNotifySettings>;
|
||||
} = initials.prevGlobal;
|
||||
|
||||
let prepared: {
|
||||
@ -219,8 +217,8 @@ function updateFolderManager(global: GlobalState) {
|
||||
const areAllLastMessageIdsChanged = global.chats.lastMessageIds.all !== prevGlobal.lastAllMessageIds;
|
||||
const areTopicsChanged = global.chats.topicsInfoById !== prevGlobal.topicsInfoById;
|
||||
const areUsersChanged = global.users.byId !== prevGlobal.usersById;
|
||||
const areNotifySettingsChanged = selectNotifySettings(global) !== prevGlobal.notifySettings;
|
||||
const areNotifyExceptionsChanged = selectNotifyExceptions(global) !== prevGlobal.notifyExceptions;
|
||||
const areNotifyDefaultsChanged = selectNotifyDefaults(global) !== prevGlobal.notifyDefaults;
|
||||
const areNotifyExceptionsChanged = global.chats.notifyExceptionById !== prevGlobal.notifyExceptions;
|
||||
|
||||
let affectedFolderIds: number[] = [];
|
||||
|
||||
@ -232,7 +230,7 @@ function updateFolderManager(global: GlobalState) {
|
||||
|
||||
if (!(
|
||||
isAllFolderChanged || isArchivedFolderChanged || isSavedFolderChanged || areFoldersChanged
|
||||
|| areChatsChanged || areUsersChanged || areTopicsChanged || areNotifySettingsChanged || areNotifyExceptionsChanged
|
||||
|| areChatsChanged || areUsersChanged || areTopicsChanged || areNotifyDefaultsChanged || areNotifyExceptionsChanged
|
||||
|| areSavedLastMessageIdsChanged || areAllLastMessageIdsChanged
|
||||
)
|
||||
) {
|
||||
@ -252,7 +250,7 @@ function updateFolderManager(global: GlobalState) {
|
||||
affectedFolderIds = affectedFolderIds.concat(updateChats(
|
||||
global,
|
||||
areFoldersChanged || isAllFolderChanged || isArchivedFolderChanged || isSavedFolderChanged,
|
||||
areNotifySettingsChanged,
|
||||
areNotifyDefaultsChanged,
|
||||
areNotifyExceptionsChanged,
|
||||
prevAllFolderListIds,
|
||||
prevArchivedFolderListIds,
|
||||
@ -411,7 +409,7 @@ function buildFolderSummary(folder: ApiChatFolder): FolderSummary {
|
||||
function updateChats(
|
||||
global: GlobalState,
|
||||
areFoldersChanged: boolean,
|
||||
areNotifySettingsChanged: boolean,
|
||||
areNotifyDefaultsChanged: boolean,
|
||||
areNotifyExceptionsChanged: boolean,
|
||||
prevAllFolderListIds?: string[],
|
||||
prevArchivedFolderListIds?: string[],
|
||||
@ -421,8 +419,8 @@ function updateChats(
|
||||
const newUsersById = global.users.byId;
|
||||
const newAllLastMessageIds = global.chats.lastMessageIds.all;
|
||||
const newSavedLastMessageIds = global.chats.lastMessageIds.saved;
|
||||
const newNotifySettings = selectNotifySettings(global);
|
||||
const newNotifyExceptions = selectNotifyExceptions(global);
|
||||
const newNotifyDefaults = selectNotifyDefaults(global);
|
||||
const newNotifyExceptions = global.chats.notifyExceptionById;
|
||||
const folderSummaries = Object.values(prepared.folderSummariesById);
|
||||
const affectedFolderIds = new Set<number>();
|
||||
|
||||
@ -445,7 +443,7 @@ function updateChats(
|
||||
|
||||
if (
|
||||
!areFoldersChanged
|
||||
&& !areNotifySettingsChanged
|
||||
&& !areNotifyDefaultsChanged
|
||||
&& !areNotifyExceptionsChanged
|
||||
&& chat === prevGlobal.chatsById[chatId]
|
||||
&& newUsersById[chatId] === prevGlobal.usersById[chatId]
|
||||
@ -463,7 +461,7 @@ function updateChats(
|
||||
const newSummary = buildChatSummary(
|
||||
global,
|
||||
chat,
|
||||
newNotifySettings,
|
||||
newNotifyDefaults,
|
||||
newNotifyExceptions,
|
||||
newUsersById[chatId],
|
||||
isRemovedFromAll,
|
||||
@ -500,7 +498,7 @@ function updateChats(
|
||||
prevGlobal.usersById = newUsersById;
|
||||
prevGlobal.lastAllMessageIds = newAllLastMessageIds;
|
||||
prevGlobal.lastSavedMessageIds = newSavedLastMessageIds;
|
||||
prevGlobal.notifySettings = newNotifySettings;
|
||||
prevGlobal.notifyDefaults = newNotifyDefaults;
|
||||
prevGlobal.notifyExceptions = newNotifyExceptions;
|
||||
|
||||
return Array.from(affectedFolderIds);
|
||||
@ -509,8 +507,8 @@ function updateChats(
|
||||
function buildChatSummary<T extends GlobalState>(
|
||||
global: T,
|
||||
chat: ApiChat,
|
||||
notifySettings: NotifySettings,
|
||||
notifyExceptions?: Record<number, NotifyException>,
|
||||
notifyDefaults?: Record<ApiNotifyPeerType, ApiPeerNotifySettings>,
|
||||
notifyExceptions?: Record<string, ApiPeerNotifySettings>,
|
||||
user?: ApiUser,
|
||||
isRemovedFromAll?: boolean,
|
||||
isRemovedFromSaved?: boolean,
|
||||
@ -548,7 +546,7 @@ function buildChatSummary<T extends GlobalState>(
|
||||
isListedInAll: Boolean(!isRestricted && !isNotJoined && !migratedTo && !shouldHideServiceChat && !isRemovedFromAll),
|
||||
isListedInSaved: !isRemovedFromSaved,
|
||||
isArchived: folderId === ARCHIVED_FOLDER_ID,
|
||||
isMuted: selectIsChatMuted(chat, notifySettings, notifyExceptions),
|
||||
isMuted: getIsChatMuted(chat, notifyDefaults, notifyExceptions?.[chat.id]),
|
||||
isUnread: Boolean(unreadCount || unreadMentionsCount || hasUnreadMark),
|
||||
unreadCount,
|
||||
unreadMentionsCount,
|
||||
@ -810,8 +808,6 @@ function buildInitials() {
|
||||
chatsById: {},
|
||||
usersById: {},
|
||||
topicsInfoById: {},
|
||||
notifySettings: {} as NotifySettings,
|
||||
notifyExceptions: {},
|
||||
},
|
||||
|
||||
prepared: {
|
||||
|
||||
@ -16,16 +16,16 @@ import {
|
||||
getPrivateChatUserId,
|
||||
getUserFullName,
|
||||
isChatChannel,
|
||||
selectIsChatMuted,
|
||||
selectShouldShowMessagePreview,
|
||||
} from '../global/helpers';
|
||||
import { addNotifyExceptions, replaceSettings } from '../global/reducers';
|
||||
import { getIsChatMuted, getIsChatSilent, getShouldShowMessagePreview } from '../global/helpers/notifications';
|
||||
import {
|
||||
selectChat,
|
||||
selectCurrentMessageList,
|
||||
selectIsChatWithSelf,
|
||||
selectNotifyExceptions,
|
||||
selectNotifySettings,
|
||||
selectNotifyDefaults,
|
||||
selectNotifyException,
|
||||
selectSettingsKeys,
|
||||
selectTopicFromMessage,
|
||||
selectUser,
|
||||
} from '../global/selectors';
|
||||
import { callApi } from '../api/gramjs';
|
||||
@ -34,6 +34,7 @@ import { buildCollectionByKey } from './iteratees';
|
||||
import * as mediaLoader from './mediaLoader';
|
||||
import { oldTranslate } from './oldLangProvider';
|
||||
import { debounce } from './schedulers';
|
||||
import { getServerTime } from './serverTime';
|
||||
import { IS_ELECTRON, IS_SERVICE_WORKER_SUPPORTED, IS_TOUCH_ENV } from './windowEnvironment';
|
||||
|
||||
import MessageSummary from '../components/common/MessageSummary';
|
||||
@ -107,7 +108,7 @@ notificationSound.setAttribute('mozaudiochannel', 'notification');
|
||||
|
||||
export async function playNotifySound(id?: string, volume?: number) {
|
||||
if (id !== undefined && soundPlayedIds.has(id)) return;
|
||||
const { notificationSoundVolume } = selectNotifySettings(getGlobal());
|
||||
const { notificationSoundVolume } = selectSettingsKeys(getGlobal());
|
||||
const currentVolume = volume ? volume / 10 : notificationSoundVolume / 10;
|
||||
if (currentVolume === 0) return;
|
||||
notificationSound.volume = currentVolume;
|
||||
@ -181,27 +182,6 @@ export async function unsubscribe() {
|
||||
await unsubscribeFromPush(subscription);
|
||||
}
|
||||
|
||||
// Indicates if notification settings are loaded from the api
|
||||
let areSettingsLoaded = false;
|
||||
|
||||
// Load notification settings from the api
|
||||
async function loadNotificationSettings() {
|
||||
if (areSettingsLoaded) return selectNotifySettings(getGlobal());
|
||||
const [resultSettings, resultExceptions] = await Promise.all([
|
||||
callApi('fetchNotificationSettings'),
|
||||
callApi('fetchNotificationExceptions'),
|
||||
]);
|
||||
if (!resultSettings) return selectNotifySettings(getGlobal());
|
||||
|
||||
let global = replaceSettings(getGlobal(), resultSettings);
|
||||
if (resultExceptions) {
|
||||
global = addNotifyExceptions(global, resultExceptions);
|
||||
}
|
||||
setGlobal(global);
|
||||
areSettingsLoaded = true;
|
||||
return selectNotifySettings(global);
|
||||
}
|
||||
|
||||
// Load custom emoji from the api if it's not cached already
|
||||
async function loadCustomEmoji(id: string) {
|
||||
let global = getGlobal();
|
||||
@ -293,9 +273,12 @@ export async function subscribe() {
|
||||
}
|
||||
|
||||
function checkIfShouldNotify(chat: ApiChat, message: Partial<ApiMessage>) {
|
||||
if (!areSettingsLoaded) return false;
|
||||
const global = getGlobal();
|
||||
const isMuted = selectIsChatMuted(chat, selectNotifySettings(global), selectNotifyExceptions(global));
|
||||
const isChatMuted = getIsChatMuted(chat, selectNotifyDefaults(global), selectNotifyException(global, chat.id));
|
||||
const topic = selectTopicFromMessage(global, message as ApiMessage);
|
||||
const topicMutedUntil = topic?.notifySettings.mutedUntil;
|
||||
const isMuted = topicMutedUntil === undefined ? isChatMuted : topicMutedUntil > getServerTime();
|
||||
|
||||
const shouldNotifyAboutMessage = message.content?.action?.type !== 'phoneCall';
|
||||
if (isMuted || !shouldNotifyAboutMessage
|
||||
|| chat.isNotJoined || !chat.isListed || selectIsChatWithSelf(global, chat.id)) {
|
||||
@ -330,7 +313,7 @@ function getNotificationContent(chat: ApiChat, message: ApiMessage, reaction?: A
|
||||
let body: string;
|
||||
if (
|
||||
!isScreenLocked
|
||||
&& selectShouldShowMessagePreview(chat, selectNotifySettings(global), selectNotifyExceptions(global))
|
||||
&& getShouldShowMessagePreview(chat, selectNotifyDefaults(global), selectNotifyException(global, chat.id))
|
||||
) {
|
||||
const isChat = chat && (isChatChannel(chat) || message.senderId === message.chatId);
|
||||
|
||||
@ -386,7 +369,7 @@ export async function notifyAboutCall({
|
||||
}: {
|
||||
call: ApiPhoneCall; user: ApiUser;
|
||||
}) {
|
||||
const { hasWebNotifications } = await loadNotificationSettings();
|
||||
const { hasWebNotifications } = selectSettingsKeys(getGlobal());
|
||||
if (document.hasFocus() || !hasWebNotifications) return;
|
||||
const areNotificationsSupported = checkIfNotificationsSupported();
|
||||
if (!areNotificationsSupported) return;
|
||||
@ -420,11 +403,18 @@ export async function notifyAboutMessage({
|
||||
message,
|
||||
isReaction = false,
|
||||
}: { chat: ApiChat; message: Partial<ApiMessage>; isReaction?: boolean }) {
|
||||
const { hasWebNotifications } = await loadNotificationSettings();
|
||||
const global = getGlobal();
|
||||
const { hasWebNotifications } = selectSettingsKeys(global);
|
||||
if (!checkIfShouldNotify(chat, message)) return;
|
||||
const isChatSilent = getIsChatSilent(
|
||||
chat, selectNotifyDefaults(getGlobal()), selectNotifyException(getGlobal(), chat.id),
|
||||
);
|
||||
const topic = selectTopicFromMessage(global, message as ApiMessage);
|
||||
const isSilent = topic?.notifySettings.hasSound === undefined ? isChatSilent : !topic.notifySettings.hasSound;
|
||||
|
||||
const areNotificationsSupported = checkIfNotificationsSupported();
|
||||
if (!hasWebNotifications || !areNotificationsSupported) {
|
||||
if (!message.isSilent && !isReaction && !IS_ELECTRON) {
|
||||
if (!isSilent && !message.isSilent && !isReaction && !IS_ELECTRON) {
|
||||
// Only play sound if web notifications are disabled
|
||||
playNotifySoundDebounced(String(message.id) || chat.id);
|
||||
}
|
||||
@ -463,7 +453,7 @@ export async function notifyAboutMessage({
|
||||
chatId: chat.id,
|
||||
messageId: message.id,
|
||||
shouldReplaceHistory: true,
|
||||
isSilent: message.isSilent,
|
||||
isSilent: isSilent || message.isSilent,
|
||||
reaction: activeReaction?.reaction,
|
||||
},
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user