diff --git a/Telegram/SourceFiles/api/api_sending.cpp b/Telegram/SourceFiles/api/api_sending.cpp index ef6744d66c..31df6b52bd 100644 --- a/Telegram/SourceFiles/api/api_sending.cpp +++ b/Telegram/SourceFiles/api/api_sending.cpp @@ -308,6 +308,25 @@ void SendExistingDocument( MessageToSend &&message, not_null document, std::optional localMessageId) { + if (!document->sticker() + && !document->isVideoMessage() + && !document->isVoiceMessage()) { + const auto clearReplyTo = prependPseudoReply(message); + if (clearReplyTo) { + message.action.replyTo.messageId = FullMsgId( + message.action.replyTo.messageId.peer, + message.action.replyTo.topicRootId); + } + } else if (message.action.replyTo && message.action.history) { + if (const auto item = message.action.history->session().data().message(message.action.replyTo.messageId)) { + if (item->isDeleted()) { + message.action.replyTo.messageId = FullMsgId( + message.action.replyTo.messageId.peer, + message.action.replyTo.topicRootId); + } + } + } + const auto inputMedia = [=] { return MTP_inputMediaDocument( MTP_flags(message.action.options.mediaSpoiler @@ -335,6 +354,13 @@ void SendExistingPhoto( MessageToSend &&message, not_null photo, std::optional localMessageId) { + const auto clearReplyTo = prependPseudoReply(message); + if (clearReplyTo) { + message.action.replyTo.messageId = FullMsgId( + message.action.replyTo.messageId.peer, + message.action.replyTo.topicRootId); + } + const auto inputMedia = [=] { return MTP_inputMediaPhoto( MTP_flags(0), @@ -558,6 +584,26 @@ void SendConfirmedFile( const auto history = session->data().history(file->to.peer); const auto peer = history->peer; + if (!isEditing + && file->type != SendMediaType::Audio + && file->type != SendMediaType::Round) { + const auto clearReplyTo = prependPseudoReply( + session, history, file->caption, file->to.replyTo); + if (clearReplyTo) { + file->to.replyTo.messageId = FullMsgId( + file->to.replyTo.messageId.peer, + file->to.replyTo.topicRootId); + } + } else if (!isEditing && file->to.replyTo) { + if (const auto item = session->data().message(file->to.replyTo.messageId)) { + if (item->isDeleted()) { + file->to.replyTo.messageId = FullMsgId( + file->to.replyTo.messageId.peer, + file->to.replyTo.topicRootId); + } + } + } + if (!isEditing) { const auto histories = &session->data().histories(); file->to.replyTo.messageId = histories->convertTopicReplyToId( diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 577c5e8b87..3d4961c8f9 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -4008,6 +4008,7 @@ void ApiWrap::sendMessage( MessageToSend &&message, std::optional localMessageId) { applyGhostScheduling(_session, message.action.options); + const auto clearReplyTo = prependPseudoReply(message); const auto history = message.action.history; const auto peer = history->peer; @@ -4034,6 +4035,11 @@ void ApiWrap::sendMessage( ? Data::CanSendTexts(topic) : Data::CanSendTexts(peer); + if (clearReplyTo) { + message.action.replyTo.messageId = FullMsgId(message.action.replyTo.messageId.peer, message.action.replyTo.topicRootId); + action.replyTo.messageId = FullMsgId(action.replyTo.messageId.peer, action.replyTo.topicRootId); + } + if ((!canSendTexts && !AyuForward::isForwarding(peer->id)) || Api::SendDice(message)) { return; } diff --git a/Telegram/SourceFiles/ayu/utils/telegram_helpers.cpp b/Telegram/SourceFiles/ayu/utils/telegram_helpers.cpp index 55c0cc6be8..283db49107 100644 --- a/Telegram/SourceFiles/ayu/utils/telegram_helpers.cpp +++ b/Telegram/SourceFiles/ayu/utils/telegram_helpers.cpp @@ -924,6 +924,125 @@ bool mediaDownloadable(const Data::Media *media) { return true; } +static bool prependPseudoReplyImpl( + not_null session, + not_null history, + TextWithTags &textWithTags, + FullReplyTo &replyTo) { + if (!replyTo) { + return false; + } + const auto replyItem = session->data().message(replyTo.messageId); + if (!replyItem || !replyItem->isDeleted()) { + return false; + } + const auto shortify = [&](const QString &text, int maxLength) { + if (text.isEmpty() || text.length() < maxLength) { + return text; + } + return text.left(maxLength - 1) + QChar(8230); // … + }; + const auto shiftEntities = [&](QVector &tags, int offset) { + if (tags.isEmpty() || !offset) { + return; + } + for (auto &tag : tags) { + tag.offset += offset; + } + }; + + const auto from = replyItem->from(); + auto name = QString(); + if (!history->peer->isUser() || replyItem->history()->peer != history->peer) { + name = from->name(); + } + + auto msgText = !replyTo.quote.empty() + ? replyTo.quote.text + : replyItem->originalText().text; + if (msgText.isEmpty()) { + msgText = replyItem->notificationText().text; + } + const auto shortifiedText = shortify(msgText, 100); + + const auto prefix = name.isEmpty() + ? shortifiedText + : (name + "\n" + shortifiedText); + + if (textWithTags.empty()) { + textWithTags.text = prefix; + } else { + textWithTags.text.prepend(prefix + "\n"); + } + const auto prefixLength = prefix.length() + (textWithTags.text.length() > prefix.length() ? 1 : 0); + + shiftEntities(textWithTags.tags, prefixLength); + + EntitiesInText newEntities; + const auto nameLength = name.length(); + + newEntities.push_back(EntityInText{ + EntityType::Blockquote, + 0, + prefix.length(), + {} + }); + + if (nameLength > 0) { + newEntities.push_back(EntityInText{ + EntityType::Bold, + 0, + nameLength, + QString() + }); + + auto accessHash = uint64(0); + if (const auto user = from->asUser()) { + accessHash = user->accessHash(); + } else if (const auto channel = from->asChannel()) { + accessHash = channel->accessHash(); + } + + if (accessHash != 0) { + const auto mentionData = QStringLiteral("%1.%2:%3") + .arg(from->id.value) + .arg(accessHash) + .arg(session->userId().bare); + + newEntities.push_back(EntityInText{ + EntityType::MentionName, + 0, + nameLength, + mentionData + }); + } + } + + const auto newTags = TextUtilities::ConvertEntitiesToTextTags(newEntities); + textWithTags.tags.append(newTags); + + return true; +} + +bool prependPseudoReply(Api::MessageToSend &message) { + if (!message.action.history) { + return false; + } + return prependPseudoReplyImpl( + &message.action.history->session(), + message.action.history, + message.textWithTags, + message.action.replyTo); +} + +bool prependPseudoReply( + not_null session, + not_null history, + TextWithTags &caption, + FullReplyTo &replyTo) { + return prependPseudoReplyImpl(session, history, caption, replyTo); +} + TextWithEntities reverseLocalPremiumEmoji(const TextWithEntities &text, not_null history, bool isForQuote) { if (text.empty()) { return text; diff --git a/Telegram/SourceFiles/ayu/utils/telegram_helpers.h b/Telegram/SourceFiles/ayu/utils/telegram_helpers.h index 9057f9d764..7dc1543f31 100644 --- a/Telegram/SourceFiles/ayu/utils/telegram_helpers.h +++ b/Telegram/SourceFiles/ayu/utils/telegram_helpers.h @@ -7,6 +7,7 @@ #pragma once #include "rc_manager.h" +#include "api/api_common.h" #include "ayu/data/entities.h" #include "core/application.h" @@ -113,6 +114,13 @@ PeerData* getPeerFromDialogId(unsigned long long id); QString filterZalgo(const QString &text); +bool prependPseudoReply(Api::MessageToSend &message); +bool prependPseudoReply( + not_null session, + not_null history, + TextWithTags &caption, + FullReplyTo &replyTo); + void getRegistrationDate(not_null peer, Fn callback); void applyGhostScheduling( diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index dc629d993f..15b658877d 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -668,7 +668,7 @@ void HistoryInner::setupSwipeReplyAndBack() { } const auto item = view->data(); const auto canSendReply = CanSendReply(item); - const auto canReply = canSendReply || (item->allowsForward() && !item->isDeleted()); + const auto canReply = canSendReply || item->allowsForward(); if (!canReply) { return true; } @@ -2312,9 +2312,7 @@ void HistoryInner::mouseDoubleClickEvent(QMouseEvent *e) { mouseActionCancel(); switch (HistoryView::CurrentQuickAction()) { case HistoryView::DoubleClickQuickAction::Reply: { - if (!view->data()->isDeleted()) { - _widget->replyToMessage(view->data()); - } + _widget->replyToMessage(view->data()); } break; case HistoryView::DoubleClickQuickAction::React: { toggleFavoriteReaction(view); @@ -2821,7 +2819,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { return; } const auto canSendReply = CanSendReply(item); - const auto canReply = canSendReply || (item->allowsForward() && !item->isDeleted()); + const auto canReply = canSendReply || item->allowsForward(); if (canReply) { const auto selected = selectedQuote(item); auto text = (selected @@ -5237,10 +5235,6 @@ auto HistoryInner::DelegateMixin() } bool CanSendReply(not_null item) { - if (item->isDeleted()) { - return false; - } - const auto peer = item->history()->peer; if (const auto topic = item->topic()) { return Data::CanSendAnything(topic); diff --git a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp index 8e397ff714..8e15a6a1c7 100644 --- a/Telegram/SourceFiles/history/view/history_view_list_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_list_widget.cpp @@ -2757,9 +2757,7 @@ void ListWidget::mouseDoubleClickEvent(QMouseEvent *e) { mouseActionCancel(); switch (CurrentQuickAction()) { case DoubleClickQuickAction::Reply: { - if (!_overElement->data()->isDeleted()) { - replyToMessageRequestNotify({ _overElement->data()->fullId() }); - } + replyToMessageRequestNotify({ _overElement->data()->fullId() }); } break; case DoubleClickQuickAction::React: { toggleFavoriteReaction(_overElement);