Composer: Support sending message as channels (#1604)
This commit is contained in:
parent
a65e6c5af9
commit
72ae33139a
@ -22,6 +22,7 @@ import {
|
|||||||
ApiThreadInfo,
|
ApiThreadInfo,
|
||||||
ApiInvoice,
|
ApiInvoice,
|
||||||
ApiGroupCall,
|
ApiGroupCall,
|
||||||
|
ApiUser,
|
||||||
ApiSponsoredMessage,
|
ApiSponsoredMessage,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
@ -857,6 +858,7 @@ export function buildLocalMessage(
|
|||||||
poll?: ApiNewPoll,
|
poll?: ApiNewPoll,
|
||||||
groupedId?: string,
|
groupedId?: string,
|
||||||
scheduledAt?: number,
|
scheduledAt?: number,
|
||||||
|
sendAs?: ApiChat | ApiUser,
|
||||||
serverTimeOffset = 0,
|
serverTimeOffset = 0,
|
||||||
): ApiMessage {
|
): ApiMessage {
|
||||||
const localId = localMessageCounter++;
|
const localId = localMessageCounter++;
|
||||||
@ -880,7 +882,7 @@ export function buildLocalMessage(
|
|||||||
},
|
},
|
||||||
date: scheduledAt || Math.round(Date.now() / 1000) + serverTimeOffset,
|
date: scheduledAt || Math.round(Date.now() / 1000) + serverTimeOffset,
|
||||||
isOutgoing: !isChannel,
|
isOutgoing: !isChannel,
|
||||||
senderId: currentUserId,
|
senderId: sendAs?.id || currentUserId,
|
||||||
...(replyingTo && { replyToMessageId: replyingTo }),
|
...(replyingTo && { replyToMessageId: replyingTo }),
|
||||||
...(groupedId && {
|
...(groupedId && {
|
||||||
groupedId,
|
groupedId,
|
||||||
|
|||||||
@ -97,12 +97,13 @@ export async function fetchInlineBotResults({
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function sendInlineBotResult({
|
export async function sendInlineBotResult({
|
||||||
chat, resultId, queryId, replyingTo,
|
chat, resultId, queryId, replyingTo, sendAs,
|
||||||
}: {
|
}: {
|
||||||
chat: ApiChat;
|
chat: ApiChat;
|
||||||
resultId: string;
|
resultId: string;
|
||||||
queryId: string;
|
queryId: string;
|
||||||
replyingTo?: number;
|
replyingTo?: number;
|
||||||
|
sendAs?: ApiUser | ApiChat;
|
||||||
}) {
|
}) {
|
||||||
const randomId = generateRandomBigInt();
|
const randomId = generateRandomBigInt();
|
||||||
|
|
||||||
@ -113,6 +114,7 @@ export async function sendInlineBotResult({
|
|||||||
peer: buildInputPeer(chat.id, chat.accessHash),
|
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||||
id: resultId,
|
id: resultId,
|
||||||
...(replyingTo && { replyToMsgId: replyingTo }),
|
...(replyingTo && { replyToMsgId: replyingTo }),
|
||||||
|
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
|
||||||
}), true);
|
}), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -402,6 +402,7 @@ async function getFullChannelInfo(
|
|||||||
hiddenPrehistory,
|
hiddenPrehistory,
|
||||||
call,
|
call,
|
||||||
botInfo,
|
botInfo,
|
||||||
|
defaultSendAs,
|
||||||
} = result.fullChat;
|
} = result.fullChat;
|
||||||
|
|
||||||
const inviteLink = exportedInvite instanceof GramJs.ChatInviteExported
|
const inviteLink = exportedInvite instanceof GramJs.ChatInviteExported
|
||||||
@ -452,6 +453,7 @@ async function getFullChannelInfo(
|
|||||||
groupCallId: call ? String(call.id) : undefined,
|
groupCallId: call ? String(call.id) : undefined,
|
||||||
linkedChatId: linkedChatId ? buildApiPeerId(linkedChatId, 'chat') : undefined,
|
linkedChatId: linkedChatId ? buildApiPeerId(linkedChatId, 'chat') : undefined,
|
||||||
botCommands,
|
botCommands,
|
||||||
|
sendAsId: defaultSendAs ? getApiChatIdFromMtpPeer(defaultSendAs) : undefined,
|
||||||
},
|
},
|
||||||
users: [...(users || []), ...(bannedUsers || []), ...(adminUsers || [])],
|
users: [...(users || []), ...(bannedUsers || []), ...(adminUsers || [])],
|
||||||
groupCall: call ? {
|
groupCall: call ? {
|
||||||
|
|||||||
@ -22,7 +22,8 @@ export {
|
|||||||
markMessageListRead, markMessagesRead, requestThreadInfoUpdate, searchMessagesLocal, searchMessagesGlobal,
|
markMessageListRead, markMessagesRead, requestThreadInfoUpdate, searchMessagesLocal, searchMessagesGlobal,
|
||||||
fetchWebPagePreview, editMessage, forwardMessages, loadPollOptionResults, sendPollVote, findFirstMessageIdAfterDate,
|
fetchWebPagePreview, editMessage, forwardMessages, loadPollOptionResults, sendPollVote, findFirstMessageIdAfterDate,
|
||||||
fetchPinnedMessages, fetchScheduledHistory, sendScheduledMessages, rescheduleMessage, deleteScheduledMessages,
|
fetchPinnedMessages, fetchScheduledHistory, sendScheduledMessages, rescheduleMessage, deleteScheduledMessages,
|
||||||
reportMessages, sendMessageAction, fetchSeenBy, fetchSponsoredMessages, viewSponsoredMessage,
|
reportMessages, sendMessageAction, fetchSeenBy, fetchSponsoredMessages, viewSponsoredMessage, fetchSendAs,
|
||||||
|
saveDefaultSendAs,
|
||||||
} from './messages';
|
} from './messages';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
@ -52,10 +52,15 @@ import {
|
|||||||
import localDb from '../localDb';
|
import localDb from '../localDb';
|
||||||
import { buildApiChatFromPreview } from '../apiBuilders/chats';
|
import { buildApiChatFromPreview } from '../apiBuilders/chats';
|
||||||
import { fetchFile } from '../../../util/files';
|
import { fetchFile } from '../../../util/files';
|
||||||
import { addMessageToLocalDb, deserializeBytes, resolveMessageApiChatId } from '../helpers';
|
import {
|
||||||
|
addEntitiesWithPhotosToLocalDb,
|
||||||
|
addMessageToLocalDb,
|
||||||
|
deserializeBytes,
|
||||||
|
resolveMessageApiChatId,
|
||||||
|
} from '../helpers';
|
||||||
import { interpolateArray } from '../../../util/waveform';
|
import { interpolateArray } from '../../../util/waveform';
|
||||||
import { requestChatUpdate } from './chats';
|
import { requestChatUpdate } from './chats';
|
||||||
import { buildApiPeerId } from '../apiBuilders/peers';
|
import { buildApiPeerId, getApiChatIdFromMtpPeer } from '../apiBuilders/peers';
|
||||||
|
|
||||||
const FAST_SEND_TIMEOUT = 1000;
|
const FAST_SEND_TIMEOUT = 1000;
|
||||||
const INPUT_WAVEFORM_LENGTH = 63;
|
const INPUT_WAVEFORM_LENGTH = 63;
|
||||||
@ -200,6 +205,7 @@ export function sendMessage(
|
|||||||
scheduledAt,
|
scheduledAt,
|
||||||
groupedId,
|
groupedId,
|
||||||
noWebPage,
|
noWebPage,
|
||||||
|
sendAs,
|
||||||
serverTimeOffset,
|
serverTimeOffset,
|
||||||
}: {
|
}: {
|
||||||
chat: ApiChat;
|
chat: ApiChat;
|
||||||
@ -214,12 +220,14 @@ export function sendMessage(
|
|||||||
scheduledAt?: number;
|
scheduledAt?: number;
|
||||||
groupedId?: string;
|
groupedId?: string;
|
||||||
noWebPage?: boolean;
|
noWebPage?: boolean;
|
||||||
|
sendAs?: ApiUser | ApiChat;
|
||||||
serverTimeOffset?: number;
|
serverTimeOffset?: number;
|
||||||
},
|
},
|
||||||
onProgress?: ApiOnProgress,
|
onProgress?: ApiOnProgress,
|
||||||
) {
|
) {
|
||||||
const localMessage = buildLocalMessage(
|
const localMessage = buildLocalMessage(
|
||||||
chat, text, entities, replyingTo, attachment, sticker, gif, poll, groupedId, scheduledAt, serverTimeOffset,
|
chat, text, entities, replyingTo, attachment, sticker, gif, poll, groupedId, scheduledAt,
|
||||||
|
sendAs, serverTimeOffset,
|
||||||
);
|
);
|
||||||
onUpdate({
|
onUpdate({
|
||||||
'@type': localMessage.isScheduled ? 'newScheduledMessage' : 'newMessage',
|
'@type': localMessage.isScheduled ? 'newScheduledMessage' : 'newMessage',
|
||||||
@ -289,6 +297,7 @@ export function sendMessage(
|
|||||||
...(replyingTo && { replyToMsgId: replyingTo }),
|
...(replyingTo && { replyToMsgId: replyingTo }),
|
||||||
...(media && { media }),
|
...(media && { media }),
|
||||||
...(noWebPage && { noWebpage: noWebPage }),
|
...(noWebPage && { noWebpage: noWebPage }),
|
||||||
|
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
|
||||||
}), true);
|
}), true);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@ -310,6 +319,7 @@ function sendGroupedMedia(
|
|||||||
groupedId,
|
groupedId,
|
||||||
isSilent,
|
isSilent,
|
||||||
scheduledAt,
|
scheduledAt,
|
||||||
|
sendAs,
|
||||||
}: {
|
}: {
|
||||||
chat: ApiChat;
|
chat: ApiChat;
|
||||||
text?: string;
|
text?: string;
|
||||||
@ -319,6 +329,7 @@ function sendGroupedMedia(
|
|||||||
groupedId: string;
|
groupedId: string;
|
||||||
isSilent?: boolean;
|
isSilent?: boolean;
|
||||||
scheduledAt?: number;
|
scheduledAt?: number;
|
||||||
|
sendAs?: ApiUser | ApiChat;
|
||||||
},
|
},
|
||||||
randomId: GramJs.long,
|
randomId: GramJs.long,
|
||||||
localMessage: ApiMessage,
|
localMessage: ApiMessage,
|
||||||
@ -391,6 +402,7 @@ function sendGroupedMedia(
|
|||||||
replyToMsgId: replyingTo,
|
replyToMsgId: replyingTo,
|
||||||
...(isSilent && { silent: isSilent }),
|
...(isSilent && { silent: isSilent }),
|
||||||
...(scheduledAt && { scheduleDate: scheduledAt }),
|
...(scheduledAt && { scheduleDate: scheduledAt }),
|
||||||
|
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
|
||||||
}), true);
|
}), true);
|
||||||
})();
|
})();
|
||||||
|
|
||||||
@ -1052,6 +1064,7 @@ export async function forwardMessages({
|
|||||||
serverTimeOffset,
|
serverTimeOffset,
|
||||||
isSilent,
|
isSilent,
|
||||||
scheduledAt,
|
scheduledAt,
|
||||||
|
sendAs,
|
||||||
}: {
|
}: {
|
||||||
fromChat: ApiChat;
|
fromChat: ApiChat;
|
||||||
toChat: ApiChat;
|
toChat: ApiChat;
|
||||||
@ -1059,6 +1072,7 @@ export async function forwardMessages({
|
|||||||
serverTimeOffset: number;
|
serverTimeOffset: number;
|
||||||
isSilent?: boolean;
|
isSilent?: boolean;
|
||||||
scheduledAt?: number;
|
scheduledAt?: number;
|
||||||
|
sendAs?: ApiUser | ApiChat;
|
||||||
}) {
|
}) {
|
||||||
const messageIds = messages.map(({ id }) => id);
|
const messageIds = messages.map(({ id }) => id);
|
||||||
const randomIds = messages.map(generateRandomBigInt);
|
const randomIds = messages.map(generateRandomBigInt);
|
||||||
@ -1082,6 +1096,7 @@ export async function forwardMessages({
|
|||||||
id: messageIds,
|
id: messageIds,
|
||||||
...(isSilent && { sil2ent: isSilent }),
|
...(isSilent && { sil2ent: isSilent }),
|
||||||
...(scheduledAt && { scheduleDate: scheduledAt }),
|
...(scheduledAt && { scheduleDate: scheduledAt }),
|
||||||
|
...(sendAs && { sendAs: buildInputPeer(sendAs.id, sendAs.accessHash) }),
|
||||||
}), true);
|
}), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1208,6 +1223,43 @@ export async function fetchSeenBy({ chat, messageId }: { chat: ApiChat; messageI
|
|||||||
return result ? result.map(String) : undefined;
|
return result ? result.map(String) : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchSendAs({
|
||||||
|
chat,
|
||||||
|
}: {
|
||||||
|
chat: ApiChat;
|
||||||
|
}) {
|
||||||
|
const result = await invokeRequest(new GramJs.channels.GetSendAs({
|
||||||
|
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
addEntitiesWithPhotosToLocalDb(result.users);
|
||||||
|
addEntitiesWithPhotosToLocalDb(result.chats);
|
||||||
|
|
||||||
|
const users = result.users.map(buildApiUser).filter<ApiUser>(Boolean as any);
|
||||||
|
const chats = result.chats.map((c) => buildApiChatFromPreview(c)).filter<ApiChat>(Boolean as any);
|
||||||
|
|
||||||
|
return {
|
||||||
|
users,
|
||||||
|
chats,
|
||||||
|
ids: result.peers.map(getApiChatIdFromMtpPeer),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function saveDefaultSendAs({
|
||||||
|
sendAs, chat,
|
||||||
|
}: {
|
||||||
|
sendAs: ApiChat | ApiUser; chat: ApiChat;
|
||||||
|
}) {
|
||||||
|
return invokeRequest(new GramJs.messages.SaveDefaultSendAs({
|
||||||
|
peer: buildInputPeer(chat.id, chat.accessHash),
|
||||||
|
sendAs: buildInputPeer(sendAs.id, sendAs.accessHash),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
export async function fetchSponsoredMessages({ chat }: { chat: ApiChat }) {
|
export async function fetchSponsoredMessages({ chat }: { chat: ApiChat }) {
|
||||||
const result = await invokeRequest(new GramJs.channels.GetSponsoredMessages({
|
const result = await invokeRequest(new GramJs.channels.GetSponsoredMessages({
|
||||||
channel: buildInputPeer(chat.id, chat.accessHash),
|
channel: buildInputPeer(chat.id, chat.accessHash),
|
||||||
|
|||||||
@ -55,6 +55,8 @@ export interface ApiChat {
|
|||||||
fullInfo?: ApiChatFullInfo;
|
fullInfo?: ApiChatFullInfo;
|
||||||
// Obtained with UpdateUserTyping or UpdateChatUserTyping updates
|
// Obtained with UpdateUserTyping or UpdateChatUserTyping updates
|
||||||
typingStatus?: ApiTypingStatus;
|
typingStatus?: ApiTypingStatus;
|
||||||
|
|
||||||
|
sendAsIds?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiTypingStatus {
|
export interface ApiTypingStatus {
|
||||||
@ -83,6 +85,7 @@ export interface ApiChatFullInfo {
|
|||||||
};
|
};
|
||||||
linkedChatId?: string;
|
linkedChatId?: string;
|
||||||
botCommands?: ApiBotCommand[];
|
botCommands?: ApiBotCommand[];
|
||||||
|
sendAsId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ApiChatMember {
|
export interface ApiChatMember {
|
||||||
|
|||||||
@ -40,6 +40,7 @@ export { default as DropArea } from '../components/middle/composer/DropArea';
|
|||||||
export { default as TextFormatter } from '../components/middle/composer/TextFormatter';
|
export { default as TextFormatter } from '../components/middle/composer/TextFormatter';
|
||||||
export { default as EmojiTooltip } from '../components/middle/composer/EmojiTooltip';
|
export { default as EmojiTooltip } from '../components/middle/composer/EmojiTooltip';
|
||||||
export { default as InlineBotTooltip } from '../components/middle/composer/InlineBotTooltip';
|
export { default as InlineBotTooltip } from '../components/middle/composer/InlineBotTooltip';
|
||||||
|
export { default as SendAsMenu } from '../components/middle/composer/SendAsMenu';
|
||||||
|
|
||||||
export { default as RightSearch } from '../components/right/RightSearch';
|
export { default as RightSearch } from '../components/right/RightSearch';
|
||||||
export { default as StickerSearch } from '../components/right/StickerSearch';
|
export { default as StickerSearch } from '../components/right/StickerSearch';
|
||||||
|
|||||||
@ -33,6 +33,22 @@
|
|||||||
--border-width: 0;
|
--border-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes show-send-as-button {
|
||||||
|
from {
|
||||||
|
width: 1rem;
|
||||||
|
transform: scale(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
width: 3.5rem;
|
||||||
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
body:not(.animation-level-0) & .send-as-button {
|
||||||
|
animation: 0.25s ease-in-out forwards show-send-as-button;
|
||||||
|
}
|
||||||
|
|
||||||
> .Button {
|
> .Button {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
margin-left: .5rem;
|
margin-left: .5rem;
|
||||||
@ -118,6 +134,10 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.send-as-button {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
.mobile-symbol-menu-button {
|
.mobile-symbol-menu-button {
|
||||||
width: 2.875rem;
|
width: 2.875rem;
|
||||||
height: 2.875rem;
|
height: 2.875rem;
|
||||||
|
|||||||
@ -96,6 +96,8 @@ import DropArea, { DropAreaState } from './DropArea.async';
|
|||||||
import WebPagePreview from './WebPagePreview';
|
import WebPagePreview from './WebPagePreview';
|
||||||
import Portal from '../../ui/Portal';
|
import Portal from '../../ui/Portal';
|
||||||
import CalendarModal from '../../common/CalendarModal.async';
|
import CalendarModal from '../../common/CalendarModal.async';
|
||||||
|
import SendAsMenu from './SendAsMenu.async';
|
||||||
|
import Avatar from '../../common/Avatar';
|
||||||
|
|
||||||
import './Composer.scss';
|
import './Composer.scss';
|
||||||
|
|
||||||
@ -140,6 +142,9 @@ type StateProps =
|
|||||||
inlineBots?: Record<string, false | InlineBotSettings>;
|
inlineBots?: Record<string, false | InlineBotSettings>;
|
||||||
botCommands?: ApiBotCommand[] | false;
|
botCommands?: ApiBotCommand[] | false;
|
||||||
chatBotCommands?: ApiBotCommand[];
|
chatBotCommands?: ApiBotCommand[];
|
||||||
|
sendAsUser?: ApiUser;
|
||||||
|
sendAsChat?: ApiChat;
|
||||||
|
sendAsId?: string;
|
||||||
}
|
}
|
||||||
& Pick<GlobalState, 'connectionState'>;
|
& Pick<GlobalState, 'connectionState'>;
|
||||||
|
|
||||||
@ -199,6 +204,9 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
isInlineBotLoading,
|
isInlineBotLoading,
|
||||||
botCommands,
|
botCommands,
|
||||||
chatBotCommands,
|
chatBotCommands,
|
||||||
|
sendAsUser,
|
||||||
|
sendAsChat,
|
||||||
|
sendAsId,
|
||||||
}) => {
|
}) => {
|
||||||
const {
|
const {
|
||||||
sendMessage,
|
sendMessage,
|
||||||
@ -213,8 +221,8 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
openChat,
|
openChat,
|
||||||
addRecentEmoji,
|
addRecentEmoji,
|
||||||
sendInlineBotResult,
|
sendInlineBotResult,
|
||||||
|
loadSendAs,
|
||||||
} = getDispatch();
|
} = getDispatch();
|
||||||
|
|
||||||
const lang = useLang();
|
const lang = useLang();
|
||||||
|
|
||||||
// eslint-disable-next-line no-null/no-null
|
// eslint-disable-next-line no-null/no-null
|
||||||
@ -227,6 +235,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
scheduledMessageArgs, setScheduledMessageArgs,
|
scheduledMessageArgs, setScheduledMessageArgs,
|
||||||
] = useState<GlobalState['messages']['contentToBeScheduled'] | undefined>();
|
] = useState<GlobalState['messages']['contentToBeScheduled'] | undefined>();
|
||||||
const { width: windowWidth } = windowSize.get();
|
const { width: windowWidth } = windowSize.get();
|
||||||
|
const sendAsIds = chat?.sendAsIds;
|
||||||
const sendMessageAction = useSendMessageAction(chatId, threadId);
|
const sendMessageAction = useSendMessageAction(chatId, threadId);
|
||||||
|
|
||||||
// Cache for frequently updated state
|
// Cache for frequently updated state
|
||||||
@ -245,6 +254,12 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
}
|
}
|
||||||
}, [isReady, chatId, loadScheduledHistory, lastSyncTime, threadId]);
|
}, [isReady, chatId, loadScheduledHistory, lastSyncTime, threadId]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (chatId && lastSyncTime && !sendAsIds && isReady) {
|
||||||
|
loadSendAs({ chatId });
|
||||||
|
}
|
||||||
|
}, [chatId, isReady, lastSyncTime, loadSendAs, sendAsIds]);
|
||||||
|
|
||||||
useLayoutEffect(() => {
|
useLayoutEffect(() => {
|
||||||
if (!appendixRef.current) return;
|
if (!appendixRef.current) return;
|
||||||
|
|
||||||
@ -264,6 +279,7 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
const [isBotCommandMenuOpen, openBotCommandMenu, closeBotCommandMenu] = useFlag();
|
const [isBotCommandMenuOpen, openBotCommandMenu, closeBotCommandMenu] = useFlag();
|
||||||
const [isAttachMenuOpen, openAttachMenu, closeAttachMenu] = useFlag();
|
const [isAttachMenuOpen, openAttachMenu, closeAttachMenu] = useFlag();
|
||||||
const [isSymbolMenuOpen, openSymbolMenu, closeSymbolMenu] = useFlag();
|
const [isSymbolMenuOpen, openSymbolMenu, closeSymbolMenu] = useFlag();
|
||||||
|
const [isSendAsMenuOpen, openSendAsMenu, closeSendAsMenu] = useFlag();
|
||||||
const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useFlag();
|
const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useFlag();
|
||||||
const [isSymbolMenuLoaded, onSymbolMenuLoadingComplete] = useFlag();
|
const [isSymbolMenuLoaded, onSymbolMenuLoadingComplete] = useFlag();
|
||||||
const [isHoverDisabled, disableHover, enableHover] = useFlag();
|
const [isHoverDisabled, disableHover, enableHover] = useFlag();
|
||||||
@ -566,8 +582,9 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
|
|
||||||
const handleActivateSymbolMenu = useCallback(() => {
|
const handleActivateSymbolMenu = useCallback(() => {
|
||||||
closeBotCommandMenu();
|
closeBotCommandMenu();
|
||||||
|
closeSendAsMenu();
|
||||||
openSymbolMenu();
|
openSymbolMenu();
|
||||||
}, [closeBotCommandMenu, openSymbolMenu]);
|
}, [closeBotCommandMenu, closeSendAsMenu, openSymbolMenu]);
|
||||||
|
|
||||||
const handleStickerSelect = useCallback((sticker: ApiSticker, shouldPreserveInput = false) => {
|
const handleStickerSelect = useCallback((sticker: ApiSticker, shouldPreserveInput = false) => {
|
||||||
sticker = {
|
sticker = {
|
||||||
@ -701,6 +718,22 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
}, MOBILE_KEYBOARD_HIDE_DELAY_MS);
|
}, MOBILE_KEYBOARD_HIDE_DELAY_MS);
|
||||||
}, [openSymbolMenu, closeBotCommandMenu]);
|
}, [openSymbolMenu, closeBotCommandMenu]);
|
||||||
|
|
||||||
|
const handleSendAsMenuOpen = useCallback(() => {
|
||||||
|
const messageInput = document.getElementById(EDITABLE_INPUT_ID)!;
|
||||||
|
|
||||||
|
if (!IS_SINGLE_COLUMN_LAYOUT || messageInput !== document.activeElement) {
|
||||||
|
openSendAsMenu();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
messageInput.blur();
|
||||||
|
setTimeout(() => {
|
||||||
|
closeBotCommandMenu();
|
||||||
|
closeSymbolMenu();
|
||||||
|
openSendAsMenu();
|
||||||
|
}, MOBILE_KEYBOARD_HIDE_DELAY_MS);
|
||||||
|
}, [closeBotCommandMenu, closeSymbolMenu, openSendAsMenu]);
|
||||||
|
|
||||||
const handleAllScheduledClick = useCallback(() => {
|
const handleAllScheduledClick = useCallback(() => {
|
||||||
openChat({ id: chatId, threadId, type: 'scheduled' });
|
openChat({ id: chatId, threadId, type: 'scheduled' });
|
||||||
}, [openChat, chatId, threadId]);
|
}, [openChat, chatId, threadId]);
|
||||||
@ -834,6 +867,13 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
message={renderedEditedMessage}
|
message={renderedEditedMessage}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
<SendAsMenu
|
||||||
|
isOpen={isSendAsMenuOpen}
|
||||||
|
onClose={closeSendAsMenu}
|
||||||
|
chatId={chatId}
|
||||||
|
selectedSendAsId={sendAsId}
|
||||||
|
sendAsIds={sendAsIds}
|
||||||
|
/>
|
||||||
<MentionTooltip
|
<MentionTooltip
|
||||||
isOpen={isMentionTooltipOpen}
|
isOpen={isMentionTooltipOpen}
|
||||||
onClose={closeMentionTooltip}
|
onClose={closeMentionTooltip}
|
||||||
@ -881,6 +921,21 @@ const Composer: FC<OwnProps & StateProps> = ({
|
|||||||
<i className="icon-bot-commands-filled" />
|
<i className="icon-bot-commands-filled" />
|
||||||
</ResponsiveHoverButton>
|
</ResponsiveHoverButton>
|
||||||
)}
|
)}
|
||||||
|
{sendAsIds && (sendAsUser || sendAsChat) && (
|
||||||
|
<Button
|
||||||
|
round
|
||||||
|
color="translucent"
|
||||||
|
onClick={isSendAsMenuOpen ? closeSendAsMenu : handleSendAsMenuOpen}
|
||||||
|
ariaLabel={lang('SendMessageAsTitle')}
|
||||||
|
className="send-as-button"
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
user={sendAsUser}
|
||||||
|
chat={sendAsChat}
|
||||||
|
size="tiny"
|
||||||
|
/>
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
{IS_SINGLE_COLUMN_LAYOUT ? (
|
{IS_SINGLE_COLUMN_LAYOUT ? (
|
||||||
<Button
|
<Button
|
||||||
className={symbolMenuButtonClassName}
|
className={symbolMenuButtonClassName}
|
||||||
@ -1079,6 +1134,12 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
const emojiKeywords = language !== BASE_EMOJI_KEYWORD_LANG ? global.emojiKeywords[language] : undefined;
|
const emojiKeywords = language !== BASE_EMOJI_KEYWORD_LANG ? global.emojiKeywords[language] : undefined;
|
||||||
const botKeyboardMessageId = messageWithActualBotKeyboard ? messageWithActualBotKeyboard.id : undefined;
|
const botKeyboardMessageId = messageWithActualBotKeyboard ? messageWithActualBotKeyboard.id : undefined;
|
||||||
const keyboardMessage = botKeyboardMessageId ? selectChatMessage(global, chatId, botKeyboardMessageId) : undefined;
|
const keyboardMessage = botKeyboardMessageId ? selectChatMessage(global, chatId, botKeyboardMessageId) : undefined;
|
||||||
|
const usersById = global.users.byId;
|
||||||
|
const chatsById = global.chats.byId;
|
||||||
|
const { currentUserId } = global;
|
||||||
|
const sendAsId = chat?.fullInfo ? chat?.fullInfo?.sendAsId || currentUserId : undefined;
|
||||||
|
const sendAsUser = sendAsId ? usersById?.[sendAsId] : undefined;
|
||||||
|
const sendAsChat = !sendAsUser && sendAsId ? chatsById?.[sendAsId] : undefined;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
editingMessage: selectEditingMessage(global, chatId, threadId, messageListType),
|
editingMessage: selectEditingMessage(global, chatId, threadId, messageListType),
|
||||||
@ -1106,8 +1167,8 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
stickersForEmoji: global.stickers.forEmoji.stickers,
|
stickersForEmoji: global.stickers.forEmoji.stickers,
|
||||||
groupChatMembers: chat?.fullInfo?.members,
|
groupChatMembers: chat?.fullInfo?.members,
|
||||||
topInlineBotIds: global.topInlineBots?.userIds,
|
topInlineBotIds: global.topInlineBots?.userIds,
|
||||||
currentUserId: global.currentUserId,
|
currentUserId,
|
||||||
usersById: global.users.byId,
|
usersById,
|
||||||
lastSyncTime: global.lastSyncTime,
|
lastSyncTime: global.lastSyncTime,
|
||||||
contentToBeScheduled: global.messages.contentToBeScheduled,
|
contentToBeScheduled: global.messages.contentToBeScheduled,
|
||||||
shouldSuggestStickers,
|
shouldSuggestStickers,
|
||||||
@ -1119,6 +1180,9 @@ export default memo(withGlobal<OwnProps>(
|
|||||||
isInlineBotLoading: global.inlineBots.isLoading,
|
isInlineBotLoading: global.inlineBots.isLoading,
|
||||||
chatBotCommands: chat && chat.fullInfo && chat.fullInfo.botCommands,
|
chatBotCommands: chat && chat.fullInfo && chat.fullInfo.botCommands,
|
||||||
botCommands: chatBot && chatBot.fullInfo ? (chatBot.fullInfo.botCommands || false) : undefined,
|
botCommands: chatBot && chatBot.fullInfo ? (chatBot.fullInfo.botCommands || false) : undefined,
|
||||||
|
sendAsUser,
|
||||||
|
sendAsChat,
|
||||||
|
sendAsId,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
)(Composer));
|
)(Composer));
|
||||||
|
|||||||
15
src/components/middle/composer/SendAsMenu.async.tsx
Normal file
15
src/components/middle/composer/SendAsMenu.async.tsx
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import React, { FC, memo } from '../../../lib/teact/teact';
|
||||||
|
import { OwnProps } from './SendAsMenu';
|
||||||
|
import { Bundles } from '../../../util/moduleLoader';
|
||||||
|
|
||||||
|
import useModuleLoader from '../../../hooks/useModuleLoader';
|
||||||
|
|
||||||
|
const SendAsMenuAsync: FC<OwnProps> = (props) => {
|
||||||
|
const { isOpen } = props;
|
||||||
|
const SendAsMenu = useModuleLoader(Bundles.Extra, 'SendAsMenu', !isOpen);
|
||||||
|
|
||||||
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
|
return SendAsMenu ? <SendAsMenu {...props} /> : undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(SendAsMenuAsync);
|
||||||
74
src/components/middle/composer/SendAsMenu.scss
Normal file
74
src/components/middle/composer/SendAsMenu.scss
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
.SendAsMenu {
|
||||||
|
.send-as-title {
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.25rem;
|
||||||
|
word-break: break-word;
|
||||||
|
margin-inline-start: 1rem;
|
||||||
|
margin-block: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.menu-container {
|
||||||
|
width: calc(100% - 4rem);
|
||||||
|
max-width: 20rem;
|
||||||
|
max-height: 40vh;
|
||||||
|
overflow: auto;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
width: calc(100% - 3rem);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-pointer-env & {
|
||||||
|
> .backdrop {
|
||||||
|
z-index: 2;
|
||||||
|
position: absolute;
|
||||||
|
top: -1rem;
|
||||||
|
left: 0;
|
||||||
|
right: auto;
|
||||||
|
width: 3.5rem;
|
||||||
|
height: 4.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.SendAsItem {
|
||||||
|
margin: 0 !important;
|
||||||
|
$border-size: 2px;
|
||||||
|
|
||||||
|
.Avatar.selected {
|
||||||
|
margin-right: 0.75rem;
|
||||||
|
position: relative;
|
||||||
|
width: calc(2.125rem - #{$border-size * 2});
|
||||||
|
height: calc(2.125rem - #{$border-size * 2});
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
display: block;
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
top: #{-$border-size * 2};
|
||||||
|
left: #{-$border-size * 2};
|
||||||
|
border: $border-size solid var(--color-primary);
|
||||||
|
width: calc(100% + #{$border-size * 4});
|
||||||
|
height: calc(100% + #{$border-size * 4});
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ListItem-button {
|
||||||
|
padding: 0.5625rem 1rem !important;
|
||||||
|
border-radius: 0;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info {
|
||||||
|
margin-inline-start: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: .9375rem;
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
line-height: 1.3125;
|
||||||
|
}
|
||||||
|
}
|
||||||
125
src/components/middle/composer/SendAsMenu.tsx
Normal file
125
src/components/middle/composer/SendAsMenu.tsx
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import React, {
|
||||||
|
FC, useCallback, useEffect, useRef, memo,
|
||||||
|
} from '../../../lib/teact/teact';
|
||||||
|
|
||||||
|
import setTooltipItemVisible from '../../../util/setTooltipItemVisible';
|
||||||
|
import { useKeyboardNavigation } from './hooks/useKeyboardNavigation';
|
||||||
|
import { IS_TOUCH_ENV } from '../../../util/environment';
|
||||||
|
import renderText from '../../common/helpers/renderText';
|
||||||
|
import { getUserFullName, isUserId } from '../../../modules/helpers';
|
||||||
|
import useMouseInside from '../../../hooks/useMouseInside';
|
||||||
|
import useLang from '../../../hooks/useLang';
|
||||||
|
import buildClassName from '../../../util/buildClassName';
|
||||||
|
import { getDispatch, getGlobal } from '../../../lib/teact/teactn';
|
||||||
|
|
||||||
|
import ListItem from '../../ui/ListItem';
|
||||||
|
import Avatar from '../../common/Avatar';
|
||||||
|
import Menu from '../../ui/Menu';
|
||||||
|
|
||||||
|
import './SendAsMenu.scss';
|
||||||
|
|
||||||
|
export type OwnProps = {
|
||||||
|
isOpen: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
chatId?: string;
|
||||||
|
selectedSendAsId?: string;
|
||||||
|
sendAsIds?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
const SendAsMenu: FC<OwnProps> = ({
|
||||||
|
isOpen,
|
||||||
|
onClose,
|
||||||
|
chatId,
|
||||||
|
selectedSendAsId,
|
||||||
|
sendAsIds,
|
||||||
|
}) => {
|
||||||
|
const { saveDefaultSendAs } = getDispatch();
|
||||||
|
|
||||||
|
// No need for expensive global updates on users and chats, so we avoid them
|
||||||
|
const usersById = getGlobal().users.byId;
|
||||||
|
const chatsById = getGlobal().chats.byId;
|
||||||
|
|
||||||
|
const lang = useLang();
|
||||||
|
// eslint-disable-next-line no-null/no-null
|
||||||
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
const [handleMouseEnter, handleMouseLeave, markMouseInside] = useMouseInside(isOpen, onClose, undefined);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isOpen) {
|
||||||
|
markMouseInside();
|
||||||
|
}
|
||||||
|
}, [isOpen, markMouseInside]);
|
||||||
|
|
||||||
|
const handleUserSelect = useCallback((id: string) => {
|
||||||
|
onClose();
|
||||||
|
saveDefaultSendAs({ chatId, sendAsId: id });
|
||||||
|
}, [chatId, onClose, saveDefaultSendAs]);
|
||||||
|
|
||||||
|
const selectedSendAsIndex = useKeyboardNavigation({
|
||||||
|
isActive: isOpen,
|
||||||
|
items: sendAsIds,
|
||||||
|
onSelect: handleUserSelect,
|
||||||
|
shouldSelectOnTab: true,
|
||||||
|
shouldSaveSelectionOnUpdateItems: true,
|
||||||
|
onClose,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTooltipItemVisible('.chat-item-clickable', selectedSendAsIndex, containerRef);
|
||||||
|
}, [selectedSendAsIndex]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (sendAsIds && !sendAsIds.length) {
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
}, [sendAsIds, onClose]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Menu
|
||||||
|
isOpen={isOpen}
|
||||||
|
positionX="left"
|
||||||
|
positionY="bottom"
|
||||||
|
onClose={onClose}
|
||||||
|
className="SendAsMenu"
|
||||||
|
onCloseAnimationEnd={onClose}
|
||||||
|
onMouseEnter={!IS_TOUCH_ENV ? handleMouseEnter : undefined}
|
||||||
|
onMouseLeave={!IS_TOUCH_ENV ? handleMouseLeave : undefined}
|
||||||
|
noCloseOnBackdrop={!IS_TOUCH_ENV}
|
||||||
|
>
|
||||||
|
<div className="send-as-title" dir="auto">{lang('SendMessageAsTitle')}</div>
|
||||||
|
{usersById && chatsById && sendAsIds?.map((id, index) => {
|
||||||
|
const user = isUserId(id) ? usersById[id] : undefined;
|
||||||
|
const chat = !user ? chatsById[id] : undefined;
|
||||||
|
const fullName = user ? getUserFullName(user) : chat?.title;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListItem
|
||||||
|
key={id}
|
||||||
|
className="SendAsItem chat-item-clickable scroll-item with-avatar"
|
||||||
|
onClick={() => handleUserSelect(id)}
|
||||||
|
focus={selectedSendAsIndex === index}
|
||||||
|
>
|
||||||
|
<Avatar
|
||||||
|
size="small"
|
||||||
|
user={user}
|
||||||
|
chat={chat}
|
||||||
|
className={buildClassName(selectedSendAsId === id && 'selected')}
|
||||||
|
/>
|
||||||
|
<div className="info">
|
||||||
|
<div className="title">
|
||||||
|
<h3 dir="auto">{fullName && renderText(fullName)}</h3>
|
||||||
|
</div>
|
||||||
|
<span className="subtitle">{user
|
||||||
|
? lang('VoipGroupPersonalAccount')
|
||||||
|
: lang('Subscribers', chat?.membersCount, 'i')}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Menu>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default memo(SendAsMenu);
|
||||||
@ -98,7 +98,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.Button.bot-commands ~ & {
|
.Button.bot-commands ~ &, .Button.send-as-button ~ & {
|
||||||
.is-pointer-env & > .backdrop {
|
.is-pointer-env & > .backdrop {
|
||||||
left: 3rem;
|
left: 3rem;
|
||||||
width: 3.25rem;
|
width: 3.25rem;
|
||||||
|
|||||||
@ -505,7 +505,7 @@ export type ActionTypes = (
|
|||||||
'setReplyingToId' | 'setEditingId' | 'editLastMessage' | 'saveDraft' | 'clearDraft' | 'loadPinnedMessages' |
|
'setReplyingToId' | 'setEditingId' | 'editLastMessage' | 'saveDraft' | 'clearDraft' | 'loadPinnedMessages' |
|
||||||
'toggleMessageWebPage' | 'replyToNextMessage' | 'deleteChatUser' | 'deleteChat' |
|
'toggleMessageWebPage' | 'replyToNextMessage' | 'deleteChatUser' | 'deleteChat' |
|
||||||
'reportMessages' | 'sendMessageAction' | 'focusNextReply' | 'openChatByInvite' | 'loadSeenBy' |
|
'reportMessages' | 'sendMessageAction' | 'focusNextReply' | 'openChatByInvite' | 'loadSeenBy' |
|
||||||
'loadSponsoredMessages' | 'viewSponsoredMessage' |
|
'loadSponsoredMessages' | 'viewSponsoredMessage' | 'loadSendAs' | 'saveDefaultSendAs' |
|
||||||
// downloads
|
// downloads
|
||||||
'downloadSelectedMessages' | 'downloadMessageMedia' | 'cancelMessageMediaDownload' |
|
'downloadSelectedMessages' | 'downloadMessageMedia' | 'cancelMessageMediaDownload' |
|
||||||
// scheduled messages
|
// scheduled messages
|
||||||
|
|||||||
@ -10,6 +10,10 @@ export default function useMouseInside(
|
|||||||
) {
|
) {
|
||||||
const isMouseInside = useRef(false);
|
const isMouseInside = useRef(false);
|
||||||
|
|
||||||
|
const markMouseInside = useCallback(() => {
|
||||||
|
isMouseInside.current = true;
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (closeTimeout) {
|
if (closeTimeout) {
|
||||||
clearTimeout(closeTimeout);
|
clearTimeout(closeTimeout);
|
||||||
@ -44,5 +48,5 @@ export default function useMouseInside(
|
|||||||
}, menuCloseTimeout);
|
}, menuCloseTimeout);
|
||||||
}, [menuCloseTimeout, onClose]);
|
}, [menuCloseTimeout, onClose]);
|
||||||
|
|
||||||
return [handleMouseEnter, handleMouseLeave];
|
return [handleMouseEnter, handleMouseLeave, markMouseInside];
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1080,6 +1080,7 @@ messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Boo
|
|||||||
messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory;
|
messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory;
|
||||||
messages.deleteChat#5bd0ee50 chat_id:long = Bool;
|
messages.deleteChat#5bd0ee50 chat_id:long = Bool;
|
||||||
messages.getMessageReadParticipants#2c6f97b7 peer:InputPeer msg_id:int = Vector<long>;
|
messages.getMessageReadParticipants#2c6f97b7 peer:InputPeer msg_id:int = Vector<long>;
|
||||||
|
messages.saveDefaultSendAs#ccfddf96 peer:InputPeer send_as:InputPeer = Bool;
|
||||||
updates.getState#edd4882a = updates.State;
|
updates.getState#edd4882a = updates.State;
|
||||||
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
||||||
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
|
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
|
||||||
@ -1117,6 +1118,7 @@ channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool
|
|||||||
channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates;
|
channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates;
|
||||||
channels.getGroupsForDiscussion#f5dad378 = messages.Chats;
|
channels.getGroupsForDiscussion#f5dad378 = messages.Chats;
|
||||||
channels.setDiscussionGroup#40582bb2 broadcast:InputChannel group:InputChannel = Bool;
|
channels.setDiscussionGroup#40582bb2 broadcast:InputChannel group:InputChannel = Bool;
|
||||||
|
channels.getSendAs#dc770ee peer:InputPeer = channels.SendAsPeers;
|
||||||
channels.viewSponsoredMessage#beaedb94 channel:InputChannel random_id:bytes = Bool;
|
channels.viewSponsoredMessage#beaedb94 channel:InputChannel random_id:bytes = Bool;
|
||||||
channels.getSponsoredMessages#ec210fbf channel:InputChannel = messages.SponsoredMessages;
|
channels.getSponsoredMessages#ec210fbf channel:InputChannel = messages.SponsoredMessages;
|
||||||
payments.getPaymentForm#8a333c8d flags:# peer:InputPeer msg_id:int theme_params:flags.0?DataJSON = payments.PaymentForm;
|
payments.getPaymentForm#8a333c8d flags:# peer:InputPeer msg_id:int theme_params:flags.0?DataJSON = payments.PaymentForm;
|
||||||
|
|||||||
@ -1081,6 +1081,7 @@ messages.readDiscussion#f731a9f4 peer:InputPeer msg_id:int read_max_id:int = Boo
|
|||||||
messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory;
|
messages.unpinAllMessages#f025bc8b peer:InputPeer = messages.AffectedHistory;
|
||||||
messages.deleteChat#5bd0ee50 chat_id:long = Bool;
|
messages.deleteChat#5bd0ee50 chat_id:long = Bool;
|
||||||
messages.getMessageReadParticipants#2c6f97b7 peer:InputPeer msg_id:int = Vector<long>;
|
messages.getMessageReadParticipants#2c6f97b7 peer:InputPeer msg_id:int = Vector<long>;
|
||||||
|
messages.saveDefaultSendAs#ccfddf96 peer:InputPeer send_as:InputPeer = Bool;
|
||||||
updates.getState#edd4882a = updates.State;
|
updates.getState#edd4882a = updates.State;
|
||||||
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
updates.getDifference#25939651 flags:# pts:int pts_total_limit:flags.0?int date:int qts:int = updates.Difference;
|
||||||
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
|
updates.getChannelDifference#3173d78 flags:# force:flags.0?true channel:InputChannel filter:ChannelMessagesFilter pts:int limit:int = updates.ChannelDifference;
|
||||||
@ -1118,6 +1119,7 @@ channels.readMessageContents#eab5dc38 channel:InputChannel id:Vector<int> = Bool
|
|||||||
channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates;
|
channels.togglePreHistoryHidden#eabbb94c channel:InputChannel enabled:Bool = Updates;
|
||||||
channels.getGroupsForDiscussion#f5dad378 = messages.Chats;
|
channels.getGroupsForDiscussion#f5dad378 = messages.Chats;
|
||||||
channels.setDiscussionGroup#40582bb2 broadcast:InputChannel group:InputChannel = Bool;
|
channels.setDiscussionGroup#40582bb2 broadcast:InputChannel group:InputChannel = Bool;
|
||||||
|
channels.getSendAs#dc770ee peer:InputPeer = channels.SendAsPeers;
|
||||||
channels.viewSponsoredMessage#beaedb94 channel:InputChannel random_id:bytes = Bool;
|
channels.viewSponsoredMessage#beaedb94 channel:InputChannel random_id:bytes = Bool;
|
||||||
channels.getSponsoredMessages#ec210fbf channel:InputChannel = messages.SponsoredMessages;
|
channels.getSponsoredMessages#ec210fbf channel:InputChannel = messages.SponsoredMessages;
|
||||||
payments.getPaymentForm#8a333c8d flags:# peer:InputPeer msg_id:int theme_params:flags.0?DataJSON = payments.PaymentForm;
|
payments.getPaymentForm#8a333c8d flags:# peer:InputPeer msg_id:int theme_params:flags.0?DataJSON = payments.PaymentForm;
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import {
|
|||||||
addReducer, getDispatch, getGlobal, setGlobal,
|
addReducer, getDispatch, getGlobal, setGlobal,
|
||||||
} from '../../../lib/teact/teactn';
|
} from '../../../lib/teact/teactn';
|
||||||
|
|
||||||
import { ApiChat } from '../../../api/types';
|
import { ApiChat, ApiUser } from '../../../api/types';
|
||||||
import { InlineBotSettings } from '../../../types';
|
import { InlineBotSettings } from '../../../types';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -11,7 +11,7 @@ import {
|
|||||||
import { callApi } from '../../../api/gramjs';
|
import { callApi } from '../../../api/gramjs';
|
||||||
import {
|
import {
|
||||||
selectChat, selectChatBot, selectChatMessage, selectCurrentChat, selectCurrentMessageList,
|
selectChat, selectChatBot, selectChatMessage, selectCurrentChat, selectCurrentMessageList,
|
||||||
selectReplyingToId, selectUser,
|
selectReplyingToId, selectSendAs, selectUser,
|
||||||
} from '../../selectors';
|
} from '../../selectors';
|
||||||
import { addChats, addUsers, removeBlockedContact } from '../../reducers';
|
import { addChats, addUsers, removeBlockedContact } from '../../reducers';
|
||||||
import { buildCollectionByKey } from '../../../util/iteratees';
|
import { buildCollectionByKey } from '../../../util/iteratees';
|
||||||
@ -81,7 +81,9 @@ addReducer('sendBotCommand', (global, actions, payload) => {
|
|||||||
actions.setReplyingToId({ messageId: undefined });
|
actions.setReplyingToId({ messageId: undefined });
|
||||||
actions.clearWebPagePreview({ chatId: chat.id, threadId, value: false });
|
actions.clearWebPagePreview({ chatId: chat.id, threadId, value: false });
|
||||||
|
|
||||||
void sendBotCommand(chat, currentUserId, command, selectReplyingToId(global, chat.id, threadId));
|
void sendBotCommand(
|
||||||
|
chat, currentUserId, command, selectReplyingToId(global, chat.id, threadId), selectSendAs(global, chatId),
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
addReducer('restartBot', (global, actions, payload) => {
|
addReducer('restartBot', (global, actions, payload) => {
|
||||||
@ -100,7 +102,7 @@ addReducer('restartBot', (global, actions, payload) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
setGlobal(removeBlockedContact(getGlobal(), bot.id));
|
setGlobal(removeBlockedContact(getGlobal(), bot.id));
|
||||||
void sendBotCommand(chat, currentUserId, '/start');
|
void sendBotCommand(chat, currentUserId, '/start', undefined, selectSendAs(global, chatId));
|
||||||
})();
|
})();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -204,6 +206,7 @@ addReducer('sendInlineBotResult', (global, actions, payload) => {
|
|||||||
resultId: id,
|
resultId: id,
|
||||||
queryId,
|
queryId,
|
||||||
replyingTo: selectReplyingToId(global, chatId, threadId),
|
replyingTo: selectReplyingToId(global, chatId, threadId),
|
||||||
|
sendAs: selectSendAs(global, chatId),
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -305,11 +308,14 @@ async function searchInlineBot({
|
|||||||
setGlobal(global);
|
setGlobal(global);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function sendBotCommand(chat: ApiChat, currentUserId: string, command: string, replyingTo?: number) {
|
async function sendBotCommand(
|
||||||
|
chat: ApiChat, currentUserId: string, command: string, replyingTo?: number, sendAs?: ApiChat | ApiUser,
|
||||||
|
) {
|
||||||
await callApi('sendMessage', {
|
await callApi('sendMessage', {
|
||||||
chat,
|
chat,
|
||||||
text: command,
|
text: command,
|
||||||
replyingTo,
|
replyingTo,
|
||||||
|
sendAs,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import {
|
|||||||
ApiNewPoll,
|
ApiNewPoll,
|
||||||
ApiOnProgress,
|
ApiOnProgress,
|
||||||
ApiSticker,
|
ApiSticker,
|
||||||
|
ApiUser,
|
||||||
ApiVideo,
|
ApiVideo,
|
||||||
MAIN_THREAD_ID,
|
MAIN_THREAD_ID,
|
||||||
MESSAGE_DELETED,
|
MESSAGE_DELETED,
|
||||||
@ -56,6 +57,8 @@ import {
|
|||||||
selectScheduledMessage,
|
selectScheduledMessage,
|
||||||
selectNoWebPage,
|
selectNoWebPage,
|
||||||
selectFirstUnreadId,
|
selectFirstUnreadId,
|
||||||
|
selectUser,
|
||||||
|
selectSendAs,
|
||||||
selectSponsoredMessage,
|
selectSponsoredMessage,
|
||||||
} from '../../selectors';
|
} from '../../selectors';
|
||||||
import { debounce, rafPromise } from '../../../util/schedulers';
|
import { debounce, rafPromise } from '../../../util/schedulers';
|
||||||
@ -204,6 +207,7 @@ addReducer('sendMessage', (global, actions, payload) => {
|
|||||||
chat,
|
chat,
|
||||||
replyingTo: selectReplyingToId(global, chatId, threadId),
|
replyingTo: selectReplyingToId(global, chatId, threadId),
|
||||||
noWebPage: selectNoWebPage(global, chatId, threadId),
|
noWebPage: selectNoWebPage(global, chatId, threadId),
|
||||||
|
sendAs: selectSendAs(global, chatId),
|
||||||
};
|
};
|
||||||
|
|
||||||
const isSingle = !payload.attachments || payload.attachments.length <= 1;
|
const isSingle = !payload.attachments || payload.attachments.length <= 1;
|
||||||
@ -586,6 +590,7 @@ addReducer('forwardMessages', (global, action, payload) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const { isSilent, scheduledAt } = payload;
|
const { isSilent, scheduledAt } = payload;
|
||||||
|
const sendAs = selectSendAs(global, toChatId!);
|
||||||
|
|
||||||
const realMessages = messages.filter((m) => !isServiceNotificationMessage(m));
|
const realMessages = messages.filter((m) => !isServiceNotificationMessage(m));
|
||||||
if (realMessages.length) {
|
if (realMessages.length) {
|
||||||
@ -596,6 +601,7 @@ addReducer('forwardMessages', (global, action, payload) => {
|
|||||||
serverTimeOffset: getGlobal().serverTimeOffset,
|
serverTimeOffset: getGlobal().serverTimeOffset,
|
||||||
isSilent,
|
isSilent,
|
||||||
scheduledAt,
|
scheduledAt,
|
||||||
|
sendAs,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -613,6 +619,7 @@ addReducer('forwardMessages', (global, action, payload) => {
|
|||||||
poll,
|
poll,
|
||||||
isSilent,
|
isSilent,
|
||||||
scheduledAt,
|
scheduledAt,
|
||||||
|
sendAs,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -853,6 +860,7 @@ async function sendMessage(params: {
|
|||||||
serverTimeOffset?: number;
|
serverTimeOffset?: number;
|
||||||
isSilent?: boolean;
|
isSilent?: boolean;
|
||||||
scheduledAt?: number;
|
scheduledAt?: number;
|
||||||
|
sendAs?: ApiChat | ApiUser;
|
||||||
}) {
|
}) {
|
||||||
let localId: number | undefined;
|
let localId: number | undefined;
|
||||||
const progressCallback = params.attachment ? (progress: number, messageLocalId: number) => {
|
const progressCallback = params.attachment ? (progress: number, messageLocalId: number) => {
|
||||||
@ -967,6 +975,47 @@ addReducer('loadSeenBy', (global, actions, payload) => {
|
|||||||
})();
|
})();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addReducer('saveDefaultSendAs', (global, actions, payload) => {
|
||||||
|
const { chatId, sendAsId } = payload;
|
||||||
|
const chat = selectChat(global, chatId);
|
||||||
|
const sendAsChat = selectChat(global, sendAsId) || selectUser(global, sendAsId);
|
||||||
|
if (!chat || !sendAsChat) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
void callApi('saveDefaultSendAs', { sendAs: sendAsChat, chat });
|
||||||
|
|
||||||
|
return updateChat(global, chatId, {
|
||||||
|
fullInfo: {
|
||||||
|
...chat.fullInfo,
|
||||||
|
sendAsId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
addReducer('loadSendAs', (global, actions, payload) => {
|
||||||
|
const { chatId } = payload;
|
||||||
|
const chat = selectChat(global, chatId);
|
||||||
|
if (!chat) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
const result = await callApi('fetchSendAs', { chat });
|
||||||
|
if (!result) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
global = getGlobal();
|
||||||
|
global = addUsers(global, buildCollectionByKey(result.users, 'id'));
|
||||||
|
global = addChats(global, buildCollectionByKey(result.chats, 'id'));
|
||||||
|
global = updateChat(global, chatId, {
|
||||||
|
sendAsIds: result.ids,
|
||||||
|
});
|
||||||
|
setGlobal(global);
|
||||||
|
})();
|
||||||
|
});
|
||||||
|
|
||||||
async function loadPinnedMessages(chat: ApiChat) {
|
async function loadPinnedMessages(chat: ApiChat) {
|
||||||
const result = await callApi('fetchPinnedMessages', { chat });
|
const result = await callApi('fetchPinnedMessages', { chat });
|
||||||
if (!result) {
|
if (!result) {
|
||||||
|
|||||||
@ -185,3 +185,13 @@ export function selectCountNotMutedUnread(global: GlobalState) {
|
|||||||
export function selectIsServiceChatReady(global: GlobalState) {
|
export function selectIsServiceChatReady(global: GlobalState) {
|
||||||
return Boolean(selectChat(global, SERVICE_NOTIFICATIONS_USER_ID));
|
return Boolean(selectChat(global, SERVICE_NOTIFICATIONS_USER_ID));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function selectSendAs(global: GlobalState, chatId: string) {
|
||||||
|
const chat = selectChat(global, chatId);
|
||||||
|
if (!chat) return undefined;
|
||||||
|
|
||||||
|
const id = chat?.fullInfo?.sendAsId;
|
||||||
|
if (!id) return undefined;
|
||||||
|
|
||||||
|
return selectUser(global, id) || selectChat(global, id);
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user