mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Allow replying with quoting message part.
This commit is contained in:
parent
00db325e91
commit
ef0539c9fc
20 changed files with 167 additions and 29 deletions
|
@ -71,11 +71,7 @@ void ApplyPeerCloudDraft(
|
|||
textWithTags,
|
||||
FullReplyTo{
|
||||
.messageId = FullMsgId(replyPeerId, reply.messageId),
|
||||
.quote = TextWithTags{
|
||||
reply.quote.text,
|
||||
TextUtilities::ConvertEntitiesToTextTags(
|
||||
reply.quote.entities),
|
||||
},
|
||||
.quote = reply.quote,
|
||||
.storyId = (reply.storyId
|
||||
? FullStoryId{ replyPeerId, reply.storyId }
|
||||
: FullStoryId()),
|
||||
|
|
|
@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#include "data/data_histories.h"
|
||||
|
||||
#include "api/api_text_entities.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
|
@ -46,10 +47,20 @@ MTPInputReplyTo ReplyToForMTP(
|
|||
}
|
||||
} else if (replyTo.messageId || replyTo.topicRootId) {
|
||||
const auto external = (replyTo.messageId.peer != history->peer->id);
|
||||
const auto quoteEntities = Api::EntitiesToMTP(
|
||||
&history->session(),
|
||||
replyTo.quote.entities,
|
||||
Api::ConvertOption::SkipLocal);
|
||||
using Flag = MTPDinputReplyToMessage::Flag;
|
||||
return MTP_inputReplyToMessage(
|
||||
MTP_flags((replyTo.topicRootId ? Flag::f_top_msg_id : Flag())
|
||||
| (external ? Flag::f_reply_to_peer_id : Flag())),
|
||||
| (external ? Flag::f_reply_to_peer_id : Flag())
|
||||
| (replyTo.quote.text.isEmpty()
|
||||
? Flag()
|
||||
: Flag::f_quote_text)
|
||||
| (quoteEntities.v.isEmpty()
|
||||
? Flag()
|
||||
: Flag::f_quote_entities)),
|
||||
MTP_int(replyTo.messageId
|
||||
? replyTo.messageId.msg
|
||||
: replyTo.topicRootId),
|
||||
|
@ -57,8 +68,8 @@ MTPInputReplyTo ReplyToForMTP(
|
|||
(external
|
||||
? owner->peer(replyTo.messageId.peer)->input
|
||||
: MTPInputPeer()),
|
||||
MTPstring(), // quote_text
|
||||
MTPVector<MTPMessageEntity>()); // quote_entities
|
||||
MTP_string(replyTo.quote.text),
|
||||
quoteEntities);
|
||||
}
|
||||
return MTPInputReplyTo();
|
||||
}
|
||||
|
@ -945,6 +956,7 @@ int Histories::sendPreparedMessage(
|
|||
}
|
||||
const auto realReplyTo = FullReplyTo{
|
||||
.messageId = convertTopicReplyToId(history, replyTo.messageId),
|
||||
.quote = replyTo.quote,
|
||||
.storyId = replyTo.storyId,
|
||||
.topicRootId = convertTopicReplyToId(history, replyTo.topicRootId),
|
||||
};
|
||||
|
|
|
@ -158,7 +158,7 @@ Q_DECLARE_METATYPE(FullMsgId);
|
|||
|
||||
struct FullReplyTo {
|
||||
FullMsgId messageId;
|
||||
TextWithTags quote;
|
||||
TextWithEntities quote;
|
||||
FullStoryId storyId;
|
||||
MsgId topicRootId = 0;
|
||||
|
||||
|
|
|
@ -2101,6 +2101,20 @@ void HistoryInner::toggleFavoriteReaction(not_null<Element*> view) const {
|
|||
item->toggleReaction(favorite, HistoryItem::ReactionSource::Quick);
|
||||
}
|
||||
|
||||
TextWithEntities HistoryInner::selectedQuote(
|
||||
not_null<HistoryItem*> item) const {
|
||||
if (_selected.size() != 1
|
||||
|| _selected.begin()->first != item
|
||||
|| _selected.begin()->second == FullSelection) {
|
||||
return {};
|
||||
}
|
||||
const auto view = item->mainView();
|
||||
if (!view) {
|
||||
return {};
|
||||
}
|
||||
return view->selectedQuote(_selected.begin()->second);
|
||||
}
|
||||
|
||||
void HistoryInner::contextMenuEvent(QContextMenuEvent *e) {
|
||||
showContextMenu(e);
|
||||
}
|
||||
|
@ -2215,13 +2229,14 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
|
|||
return true;
|
||||
}();
|
||||
if (canReply) {
|
||||
const auto quote = selectedQuote(item);
|
||||
_menu->addAction(tr::lng_context_reply_msg(tr::now), [=] {
|
||||
if (canSendReply) {
|
||||
_widget->replyToMessage({ itemId });
|
||||
_widget->replyToMessage({ itemId, quote });
|
||||
} else {
|
||||
HistoryView::Controls::ShowReplyToChatBox(
|
||||
controller->uiShow(),
|
||||
{ itemId });
|
||||
{ itemId, quote });
|
||||
}
|
||||
}, &st::menuIconReply);
|
||||
}
|
||||
|
|
|
@ -314,6 +314,8 @@ private:
|
|||
|
||||
QPoint mapPointToItem(QPoint p, const Element *view) const;
|
||||
QPoint mapPointToItem(QPoint p, const HistoryItem *item) const;
|
||||
[[nodiscard]] TextWithEntities selectedQuote(
|
||||
not_null<HistoryItem*> item) const;
|
||||
|
||||
void showContextMenu(QContextMenuEvent *e, bool showFromTouch = false);
|
||||
void cancelContextDownload(not_null<DocumentData*> document);
|
||||
|
|
|
@ -7072,7 +7072,7 @@ void HistoryWidget::replyToMessage(FullReplyTo id) {
|
|||
|
||||
void HistoryWidget::replyToMessage(
|
||||
not_null<HistoryItem*> item,
|
||||
TextWithTags quote) {
|
||||
TextWithEntities quote) {
|
||||
if (isJoinChannel()) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -185,7 +185,7 @@ public:
|
|||
void replyToMessage(FullReplyTo id);
|
||||
void replyToMessage(
|
||||
not_null<HistoryItem*> item,
|
||||
TextWithTags quote = {});
|
||||
TextWithEntities quote = {});
|
||||
void editMessage(FullMsgId itemId);
|
||||
void editMessage(not_null<HistoryItem*> item);
|
||||
|
||||
|
|
|
@ -388,7 +388,10 @@ public:
|
|||
int bottom,
|
||||
QPoint point,
|
||||
InfoDisplayType type) const;
|
||||
virtual TextForMimeData selectedText(
|
||||
virtual TextForMimeData selectedText(TextSelection selection) const = 0;
|
||||
virtual TextWithEntities selectedQuote(TextSelection selection) const = 0;
|
||||
virtual TextWithEntities selectedQuote(
|
||||
const Ui::Text::String &text,
|
||||
TextSelection selection) const = 0;
|
||||
[[nodiscard]] virtual TextSelection adjustSelection(
|
||||
TextSelection selection,
|
||||
|
|
|
@ -2611,6 +2611,80 @@ TextForMimeData Message::selectedText(TextSelection selection) const {
|
|||
return result;
|
||||
}
|
||||
|
||||
TextWithEntities Message::selectedQuote(TextSelection selection) const {
|
||||
const auto item = data();
|
||||
const auto &translated = item->translatedText();
|
||||
const auto &original = item->originalText();
|
||||
if (&translated != &original
|
||||
|| selection.empty()
|
||||
|| selection == FullSelection) {
|
||||
return {};
|
||||
} else if (hasVisibleText()) {
|
||||
return selectedQuote(text(), selection);
|
||||
} else if (const auto media = this->media()) {
|
||||
if (media->isDisplayed() || isHiddenByGroup()) {
|
||||
return media->selectedQuote(selection);
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
TextWithEntities Message::selectedQuote(
|
||||
const Ui::Text::String &text,
|
||||
TextSelection selection) const {
|
||||
if (selection.to > text.length()) {
|
||||
return {};
|
||||
}
|
||||
auto modified = selection;
|
||||
const auto &modifications = text.modifications();
|
||||
for (const auto &modification : text.modifications()) {
|
||||
if (modification.position >= selection.to) {
|
||||
break;
|
||||
} else if (modification.position <= selection.from) {
|
||||
modified.from += modification.skipped;
|
||||
if (modification.added
|
||||
&& modification.position < selection.from) {
|
||||
--modified.from;
|
||||
}
|
||||
}
|
||||
modified.to += modification.skipped;
|
||||
if (modification.added && modified.to > modified.from) {
|
||||
--modified.to;
|
||||
}
|
||||
}
|
||||
auto result = data()->originalText();
|
||||
if (modified.empty() || modified.to > result.text.size()) {
|
||||
return {};
|
||||
}
|
||||
result.text = result.text.mid(
|
||||
modified.from,
|
||||
modified.to - modified.from);
|
||||
const auto allowed = std::array{
|
||||
EntityType::Bold,
|
||||
EntityType::Italic,
|
||||
EntityType::Underline,
|
||||
EntityType::StrikeOut,
|
||||
EntityType::Spoiler,
|
||||
EntityType::CustomEmoji,
|
||||
};
|
||||
for (auto i = result.entities.begin(); i != result.entities.end();) {
|
||||
const auto offset = i->offset();
|
||||
const auto till = offset + i->length();
|
||||
if ((till <= modified.from)
|
||||
|| (offset >= modified.to)
|
||||
|| !ranges::contains(allowed, i->type())) {
|
||||
i = result.entities.erase(i);
|
||||
} else {
|
||||
if (till > modified.to) {
|
||||
i->shrinkFromRight(till - modified.to);
|
||||
}
|
||||
i->shiftLeft(modified.from);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TextSelection Message::adjustSelection(
|
||||
TextSelection selection,
|
||||
TextSelectType type) const {
|
||||
|
|
|
@ -99,6 +99,10 @@ public:
|
|||
QPoint point,
|
||||
InfoDisplayType type) const override;
|
||||
TextForMimeData selectedText(TextSelection selection) const override;
|
||||
TextWithEntities selectedQuote(TextSelection selection) const override;
|
||||
TextWithEntities selectedQuote(
|
||||
const Ui::Text::String &text,
|
||||
TextSelection selection) const override;
|
||||
TextSelection adjustSelection(
|
||||
TextSelection selection,
|
||||
TextSelectType type) const override;
|
||||
|
|
|
@ -669,6 +669,16 @@ TextForMimeData Service::selectedText(TextSelection selection) const {
|
|||
return text().toTextForMimeData(selection);
|
||||
}
|
||||
|
||||
TextWithEntities Service::selectedQuote(TextSelection selection) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
TextWithEntities Service::selectedQuote(
|
||||
const Ui::Text::String &text,
|
||||
TextSelection selection) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
TextSelection Service::adjustSelection(
|
||||
TextSelection selection,
|
||||
TextSelectType type) const {
|
||||
|
|
|
@ -43,6 +43,10 @@ public:
|
|||
StateRequest request) const override;
|
||||
void updatePressed(QPoint point) override;
|
||||
TextForMimeData selectedText(TextSelection selection) const override;
|
||||
TextWithEntities selectedQuote(TextSelection selection) const override;
|
||||
TextWithEntities selectedQuote(
|
||||
const Ui::Text::String &text,
|
||||
TextSelection selection) const override;
|
||||
TextSelection adjustSelection(
|
||||
TextSelection selection,
|
||||
TextSelectType type) const override;
|
||||
|
|
|
@ -1210,6 +1210,22 @@ TextForMimeData Document::selectedText(TextSelection selection) const {
|
|||
return result;
|
||||
}
|
||||
|
||||
TextWithEntities Document::selectedQuote(TextSelection selection) const {
|
||||
if (const auto voice = Get<HistoryDocumentVoice>()) {
|
||||
const auto length = voice->transcribeText.length();
|
||||
if (selection.from < length) {
|
||||
return {};
|
||||
}
|
||||
selection = HistoryView::UnshiftItemSelection(
|
||||
selection,
|
||||
voice->transcribeText);
|
||||
}
|
||||
if (const auto captioned = Get<HistoryDocumentCaptioned>()) {
|
||||
return parent()->selectedQuote(captioned->caption, selection);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool Document::uploading() const {
|
||||
return _data->uploading();
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ public:
|
|||
bool hasTextForCopy() const override;
|
||||
|
||||
TextForMimeData selectedText(TextSelection selection) const override;
|
||||
TextWithEntities selectedQuote(TextSelection selection) const override;
|
||||
|
||||
bool uploading() const override;
|
||||
|
||||
|
|
|
@ -1181,6 +1181,10 @@ TextForMimeData Gif::selectedText(TextSelection selection) const {
|
|||
return _caption.toTextForMimeData(selection);
|
||||
}
|
||||
|
||||
TextWithEntities Gif::selectedQuote(TextSelection selection) const {
|
||||
return parent()->selectedQuote(_caption, selection);
|
||||
}
|
||||
|
||||
bool Gif::fullFeaturedGrouped(RectParts sides) const {
|
||||
return (sides & RectPart::Left) && (sides & RectPart::Right);
|
||||
}
|
||||
|
|
|
@ -68,6 +68,7 @@ public:
|
|||
}
|
||||
|
||||
TextForMimeData selectedText(TextSelection selection) const override;
|
||||
TextWithEntities selectedQuote(TextSelection selection) const override;
|
||||
|
||||
bool uploading() const override;
|
||||
|
||||
|
|
|
@ -86,7 +86,11 @@ public:
|
|||
|
||||
[[nodiscard]] virtual TextForMimeData selectedText(
|
||||
TextSelection selection) const {
|
||||
return TextForMimeData();
|
||||
return {};
|
||||
}
|
||||
[[nodiscard]] virtual TextWithEntities selectedQuote(
|
||||
TextSelection selection) const {
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual bool isDisplayed() const;
|
||||
|
|
|
@ -1049,6 +1049,10 @@ TextForMimeData Photo::selectedText(TextSelection selection) const {
|
|||
return _caption.toTextForMimeData(selection);
|
||||
}
|
||||
|
||||
TextWithEntities Photo::selectedQuote(TextSelection selection) const {
|
||||
return parent()->selectedQuote(_caption, selection);
|
||||
}
|
||||
|
||||
void Photo::hideSpoilers() {
|
||||
_caption.setSpoilerRevealed(false, anim::type::instant);
|
||||
if (_spoiler) {
|
||||
|
|
|
@ -57,6 +57,7 @@ public:
|
|||
}
|
||||
|
||||
TextForMimeData selectedText(TextSelection selection) const override;
|
||||
TextWithEntities selectedQuote(TextSelection selection) const override;
|
||||
|
||||
PhotoData *getPhoto() const override {
|
||||
return _data;
|
||||
|
|
|
@ -1126,11 +1126,8 @@ void Account::writeDrafts(not_null<History*> history) {
|
|||
auto&&) { // cursor
|
||||
size += sizeof(qint64) // key
|
||||
+ Serialize::stringSize(text.text)
|
||||
+ sizeof(qint64) + TextUtilities::SerializeTagsSize(text.tags)
|
||||
+ TextUtilities::SerializeTagsSize(text.tags)
|
||||
+ sizeof(qint64) + sizeof(qint64) // messageId
|
||||
+ Serialize::stringSize(reply.quote.text)
|
||||
+ sizeof(qint64)
|
||||
+ TextUtilities::SerializeTagsSize(reply.quote.tags)
|
||||
+ sizeof(qint32); // previewState
|
||||
};
|
||||
EnumerateDrafts(
|
||||
|
@ -1157,8 +1154,6 @@ void Account::writeDrafts(not_null<History*> history) {
|
|||
<< TextUtilities::SerializeTags(text.tags)
|
||||
<< qint64(reply.messageId.peer.value)
|
||||
<< qint64(reply.messageId.msg.bare)
|
||||
<< reply.quote.text
|
||||
<< TextUtilities::SerializeTags(reply.quote.tags)
|
||||
<< qint32(previewState);
|
||||
};
|
||||
EnumerateDrafts(
|
||||
|
@ -1371,10 +1366,8 @@ void Account::readDraftsWithCursors(not_null<History*> history) {
|
|||
const auto keysOld = (tag == kMultiDraftTagOld);
|
||||
const auto rich = (tag == kRichDraftsTag);
|
||||
for (auto i = 0; i != count; ++i) {
|
||||
TextWithTags quote;
|
||||
TextWithTags text;
|
||||
QByteArray textTagsSerialized;
|
||||
QByteArray quoteTagsSerialized;
|
||||
qint64 keyValue = 0;
|
||||
qint64 messageIdPeer = 0, messageIdMsg = 0;
|
||||
qint32 keyValueOld = 0, uncheckedPreviewState = 0;
|
||||
|
@ -1396,12 +1389,7 @@ void Account::readDraftsWithCursors(not_null<History*> history) {
|
|||
>> textTagsSerialized
|
||||
>> messageIdPeer
|
||||
>> messageIdMsg
|
||||
>> quote.text
|
||||
>> quoteTagsSerialized
|
||||
>> uncheckedPreviewState;
|
||||
quote.tags = TextUtilities::DeserializeTags(
|
||||
quoteTagsSerialized,
|
||||
quote.text.size());
|
||||
}
|
||||
text.tags = TextUtilities::DeserializeTags(
|
||||
textTagsSerialized,
|
||||
|
@ -1422,7 +1410,6 @@ void Account::readDraftsWithCursors(not_null<History*> history) {
|
|||
.messageId = FullMsgId(
|
||||
PeerId(messageIdPeer),
|
||||
MsgId(messageIdMsg)),
|
||||
.quote = quote,
|
||||
.topicRootId = key.topicRootId(),
|
||||
},
|
||||
MessageCursor(),
|
||||
|
|
Loading…
Add table
Reference in a new issue