ClosableEmbeddedMessage Menu: Reply in Another Chat (#4504)
This commit is contained in:
parent
6eaaa5dce5
commit
6e37c5b163
@ -409,6 +409,7 @@ export interface ApiInputMessageReplyInfo {
|
|||||||
replyToTopId?: number;
|
replyToTopId?: number;
|
||||||
replyToPeerId?: string;
|
replyToPeerId?: string;
|
||||||
quoteText?: ApiFormattedText;
|
quoteText?: ApiFormattedText;
|
||||||
|
isShowingDelayNeeded?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiInputStoryReplyInfo {
|
export interface ApiInputStoryReplyInfo {
|
||||||
@ -532,6 +533,9 @@ export type MediaContent = {
|
|||||||
isExpiredRoundVideo?: boolean;
|
isExpiredRoundVideo?: boolean;
|
||||||
ttlSeconds?: number;
|
ttlSeconds?: number;
|
||||||
};
|
};
|
||||||
|
export type MediaContainer = {
|
||||||
|
content: MediaContent;
|
||||||
|
};
|
||||||
|
|
||||||
export interface ApiMessage {
|
export interface ApiMessage {
|
||||||
id: number;
|
id: number;
|
||||||
|
|||||||
1
src/assets/font-icons/remove-quote.svg
Normal file
1
src/assets/font-icons/remove-quote.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="M20 2.667a2.667 2.667 0 0 0-.306 5.316 8.3 8.3 0 0 1-.562 1.45l-.32.64a1.328 1.328 0 0 0 2.376 1.188l.32-.64a10.9 10.9 0 0 0 1.153-4.887v-.23A2.667 2.667 0 0 0 20 2.666zm6.667 0a2.667 2.667 0 0 0-.307 5.316q-.21.749-.561 1.45l-.32.64a1.328 1.328 0 0 0 2.375 1.188l.32-.64a10.9 10.9 0 0 0 1.154-4.887v-.23a2.667 2.667 0 0 0-2.661-2.837M5.333 14.672h4.634l2.656 2.656h-7.29a1.328 1.328 0 0 1 0-2.656m0 8h12.634l2.656 2.656H5.333a1.328 1.328 0 0 1 0-2.656m21.792 2.575 1.814 1.814a1.328 1.328 0 0 1-1.878 1.878l-24-24a1.328 1.328 0 1 1 1.878-1.878L8.55 6.672H14a1.328 1.328 0 0 1 0 2.656h-2.794l5.344 5.344h10.117a1.328 1.328 0 1 1 0 2.656h-7.461l5.344 5.344h2.117a1.328 1.328 0 0 1 .458 2.575"/></svg>
|
||||||
|
After Width: | Height: | Size: 771 B |
1
src/assets/font-icons/show-message.svg
Normal file
1
src/assets/font-icons/show-message.svg
Normal file
@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><path d="M3.4 30.7c.2.1.4.1.6.1.4 0 .8-.1 1.1-.5l4.1-4.1c.4-.3.6-.5.7-.6.05 0 .1-.025.15-.05s.1-.05.15-.05h11.4c2.4 0 3.7 0 4.9-.6 1-.5 1.9-1.4 2.4-2.4.6-1.2.6-2.5.6-4.9v-7.2c0-2.4 0-3.7-.6-4.9-.5-1-1.4-1.9-2.4-2.4-1.2-.6-2.5-.6-4.9-.6H10.4c-2.4 0-3.7 0-4.9.6-1 .5-1.9 1.4-2.4 2.4s-.6 2.2-.6 3.8c0 .8.7 1.5 1.5 1.5s1.5-.7 1.5-1.5q0-1.8.3-2.4c.2-.5.6-.9 1.1-1.1.5-.3 1.6-.3 3.5-.3h11.2c1.9 0 3 0 3.5.3.5.2.9.6 1 1.1.3.5.3 1.6.3 3.5v7.2c0 1.9 0 3-.3 3.5s-.6.9-1.1 1.1c-.5.3-1.6.3-3.5.3H11c-.7 0-1.1 0-1.6.1-.4.1-.8.3-1.2.5-.4.3-.7.6-1.2 1.1l-1.5 1.5v-7c0-.8-.7-1.5-1.5-1.5s-1.5.7-1.5 1.5v10.6c0 .6.3 1.2.9 1.4m9.7-11.1c-.5-.5-.5-1.4 0-1.9l2.3-2.4H1.3C.6 15.3 0 14.7 0 14s.6-1.3 1.3-1.3h14.2l-2.4-2.4c-.5-.5-.5-1.4 0-1.9s1.4-.5 1.9 0l4.7 4.7c.2.2.4.5.4.9 0 .3-.1.6-.3.8L15 19.6c-.5.5-1.4.5-1.9 0"/></svg>
|
||||||
|
After Width: | Height: | Size: 864 B |
@ -110,12 +110,12 @@
|
|||||||
.message-title {
|
.message-title {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-wrap: wrap;
|
flex-wrap: nowrap;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
column-gap: 0.25rem;
|
column-gap: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.message-title, .embedded-sender {
|
.message-title, .embedded-sender, .embedded-sender-chat {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
|
|||||||
@ -3,13 +3,15 @@ import React, { useMemo, useRef } from '../../../lib/teact/teact';
|
|||||||
|
|
||||||
import type {
|
import type {
|
||||||
ApiChat,
|
ApiChat,
|
||||||
ApiMessage, ApiPeer, ApiReplyInfo,
|
ApiMessage, ApiPeer, ApiReplyInfo, MediaContainer,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
import type { ChatTranslatedMessages } from '../../../global/types';
|
import type { ChatTranslatedMessages } from '../../../global/types';
|
||||||
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
import type { ObserveFn } from '../../../hooks/useIntersectionObserver';
|
||||||
import type { IconName } from '../../../types/icons';
|
import type { IconName } from '../../../types/icons';
|
||||||
|
|
||||||
|
import { CONTENT_NOT_SUPPORTED } from '../../../config';
|
||||||
import {
|
import {
|
||||||
|
getMediaContentTypeDescription,
|
||||||
getMessageIsSpoiler,
|
getMessageIsSpoiler,
|
||||||
getMessageMediaHash,
|
getMessageMediaHash,
|
||||||
getMessageRoundVideo,
|
getMessageRoundVideo,
|
||||||
@ -18,6 +20,7 @@ import {
|
|||||||
isChatChannel,
|
isChatChannel,
|
||||||
isChatGroup,
|
isChatGroup,
|
||||||
isMessageTranslatable,
|
isMessageTranslatable,
|
||||||
|
isUserId,
|
||||||
} from '../../../global/helpers';
|
} from '../../../global/helpers';
|
||||||
import buildClassName from '../../../util/buildClassName';
|
import buildClassName from '../../../util/buildClassName';
|
||||||
import freezeWhenClosed from '../../../util/hoc/freezeWhenClosed';
|
import freezeWhenClosed from '../../../util/hoc/freezeWhenClosed';
|
||||||
@ -58,7 +61,7 @@ type OwnProps = {
|
|||||||
isOpen?: boolean;
|
isOpen?: boolean;
|
||||||
observeIntersectionForLoading?: ObserveFn;
|
observeIntersectionForLoading?: ObserveFn;
|
||||||
observeIntersectionForPlaying?: ObserveFn;
|
observeIntersectionForPlaying?: ObserveFn;
|
||||||
onClick: NoneToVoidFunction;
|
onClick: ((e: React.MouseEvent) => void);
|
||||||
};
|
};
|
||||||
|
|
||||||
const NBSP = '\u00A0';
|
const NBSP = '\u00A0';
|
||||||
@ -128,7 +131,7 @@ const EmbeddedMessage: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!message) {
|
if (!message) {
|
||||||
return customText || NBSP;
|
return customText || renderMediaContentType(wrappedMedia) || NBSP;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isActionMessage(message)) {
|
if (isActionMessage(message)) {
|
||||||
@ -155,6 +158,23 @@ const EmbeddedMessage: FC<OwnProps> = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function renderMediaContentType(media?: MediaContainer) {
|
||||||
|
if (!media || media.content.text) return NBSP;
|
||||||
|
const description = getMediaContentTypeDescription(lang, media.content);
|
||||||
|
if (!description || description === CONTENT_NOT_SUPPORTED) return NBSP;
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
{renderText(description)}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkShouldRenderSenderTitle() {
|
||||||
|
if (!senderChat) return true;
|
||||||
|
if (isUserId(senderChat?.id)) return true;
|
||||||
|
if (senderChat.id === sender?.id) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
function renderSender() {
|
function renderSender() {
|
||||||
if (title) {
|
if (title) {
|
||||||
return renderText(title);
|
return renderText(title);
|
||||||
@ -175,18 +195,21 @@ const EmbeddedMessage: FC<OwnProps> = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const isChatSender = senderChat && senderChat.id === sender?.id;
|
|
||||||
const isReplyToQuote = isInComposer && Boolean(replyInfo && 'quoteText' in replyInfo && replyInfo?.quoteText);
|
const isReplyToQuote = isInComposer && Boolean(replyInfo && 'quoteText' in replyInfo && replyInfo?.quoteText);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!isChatSender && (
|
{checkShouldRenderSenderTitle() && (
|
||||||
<span className="embedded-sender">
|
<span className="embedded-sender">
|
||||||
{renderText(isReplyToQuote ? lang('ReplyToQuote', senderTitle) : senderTitle)}
|
{renderText(isReplyToQuote ? lang('ReplyToQuote', senderTitle) : senderTitle)}
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
{icon && <Icon name={icon} className="embedded-chat-icon" />}
|
{icon && <Icon name={icon} className="embedded-chat-icon" />}
|
||||||
{icon && senderChatTitle && renderText(senderChatTitle)}
|
{icon && senderChatTitle && (
|
||||||
|
<span className="embedded-sender-chat">
|
||||||
|
{renderText(senderChatTitle)}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,9 +5,16 @@ import React, {
|
|||||||
import { getActions, getGlobal, withGlobal } from '../../global';
|
import { getActions, getGlobal, withGlobal } from '../../global';
|
||||||
|
|
||||||
import type { ThreadId } from '../../types';
|
import type { ThreadId } from '../../types';
|
||||||
|
import { MAIN_THREAD_ID } from '../../api/types';
|
||||||
|
|
||||||
import { getChatTitle, getUserFirstOrLastName, isUserId } from '../../global/helpers';
|
import { getChatTitle, getUserFirstOrLastName, isUserId } from '../../global/helpers';
|
||||||
import { selectChat, selectTabState, selectUser } from '../../global/selectors';
|
import {
|
||||||
|
selectChat,
|
||||||
|
selectCurrentChat,
|
||||||
|
selectDraft,
|
||||||
|
selectTabState,
|
||||||
|
selectUser,
|
||||||
|
} from '../../global/selectors';
|
||||||
|
|
||||||
import useFlag from '../../hooks/useFlag';
|
import useFlag from '../../hooks/useFlag';
|
||||||
import useLang from '../../hooks/useLang';
|
import useLang from '../../hooks/useLang';
|
||||||
@ -23,6 +30,7 @@ interface StateProps {
|
|||||||
currentUserId?: string;
|
currentUserId?: string;
|
||||||
isManyMessages?: boolean;
|
isManyMessages?: boolean;
|
||||||
isStory?: boolean;
|
isStory?: boolean;
|
||||||
|
isReplying?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ForwardRecipientPicker: FC<OwnProps & StateProps> = ({
|
const ForwardRecipientPicker: FC<OwnProps & StateProps> = ({
|
||||||
@ -30,8 +38,10 @@ const ForwardRecipientPicker: FC<OwnProps & StateProps> = ({
|
|||||||
currentUserId,
|
currentUserId,
|
||||||
isManyMessages,
|
isManyMessages,
|
||||||
isStory,
|
isStory,
|
||||||
|
isReplying,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
|
openChatOrTopicWithReplyInDraft,
|
||||||
setForwardChatOrTopic,
|
setForwardChatOrTopic,
|
||||||
exitForwardMode,
|
exitForwardMode,
|
||||||
forwardToSavedMessages,
|
forwardToSavedMessages,
|
||||||
@ -84,9 +94,15 @@ const ForwardRecipientPicker: FC<OwnProps & StateProps> = ({
|
|||||||
forwardToSavedMessages();
|
forwardToSavedMessages();
|
||||||
showNotification({ message });
|
showNotification({ message });
|
||||||
} else {
|
} else {
|
||||||
setForwardChatOrTopic({ chatId: recipientId, topicId: Number(threadId) });
|
const chatId = recipientId;
|
||||||
|
const topicId = threadId ? Number(threadId) : undefined;
|
||||||
|
if (isReplying) {
|
||||||
|
openChatOrTopicWithReplyInDraft({ chatId, topicId });
|
||||||
|
} else {
|
||||||
|
setForwardChatOrTopic({ chatId, topicId });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [currentUserId, isManyMessages, isStory, lang]);
|
}, [currentUserId, isManyMessages, isStory, lang, isReplying]);
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
exitForwardMode();
|
exitForwardMode();
|
||||||
@ -110,9 +126,12 @@ const ForwardRecipientPicker: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
export default memo(withGlobal<OwnProps>((global): StateProps => {
|
export default memo(withGlobal<OwnProps>((global): StateProps => {
|
||||||
const { messageIds, storyId } = selectTabState(global).forwardMessages;
|
const { messageIds, storyId } = selectTabState(global).forwardMessages;
|
||||||
|
const currentChatId = selectCurrentChat(global)?.id;
|
||||||
|
const isReplying = currentChatId && selectDraft(global, currentChatId, MAIN_THREAD_ID)?.replyInfo;
|
||||||
return {
|
return {
|
||||||
currentUserId: global.currentUserId,
|
currentUserId: global.currentUserId,
|
||||||
isManyMessages: (messageIds?.length || 0) > 1,
|
isManyMessages: (messageIds?.length || 0) > 1,
|
||||||
isStory: Boolean(storyId),
|
isStory: Boolean(storyId),
|
||||||
|
isReplying: Boolean(isReplying),
|
||||||
};
|
};
|
||||||
})(ForwardRecipientPicker));
|
})(ForwardRecipientPicker));
|
||||||
|
|||||||
@ -4,11 +4,14 @@ import React, {
|
|||||||
} from '../../../lib/teact/teact';
|
} from '../../../lib/teact/teact';
|
||||||
import { getActions, withGlobal } from '../../../global';
|
import { getActions, withGlobal } from '../../../global';
|
||||||
|
|
||||||
import type { ApiInputMessageReplyInfo, ApiMessage, ApiPeer } from '../../../api/types';
|
import type {
|
||||||
|
ApiChat, ApiInputMessageReplyInfo, ApiMessage, ApiPeer,
|
||||||
|
} from '../../../api/types';
|
||||||
|
|
||||||
import { stripCustomEmoji } from '../../../global/helpers';
|
import { stripCustomEmoji } from '../../../global/helpers';
|
||||||
import {
|
import {
|
||||||
selectCanAnimateInterface,
|
selectCanAnimateInterface,
|
||||||
|
selectChat,
|
||||||
selectChatMessage,
|
selectChatMessage,
|
||||||
selectCurrentMessageList,
|
selectCurrentMessageList,
|
||||||
selectDraft,
|
selectDraft,
|
||||||
@ -56,6 +59,9 @@ type StateProps = {
|
|||||||
isCurrentUserPremium?: boolean;
|
isCurrentUserPremium?: boolean;
|
||||||
isContextMenuDisabled?: boolean;
|
isContextMenuDisabled?: boolean;
|
||||||
isReplyToDiscussion?: boolean;
|
isReplyToDiscussion?: boolean;
|
||||||
|
isInChangingRecipientMode?: boolean;
|
||||||
|
isChangingChats?: boolean;
|
||||||
|
senderChat?: ApiChat;
|
||||||
};
|
};
|
||||||
|
|
||||||
type OwnProps = {
|
type OwnProps = {
|
||||||
@ -80,12 +86,16 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
isContextMenuDisabled,
|
isContextMenuDisabled,
|
||||||
isReplyToDiscussion,
|
isReplyToDiscussion,
|
||||||
onClear,
|
onClear,
|
||||||
|
isInChangingRecipientMode,
|
||||||
|
isChangingChats,
|
||||||
|
senderChat,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
resetDraftReplyInfo,
|
resetDraftReplyInfo,
|
||||||
|
updateDraftReplyInfo,
|
||||||
setEditingId,
|
setEditingId,
|
||||||
focusMessage,
|
focusMessage,
|
||||||
changeForwardRecipient,
|
changeRecipient,
|
||||||
setForwardNoAuthors,
|
setForwardNoAuthors,
|
||||||
setForwardNoCaptions,
|
setForwardNoCaptions,
|
||||||
exitForwardMode,
|
exitForwardMode,
|
||||||
@ -95,15 +105,17 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
const isReplyToTopicStart = message?.content.action?.type === 'topicCreate';
|
const isReplyToTopicStart = message?.content.action?.type === 'topicCreate';
|
||||||
|
const isShowingReply = replyInfo && !shouldForceShowEditing;
|
||||||
|
const isReplyWithQuote = Boolean(replyInfo?.quoteText);
|
||||||
|
|
||||||
const isForwarding = Boolean(forwardedMessagesCount);
|
const isForwarding = Boolean(forwardedMessagesCount);
|
||||||
const isShown = Boolean(
|
const isShown = Boolean(
|
||||||
((replyInfo || editingId) && message)
|
((replyInfo || editingId) && message && !isInChangingRecipientMode)
|
||||||
|| (sender && forwardedMessagesCount),
|
|| (sender && forwardedMessagesCount),
|
||||||
);
|
);
|
||||||
const canAnimate = useAsyncRendering(
|
const canAnimate = useAsyncRendering(
|
||||||
[isShown, isForwarding],
|
[isShown, isForwarding],
|
||||||
isShown && isForwarding ? FORWARD_RENDERING_DELAY : undefined,
|
isShown && isChangingChats ? FORWARD_RENDERING_DELAY : undefined,
|
||||||
);
|
);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@ -115,6 +127,11 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
undefined,
|
undefined,
|
||||||
!shouldAnimate,
|
!shouldAnimate,
|
||||||
);
|
);
|
||||||
|
useEffect(() => {
|
||||||
|
if (canAnimate && replyInfo?.isShowingDelayNeeded) {
|
||||||
|
updateDraftReplyInfo({ isShowingDelayNeeded: false });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
const clearEmbedded = useLastCallback(() => {
|
const clearEmbedded = useLastCallback(() => {
|
||||||
if (replyInfo && !shouldForceShowEditing) {
|
if (replyInfo && !shouldForceShowEditing) {
|
||||||
@ -129,24 +146,36 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
useEffect(() => (isShown ? captureEscKeyListener(clearEmbedded) : undefined), [isShown, clearEmbedded]);
|
useEffect(() => (isShown ? captureEscKeyListener(clearEmbedded) : undefined), [isShown, clearEmbedded]);
|
||||||
|
|
||||||
const handleMessageClick = useLastCallback((): void => {
|
const {
|
||||||
if (isForwarding) return;
|
isContextMenuOpen, contextMenuPosition, handleContextMenu,
|
||||||
|
handleContextMenuClose, handleContextMenuHide,
|
||||||
|
} = useContextMenuHandlers(ref);
|
||||||
|
|
||||||
|
const focusMessageFromDraft = () => {
|
||||||
focusMessage({ chatId: message!.chatId, messageId: message!.id, noForumTopicPanel: true });
|
focusMessage({ chatId: message!.chatId, messageId: message!.id, noForumTopicPanel: true });
|
||||||
|
};
|
||||||
|
const handleMessageClick = useLastCallback((e: React.MouseEvent): void => {
|
||||||
|
handleContextMenu(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
const handleClearClick = useLastCallback((e: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
|
const handleClearClick = useLastCallback((e: React.MouseEvent<HTMLButtonElement, MouseEvent>): void => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
clearEmbedded();
|
clearEmbedded();
|
||||||
|
handleContextMenuHide();
|
||||||
});
|
});
|
||||||
|
const buildAutoCloseMenuItemHandler = (action: NoneToVoidFunction) => {
|
||||||
const handleChangeRecipientClick = useLastCallback(() => {
|
return () => {
|
||||||
changeForwardRecipient();
|
handleContextMenuClose();
|
||||||
});
|
action();
|
||||||
|
};
|
||||||
const {
|
};
|
||||||
isContextMenuOpen, contextMenuPosition, handleContextMenu,
|
const handleForwardToAnotherChatClick = useLastCallback(buildAutoCloseMenuItemHandler(changeRecipient));
|
||||||
handleContextMenuClose, handleContextMenuHide,
|
const handleShowMessageClick = useLastCallback(buildAutoCloseMenuItemHandler(focusMessageFromDraft));
|
||||||
} = useContextMenuHandlers(ref);
|
const handleRemoveQuoteClick = useLastCallback(buildAutoCloseMenuItemHandler(
|
||||||
|
() => updateDraftReplyInfo({ quoteText: undefined }),
|
||||||
|
));
|
||||||
|
const handleChangeReplyRecipientClick = useLastCallback(buildAutoCloseMenuItemHandler(changeRecipient));
|
||||||
|
const handleDoNotReplyClick = useLastCallback(buildAutoCloseMenuItemHandler(clearEmbedded));
|
||||||
|
|
||||||
const getTriggerElement = useLastCallback(() => ref.current);
|
const getTriggerElement = useLastCallback(() => ref.current);
|
||||||
const getRootElement = useLastCallback(() => ref.current!);
|
const getRootElement = useLastCallback(() => ref.current!);
|
||||||
@ -162,8 +191,11 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!shouldRender) handleContextMenuClose();
|
if (!shouldRender) {
|
||||||
}, [handleContextMenuClose, shouldRender]);
|
handleContextMenuClose();
|
||||||
|
handleContextMenuHide();
|
||||||
|
}
|
||||||
|
}, [handleContextMenuClose, handleContextMenuHide, shouldRender]);
|
||||||
|
|
||||||
const className = buildClassName('ComposerEmbeddedMessage', transitionClassNames);
|
const className = buildClassName('ComposerEmbeddedMessage', transitionClassNames);
|
||||||
const renderingSender = useCurrentOrPrev(sender, true);
|
const renderingSender = useCurrentOrPrev(sender, true);
|
||||||
@ -172,8 +204,6 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
getPeerColorClass(renderingSender),
|
getPeerColorClass(renderingSender),
|
||||||
);
|
);
|
||||||
|
|
||||||
const isShowingReply = replyInfo && !shouldForceShowEditing;
|
|
||||||
|
|
||||||
const leftIcon = useMemo(() => {
|
const leftIcon = useMemo(() => {
|
||||||
if (isShowingReply) {
|
if (isShowingReply) {
|
||||||
return 'reply';
|
return 'reply';
|
||||||
@ -212,9 +242,9 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={className} ref={ref} onContextMenu={handleContextMenu} onClick={handleContextMenu}>
|
<div className={className} ref={ref} onContextMenu={handleContextMenu}>
|
||||||
<div className={innerClassName}>
|
<div className={innerClassName}>
|
||||||
<div className="embedded-left-icon">
|
<div className="embedded-left-icon" onClick={handleContextMenu}>
|
||||||
{renderingLeftIcon && <Icon name={renderingLeftIcon} />}
|
{renderingLeftIcon && <Icon name={renderingLeftIcon} />}
|
||||||
{Boolean(replyInfo?.quoteText) && (
|
{Boolean(replyInfo?.quoteText) && (
|
||||||
<Icon name="quote" className="quote-reply" />
|
<Icon name="quote" className="quote-reply" />
|
||||||
@ -231,6 +261,7 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
title={(editingId && !isShowingReply) ? lang('EditMessage')
|
title={(editingId && !isShowingReply) ? lang('EditMessage')
|
||||||
: noAuthors ? lang('HiddenSendersNameDescription') : undefined}
|
: noAuthors ? lang('HiddenSendersNameDescription') : undefined}
|
||||||
onClick={handleMessageClick}
|
onClick={handleMessageClick}
|
||||||
|
senderChat={senderChat}
|
||||||
/>
|
/>
|
||||||
<Button
|
<Button
|
||||||
className="embedded-cancel"
|
className="embedded-cancel"
|
||||||
@ -242,7 +273,7 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
>
|
>
|
||||||
<i className="icon icon-close" />
|
<i className="icon icon-close" />
|
||||||
</Button>
|
</Button>
|
||||||
{isForwarding && !isContextMenuDisabled && (
|
{(isShowingReply || isForwarding) && !isContextMenuDisabled && (
|
||||||
<Menu
|
<Menu
|
||||||
isOpen={isContextMenuOpen}
|
isOpen={isContextMenuOpen}
|
||||||
transformOriginX={transformOriginX}
|
transformOriginX={transformOriginX}
|
||||||
@ -254,55 +285,83 @@ const ComposerEmbeddedMessage: FC<OwnProps & StateProps> = ({
|
|||||||
onClose={handleContextMenuClose}
|
onClose={handleContextMenuClose}
|
||||||
onCloseAnimationEnd={handleContextMenuHide}
|
onCloseAnimationEnd={handleContextMenuHide}
|
||||||
>
|
>
|
||||||
<MenuItem
|
{isForwarding && (
|
||||||
icon={!noAuthors ? 'message-succeeded' : undefined}
|
|
||||||
customIcon={noAuthors ? <i className="icon icon-placeholder" /> : undefined}
|
|
||||||
// eslint-disable-next-line react/jsx-no-bind
|
|
||||||
onClick={() => setForwardNoAuthors({
|
|
||||||
noAuthors: false,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{lang(forwardedMessagesCount > 1 ? 'ShowSenderNames' : 'ShowSendersName')}
|
|
||||||
</MenuItem>
|
|
||||||
<MenuItem
|
|
||||||
icon={noAuthors ? 'message-succeeded' : undefined}
|
|
||||||
customIcon={!noAuthors ? <i className="icon icon-placeholder" /> : undefined}
|
|
||||||
// eslint-disable-next-line react/jsx-no-bind
|
|
||||||
onClick={() => setForwardNoAuthors({
|
|
||||||
noAuthors: true,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
{lang(forwardedMessagesCount > 1 ? 'HideSenderNames' : 'HideSendersName')}
|
|
||||||
</MenuItem>
|
|
||||||
{forwardsHaveCaptions && (
|
|
||||||
<>
|
<>
|
||||||
<MenuSeparator />
|
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={!noCaptions ? 'message-succeeded' : undefined}
|
icon={!noAuthors ? 'message-succeeded' : undefined}
|
||||||
customIcon={noCaptions ? <i className="icon icon-placeholder" /> : undefined}
|
customIcon={noAuthors ? <i className="icon icon-placeholder" /> : undefined}
|
||||||
// eslint-disable-next-line react/jsx-no-bind
|
// eslint-disable-next-line react/jsx-no-bind
|
||||||
onClick={() => setForwardNoCaptions({
|
onClick={() => setForwardNoAuthors({
|
||||||
noCaptions: false,
|
noAuthors: false,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{lang(forwardedMessagesCount > 1 ? 'Conversation.ForwardOptions.ShowCaption' : 'ShowCaption')}
|
{lang(forwardedMessagesCount > 1 ? 'ShowSenderNames' : 'ShowSendersName')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
<MenuItem
|
<MenuItem
|
||||||
icon={noCaptions ? 'message-succeeded' : undefined}
|
icon={noAuthors ? 'message-succeeded' : undefined}
|
||||||
customIcon={!noCaptions ? <i className="icon icon-placeholder" /> : undefined}
|
customIcon={!noAuthors ? <i className="icon icon-placeholder" /> : undefined}
|
||||||
// eslint-disable-next-line react/jsx-no-bind
|
// eslint-disable-next-line react/jsx-no-bind
|
||||||
onClick={() => setForwardNoCaptions({
|
onClick={() => setForwardNoAuthors({
|
||||||
noCaptions: true,
|
noAuthors: true,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{lang(forwardedMessagesCount > 1 ? 'Conversation.ForwardOptions.HideCaption' : 'HideCaption')}
|
{lang(forwardedMessagesCount > 1 ? 'HideSenderNames' : 'HideSendersName')}
|
||||||
|
</MenuItem>
|
||||||
|
{forwardsHaveCaptions && (
|
||||||
|
<>
|
||||||
|
<MenuSeparator />
|
||||||
|
<MenuItem
|
||||||
|
icon={!noCaptions ? 'message-succeeded' : undefined}
|
||||||
|
customIcon={noCaptions ? <i className="icon icon-placeholder" /> : undefined}
|
||||||
|
// eslint-disable-next-line react/jsx-no-bind
|
||||||
|
onClick={() => setForwardNoCaptions({
|
||||||
|
noCaptions: false,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{lang(forwardedMessagesCount > 1 ? 'Conversation.ForwardOptions.ShowCaption' : 'ShowCaption')}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem
|
||||||
|
icon={noCaptions ? 'message-succeeded' : undefined}
|
||||||
|
customIcon={!noCaptions ? <i className="icon icon-placeholder" /> : undefined}
|
||||||
|
// eslint-disable-next-line react/jsx-no-bind
|
||||||
|
onClick={() => setForwardNoCaptions({
|
||||||
|
noCaptions: true,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{lang(forwardedMessagesCount > 1 ? 'Conversation.ForwardOptions.HideCaption' : 'HideCaption')}
|
||||||
|
</MenuItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<MenuSeparator />
|
||||||
|
<MenuItem icon="replace" onClick={handleForwardToAnotherChatClick}>
|
||||||
|
{lang('ForwardAnotherChat')}
|
||||||
|
</MenuItem>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{isShowingReply && (
|
||||||
|
<>
|
||||||
|
<MenuItem
|
||||||
|
icon="show-message"
|
||||||
|
onClick={handleShowMessageClick}
|
||||||
|
>
|
||||||
|
{lang('Message.Context.Goto')}
|
||||||
|
</MenuItem>
|
||||||
|
{isReplyWithQuote && (
|
||||||
|
<MenuItem
|
||||||
|
icon="remove-quote"
|
||||||
|
onClick={handleRemoveQuoteClick}
|
||||||
|
>
|
||||||
|
{lang('RemoveQuote')}
|
||||||
|
</MenuItem>
|
||||||
|
)}
|
||||||
|
<MenuItem icon="replace" onClick={handleChangeReplyRecipientClick}>
|
||||||
|
{lang('ReplyToAnotherChat')}
|
||||||
|
</MenuItem>
|
||||||
|
<MenuItem icon="delete" onClick={handleDoNotReplyClick}>
|
||||||
|
{lang('DoNotReply')}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<MenuSeparator />
|
|
||||||
<MenuItem icon="replace" onClick={handleChangeRecipientClick}>
|
|
||||||
{lang('ChangeRecipient')}
|
|
||||||
</MenuItem>
|
|
||||||
</Menu>
|
</Menu>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@ -319,7 +378,7 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
|
|
||||||
const {
|
const {
|
||||||
forwardMessages: {
|
forwardMessages: {
|
||||||
fromChatId, toChatId, messageIds: forwardMessageIds, noAuthors, noCaptions,
|
fromChatId, toChatId, messageIds: forwardMessageIds, noAuthors, noCaptions, isModalShown,
|
||||||
},
|
},
|
||||||
} = selectTabState(global);
|
} = selectTabState(global);
|
||||||
|
|
||||||
@ -332,7 +391,10 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
|
|
||||||
const draft = selectDraft(global, chatId, threadId);
|
const draft = selectDraft(global, chatId, threadId);
|
||||||
const replyInfo = draft?.replyInfo;
|
const replyInfo = draft?.replyInfo;
|
||||||
|
const replyToPeerId = replyInfo?.replyToPeerId;
|
||||||
|
const senderChat = replyToPeerId ? selectChat(global, replyToPeerId) : undefined;
|
||||||
|
|
||||||
|
const isChangingChats = isForwarding || replyInfo?.isShowingDelayNeeded;
|
||||||
let message: ApiMessage | undefined;
|
let message: ApiMessage | undefined;
|
||||||
if (replyInfo && !shouldForceShowEditing) {
|
if (replyInfo && !shouldForceShowEditing) {
|
||||||
message = selectChatMessage(global, replyInfo.replyToPeerId || chatId, replyInfo.replyToMsgId);
|
message = selectChatMessage(global, replyInfo.replyToPeerId || chatId, replyInfo.replyToMsgId);
|
||||||
@ -389,6 +451,9 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
isCurrentUserPremium: selectIsCurrentUserPremium(global),
|
isCurrentUserPremium: selectIsCurrentUserPremium(global),
|
||||||
isContextMenuDisabled,
|
isContextMenuDisabled,
|
||||||
isReplyToDiscussion,
|
isReplyToDiscussion,
|
||||||
|
isInChangingRecipientMode: isModalShown,
|
||||||
|
isChangingChats,
|
||||||
|
senderChat,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(ComposerEmbeddedMessage));
|
)(ComposerEmbeddedMessage));
|
||||||
|
|||||||
@ -52,13 +52,11 @@ const MenuItem: FC<MenuItemProps> = (props) => {
|
|||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
const { isTouchScreen } = useAppLayout();
|
const { isTouchScreen } = useAppLayout();
|
||||||
const handleClick = useLastCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
const handleClick = useLastCallback((e: React.MouseEvent<HTMLDivElement>) => {
|
||||||
|
e.stopPropagation();
|
||||||
if (disabled || !onClick) {
|
if (disabled || !onClick) {
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick(e, clickArg);
|
onClick(e, clickArg);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -67,13 +65,12 @@ const MenuItem: FC<MenuItemProps> = (props) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
e.stopPropagation();
|
||||||
if (disabled || !onClick) {
|
if (disabled || !onClick) {
|
||||||
e.stopPropagation();
|
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
onClick(e, clickArg);
|
onClick(e, clickArg);
|
||||||
});
|
});
|
||||||
const handleMouseDown = useLastCallback((e: React.SyntheticEvent<HTMLDivElement | HTMLAnchorElement>) => {
|
const handleMouseDown = useLastCallback((e: React.SyntheticEvent<HTMLDivElement | HTMLAnchorElement>) => {
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import type {
|
|||||||
ApiSticker,
|
ApiSticker,
|
||||||
ApiStory,
|
ApiStory,
|
||||||
ApiStorySkipped,
|
ApiStorySkipped,
|
||||||
|
ApiUser,
|
||||||
ApiVideo,
|
ApiVideo,
|
||||||
} from '../../../api/types';
|
} from '../../../api/types';
|
||||||
import type { MessageKey } from '../../../util/messageKey';
|
import type { MessageKey } from '../../../util/messageKey';
|
||||||
@ -122,6 +123,7 @@ import {
|
|||||||
selectPeerStory,
|
selectPeerStory,
|
||||||
selectPinnedIds,
|
selectPinnedIds,
|
||||||
selectRealLastReadId,
|
selectRealLastReadId,
|
||||||
|
selectReplyCanBeSentToChat,
|
||||||
selectScheduledMessage,
|
selectScheduledMessage,
|
||||||
selectSendAs,
|
selectSendAs,
|
||||||
selectSponsoredMessage,
|
selectSponsoredMessage,
|
||||||
@ -1750,29 +1752,104 @@ addActionHandler('openUrl', (global, actions, payload): ActionReturnType => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function checkIfVoiceMessagesAllowed<T extends GlobalState>(
|
||||||
|
global: T,
|
||||||
|
user: ApiUser,
|
||||||
|
chatId: string,
|
||||||
|
): Promise<boolean> {
|
||||||
|
let fullInfo = selectUserFullInfo(global, chatId);
|
||||||
|
if (!fullInfo) {
|
||||||
|
const { accessHash } = user;
|
||||||
|
const result = await callApi('fetchFullUser', { id: chatId, accessHash });
|
||||||
|
fullInfo = result?.fullInfo;
|
||||||
|
}
|
||||||
|
return Boolean(!fullInfo?.noVoiceMessages);
|
||||||
|
}
|
||||||
|
|
||||||
|
function moveReplyToNewDraft<T extends GlobalState>(
|
||||||
|
global: T,
|
||||||
|
threadId: ThreadId,
|
||||||
|
replyInfo: ApiInputMessageReplyInfo,
|
||||||
|
toChatId: string,
|
||||||
|
) {
|
||||||
|
const currentDraft = selectDraft(global, toChatId, threadId);
|
||||||
|
|
||||||
|
if (!replyInfo.replyToMsgId) return;
|
||||||
|
|
||||||
|
const newDraft: ApiDraft = {
|
||||||
|
...currentDraft,
|
||||||
|
replyInfo,
|
||||||
|
};
|
||||||
|
|
||||||
|
saveDraft({
|
||||||
|
global, chatId: toChatId, threadId, draft: newDraft, isLocalOnly: true, noLocalTimeUpdate: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
addActionHandler('openChatOrTopicWithReplyInDraft', (global, actions, payload): ActionReturnType => {
|
||||||
|
const { chatId: toChatId, topicId, tabId = getCurrentTabId() } = payload;
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
|
||||||
|
if (!selectReplyCanBeSentToChat(global, toChatId, tabId)) {
|
||||||
|
actions.showNotification({ message: translate('Chat.SendNotAllowedText'), tabId });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
global = updateTabState(global, {
|
||||||
|
forwardMessages: {
|
||||||
|
...selectTabState(global, tabId).forwardMessages,
|
||||||
|
isModalShown: false,
|
||||||
|
},
|
||||||
|
}, tabId);
|
||||||
|
setGlobal(global);
|
||||||
|
|
||||||
|
const currentChat = selectCurrentChat(global, tabId);
|
||||||
|
if (!currentChat) return;
|
||||||
|
|
||||||
|
const threadId = topicId || MAIN_THREAD_ID;
|
||||||
|
const currentChatId = currentChat.id;
|
||||||
|
|
||||||
|
const currentReplyInfo = selectDraft(global, currentChatId, threadId)?.replyInfo;
|
||||||
|
if (!currentReplyInfo) return;
|
||||||
|
if (!currentReplyInfo.replyToPeerId && toChatId === currentChat.id) return;
|
||||||
|
|
||||||
|
const getPeerId = () => {
|
||||||
|
if (!currentReplyInfo?.replyToPeerId) return currentChatId;
|
||||||
|
return currentReplyInfo.replyToPeerId === toChatId ? undefined : currentReplyInfo.replyToPeerId;
|
||||||
|
};
|
||||||
|
const currentThreadId = selectCurrentMessageList(global, tabId)?.threadId;
|
||||||
|
if (!currentThreadId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const replyToPeerId = getPeerId();
|
||||||
|
const newReply: ApiInputMessageReplyInfo = {
|
||||||
|
...currentReplyInfo,
|
||||||
|
replyToPeerId,
|
||||||
|
type: 'message',
|
||||||
|
isShowingDelayNeeded: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
moveReplyToNewDraft(global, threadId, newReply, toChatId);
|
||||||
|
actions.openThread({ chatId: toChatId, threadId, tabId });
|
||||||
|
actions.closeMediaViewer({ tabId });
|
||||||
|
actions.exitMessageSelectMode({ tabId });
|
||||||
|
actions.clearDraft({ chatId: currentChatId, threadId: currentThreadId });
|
||||||
|
});
|
||||||
|
|
||||||
addActionHandler('setForwardChatOrTopic', async (global, actions, payload): Promise<void> => {
|
addActionHandler('setForwardChatOrTopic', async (global, actions, payload): Promise<void> => {
|
||||||
const { chatId, topicId, tabId = getCurrentTabId() } = payload;
|
const { chatId, topicId, tabId = getCurrentTabId() } = payload;
|
||||||
let user = selectUser(global, chatId);
|
const user = selectUser(global, chatId);
|
||||||
if (user && selectForwardsContainVoiceMessages(global, tabId)) {
|
const isSelectForwardsContainVoiceMessages = selectForwardsContainVoiceMessages(global, tabId);
|
||||||
let fullInfo = selectUserFullInfo(global, chatId);
|
if (isSelectForwardsContainVoiceMessages && user && !await checkIfVoiceMessagesAllowed(global, user, chatId)) {
|
||||||
if (!fullInfo) {
|
actions.showDialog({
|
||||||
const { accessHash } = user;
|
data: {
|
||||||
const result = await callApi('fetchFullUser', { id: chatId, accessHash });
|
message: translate('VoiceMessagesRestrictedByPrivacy', getUserFullName(user)),
|
||||||
global = getGlobal();
|
},
|
||||||
user = result?.user;
|
tabId,
|
||||||
fullInfo = result?.fullInfo;
|
});
|
||||||
}
|
return;
|
||||||
|
|
||||||
if (fullInfo!.noVoiceMessages) {
|
|
||||||
actions.showDialog({
|
|
||||||
data: {
|
|
||||||
message: translate('VoiceMessagesRestrictedByPrivacy', getUserFullName(user)),
|
|
||||||
},
|
|
||||||
tabId,
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
global = getGlobal();
|
||||||
|
|
||||||
if (!selectForwardsCanBeSentToChat(global, chatId, tabId)) {
|
if (!selectForwardsCanBeSentToChat(global, chatId, tabId)) {
|
||||||
actions.showAllowedMessageTypesNotification({ chatId, tabId });
|
actions.showAllowedMessageTypesNotification({ chatId, tabId });
|
||||||
|
|||||||
@ -515,7 +515,7 @@ addActionHandler('openForwardMenu', (global, actions, payload): ActionReturnType
|
|||||||
}, tabId);
|
}, tabId);
|
||||||
});
|
});
|
||||||
|
|
||||||
addActionHandler('changeForwardRecipient', (global, actions, payload): ActionReturnType => {
|
addActionHandler('changeRecipient', (global, actions, payload): ActionReturnType => {
|
||||||
const { tabId = getCurrentTabId() } = payload || {};
|
const { tabId = getCurrentTabId() } = payload || {};
|
||||||
return updateTabState(global, {
|
return updateTabState(global, {
|
||||||
forwardMessages: {
|
forwardMessages: {
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import type {
|
|||||||
ApiPhoto,
|
ApiPhoto,
|
||||||
ApiVideo,
|
ApiVideo,
|
||||||
ApiWebDocument,
|
ApiWebDocument,
|
||||||
MediaContent,
|
MediaContainer,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
import { ApiMediaFormat } from '../../api/types';
|
import { ApiMediaFormat } from '../../api/types';
|
||||||
|
|
||||||
@ -25,10 +25,6 @@ import {
|
|||||||
import { getDocumentHasPreview } from '../../components/common/helpers/documentInfo';
|
import { getDocumentHasPreview } from '../../components/common/helpers/documentInfo';
|
||||||
import { getAttachmentType, matchLinkInMessageText } from './messages';
|
import { getAttachmentType, matchLinkInMessageText } from './messages';
|
||||||
|
|
||||||
type MediaContainer = {
|
|
||||||
content: MediaContent;
|
|
||||||
};
|
|
||||||
|
|
||||||
type Target =
|
type Target =
|
||||||
'micro'
|
'micro'
|
||||||
| 'pictogram'
|
| 'pictogram'
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import type { TeactNode } from '../../lib/teact/teact';
|
import type { TeactNode } from '../../lib/teact/teact';
|
||||||
|
|
||||||
import type { ApiMessage } from '../../api/types';
|
import type { ApiMessage, MediaContent } from '../../api/types';
|
||||||
import type { LangFn } from '../../hooks/useLang';
|
import type { LangFn } from '../../hooks/useLang';
|
||||||
import { ApiMessageEntityTypes } from '../../api/types';
|
import { ApiMessageEntityTypes } from '../../api/types';
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ import { CONTENT_NOT_SUPPORTED } from '../../config';
|
|||||||
import trimText from '../../util/trimText';
|
import trimText from '../../util/trimText';
|
||||||
import { getGlobal } from '../index';
|
import { getGlobal } from '../index';
|
||||||
import {
|
import {
|
||||||
getExpiredMessageDescription, getMessageText, getMessageTranscription, isExpiredMessage,
|
getExpiredMessageContentDescription, getMessageText, getMessageTranscription, isExpiredMessageContent,
|
||||||
} from './messages';
|
} from './messages';
|
||||||
import { getUserFirstOrLastName } from './users';
|
import { getUserFirstOrLastName } from './users';
|
||||||
|
|
||||||
@ -102,11 +102,23 @@ export function getMessageSummaryEmoji(message: ApiMessage) {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getMediaContentTypeDescription(lang: LangFn, content: MediaContent) {
|
||||||
|
return getSummaryDescription(lang, content);
|
||||||
|
}
|
||||||
export function getMessageSummaryDescription(
|
export function getMessageSummaryDescription(
|
||||||
lang: LangFn,
|
lang: LangFn,
|
||||||
message: ApiMessage,
|
message: ApiMessage,
|
||||||
truncatedText?: string | TeactNode,
|
truncatedText?: string | TeactNode,
|
||||||
isExtended = false,
|
isExtended = false,
|
||||||
|
) {
|
||||||
|
return getSummaryDescription(lang, message.content, message, truncatedText, isExtended);
|
||||||
|
}
|
||||||
|
function getSummaryDescription(
|
||||||
|
lang: LangFn,
|
||||||
|
mediaContent: MediaContent,
|
||||||
|
message?: ApiMessage,
|
||||||
|
truncatedText?: string | TeactNode,
|
||||||
|
isExtended = false,
|
||||||
) {
|
) {
|
||||||
const {
|
const {
|
||||||
text,
|
text,
|
||||||
@ -124,12 +136,12 @@ export function getMessageSummaryDescription(
|
|||||||
storyData,
|
storyData,
|
||||||
giveaway,
|
giveaway,
|
||||||
giveawayResults,
|
giveawayResults,
|
||||||
} = message.content;
|
} = mediaContent;
|
||||||
|
|
||||||
let hasUsedTruncatedText = false;
|
let hasUsedTruncatedText = false;
|
||||||
let summary: string | TeactNode | undefined;
|
let summary: string | TeactNode | undefined;
|
||||||
|
|
||||||
if (message.groupedId) {
|
if (message?.groupedId) {
|
||||||
hasUsedTruncatedText = true;
|
hasUsedTruncatedText = true;
|
||||||
summary = truncatedText || lang('lng_in_dlg_album');
|
summary = truncatedText || lang('lng_in_dlg_album');
|
||||||
}
|
}
|
||||||
@ -149,7 +161,7 @@ export function getMessageSummaryDescription(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (audio) {
|
if (audio) {
|
||||||
summary = getMessageAudioCaption(message) || lang('AttachMusic');
|
summary = getMessageAudioCaption(mediaContent) || lang('AttachMusic');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (voice) {
|
if (voice) {
|
||||||
@ -203,7 +215,7 @@ export function getMessageSummaryDescription(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (storyData) {
|
if (storyData) {
|
||||||
if (storyData.isMention) {
|
if (message && storyData.isMention) {
|
||||||
// eslint-disable-next-line eslint-multitab-tt/no-immediate-global
|
// eslint-disable-next-line eslint-multitab-tt/no-immediate-global
|
||||||
const global = getGlobal();
|
const global = getGlobal();
|
||||||
const firstName = getUserFirstOrLastName(global.users.byId[message.chatId]);
|
const firstName = getUserFirstOrLastName(global.users.byId[message.chatId]);
|
||||||
@ -211,12 +223,12 @@ export function getMessageSummaryDescription(
|
|||||||
? lang('Chat.Service.StoryMentioned.You', firstName)
|
? lang('Chat.Service.StoryMentioned.You', firstName)
|
||||||
: lang('Chat.Service.StoryMentioned', firstName);
|
: lang('Chat.Service.StoryMentioned', firstName);
|
||||||
} else {
|
} else {
|
||||||
summary = lang('ForwardedStory');
|
summary = message ? lang('ForwardedStory') : lang('Chat.ReplyStory');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isExpiredMessage(message)) {
|
if (isExpiredMessageContent(mediaContent)) {
|
||||||
const expiredMessageText = getExpiredMessageDescription(lang, message);
|
const expiredMessageText = getExpiredMessageContentDescription(lang, mediaContent);
|
||||||
if (expiredMessageText) {
|
if (expiredMessageText) {
|
||||||
summary = expiredMessageText;
|
summary = expiredMessageText;
|
||||||
}
|
}
|
||||||
@ -232,11 +244,11 @@ export function generateBrailleSpoiler(length: number) {
|
|||||||
.join('');
|
.join('');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMessageAudioCaption(message: ApiMessage) {
|
function getMessageAudioCaption(mediaContent: MediaContent) {
|
||||||
const {
|
const {
|
||||||
audio,
|
audio,
|
||||||
text,
|
text,
|
||||||
} = message.content;
|
} = mediaContent;
|
||||||
|
|
||||||
return (audio && [audio.title, audio.performer].filter(Boolean)
|
return (audio && [audio.title, audio.performer].filter(Boolean)
|
||||||
.join(' — ')) || (text?.text);
|
.join(' — ')) || (text?.text);
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import type {
|
import type {
|
||||||
ApiAttachment, ApiChat, ApiMessage, ApiMessageEntityTextUrl, ApiPeer, ApiStory, ApiUser,
|
ApiAttachment, ApiChat, ApiMessage, ApiMessageEntityTextUrl, ApiPeer, ApiStory, ApiUser,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
|
import type { MediaContent } from '../../api/types/messages';
|
||||||
import type { LangFn } from '../../hooks/useLang';
|
import type { LangFn } from '../../hooks/useLang';
|
||||||
import { ApiMessageEntityTypes } from '../../api/types';
|
import { ApiMessageEntityTypes } from '../../api/types';
|
||||||
|
|
||||||
@ -320,7 +321,10 @@ export function extractMessageText(message: ApiMessage | ApiStory, inChatList =
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function getExpiredMessageDescription(langFn: LangFn, message: ApiMessage): string | undefined {
|
export function getExpiredMessageDescription(langFn: LangFn, message: ApiMessage): string | undefined {
|
||||||
const { isExpiredVoice, isExpiredRoundVideo } = message.content;
|
return getExpiredMessageContentDescription(langFn, message.content);
|
||||||
|
}
|
||||||
|
export function getExpiredMessageContentDescription(langFn: LangFn, mediaContent: MediaContent): string | undefined {
|
||||||
|
const { isExpiredVoice, isExpiredRoundVideo } = mediaContent;
|
||||||
if (isExpiredVoice) {
|
if (isExpiredVoice) {
|
||||||
return langFn('Message.VoiceMessageExpired');
|
return langFn('Message.VoiceMessageExpired');
|
||||||
} else if (isExpiredRoundVideo) {
|
} else if (isExpiredRoundVideo) {
|
||||||
@ -330,7 +334,11 @@ export function getExpiredMessageDescription(langFn: LangFn, message: ApiMessage
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function isExpiredMessage(message: ApiMessage) {
|
export function isExpiredMessage(message: ApiMessage) {
|
||||||
const { isExpiredVoice, isExpiredRoundVideo } = message.content ?? {};
|
return isExpiredMessageContent(message.content);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function isExpiredMessageContent(content: MediaContent) {
|
||||||
|
const { isExpiredVoice, isExpiredRoundVideo } = content ?? {};
|
||||||
return Boolean(isExpiredVoice || isExpiredRoundVideo);
|
return Boolean(isExpiredVoice || isExpiredRoundVideo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import type {
|
|||||||
ApiStickerSetInfo,
|
ApiStickerSetInfo,
|
||||||
} from '../../api/types';
|
} from '../../api/types';
|
||||||
import type { ThreadId } from '../../types';
|
import type { ThreadId } from '../../types';
|
||||||
|
import type { IAllowedAttachmentOptions } from '../helpers';
|
||||||
import type {
|
import type {
|
||||||
ChatTranslatedMessages,
|
ChatTranslatedMessages,
|
||||||
GlobalState, MessageListType, TabArgs, TabThread, Thread,
|
GlobalState, MessageListType, TabArgs, TabThread, Thread,
|
||||||
@ -47,6 +48,7 @@ import {
|
|||||||
isChatGroup,
|
isChatGroup,
|
||||||
isChatSuperGroup,
|
isChatSuperGroup,
|
||||||
isCommonBoxChat,
|
isCommonBoxChat,
|
||||||
|
isExpiredMessage,
|
||||||
isForwardedMessage,
|
isForwardedMessage,
|
||||||
isMessageDocumentSticker,
|
isMessageDocumentSticker,
|
||||||
isMessageFailed,
|
isMessageFailed,
|
||||||
@ -1336,7 +1338,7 @@ export function selectForwardsContainVoiceMessages<T extends GlobalState>(
|
|||||||
const chatMessages = selectChatMessages(global, fromChatId!);
|
const chatMessages = selectChatMessages(global, fromChatId!);
|
||||||
return messageIds.some((messageId) => {
|
return messageIds.some((messageId) => {
|
||||||
const message = chatMessages[messageId];
|
const message = chatMessages[messageId];
|
||||||
return Boolean(message.content.voice) || message.content.video?.isRound;
|
return Boolean(message.content.voice) || Boolean(message.content.video?.isRound);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1358,7 +1360,22 @@ export function selectRequestedMessageTranslationLanguage<T extends GlobalState>
|
|||||||
const requestedInChat = selectTabState(global, tabId).requestedTranslations.byChatId[chatId];
|
const requestedInChat = selectTabState(global, tabId).requestedTranslations.byChatId[chatId];
|
||||||
return requestedInChat?.toLanguage || requestedInChat?.manualMessages?.[messageId];
|
return requestedInChat?.toLanguage || requestedInChat?.manualMessages?.[messageId];
|
||||||
}
|
}
|
||||||
|
export function selectReplyCanBeSentToChat<T extends GlobalState>(
|
||||||
|
global: T,
|
||||||
|
toChatId: string,
|
||||||
|
...[tabId = getCurrentTabId()]: TabArgs<T>
|
||||||
|
) {
|
||||||
|
const currentChat = selectCurrentChat(global, tabId);
|
||||||
|
if (!currentChat) return false;
|
||||||
|
const replyInfo = selectDraft(global, currentChat.id, MAIN_THREAD_ID)?.replyInfo;
|
||||||
|
if (!replyInfo || !replyInfo.replyToMsgId) return false;
|
||||||
|
const fromChatId = replyInfo?.replyToPeerId ?? currentChat.id;
|
||||||
|
if (toChatId === fromChatId) return true;
|
||||||
|
const chatMessages = selectChatMessages(global, fromChatId!);
|
||||||
|
const message = chatMessages[replyInfo.replyToMsgId];
|
||||||
|
|
||||||
|
return !isExpiredMessage(message);
|
||||||
|
}
|
||||||
export function selectForwardsCanBeSentToChat<T extends GlobalState>(
|
export function selectForwardsCanBeSentToChat<T extends GlobalState>(
|
||||||
global: T,
|
global: T,
|
||||||
toChatId: string,
|
toChatId: string,
|
||||||
@ -1374,33 +1391,30 @@ export function selectForwardsCanBeSentToChat<T extends GlobalState>(
|
|||||||
|
|
||||||
const chatFullInfo = selectChatFullInfo(global, toChatId);
|
const chatFullInfo = selectChatFullInfo(global, toChatId);
|
||||||
const chatMessages = selectChatMessages(global, fromChatId!);
|
const chatMessages = selectChatMessages(global, fromChatId!);
|
||||||
const {
|
const options = getAllowedAttachmentOptions(chat, chatFullInfo);
|
||||||
canSendVoices, canSendRoundVideos, canSendStickers, canSendDocuments, canSendAudios, canSendVideos,
|
return !messageIds!.some((messageId) => сheckMessageSendingDenied(chatMessages[messageId], options));
|
||||||
canSendPhotos, canSendGifs, canSendPlainText,
|
}
|
||||||
} = getAllowedAttachmentOptions(chat, chatFullInfo);
|
function сheckMessageSendingDenied(message: ApiMessage, options: IAllowedAttachmentOptions) {
|
||||||
return !messageIds!.some((messageId) => {
|
const isVoice = message.content.voice;
|
||||||
const message = chatMessages[messageId];
|
const isRoundVideo = message.content.video?.isRound;
|
||||||
const isVoice = message.content.voice;
|
const isPhoto = message.content.photo;
|
||||||
const isRoundVideo = message.content.video?.isRound;
|
const isGif = message.content.video?.isGif;
|
||||||
const isPhoto = message.content.photo;
|
const isVideo = message.content.video && !isRoundVideo && !isGif;
|
||||||
const isGif = message.content.video?.isGif;
|
const isAudio = message.content.audio;
|
||||||
const isVideo = message.content.video && !isRoundVideo && !isGif;
|
const isDocument = message.content.document;
|
||||||
const isAudio = message.content.audio;
|
const isSticker = message.content.sticker;
|
||||||
const isDocument = message.content.document;
|
const isPlainText = message.content.text
|
||||||
const isSticker = message.content.sticker;
|
&& !isVoice && !isRoundVideo && !isSticker && !isDocument && !isAudio && !isVideo && !isPhoto && !isGif;
|
||||||
const isPlainText = message.content.text
|
|
||||||
&& !isVoice && !isRoundVideo && !isSticker && !isDocument && !isAudio && !isVideo && !isPhoto && !isGif;
|
|
||||||
|
|
||||||
return (isVoice && !canSendVoices)
|
return (isVoice && !options.canSendVoices)
|
||||||
|| (isRoundVideo && !canSendRoundVideos)
|
|| (isRoundVideo && !options.canSendRoundVideos)
|
||||||
|| (isSticker && !canSendStickers)
|
|| (isSticker && !options.canSendStickers)
|
||||||
|| (isDocument && !canSendDocuments)
|
|| (isDocument && !options.canSendDocuments)
|
||||||
|| (isAudio && !canSendAudios)
|
|| (isAudio && !options.canSendAudios)
|
||||||
|| (isVideo && !canSendVideos)
|
|| (isVideo && !options.canSendVideos)
|
||||||
|| (isPhoto && !canSendPhotos)
|
|| (isPhoto && !options.canSendPhotos)
|
||||||
|| (isGif && !canSendGifs)
|
|| (isGif && !options.canSendGifs)
|
||||||
|| (isPlainText && !canSendPlainText);
|
|| (isPlainText && !options.canSendPlainText);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export function selectCanTranslateMessage<T extends GlobalState>(
|
export function selectCanTranslateMessage<T extends GlobalState>(
|
||||||
|
|||||||
@ -2540,6 +2540,10 @@ export interface ActionPayloads {
|
|||||||
chatId: string;
|
chatId: string;
|
||||||
topicId?: number;
|
topicId?: number;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
|
openChatOrTopicWithReplyInDraft: {
|
||||||
|
chatId: string;
|
||||||
|
topicId?: number;
|
||||||
|
} & WithTabId;
|
||||||
forwardMessages: {
|
forwardMessages: {
|
||||||
isSilent?: boolean;
|
isSilent?: boolean;
|
||||||
scheduledAt?: number;
|
scheduledAt?: number;
|
||||||
@ -2551,7 +2555,7 @@ export interface ActionPayloads {
|
|||||||
noCaptions: boolean;
|
noCaptions: boolean;
|
||||||
} & WithTabId;
|
} & WithTabId;
|
||||||
exitForwardMode: WithTabId | undefined;
|
exitForwardMode: WithTabId | undefined;
|
||||||
changeForwardRecipient: WithTabId | undefined;
|
changeRecipient: WithTabId | undefined;
|
||||||
forwardToSavedMessages: WithTabId | undefined;
|
forwardToSavedMessages: WithTabId | undefined;
|
||||||
forwardStory: {
|
forwardStory: {
|
||||||
toChatId: string;
|
toChatId: string;
|
||||||
|
|||||||
@ -24,6 +24,7 @@ const useShowTransition = (
|
|||||||
|
|
||||||
if (closeTimeoutRef.current) {
|
if (closeTimeoutRef.current) {
|
||||||
window.clearTimeout(closeTimeoutRef.current);
|
window.clearTimeout(closeTimeoutRef.current);
|
||||||
|
|
||||||
closeTimeoutRef.current = undefined;
|
closeTimeoutRef.current = undefined;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -187,83 +187,85 @@ $icons-map: (
|
|||||||
"readchats": "\f19c",
|
"readchats": "\f19c",
|
||||||
"recent": "\f19d",
|
"recent": "\f19d",
|
||||||
"reload": "\f19e",
|
"reload": "\f19e",
|
||||||
"remove": "\f19f",
|
"remove-quote": "\f19f",
|
||||||
"reopen-topic": "\f1a0",
|
"remove": "\f1a0",
|
||||||
"replace": "\f1a1",
|
"reopen-topic": "\f1a1",
|
||||||
"replies": "\f1a2",
|
"replace": "\f1a2",
|
||||||
"reply-filled": "\f1a3",
|
"replies": "\f1a3",
|
||||||
"reply": "\f1a4",
|
"reply-filled": "\f1a4",
|
||||||
"revenue-split": "\f1a5",
|
"reply": "\f1a5",
|
||||||
"revote": "\f1a6",
|
"revenue-split": "\f1a6",
|
||||||
"save-story": "\f1a7",
|
"revote": "\f1a7",
|
||||||
"saved-messages": "\f1a8",
|
"save-story": "\f1a8",
|
||||||
"schedule": "\f1a9",
|
"saved-messages": "\f1a9",
|
||||||
"search": "\f1aa",
|
"schedule": "\f1aa",
|
||||||
"select": "\f1ab",
|
"search": "\f1ab",
|
||||||
"send-outline": "\f1ac",
|
"select": "\f1ac",
|
||||||
"send": "\f1ad",
|
"send-outline": "\f1ad",
|
||||||
"settings-filled": "\f1ae",
|
"send": "\f1ae",
|
||||||
"settings": "\f1af",
|
"settings-filled": "\f1af",
|
||||||
"share-filled": "\f1b0",
|
"settings": "\f1b0",
|
||||||
"share-screen-outlined": "\f1b1",
|
"share-filled": "\f1b1",
|
||||||
"share-screen-stop": "\f1b2",
|
"share-screen-outlined": "\f1b2",
|
||||||
"share-screen": "\f1b3",
|
"share-screen-stop": "\f1b3",
|
||||||
"sidebar": "\f1b4",
|
"share-screen": "\f1b4",
|
||||||
"skip-next": "\f1b5",
|
"show-message": "\f1b5",
|
||||||
"skip-previous": "\f1b6",
|
"sidebar": "\f1b6",
|
||||||
"smallscreen": "\f1b7",
|
"skip-next": "\f1b7",
|
||||||
"smile": "\f1b8",
|
"skip-previous": "\f1b8",
|
||||||
"sort": "\f1b9",
|
"smallscreen": "\f1b9",
|
||||||
"speaker-muted-story": "\f1ba",
|
"smile": "\f1ba",
|
||||||
"speaker-outline": "\f1bb",
|
"sort": "\f1bb",
|
||||||
"speaker-story": "\f1bc",
|
"speaker-muted-story": "\f1bc",
|
||||||
"speaker": "\f1bd",
|
"speaker-outline": "\f1bd",
|
||||||
"spoiler-disable": "\f1be",
|
"speaker-story": "\f1be",
|
||||||
"spoiler": "\f1bf",
|
"speaker": "\f1bf",
|
||||||
"sport": "\f1c0",
|
"spoiler-disable": "\f1c0",
|
||||||
"stats": "\f1c1",
|
"spoiler": "\f1c1",
|
||||||
"stealth-future": "\f1c2",
|
"sport": "\f1c2",
|
||||||
"stealth-past": "\f1c3",
|
"stats": "\f1c3",
|
||||||
"stickers": "\f1c4",
|
"stealth-future": "\f1c4",
|
||||||
"stop-raising-hand": "\f1c5",
|
"stealth-past": "\f1c5",
|
||||||
"stop": "\f1c6",
|
"stickers": "\f1c6",
|
||||||
"story-caption": "\f1c7",
|
"stop-raising-hand": "\f1c7",
|
||||||
"story-expired": "\f1c8",
|
"stop": "\f1c8",
|
||||||
"story-priority": "\f1c9",
|
"story-caption": "\f1c9",
|
||||||
"story-reply": "\f1ca",
|
"story-expired": "\f1ca",
|
||||||
"strikethrough": "\f1cb",
|
"story-priority": "\f1cb",
|
||||||
"tag-add": "\f1cc",
|
"story-reply": "\f1cc",
|
||||||
"tag-crossed": "\f1cd",
|
"strikethrough": "\f1cd",
|
||||||
"tag-filter": "\f1ce",
|
"tag-add": "\f1ce",
|
||||||
"tag-name": "\f1cf",
|
"tag-crossed": "\f1cf",
|
||||||
"tag": "\f1d0",
|
"tag-filter": "\f1d0",
|
||||||
"timer": "\f1d1",
|
"tag-name": "\f1d1",
|
||||||
"transcribe": "\f1d2",
|
"tag": "\f1d2",
|
||||||
"truck": "\f1d3",
|
"timer": "\f1d3",
|
||||||
"unarchive": "\f1d4",
|
"transcribe": "\f1d4",
|
||||||
"underlined": "\f1d5",
|
"truck": "\f1d5",
|
||||||
"unlock-badge": "\f1d6",
|
"unarchive": "\f1d6",
|
||||||
"unlock": "\f1d7",
|
"underlined": "\f1d7",
|
||||||
"unmute": "\f1d8",
|
"unlock-badge": "\f1d8",
|
||||||
"unpin": "\f1d9",
|
"unlock": "\f1d9",
|
||||||
"unread": "\f1da",
|
"unmute": "\f1da",
|
||||||
"up": "\f1db",
|
"unpin": "\f1db",
|
||||||
"user-filled": "\f1dc",
|
"unread": "\f1dc",
|
||||||
"user-online": "\f1dd",
|
"up": "\f1dd",
|
||||||
"user": "\f1de",
|
"user-filled": "\f1de",
|
||||||
"video-outlined": "\f1df",
|
"user-online": "\f1df",
|
||||||
"video-stop": "\f1e0",
|
"user": "\f1e0",
|
||||||
"video": "\f1e1",
|
"video-outlined": "\f1e1",
|
||||||
"view-once": "\f1e2",
|
"video-stop": "\f1e2",
|
||||||
"voice-chat": "\f1e3",
|
"video": "\f1e3",
|
||||||
"volume-1": "\f1e4",
|
"view-once": "\f1e4",
|
||||||
"volume-2": "\f1e5",
|
"voice-chat": "\f1e5",
|
||||||
"volume-3": "\f1e6",
|
"volume-1": "\f1e6",
|
||||||
"web": "\f1e7",
|
"volume-2": "\f1e7",
|
||||||
"webapp": "\f1e8",
|
"volume-3": "\f1e8",
|
||||||
"word-wrap": "\f1e9",
|
"web": "\f1e9",
|
||||||
"zoom-in": "\f1ea",
|
"webapp": "\f1ea",
|
||||||
"zoom-out": "\f1eb",
|
"word-wrap": "\f1eb",
|
||||||
|
"zoom-in": "\f1ec",
|
||||||
|
"zoom-out": "\f1ed",
|
||||||
);
|
);
|
||||||
|
|
||||||
.icon-active-sessions::before {
|
.icon-active-sessions::before {
|
||||||
@ -740,6 +742,9 @@ $icons-map: (
|
|||||||
.icon-reload::before {
|
.icon-reload::before {
|
||||||
content: map.get($icons-map, "reload");
|
content: map.get($icons-map, "reload");
|
||||||
}
|
}
|
||||||
|
.icon-remove-quote::before {
|
||||||
|
content: map.get($icons-map, "remove-quote");
|
||||||
|
}
|
||||||
.icon-remove::before {
|
.icon-remove::before {
|
||||||
content: map.get($icons-map, "remove");
|
content: map.get($icons-map, "remove");
|
||||||
}
|
}
|
||||||
@ -803,6 +808,9 @@ $icons-map: (
|
|||||||
.icon-share-screen::before {
|
.icon-share-screen::before {
|
||||||
content: map.get($icons-map, "share-screen");
|
content: map.get($icons-map, "share-screen");
|
||||||
}
|
}
|
||||||
|
.icon-show-message::before {
|
||||||
|
content: map.get($icons-map, "show-message");
|
||||||
|
}
|
||||||
.icon-sidebar::before {
|
.icon-sidebar::before {
|
||||||
content: map.get($icons-map, "sidebar");
|
content: map.get($icons-map, "sidebar");
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@ -157,6 +157,7 @@ export type FontIconName =
|
|||||||
| 'readchats'
|
| 'readchats'
|
||||||
| 'recent'
|
| 'recent'
|
||||||
| 'reload'
|
| 'reload'
|
||||||
|
| 'remove-quote'
|
||||||
| 'remove'
|
| 'remove'
|
||||||
| 'reopen-topic'
|
| 'reopen-topic'
|
||||||
| 'replace'
|
| 'replace'
|
||||||
@ -178,6 +179,7 @@ export type FontIconName =
|
|||||||
| 'share-screen-outlined'
|
| 'share-screen-outlined'
|
||||||
| 'share-screen-stop'
|
| 'share-screen-stop'
|
||||||
| 'share-screen'
|
| 'share-screen'
|
||||||
|
| 'show-message'
|
||||||
| 'sidebar'
|
| 'sidebar'
|
||||||
| 'skip-next'
|
| 'skip-next'
|
||||||
| 'skip-previous'
|
| 'skip-previous'
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user