Chat: Mute options (#3233)
This commit is contained in:
parent
f90317c650
commit
28fc59e070
@ -100,6 +100,7 @@ export function buildApiChatFromDialog(
|
|||||||
unreadMentionsCount,
|
unreadMentionsCount,
|
||||||
unreadReactionsCount,
|
unreadReactionsCount,
|
||||||
isMuted,
|
isMuted,
|
||||||
|
muteUntil,
|
||||||
...(unreadMark && { hasUnreadMark: true }),
|
...(unreadMark && { hasUnreadMark: true }),
|
||||||
...(draft instanceof GramJs.DraftMessage && { draftDate: draft.date }),
|
...(draft instanceof GramJs.DraftMessage && { draftDate: draft.date }),
|
||||||
...buildApiChatFieldsFromPeerEntity(peerEntity),
|
...buildApiChatFieldsFromPeerEntity(peerEntity),
|
||||||
@ -547,8 +548,8 @@ export function buildApiTopic(forumTopic: GramJs.TypeForumTopic): ApiTopic | und
|
|||||||
unreadMentionsCount,
|
unreadMentionsCount,
|
||||||
unreadReactionsCount,
|
unreadReactionsCount,
|
||||||
fromId: getApiChatIdFromMtpPeer(fromId),
|
fromId: getApiChatIdFromMtpPeer(fromId),
|
||||||
// TODO[forums] `muteUntil` should not really be parsed here
|
isMuted: silent || (typeof muteUntil === 'number' && getServerTime() < muteUntil),
|
||||||
isMuted: silent || (muteUntil !== undefined ? muteUntil > 0 : undefined),
|
muteUntil,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -136,6 +136,7 @@ export function buildApiNotifyException(
|
|||||||
isMuted: silent || (typeof muteUntil === 'number' && getServerTime() < muteUntil),
|
isMuted: silent || (typeof muteUntil === 'number' && getServerTime() < muteUntil),
|
||||||
...(!hasSound && { isSilent: true }),
|
...(!hasSound && { isSilent: true }),
|
||||||
...(showPreviews !== undefined && { shouldShowPreviews: Boolean(showPreviews) }),
|
...(showPreviews !== undefined && { shouldShowPreviews: Boolean(showPreviews) }),
|
||||||
|
muteUntil,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -154,6 +155,7 @@ export function buildApiNotifyExceptionTopic(
|
|||||||
isMuted: silent || (typeof muteUntil === 'number' && getServerTime() < muteUntil),
|
isMuted: silent || (typeof muteUntil === 'number' && getServerTime() < muteUntil),
|
||||||
...(!hasSound && { isSilent: true }),
|
...(!hasSound && { isSilent: true }),
|
||||||
...(showPreviews !== undefined && { shouldShowPreviews: Boolean(showPreviews) }),
|
...(showPreviews !== undefined && { shouldShowPreviews: Boolean(showPreviews) }),
|
||||||
|
muteUntil,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -40,7 +40,8 @@ import {
|
|||||||
buildApiChatSettings,
|
buildApiChatSettings,
|
||||||
buildApiChatReactions,
|
buildApiChatReactions,
|
||||||
buildApiTopic,
|
buildApiTopic,
|
||||||
buildApiChatlistInvite, buildApiChatlistExportedInvite,
|
buildApiChatlistInvite,
|
||||||
|
buildApiChatlistExportedInvite,
|
||||||
} from '../apiBuilders/chats';
|
} from '../apiBuilders/chats';
|
||||||
import { buildApiMessage, buildMessageDraft } from '../apiBuilders/messages';
|
import { buildApiMessage, buildMessageDraft } from '../apiBuilders/messages';
|
||||||
import { buildApiUser, buildApiUsersAndStatuses } from '../apiBuilders/users';
|
import { buildApiUser, buildApiUsersAndStatuses } from '../apiBuilders/users';
|
||||||
@ -58,12 +59,17 @@ import {
|
|||||||
generateRandomBigInt,
|
generateRandomBigInt,
|
||||||
} from '../gramjsBuilders';
|
} from '../gramjsBuilders';
|
||||||
import {
|
import {
|
||||||
addEntitiesWithPhotosToLocalDb, addMessageToLocalDb, addPhotoToLocalDb, isChatFolder,
|
addEntitiesWithPhotosToLocalDb,
|
||||||
|
addMessageToLocalDb,
|
||||||
|
addPhotoToLocalDb,
|
||||||
|
isChatFolder,
|
||||||
|
|
||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
import { buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
import { buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
||||||
import { buildApiPhoto } from '../apiBuilders/common';
|
import { buildApiPhoto } from '../apiBuilders/common';
|
||||||
import { buildStickerSet } from '../apiBuilders/symbols';
|
import { buildStickerSet } from '../apiBuilders/symbols';
|
||||||
import localDb from '../localDb';
|
import localDb from '../localDb';
|
||||||
|
import { scheduleMutedChatUpdate } from '../scheduleUnmute';
|
||||||
|
|
||||||
type FullChatData = {
|
type FullChatData = {
|
||||||
fullInfo: ApiChatFullInfo;
|
fullInfo: ApiChatFullInfo;
|
||||||
@ -161,6 +167,8 @@ export async function fetchChats({
|
|||||||
|
|
||||||
chats.push(chat);
|
chats.push(chat);
|
||||||
|
|
||||||
|
scheduleMutedChatUpdate(chat.id, chat.muteUntil, onUpdate);
|
||||||
|
|
||||||
if (withPinned && dialog.pinned) {
|
if (withPinned && dialog.pinned) {
|
||||||
orderedPinnedIds.push(chat.id);
|
orderedPinnedIds.push(chat.id);
|
||||||
}
|
}
|
||||||
@ -330,14 +338,18 @@ export async function requestChatUpdate({
|
|||||||
? lastLocalMessage
|
? lastLocalMessage
|
||||||
: lastRemoteMessage;
|
: lastRemoteMessage;
|
||||||
|
|
||||||
|
const chatUpdate = {
|
||||||
|
...buildApiChatFromDialog(dialog, peerEntity),
|
||||||
|
...(!noLastMessage && { lastMessage }),
|
||||||
|
};
|
||||||
|
|
||||||
onUpdate({
|
onUpdate({
|
||||||
'@type': 'updateChat',
|
'@type': 'updateChat',
|
||||||
id,
|
id,
|
||||||
chat: {
|
chat: chatUpdate,
|
||||||
...buildApiChatFromDialog(dialog, peerEntity),
|
|
||||||
...(!noLastMessage && { lastMessage }),
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
scheduleMutedChatUpdate(chatUpdate.id, chatUpdate.muteUntil, onUpdate);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function saveDraft({
|
export function saveDraft({
|
||||||
@ -558,15 +570,18 @@ async function getFullChannelInfo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function updateChatMutedState({
|
export async function updateChatMutedState({
|
||||||
chat, isMuted,
|
chat, isMuted, muteUntil = 0,
|
||||||
}: {
|
}: {
|
||||||
chat: ApiChat; isMuted: boolean;
|
chat: ApiChat; isMuted: boolean; muteUntil?: number;
|
||||||
}) {
|
}) {
|
||||||
|
if (isMuted && !muteUntil) {
|
||||||
|
muteUntil = MAX_INT_32;
|
||||||
|
}
|
||||||
await invokeRequest(new GramJs.account.UpdateNotifySettings({
|
await invokeRequest(new GramJs.account.UpdateNotifySettings({
|
||||||
peer: new GramJs.InputNotifyPeer({
|
peer: new GramJs.InputNotifyPeer({
|
||||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||||
}),
|
}),
|
||||||
settings: new GramJs.InputPeerNotifySettings({ muteUntil: isMuted ? MAX_INT_32 : 0 }),
|
settings: new GramJs.InputPeerNotifySettings({ muteUntil }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
onUpdate({
|
onUpdate({
|
||||||
@ -582,16 +597,19 @@ export async function updateChatMutedState({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function updateTopicMutedState({
|
export async function updateTopicMutedState({
|
||||||
chat, topicId, isMuted,
|
chat, topicId, isMuted, muteUntil = 0,
|
||||||
}: {
|
}: {
|
||||||
chat: ApiChat; topicId: number; isMuted: boolean;
|
chat: ApiChat; topicId: number; isMuted: boolean; muteUntil?: number;
|
||||||
}) {
|
}) {
|
||||||
|
if (isMuted && !muteUntil) {
|
||||||
|
muteUntil = MAX_INT_32;
|
||||||
|
}
|
||||||
await invokeRequest(new GramJs.account.UpdateNotifySettings({
|
await invokeRequest(new GramJs.account.UpdateNotifySettings({
|
||||||
peer: new GramJs.InputNotifyForumTopic({
|
peer: new GramJs.InputNotifyForumTopic({
|
||||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||||
topMsgId: topicId,
|
topMsgId: topicId,
|
||||||
}),
|
}),
|
||||||
settings: new GramJs.InputPeerNotifySettings({ muteUntil: isMuted ? MAX_INT_32 : 0 }),
|
settings: new GramJs.InputPeerNotifySettings({ muteUntil }),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
onUpdate({
|
onUpdate({
|
||||||
|
|||||||
51
src/api/gramjs/scheduleUnmute.ts
Normal file
51
src/api/gramjs/scheduleUnmute.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import type { OnApiUpdate } from '../types';
|
||||||
|
import { getServerTime } from '../../util/serverTime';
|
||||||
|
import { MAX_INT_32 } from '../../config';
|
||||||
|
|
||||||
|
type UnmuteQueueItem = { chatId: string; topicId?: number; muteUntil: number };
|
||||||
|
const unmuteTimers = new Map<string, any>();
|
||||||
|
const unmuteQueue: Array<UnmuteQueueItem> = [];
|
||||||
|
const scheduleUnmute = (item: UnmuteQueueItem, onUpdate: NoneToVoidFunction) => {
|
||||||
|
const id = item.topicId ? `${item.chatId}-${item.topicId}` : item.chatId;
|
||||||
|
if (unmuteTimers.has(id)) {
|
||||||
|
clearTimeout(unmuteTimers.get(id));
|
||||||
|
unmuteTimers.delete(id);
|
||||||
|
}
|
||||||
|
if (item.muteUntil === MAX_INT_32 || item.muteUntil <= getServerTime()) return;
|
||||||
|
unmuteQueue.push(item);
|
||||||
|
unmuteQueue.sort((a, b) => b.muteUntil - a.muteUntil);
|
||||||
|
const next = unmuteQueue.pop();
|
||||||
|
if (!next) return;
|
||||||
|
const timer = setTimeout(() => {
|
||||||
|
onUpdate();
|
||||||
|
if (unmuteQueue.length) {
|
||||||
|
const afterNext = unmuteQueue.pop();
|
||||||
|
if (afterNext) scheduleUnmute(afterNext, onUpdate);
|
||||||
|
}
|
||||||
|
}, (item.muteUntil - getServerTime()) * 1000);
|
||||||
|
unmuteTimers.set(id, timer);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function scheduleMutedChatUpdate(chatId: string, muteUntil = 0, onUpdate: OnApiUpdate) {
|
||||||
|
scheduleUnmute({
|
||||||
|
chatId,
|
||||||
|
muteUntil,
|
||||||
|
}, () => onUpdate({
|
||||||
|
'@type': 'updateNotifyExceptions',
|
||||||
|
chatId,
|
||||||
|
isMuted: false,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function scheduleMutedTopicUpdate(chatId: string, topicId: number, muteUntil = 0, onUpdate: OnApiUpdate) {
|
||||||
|
scheduleUnmute({
|
||||||
|
chatId,
|
||||||
|
topicId,
|
||||||
|
muteUntil,
|
||||||
|
}, () => onUpdate({
|
||||||
|
'@type': 'updateTopicNotifyExceptions',
|
||||||
|
chatId,
|
||||||
|
topicId,
|
||||||
|
isMuted: false,
|
||||||
|
}));
|
||||||
|
}
|
||||||
@ -66,6 +66,7 @@ import {
|
|||||||
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './apiBuilders/peers';
|
import { buildApiPeerId, getApiChatIdFromMtpPeer } from './apiBuilders/peers';
|
||||||
import { buildApiEmojiInteraction, buildStickerSet } from './apiBuilders/symbols';
|
import { buildApiEmojiInteraction, buildStickerSet } from './apiBuilders/symbols';
|
||||||
import { buildApiBotMenuButton } from './apiBuilders/bots';
|
import { buildApiBotMenuButton } from './apiBuilders/bots';
|
||||||
|
import { scheduleMutedTopicUpdate, scheduleMutedChatUpdate } from './scheduleUnmute';
|
||||||
|
|
||||||
type Update = (
|
type Update = (
|
||||||
(GramJs.TypeUpdate | GramJs.TypeUpdates) & { _entities?: (GramJs.TypeUser | GramJs.TypeChat)[] }
|
(GramJs.TypeUpdate | GramJs.TypeUpdates) & { _entities?: (GramJs.TypeUser | GramJs.TypeChat)[] }
|
||||||
@ -635,19 +636,23 @@ export function updater(update: Update) {
|
|||||||
update instanceof GramJs.UpdateNotifySettings
|
update instanceof GramJs.UpdateNotifySettings
|
||||||
&& update.peer instanceof GramJs.NotifyPeer
|
&& update.peer instanceof GramJs.NotifyPeer
|
||||||
) {
|
) {
|
||||||
|
const payload = buildApiNotifyException(update.notifySettings, update.peer.peer);
|
||||||
|
scheduleMutedChatUpdate(payload.chatId, payload.muteUntil, onUpdate);
|
||||||
onUpdate({
|
onUpdate({
|
||||||
'@type': 'updateNotifyExceptions',
|
'@type': 'updateNotifyExceptions',
|
||||||
...buildApiNotifyException(update.notifySettings, update.peer.peer),
|
...payload,
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
update instanceof GramJs.UpdateNotifySettings
|
update instanceof GramJs.UpdateNotifySettings
|
||||||
&& update.peer instanceof GramJs.NotifyForumTopic
|
&& update.peer instanceof GramJs.NotifyForumTopic
|
||||||
) {
|
) {
|
||||||
|
const payload = buildApiNotifyExceptionTopic(
|
||||||
|
update.notifySettings, update.peer.peer, update.peer.topMsgId,
|
||||||
|
);
|
||||||
|
scheduleMutedTopicUpdate(payload.chatId, payload.topicId, payload.muteUntil, onUpdate);
|
||||||
onUpdate({
|
onUpdate({
|
||||||
'@type': 'updateTopicNotifyExceptions',
|
'@type': 'updateTopicNotifyExceptions',
|
||||||
...buildApiNotifyExceptionTopic(
|
...payload,
|
||||||
update.notifySettings, update.peer.peer, update.peer.topMsgId,
|
|
||||||
),
|
|
||||||
});
|
});
|
||||||
} else if (
|
} else if (
|
||||||
update instanceof GramJs.UpdateUserTyping
|
update instanceof GramJs.UpdateUserTyping
|
||||||
|
|||||||
@ -25,6 +25,7 @@ export interface ApiChat {
|
|||||||
unreadReactionsCount?: number;
|
unreadReactionsCount?: number;
|
||||||
isVerified?: boolean;
|
isVerified?: boolean;
|
||||||
isMuted?: boolean;
|
isMuted?: boolean;
|
||||||
|
muteUntil?: number;
|
||||||
isSignaturesShown?: boolean;
|
isSignaturesShown?: boolean;
|
||||||
hasPrivateLink?: boolean;
|
hasPrivateLink?: boolean;
|
||||||
accessHash?: string;
|
accessHash?: string;
|
||||||
@ -221,8 +222,8 @@ export interface ApiTopic {
|
|||||||
unreadMentionsCount: number;
|
unreadMentionsCount: number;
|
||||||
unreadReactionsCount: number;
|
unreadReactionsCount: number;
|
||||||
fromId: string;
|
fromId: string;
|
||||||
|
|
||||||
isMuted?: boolean;
|
isMuted?: boolean;
|
||||||
|
muteUntil?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiChatlistInviteNew {
|
export interface ApiChatlistInviteNew {
|
||||||
|
|||||||
@ -39,6 +39,7 @@ export { default as NewChatStep1 } from '../components/left/newChat/NewChatStep1
|
|||||||
export { default as NewChatStep2 } from '../components/left/newChat/NewChatStep2';
|
export { default as NewChatStep2 } from '../components/left/newChat/NewChatStep2';
|
||||||
export { default as ArchivedChats } from '../components/left/ArchivedChats';
|
export { default as ArchivedChats } from '../components/left/ArchivedChats';
|
||||||
export { default as ChatFolderModal } from '../components/left/ChatFolderModal';
|
export { default as ChatFolderModal } from '../components/left/ChatFolderModal';
|
||||||
|
export { default as MuteChatModal } from '../components/left/MuteChatModal';
|
||||||
|
|
||||||
export { default as ContextMenuContainer } from '../components/middle/message/ContextMenuContainer';
|
export { default as ContextMenuContainer } from '../components/middle/message/ContextMenuContainer';
|
||||||
export { default as SponsoredMessageContextMenuContainer }
|
export { default as SponsoredMessageContextMenuContainer }
|
||||||
|
|||||||
16
src/components/left/MuteChatModal.async.tsx
Normal file
16
src/components/left/MuteChatModal.async.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import type { FC } from '../../lib/teact/teact';
|
||||||
|
import React, { memo } from '../../lib/teact/teact';
|
||||||
|
import { Bundles } from '../../util/moduleLoader';
|
||||||
|
import type { OwnProps } from './MuteChatModal';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
const MuteChatModalAsync: FC<OwnProps> = (props) => {
|
||||||
|
const { isOpen } = props;
|
||||||
|
const MuteChatModal = useModuleLoader(Bundles.Extra, 'MuteChatModal', !isOpen);
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
return MuteChatModal ? <MuteChatModal {...props} /> : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(MuteChatModalAsync);
|
||||||
92
src/components/left/MuteChatModal.tsx
Normal file
92
src/components/left/MuteChatModal.tsx
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import type { FC } from '../../lib/teact/teact';
|
||||||
|
import React, {
|
||||||
|
useCallback, memo, useMemo, useState,
|
||||||
|
} from '../../lib/teact/teact';
|
||||||
|
import { getActions } from '../../global';
|
||||||
|
|
||||||
|
import { MAX_INT_32 } from '../../config';
|
||||||
|
import useLang from '../../hooks/useLang';
|
||||||
|
|
||||||
|
import Modal from '../ui/Modal';
|
||||||
|
import Button from '../ui/Button';
|
||||||
|
import RadioGroup from '../ui/RadioGroup';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
isOpen: boolean;
|
||||||
|
chatId: string;
|
||||||
|
topicId?: number;
|
||||||
|
onClose: () => void;
|
||||||
|
onCloseAnimationEnd?: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum MuteDuration {
|
||||||
|
OneHour = '3600',
|
||||||
|
FourHours = '14400',
|
||||||
|
EightHours = '28800',
|
||||||
|
OneDay = '86400',
|
||||||
|
ThreeDays = '259200',
|
||||||
|
Forever = '-1',
|
||||||
|
}
|
||||||
|
|
||||||
|
const MuteChatModal: FC<OwnProps> = ({
|
||||||
|
isOpen,
|
||||||
|
chatId,
|
||||||
|
topicId,
|
||||||
|
onClose,
|
||||||
|
onCloseAnimationEnd,
|
||||||
|
}) => {
|
||||||
|
const [muteUntilOption, setMuteUntilOption] = useState(MuteDuration.Forever);
|
||||||
|
const { updateChatMutedState, updateTopicMutedState } = getActions();
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
|
||||||
|
const muteForOptions = useMemo(() => [
|
||||||
|
{ label: lang('MuteFor.Hours', 1), value: MuteDuration.OneHour },
|
||||||
|
{ label: lang('MuteFor.Hours', 4), value: MuteDuration.FourHours },
|
||||||
|
{ label: lang('MuteFor.Hours', 8), value: MuteDuration.EightHours },
|
||||||
|
{ label: lang('MuteFor.Days', 1), value: MuteDuration.OneDay },
|
||||||
|
{ label: lang('MuteFor.Days', 3), value: MuteDuration.ThreeDays },
|
||||||
|
{ label: lang('MuteFor.Forever'), value: MuteDuration.Forever },
|
||||||
|
], [lang]);
|
||||||
|
|
||||||
|
const handleSubmit = useCallback(() => {
|
||||||
|
let muteUntil: number;
|
||||||
|
if (muteUntilOption === MuteDuration.Forever) {
|
||||||
|
muteUntil = MAX_INT_32;
|
||||||
|
} else {
|
||||||
|
muteUntil = Math.floor(Date.now() / 1000) + Number(muteUntilOption);
|
||||||
|
}
|
||||||
|
if (topicId) {
|
||||||
|
updateTopicMutedState({ chatId, topicId, muteUntil });
|
||||||
|
} else {
|
||||||
|
updateChatMutedState({ chatId, muteUntil });
|
||||||
|
}
|
||||||
|
onClose();
|
||||||
|
}, [chatId, muteUntilOption, onClose, topicId]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isOpen={isOpen}
|
||||||
|
onClose={onClose}
|
||||||
|
onCloseAnimationEnd={onCloseAnimationEnd}
|
||||||
|
onEnter={handleSubmit}
|
||||||
|
className="delete"
|
||||||
|
title={lang('Notifications')}
|
||||||
|
>
|
||||||
|
<RadioGroup
|
||||||
|
name="muteFor"
|
||||||
|
options={muteForOptions}
|
||||||
|
selected={muteUntilOption}
|
||||||
|
onChange={setMuteUntilOption as any}
|
||||||
|
/>
|
||||||
|
<div className="dialog-buttons">
|
||||||
|
<Button color="primary" className="confirm-dialog-button" isText onClick={handleSubmit}>
|
||||||
|
{lang('Common.Done')}
|
||||||
|
</Button>
|
||||||
|
<Button className="confirm-dialog-button" isText onClick={onClose}>{lang('Cancel')}</Button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(MuteChatModal);
|
||||||
@ -54,6 +54,7 @@ import DeleteChatModal from '../../common/DeleteChatModal';
|
|||||||
import ReportModal from '../../common/ReportModal';
|
import ReportModal from '../../common/ReportModal';
|
||||||
import FullNameTitle from '../../common/FullNameTitle';
|
import FullNameTitle from '../../common/FullNameTitle';
|
||||||
import ChatFolderModal from '../ChatFolderModal.async';
|
import ChatFolderModal from '../ChatFolderModal.async';
|
||||||
|
import MuteChatModal from '../MuteChatModal.async';
|
||||||
import ChatCallStatus from './ChatCallStatus';
|
import ChatCallStatus from './ChatCallStatus';
|
||||||
import ChatBadge from './ChatBadge';
|
import ChatBadge from './ChatBadge';
|
||||||
import AvatarBadge from './AvatarBadge';
|
import AvatarBadge from './AvatarBadge';
|
||||||
@ -130,9 +131,11 @@ const Chat: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const { isMobile } = useAppLayout();
|
const { isMobile } = useAppLayout();
|
||||||
const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useFlag();
|
const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useFlag();
|
||||||
|
const [isMuteModalOpen, openMuteModal, closeMuteModal] = useFlag();
|
||||||
const [isChatFolderModalOpen, openChatFolderModal, closeChatFolderModal] = useFlag();
|
const [isChatFolderModalOpen, openChatFolderModal, closeChatFolderModal] = useFlag();
|
||||||
const [isReportModalOpen, openReportModal, closeReportModal] = useFlag();
|
const [isReportModalOpen, openReportModal, closeReportModal] = useFlag();
|
||||||
const [shouldRenderDeleteModal, markRenderDeleteModal, unmarkRenderDeleteModal] = useFlag();
|
const [shouldRenderDeleteModal, markRenderDeleteModal, unmarkRenderDeleteModal] = useFlag();
|
||||||
|
const [shouldRenderMuteModal, markRenderMuteModal, unmarkRenderMuteModal] = useFlag();
|
||||||
const [shouldRenderChatFolderModal, markRenderChatFolderModal, unmarkRenderChatFolderModal] = useFlag();
|
const [shouldRenderChatFolderModal, markRenderChatFolderModal, unmarkRenderChatFolderModal] = useFlag();
|
||||||
const [shouldRenderReportModal, markRenderReportModal, unmarkRenderReportModal] = useFlag();
|
const [shouldRenderReportModal, markRenderReportModal, unmarkRenderReportModal] = useFlag();
|
||||||
|
|
||||||
@ -183,6 +186,11 @@ const Chat: FC<OwnProps & StateProps> = ({
|
|||||||
openDeleteModal();
|
openDeleteModal();
|
||||||
}, [markRenderDeleteModal, openDeleteModal]);
|
}, [markRenderDeleteModal, openDeleteModal]);
|
||||||
|
|
||||||
|
const handleMute = useCallback(() => {
|
||||||
|
markRenderMuteModal();
|
||||||
|
openMuteModal();
|
||||||
|
}, [markRenderMuteModal, openMuteModal]);
|
||||||
|
|
||||||
const handleChatFolderChange = useCallback(() => {
|
const handleChatFolderChange = useCallback(() => {
|
||||||
markRenderChatFolderModal();
|
markRenderChatFolderModal();
|
||||||
openChatFolderModal();
|
openChatFolderModal();
|
||||||
@ -197,6 +205,7 @@ const Chat: FC<OwnProps & StateProps> = ({
|
|||||||
chat,
|
chat,
|
||||||
user,
|
user,
|
||||||
handleDelete,
|
handleDelete,
|
||||||
|
handleMute,
|
||||||
handleChatFolderChange,
|
handleChatFolderChange,
|
||||||
handleReport,
|
handleReport,
|
||||||
folderId,
|
folderId,
|
||||||
@ -281,6 +290,14 @@ const Chat: FC<OwnProps & StateProps> = ({
|
|||||||
chat={chat}
|
chat={chat}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{shouldRenderMuteModal && (
|
||||||
|
<MuteChatModal
|
||||||
|
isOpen={isMuteModalOpen}
|
||||||
|
onClose={closeMuteModal}
|
||||||
|
onCloseAnimationEnd={unmarkRenderMuteModal}
|
||||||
|
chatId={chatId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{shouldRenderChatFolderModal && (
|
{shouldRenderChatFolderModal && (
|
||||||
<ChatFolderModal
|
<ChatFolderModal
|
||||||
isOpen={isChatFolderModalOpen}
|
isOpen={isChatFolderModalOpen}
|
||||||
|
|||||||
@ -36,6 +36,7 @@ import useLang from '../../../hooks/useLang';
|
|||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import LastMessageMeta from '../../common/LastMessageMeta';
|
import LastMessageMeta from '../../common/LastMessageMeta';
|
||||||
import ChatBadge from './ChatBadge';
|
import ChatBadge from './ChatBadge';
|
||||||
|
import MuteChatModal from '../MuteChatModal.async';
|
||||||
import ConfirmDialog from '../../ui/ConfirmDialog';
|
import ConfirmDialog from '../../ui/ConfirmDialog';
|
||||||
import TopicIcon from '../../common/TopicIcon';
|
import TopicIcon from '../../common/TopicIcon';
|
||||||
|
|
||||||
@ -95,7 +96,9 @@ const Topic: FC<OwnProps & StateProps> = ({
|
|||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useFlag();
|
const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useFlag();
|
||||||
|
const [isMuteModalOpen, openMuteModal, closeMuteModal] = useFlag();
|
||||||
const [shouldRenderDeleteModal, markRenderDeleteModal, unmarkRenderDeleteModal] = useFlag();
|
const [shouldRenderDeleteModal, markRenderDeleteModal, unmarkRenderDeleteModal] = useFlag();
|
||||||
|
const [shouldRenderMuteModal, markRenderMuteModal, unmarkRenderMuteModal] = useFlag();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
isPinned, isClosed,
|
isPinned, isClosed,
|
||||||
@ -111,6 +114,11 @@ const Topic: FC<OwnProps & StateProps> = ({
|
|||||||
deleteTopic({ chatId: chat.id, topicId: topic.id });
|
deleteTopic({ chatId: chat.id, topicId: topic.id });
|
||||||
}, [chat.id, deleteTopic, topic.id]);
|
}, [chat.id, deleteTopic, topic.id]);
|
||||||
|
|
||||||
|
const handleMute = useCallback(() => {
|
||||||
|
markRenderMuteModal();
|
||||||
|
openMuteModal();
|
||||||
|
}, [markRenderMuteModal, openMuteModal]);
|
||||||
|
|
||||||
const { renderSubtitle, ref } = useChatListEntry({
|
const { renderSubtitle, ref } = useChatListEntry({
|
||||||
chat,
|
chat,
|
||||||
chatId,
|
chatId,
|
||||||
@ -138,7 +146,14 @@ const Topic: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
}, [openChat, chatId, topic.id, canScrollDown, focusLastMessage]);
|
}, [openChat, chatId, topic.id, canScrollDown, focusLastMessage]);
|
||||||
|
|
||||||
const contextActions = useTopicContextActions(topic, chat, wasTopicOpened, canDelete, handleOpenDeleteModal);
|
const contextActions = useTopicContextActions({
|
||||||
|
topic,
|
||||||
|
chat,
|
||||||
|
wasOpened: wasTopicOpened,
|
||||||
|
canDelete,
|
||||||
|
handleDelete: handleOpenDeleteModal,
|
||||||
|
handleMute,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ListItem
|
<ListItem
|
||||||
@ -188,7 +203,6 @@ const Topic: FC<OwnProps & StateProps> = ({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{shouldRenderDeleteModal && (
|
{shouldRenderDeleteModal && (
|
||||||
<ConfirmDialog
|
<ConfirmDialog
|
||||||
isOpen={isDeleteModalOpen}
|
isOpen={isDeleteModalOpen}
|
||||||
@ -200,6 +214,15 @@ const Topic: FC<OwnProps & StateProps> = ({
|
|||||||
confirmLabel={lang('Delete')}
|
confirmLabel={lang('Delete')}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{shouldRenderMuteModal && (
|
||||||
|
<MuteChatModal
|
||||||
|
isOpen={isMuteModalOpen}
|
||||||
|
onClose={closeMuteModal}
|
||||||
|
onCloseAnimationEnd={unmarkRenderMuteModal}
|
||||||
|
chatId={chatId}
|
||||||
|
topicId={topic.id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -10,13 +10,21 @@ import { IS_OPEN_IN_NEW_TAB_SUPPORTED } from '../../../../util/windowEnvironment
|
|||||||
|
|
||||||
import useLang from '../../../../hooks/useLang';
|
import useLang from '../../../../hooks/useLang';
|
||||||
|
|
||||||
export default function useTopicContextActions(
|
export default function useTopicContextActions({
|
||||||
topic: ApiTopic,
|
topic,
|
||||||
chat: ApiChat,
|
chat,
|
||||||
wasOpened?: boolean,
|
wasOpened,
|
||||||
canDelete?: boolean,
|
canDelete,
|
||||||
handleDelete?: NoneToVoidFunction,
|
handleDelete,
|
||||||
) {
|
handleMute,
|
||||||
|
}: {
|
||||||
|
topic: ApiTopic;
|
||||||
|
chat: ApiChat;
|
||||||
|
wasOpened?: boolean;
|
||||||
|
canDelete?: boolean;
|
||||||
|
handleDelete?: NoneToVoidFunction;
|
||||||
|
handleMute?: NoneToVoidFunction;
|
||||||
|
}) {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
return useMemo(() => {
|
return useMemo(() => {
|
||||||
@ -76,9 +84,9 @@ export default function useTopicContextActions(
|
|||||||
handler: () => updateTopicMutedState({ chatId, topicId, isMuted: false }),
|
handler: () => updateTopicMutedState({ chatId, topicId, isMuted: false }),
|
||||||
}
|
}
|
||||||
: {
|
: {
|
||||||
title: lang('ChatList.Mute'),
|
title: `${lang('ChatList.Mute')}...`,
|
||||||
icon: 'mute',
|
icon: 'mute',
|
||||||
handler: () => updateTopicMutedState({ chatId, topicId, isMuted: true }),
|
handler: handleMute,
|
||||||
};
|
};
|
||||||
|
|
||||||
const actionCloseTopic = canToggleClosed ? (isClosed
|
const actionCloseTopic = canToggleClosed ? (isClosed
|
||||||
@ -109,5 +117,5 @@ export default function useTopicContextActions(
|
|||||||
actionCloseTopic,
|
actionCloseTopic,
|
||||||
actionDelete,
|
actionDelete,
|
||||||
]) as MenuItemContextAction[];
|
]) as MenuItemContextAction[];
|
||||||
}, [topic, chat, wasOpened, lang, canDelete, handleDelete]);
|
}, [topic, chat, wasOpened, lang, canDelete, handleDelete, handleMute]);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,9 +14,9 @@ import useSelectWithEnter from '../../../hooks/useSelectWithEnter';
|
|||||||
|
|
||||||
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
import PrivateChatInfo from '../../common/PrivateChatInfo';
|
||||||
import GroupChatInfo from '../../common/GroupChatInfo';
|
import GroupChatInfo from '../../common/GroupChatInfo';
|
||||||
import DeleteChatModal from '../../common/DeleteChatModal';
|
|
||||||
import ListItem from '../../ui/ListItem';
|
import ListItem from '../../ui/ListItem';
|
||||||
import ChatFolderModal from '../ChatFolderModal.async';
|
import ChatFolderModal from '../ChatFolderModal.async';
|
||||||
|
import MuteChatModal from '../MuteChatModal.async';
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
@ -42,8 +42,20 @@ const LeftSearchResultChat: FC<OwnProps & StateProps> = ({
|
|||||||
isMuted,
|
isMuted,
|
||||||
canChangeFolder,
|
canChangeFolder,
|
||||||
}) => {
|
}) => {
|
||||||
const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useFlag();
|
const [isMuteModalOpen, openMuteModal, closeMuteModal] = useFlag();
|
||||||
const [isChatFolderModalOpen, openChatFolderModal, closeChatFolderModal] = useFlag();
|
const [isChatFolderModalOpen, openChatFolderModal, closeChatFolderModal] = useFlag();
|
||||||
|
const [shouldRenderChatFolderModal, markRenderChatFolderModal, unmarkRenderChatFolderModal] = useFlag();
|
||||||
|
const [shouldRenderMuteModal, markRenderMuteModal, unmarkRenderMuteModal] = useFlag();
|
||||||
|
|
||||||
|
const handleChatFolderChange = useCallback(() => {
|
||||||
|
markRenderChatFolderModal();
|
||||||
|
openChatFolderModal();
|
||||||
|
}, [markRenderChatFolderModal, openChatFolderModal]);
|
||||||
|
|
||||||
|
const handleMute = useCallback(() => {
|
||||||
|
markRenderMuteModal();
|
||||||
|
openMuteModal();
|
||||||
|
}, [markRenderMuteModal, openMuteModal]);
|
||||||
|
|
||||||
const contextActions = useChatContextActions({
|
const contextActions = useChatContextActions({
|
||||||
chat,
|
chat,
|
||||||
@ -51,8 +63,8 @@ const LeftSearchResultChat: FC<OwnProps & StateProps> = ({
|
|||||||
isPinned,
|
isPinned,
|
||||||
isMuted,
|
isMuted,
|
||||||
canChangeFolder,
|
canChangeFolder,
|
||||||
handleDelete: openDeleteModal,
|
handleMute,
|
||||||
handleChatFolderChange: openChatFolderModal,
|
handleChatFolderChange,
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
@ -77,16 +89,22 @@ const LeftSearchResultChat: FC<OwnProps & StateProps> = ({
|
|||||||
) : (
|
) : (
|
||||||
<GroupChatInfo chatId={chatId} withUsername={withUsername} avatarSize="large" />
|
<GroupChatInfo chatId={chatId} withUsername={withUsername} avatarSize="large" />
|
||||||
)}
|
)}
|
||||||
<DeleteChatModal
|
{shouldRenderMuteModal && (
|
||||||
isOpen={isDeleteModalOpen}
|
<MuteChatModal
|
||||||
onClose={closeDeleteModal}
|
isOpen={isMuteModalOpen}
|
||||||
chat={chat}
|
onClose={closeMuteModal}
|
||||||
/>
|
onCloseAnimationEnd={unmarkRenderMuteModal}
|
||||||
<ChatFolderModal
|
chatId={chatId}
|
||||||
isOpen={isChatFolderModalOpen}
|
/>
|
||||||
onClose={closeChatFolderModal}
|
)}
|
||||||
chatId={chatId}
|
{shouldRenderChatFolderModal && (
|
||||||
/>
|
<ChatFolderModal
|
||||||
|
isOpen={isChatFolderModalOpen}
|
||||||
|
onClose={closeChatFolderModal}
|
||||||
|
onCloseAnimationEnd={unmarkRenderChatFolderModal}
|
||||||
|
chatId={chatId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</ListItem>
|
</ListItem>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -36,6 +36,7 @@ import {
|
|||||||
import useShowTransition from '../../hooks/useShowTransition';
|
import useShowTransition from '../../hooks/useShowTransition';
|
||||||
import usePrevDuringAnimation from '../../hooks/usePrevDuringAnimation';
|
import usePrevDuringAnimation from '../../hooks/usePrevDuringAnimation';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
|
import useFlag from '../../hooks/useFlag';
|
||||||
import useAppLayout from '../../hooks/useAppLayout';
|
import useAppLayout from '../../hooks/useAppLayout';
|
||||||
|
|
||||||
import Portal from '../ui/Portal';
|
import Portal from '../ui/Portal';
|
||||||
@ -43,6 +44,7 @@ import Menu from '../ui/Menu';
|
|||||||
import MenuItem from '../ui/MenuItem';
|
import MenuItem from '../ui/MenuItem';
|
||||||
import MenuSeparator from '../ui/MenuSeparator';
|
import MenuSeparator from '../ui/MenuSeparator';
|
||||||
import DeleteChatModal from '../common/DeleteChatModal';
|
import DeleteChatModal from '../common/DeleteChatModal';
|
||||||
|
import MuteChatModal from '../left/MuteChatModal.async';
|
||||||
import ReportModal from '../common/ReportModal';
|
import ReportModal from '../common/ReportModal';
|
||||||
|
|
||||||
import './HeaderMenuContainer.scss';
|
import './HeaderMenuContainer.scss';
|
||||||
@ -170,6 +172,8 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
|
|||||||
const [isMenuOpen, setIsMenuOpen] = useState(true);
|
const [isMenuOpen, setIsMenuOpen] = useState(true);
|
||||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||||
const [isReportModalOpen, setIsReportModalOpen] = useState(false);
|
const [isReportModalOpen, setIsReportModalOpen] = useState(false);
|
||||||
|
const [isMuteModalOpen, setIsMuteModalOpen] = useState(false);
|
||||||
|
const [shouldRenderMuteModal, markRenderMuteModal, unmarkRenderMuteModal] = useFlag();
|
||||||
const { x, y } = anchor;
|
const { x, y } = anchor;
|
||||||
|
|
||||||
useShowTransition(isOpen, onCloseAnimationEnd, undefined, false);
|
useShowTransition(isOpen, onCloseAnimationEnd, undefined, false);
|
||||||
@ -187,6 +191,11 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
|
|||||||
onClose();
|
onClose();
|
||||||
}, [onClose]);
|
}, [onClose]);
|
||||||
|
|
||||||
|
const closeMuteModal = useCallback(() => {
|
||||||
|
setIsMuteModalOpen(false);
|
||||||
|
onClose();
|
||||||
|
}, [onClose]);
|
||||||
|
|
||||||
const handleDelete = useCallback(() => {
|
const handleDelete = useCallback(() => {
|
||||||
setIsMenuOpen(false);
|
setIsMenuOpen(false);
|
||||||
setIsDeleteModalOpen(true);
|
setIsDeleteModalOpen(true);
|
||||||
@ -215,10 +224,16 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
|
|||||||
restartBot({ chatId });
|
restartBot({ chatId });
|
||||||
}, [chatId, restartBot]);
|
}, [chatId, restartBot]);
|
||||||
|
|
||||||
const handleToggleMuteClick = useCallback(() => {
|
const handleUnmuteClick = useCallback(() => {
|
||||||
updateChatMutedState({ chatId, isMuted: !isMuted });
|
updateChatMutedState({ chatId, isMuted: false });
|
||||||
closeMenu();
|
closeMenu();
|
||||||
}, [chatId, closeMenu, isMuted, updateChatMutedState]);
|
}, [chatId, closeMenu, updateChatMutedState]);
|
||||||
|
|
||||||
|
const handleMuteClick = useCallback(() => {
|
||||||
|
markRenderMuteModal();
|
||||||
|
setIsMuteModalOpen(true);
|
||||||
|
setIsMenuOpen(false);
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleCreateTopicClick = useCallback(() => {
|
const handleCreateTopicClick = useCallback(() => {
|
||||||
openCreateTopicPanel({ chatId });
|
openCreateTopicPanel({ chatId });
|
||||||
@ -446,13 +461,22 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
|
|||||||
{lang('VideoCall')}
|
{lang('VideoCall')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
)}
|
)}
|
||||||
{canMute && (
|
{canMute && (isMuted ? (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={isMuted ? 'unmute' : 'mute'}
|
icon="unmute"
|
||||||
onClick={handleToggleMuteClick}
|
onClick={handleUnmuteClick}
|
||||||
>
|
>
|
||||||
{lang(isMuted ? 'ChatsUnmute' : 'ChatsMute')}
|
{lang('ChatsUnmute')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
|
)
|
||||||
|
: (
|
||||||
|
<MenuItem
|
||||||
|
icon="mute"
|
||||||
|
onClick={handleMuteClick}
|
||||||
|
>
|
||||||
|
{lang('ChatsMute')}...
|
||||||
|
</MenuItem>
|
||||||
|
)
|
||||||
)}
|
)}
|
||||||
{(canEnterVoiceChat || canCreateVoiceChat) && (
|
{(canEnterVoiceChat || canCreateVoiceChat) && (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
@ -525,6 +549,14 @@ const HeaderMenuContainer: FC<OwnProps & StateProps> = ({
|
|||||||
chat={chat}
|
chat={chat}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{canMute && shouldRenderMuteModal && chat?.id && (
|
||||||
|
<MuteChatModal
|
||||||
|
isOpen={isMuteModalOpen}
|
||||||
|
onClose={closeMuteModal}
|
||||||
|
onCloseAnimationEnd={unmarkRenderMuteModal}
|
||||||
|
chatId={chat.id}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{canReportChat && chat?.id && (
|
{canReportChat && chat?.id && (
|
||||||
<ReportModal
|
<ReportModal
|
||||||
isOpen={isReportModalOpen}
|
isOpen={isReportModalOpen}
|
||||||
|
|||||||
@ -349,28 +349,32 @@ addActionHandler('requestChatUpdate', (global, actions, payload): ActionReturnTy
|
|||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('updateChatMutedState', (global, actions, payload): ActionReturnType => {
|
addActionHandler('updateChatMutedState', (global, actions, payload): ActionReturnType => {
|
||||||
const { chatId, isMuted } = payload;
|
const { chatId, muteUntil = 0 } = payload;
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isMuted = payload.isMuted ?? muteUntil > 0;
|
||||||
|
|
||||||
global = updateChat(global, chatId, { isMuted });
|
global = updateChat(global, chatId, { isMuted });
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
void callApi('updateChatMutedState', { chat, isMuted });
|
void callApi('updateChatMutedState', { chat, isMuted, muteUntil });
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('updateTopicMutedState', (global, actions, payload): ActionReturnType => {
|
addActionHandler('updateTopicMutedState', (global, actions, payload): ActionReturnType => {
|
||||||
const { chatId, isMuted, topicId } = payload;
|
const { chatId, topicId, muteUntil = 0 } = payload;
|
||||||
const chat = selectChat(global, chatId);
|
const chat = selectChat(global, chatId);
|
||||||
if (!chat) {
|
if (!chat) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const isMuted = payload.isMuted ?? muteUntil > 0;
|
||||||
|
|
||||||
global = updateTopic(global, chatId, topicId, { isMuted });
|
global = updateTopic(global, chatId, topicId, { isMuted });
|
||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
void callApi('updateTopicMutedState', {
|
void callApi('updateTopicMutedState', {
|
||||||
chat, topicId, isMuted,
|
chat, topicId, isMuted, muteUntil,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -1624,7 +1624,8 @@ export interface ActionPayloads {
|
|||||||
};
|
};
|
||||||
updateChatMutedState: {
|
updateChatMutedState: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
isMuted: boolean;
|
isMuted?: boolean;
|
||||||
|
muteUntil?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
updateChat: {
|
updateChat: {
|
||||||
@ -2475,7 +2476,8 @@ export interface ActionPayloads {
|
|||||||
updateTopicMutedState: {
|
updateTopicMutedState: {
|
||||||
chatId: string;
|
chatId: string;
|
||||||
topicId: number;
|
topicId: number;
|
||||||
isMuted: boolean;
|
isMuted?: boolean;
|
||||||
|
muteUntil?: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
openCreateTopicPanel: {
|
openCreateTopicPanel: {
|
||||||
|
|||||||
@ -20,6 +20,7 @@ const useChatContextActions = ({
|
|||||||
isMuted,
|
isMuted,
|
||||||
canChangeFolder,
|
canChangeFolder,
|
||||||
handleDelete,
|
handleDelete,
|
||||||
|
handleMute,
|
||||||
handleChatFolderChange,
|
handleChatFolderChange,
|
||||||
handleReport,
|
handleReport,
|
||||||
}: {
|
}: {
|
||||||
@ -29,9 +30,10 @@ const useChatContextActions = ({
|
|||||||
isPinned?: boolean;
|
isPinned?: boolean;
|
||||||
isMuted?: boolean;
|
isMuted?: boolean;
|
||||||
canChangeFolder?: boolean;
|
canChangeFolder?: boolean;
|
||||||
handleDelete: () => void;
|
handleDelete?: NoneToVoidFunction;
|
||||||
handleChatFolderChange: () => void;
|
handleMute?: NoneToVoidFunction;
|
||||||
handleReport?: () => void;
|
handleChatFolderChange: NoneToVoidFunction;
|
||||||
|
handleReport?: NoneToVoidFunction;
|
||||||
}, isInSearch = false) => {
|
}, isInSearch = false) => {
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
@ -73,8 +75,20 @@ const useChatContextActions = ({
|
|||||||
}
|
}
|
||||||
: { title: lang('PinToTop'), icon: 'pin', handler: () => toggleChatPinned({ id: chat.id, folderId: folderId! }) };
|
: { title: lang('PinToTop'), icon: 'pin', handler: () => toggleChatPinned({ id: chat.id, folderId: folderId! }) };
|
||||||
|
|
||||||
|
const actionMute = isMuted
|
||||||
|
? {
|
||||||
|
title: lang('ChatList.Unmute'),
|
||||||
|
icon: 'unmute',
|
||||||
|
handler: () => updateChatMutedState({ chatId: chat.id, isMuted: false }),
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
title: `${lang('ChatList.Mute')}...`,
|
||||||
|
icon: 'mute',
|
||||||
|
handler: handleMute,
|
||||||
|
};
|
||||||
|
|
||||||
if (isInSearch) {
|
if (isInSearch) {
|
||||||
return compact([actionOpenInNewTab, actionPin, actionAddToFolder]);
|
return compact([actionOpenInNewTab, actionPin, actionAddToFolder, actionMute]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const actionMaskAsRead = (chat.unreadCount || chat.hasUnreadMark)
|
const actionMaskAsRead = (chat.unreadCount || chat.hasUnreadMark)
|
||||||
@ -84,18 +98,6 @@ const useChatContextActions = ({
|
|||||||
? { title: lang('MarkAsUnread'), icon: 'unread', handler: () => toggleChatUnread({ id: chat.id }) }
|
? { title: lang('MarkAsUnread'), icon: 'unread', handler: () => toggleChatUnread({ id: chat.id }) }
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
const actionMute = isMuted
|
|
||||||
? {
|
|
||||||
title: lang('ChatList.Unmute'),
|
|
||||||
icon: 'unmute',
|
|
||||||
handler: () => updateChatMutedState({ chatId: chat.id, isMuted: false }),
|
|
||||||
}
|
|
||||||
: {
|
|
||||||
title: lang('ChatList.Mute'),
|
|
||||||
icon: 'mute',
|
|
||||||
handler: () => updateChatMutedState({ chatId: chat.id, isMuted: true }),
|
|
||||||
};
|
|
||||||
|
|
||||||
const actionArchive = isChatArchived(chat)
|
const actionArchive = isChatArchived(chat)
|
||||||
? { title: lang('Unarchive'), icon: 'unarchive', handler: () => toggleChatArchived({ id: chat.id }) }
|
? { title: lang('Unarchive'), icon: 'unarchive', handler: () => toggleChatArchived({ id: chat.id }) }
|
||||||
: { title: lang('Archive'), icon: 'archive', handler: () => toggleChatArchived({ id: chat.id }) };
|
: { title: lang('Archive'), icon: 'archive', handler: () => toggleChatArchived({ id: chat.id }) };
|
||||||
@ -131,7 +133,7 @@ const useChatContextActions = ({
|
|||||||
]) as MenuItemContextAction[];
|
]) as MenuItemContextAction[];
|
||||||
}, [
|
}, [
|
||||||
chat, user, canChangeFolder, lang, handleChatFolderChange, isPinned, isInSearch, isMuted,
|
chat, user, canChangeFolder, lang, handleChatFolderChange, isPinned, isInSearch, isMuted,
|
||||||
handleDelete, handleReport, folderId, isSelf, isServiceNotifications,
|
handleDelete, handleMute, handleReport, folderId, isSelf, isServiceNotifications,
|
||||||
]);
|
]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user