Pass FullReplyTo everywhere.

This commit is contained in:
John Preston 2023-10-10 10:49:22 +04:00
parent a77131dfd6
commit 4240568ea5
53 changed files with 964 additions and 744 deletions

View file

@ -169,9 +169,7 @@ void SendBotCallbackData(
void HideSingleUseKeyboard( void HideSingleUseKeyboard(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<HistoryItem*> item) { not_null<HistoryItem*> item) {
controller->content()->hideSingleUseKeyboard( controller->content()->hideSingleUseKeyboard(item->fullId());
item->history()->peer,
item->id);
} }
} // namespace } // namespace
@ -312,7 +310,9 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
case ButtonType::Default: { case ButtonType::Default: {
// Copy string before passing it to the sending method // Copy string before passing it to the sending method
// because the original button can be destroyed inside. // because the original button can be destroyed inside.
const auto replyTo = item->isRegular() ? item->id : 0; const auto replyTo = item->isRegular()
? item->fullId()
: FullMsgId();
controller->content()->sendBotCommand({ controller->content()->sendBotCommand({
.peer = item->history()->peer, .peer = item->history()->peer,
.command = QString(button->text), .command = QString(button->text),
@ -363,7 +363,7 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
case ButtonType::RequestPhone: { case ButtonType::RequestPhone: {
HideSingleUseKeyboard(controller, item); HideSingleUseKeyboard(controller, item);
const auto itemId = item->id; const auto itemId = item->fullId();
const auto topicRootId = item->topicRootId(); const auto topicRootId = item->topicRootId();
const auto history = item->history(); const auto history = item->history();
controller->show(Ui::MakeConfirmBox({ controller->show(Ui::MakeConfirmBox({
@ -376,7 +376,7 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
auto action = Api::SendAction(history); auto action = Api::SendAction(history);
action.clearDraft = false; action.clearDraft = false;
action.replyTo = { action.replyTo = {
.msgId = itemId, .messageId = itemId,
.topicRootId = topicRootId, .topicRootId = topicRootId,
}; };
history->session().api().shareContact( history->session().api().shareContact(
@ -397,13 +397,11 @@ void ActivateBotCommand(ClickHandlerContext context, int row, int column) {
chosen |= PollData::Flag::Quiz; chosen |= PollData::Flag::Quiz;
} }
} }
const auto replyToId = MsgId(0); const auto replyTo = FullReplyTo();
const auto topicRootId = MsgId(0);
Window::PeerMenuCreatePoll( Window::PeerMenuCreatePoll(
controller, controller,
item->history()->peer, item->history()->peer,
replyToId, replyTo,
topicRootId,
chosen, chosen,
disabled); disabled);
} break; } break;

View file

@ -19,8 +19,8 @@ SendAction::SendAction(
SendOptions options) SendOptions options)
: history(thread->owningHistory()) : history(thread->owningHistory())
, options(options) , options(options)
, replyTo({ .msgId = thread->topicRootId() }) { , replyTo({ .messageId = { history->peer->id, thread->topicRootId() } }) {
replyTo.topicRootId = replyTo.msgId; replyTo.topicRootId = replyTo.messageId.msg;
} }
SendOptions DefaultSendWhenOnlineOptions() { SendOptions DefaultSendWhenOnlineOptions() {
@ -31,7 +31,7 @@ SendOptions DefaultSendWhenOnlineOptions() {
} }
MTPInputReplyTo SendAction::mtpReplyTo() const { MTPInputReplyTo SendAction::mtpReplyTo() const {
return Data::ReplyToForMTP(&history->owner(), replyTo); return Data::ReplyToForMTP(history, replyTo);
} }
} // namespace Api } // namespace Api

View file

@ -43,7 +43,7 @@ void Polls::create(
const auto history = action.history; const auto history = action.history;
const auto peer = history->peer; const auto peer = history->peer;
const auto topicRootId = action.replyTo.msgId const auto topicRootId = action.replyTo.messageId
? action.replyTo.topicRootId ? action.replyTo.topicRootId
: 0; : 0;
auto sendFlags = MTPmessages_SendMedia::Flags(0); auto sendFlags = MTPmessages_SendMedia::Flags(0);

View file

@ -368,9 +368,9 @@ void SendConfirmedFile(
if (!isEditing) { if (!isEditing) {
const auto histories = &session->data().histories(); const auto histories = &session->data().histories();
file->to.replyTo.msgId = histories->convertTopicReplyToId( file->to.replyTo.messageId = histories->convertTopicReplyToId(
history, history,
file->to.replyTo.msgId); file->to.replyTo.messageId);
file->to.replyTo.topicRootId = histories->convertTopicReplyToId( file->to.replyTo.topicRootId = histories->convertTopicReplyToId(
history, history,
file->to.replyTo.topicRootId); file->to.replyTo.topicRootId);

View file

@ -2134,16 +2134,12 @@ void ApiWrap::saveDraftsToCloud() {
} }
auto flags = MTPmessages_SaveDraft::Flags(0); auto flags = MTPmessages_SaveDraft::Flags(0);
auto replyFlags = MTPDinputReplyToMessage::Flags(0);
auto &textWithTags = cloudDraft->textWithTags; auto &textWithTags = cloudDraft->textWithTags;
if (cloudDraft->previewState != Data::PreviewState::Allowed) { if (cloudDraft->previewState != Data::PreviewState::Allowed) {
flags |= MTPmessages_SaveDraft::Flag::f_no_webpage; flags |= MTPmessages_SaveDraft::Flag::f_no_webpage;
} }
if (cloudDraft->msgId || cloudDraft->topicRootId) { if (cloudDraft->reply.messageId || cloudDraft->reply.topicRootId) {
flags |= MTPmessages_SaveDraft::Flag::f_reply_to; flags |= MTPmessages_SaveDraft::Flag::f_reply_to;
if (cloudDraft->topicRootId) {
replyFlags |= MTPDinputReplyToMessage::Flag::f_top_msg_id;
}
} }
if (!textWithTags.tags.isEmpty()) { if (!textWithTags.tags.isEmpty()) {
flags |= MTPmessages_SaveDraft::Flag::f_entities; flags |= MTPmessages_SaveDraft::Flag::f_entities;
@ -2156,15 +2152,7 @@ void ApiWrap::saveDraftsToCloud() {
history->startSavingCloudDraft(topicRootId); history->startSavingCloudDraft(topicRootId);
cloudDraft->saveRequestId = request(MTPmessages_SaveDraft( cloudDraft->saveRequestId = request(MTPmessages_SaveDraft(
MTP_flags(flags), MTP_flags(flags),
MTP_inputReplyToMessage( ReplyToForMTP(history, cloudDraft->reply),
MTP_flags(replyFlags),
MTP_int(cloudDraft->msgId
? cloudDraft->msgId
: cloudDraft->topicRootId),
MTP_int(cloudDraft->topicRootId),
MTPInputPeer(), // reply_to_peer_id
MTPstring(), // quote_text
MTPVector<MTPMessageEntity>()), // quote_entities
history->peer->input, history->peer->input,
MTP_string(textWithTags.text), MTP_string(textWithTags.text),
entities entities
@ -3586,9 +3574,8 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
action.generateLocal = true; action.generateLocal = true;
sendAction(action); sendAction(action);
const auto replyToId = action.replyTo.msgId; const auto replyTo = action.replyTo.messageId
const auto replyTo = replyToId ? peer->owner().message(action.replyTo.messageId)
? peer->owner().message(peer, replyToId)
: nullptr; : nullptr;
const auto topicRootId = replyTo const auto topicRootId = replyTo
? replyTo->topicRootId() ? replyTo->topicRootId()
@ -3789,7 +3776,7 @@ void ApiWrap::sendInlineResult(
? (*localMessageId) ? (*localMessageId)
: _session->data().nextLocalMessageId()); : _session->data().nextLocalMessageId());
const auto randomId = base::RandomValue<uint64>(); const auto randomId = base::RandomValue<uint64>();
const auto topicRootId = action.replyTo.msgId const auto topicRootId = action.replyTo.messageId
? action.replyTo.topicRootId ? action.replyTo.topicRootId
: 0; : 0;

View file

@ -71,7 +71,7 @@ AdminLog::OwnedItem GenerateItem(
not_null<HistoryView::ElementDelegate*> delegate, not_null<HistoryView::ElementDelegate*> delegate,
not_null<History*> history, not_null<History*> history,
PeerId from, PeerId from,
MsgId replyTo, FullMsgId replyTo,
const QString &text) { const QString &text) {
Expects(history->peer->isUser()); Expects(history->peer->isUser());
@ -81,7 +81,7 @@ AdminLog::OwnedItem GenerateItem(
| MessageFlag::HasFromId | MessageFlag::HasFromId
| MessageFlag::HasReplyInfo), | MessageFlag::HasReplyInfo),
UserId(), // via UserId(), // via
FullReplyTo{ .msgId = replyTo }, FullReplyTo{ .messageId = replyTo },
base::unixtime::now(), // date base::unixtime::now(), // date
from, from,
QString(), // postAuthor QString(), // postAuthor
@ -143,13 +143,13 @@ void AddMessage(
GenerateUser( GenerateUser(
history, history,
tr::lng_settings_chat_message_reply_from(tr::now)), tr::lng_settings_chat_message_reply_from(tr::now)),
0, FullMsgId(),
tr::lng_settings_chat_message_reply(tr::now)); tr::lng_settings_chat_message_reply(tr::now));
auto message = GenerateItem( auto message = GenerateItem(
state->delegate.get(), state->delegate.get(),
history, history,
history->peer->id, history->peer->id,
state->reply->data()->fullId().msg, state->reply->data()->fullId(),
tr::lng_settings_chat_message(tr::now)); tr::lng_settings_chat_message(tr::now));
const auto view = message.get(); const auto view = message.get();
state->item = std::move(message); state->item = std::move(message);

View file

@ -16,7 +16,7 @@ struct SendCommandRequest {
not_null<PeerData*> peer; not_null<PeerData*> peer;
QString command; QString command;
FullMsgId context; FullMsgId context;
MsgId replyTo = 0; FullReplyTo replyTo;
}; };
[[nodiscard]] QString WrapCommandInChat( [[nodiscard]] QString WrapCommandInChat(

View file

@ -308,7 +308,6 @@ void BotCommandClickHandler::onClick(ClickContext context) const {
.peer = peer, .peer = peer,
.command = _cmd, .command = _cmd,
.context = my.itemId, .context = my.itemId,
.replyTo = 0,
}); });
} }
} }

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "chat_helpers/message_field.h" #include "chat_helpers/message_field.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_widget.h" #include "history/history_widget.h"
#include "history/history_item_components.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "mainwidget.h" #include "mainwidget.h"
@ -21,14 +22,12 @@ namespace Data {
Draft::Draft( Draft::Draft(
const TextWithTags &textWithTags, const TextWithTags &textWithTags,
MsgId msgId, FullReplyTo reply,
MsgId topicRootId,
const MessageCursor &cursor, const MessageCursor &cursor,
PreviewState previewState, PreviewState previewState,
mtpRequestId saveRequestId) mtpRequestId saveRequestId)
: textWithTags(textWithTags) : textWithTags(textWithTags)
, msgId(msgId) , reply(std::move(reply))
, topicRootId(topicRootId)
, cursor(cursor) , cursor(cursor)
, previewState(previewState) , previewState(previewState)
, saveRequestId(saveRequestId) { , saveRequestId(saveRequestId) {
@ -36,13 +35,11 @@ Draft::Draft(
Draft::Draft( Draft::Draft(
not_null<const Ui::InputField*> field, not_null<const Ui::InputField*> field,
MsgId msgId, FullReplyTo reply,
MsgId topicRootId,
PreviewState previewState, PreviewState previewState,
mtpRequestId saveRequestId) mtpRequestId saveRequestId)
: textWithTags(field->getTextWithTags()) : textWithTags(field->getTextWithTags())
, msgId(msgId) , reply(std::move(reply))
, topicRootId(topicRootId)
, cursor(field) , cursor(field)
, previewState(previewState) { , previewState(previewState) {
} }
@ -64,22 +61,26 @@ void ApplyPeerCloudDraft(
session, session,
draft.ventities().value_or_empty())) draft.ventities().value_or_empty()))
}; };
auto replyTo = MsgId(); const auto reply = draft.vreply_to()
if (const auto reply = draft.vreply_to()) { ? ReplyFieldsFromMTP(history, *draft.vreply_to())
reply->match([&](const MTPDmessageReplyHeader &data) { : ReplyFields();
if (!data.vreply_to_peer_id() const auto replyPeerId = reply.externalPeerId
|| (peerFromMTP(*data.vreply_to_peer_id()) == peerId)) { ? reply.externalPeerId
replyTo = data.vreply_to_msg_id().value_or_empty(); : peerId;
} else {
// #TODO replies
}
}, [&](const MTPDmessageReplyStoryHeader &data) {
});
}
auto cloudDraft = std::make_unique<Draft>( auto cloudDraft = std::make_unique<Draft>(
textWithTags, textWithTags,
replyTo, FullReplyTo{
topicRootId, .messageId = FullMsgId(replyPeerId, reply.messageId),
.quote = TextWithTags{
reply.quote.text,
TextUtilities::ConvertEntitiesToTextTags(
reply.quote.entities),
},
.storyId = (reply.storyId
? FullStoryId(replyPeerId, reply.storyId)
: FullStoryId()),
.topicRootId = topicRootId,
},
MessageCursor(Ui::kQFixedMax, Ui::kQFixedMax, Ui::kQFixedMax), MessageCursor(Ui::kQFixedMax, Ui::kQFixedMax, Ui::kQFixedMax),
(draft.is_no_webpage() (draft.is_no_webpage()
? Data::PreviewState::Cancelled ? Data::PreviewState::Cancelled

View file

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include "data/data_msg_id.h"
namespace Ui { namespace Ui {
class InputField; class InputField;
} // namespace Ui } // namespace Ui
@ -38,22 +40,19 @@ struct Draft {
Draft() = default; Draft() = default;
Draft( Draft(
const TextWithTags &textWithTags, const TextWithTags &textWithTags,
MsgId msgId, FullReplyTo reply,
MsgId topicRootId,
const MessageCursor &cursor, const MessageCursor &cursor,
PreviewState previewState, PreviewState previewState,
mtpRequestId saveRequestId = 0); mtpRequestId saveRequestId = 0);
Draft( Draft(
not_null<const Ui::InputField*> field, not_null<const Ui::InputField*> field,
MsgId msgId, FullReplyTo reply,
MsgId topicRootId,
PreviewState previewState, PreviewState previewState,
mtpRequestId saveRequestId = 0); mtpRequestId saveRequestId = 0);
TimeId date = 0; TimeId date = 0;
TextWithTags textWithTags; TextWithTags textWithTags;
MsgId msgId = 0; // replyToId for message draft, editMsgId for edit draft FullReplyTo reply; // reply.messageId.msg is editMsgId for edit draft.
MsgId topicRootId = 0;
MessageCursor cursor; MessageCursor cursor;
PreviewState previewState = PreviewState::Allowed; PreviewState previewState = PreviewState::Allowed;
mtpRequestId saveRequestId = 0; mtpRequestId saveRequestId = 0;
@ -167,7 +166,8 @@ using HistoryDrafts = base::flat_map<DraftKey, std::unique_ptr<Draft>>;
[[nodiscard]] inline bool DraftIsNull(const Draft *draft) { [[nodiscard]] inline bool DraftIsNull(const Draft *draft) {
return !draft return !draft
|| (!draft->msgId && DraftStringIsEmpty(draft->textWithTags.text)); || (!draft->reply.messageId
&& DraftStringIsEmpty(draft->textWithTags.text));
} }
[[nodiscard]] inline bool DraftsAreEqual(const Draft *a, const Draft *b) { [[nodiscard]] inline bool DraftsAreEqual(const Draft *a, const Draft *b) {
@ -179,7 +179,7 @@ using HistoryDrafts = base::flat_map<DraftKey, std::unique_ptr<Draft>>;
return false; return false;
} }
return (a->textWithTags == b->textWithTags) return (a->textWithTags == b->textWithTags)
&& (a->msgId == b->msgId) && (a->reply == b->reply)
&& (a->previewState == b->previewState); && (a->previewState == b->previewState);
} }

View file

@ -33,8 +33,9 @@ constexpr auto kReadRequestTimeout = 3 * crl::time(1000);
} // namespace } // namespace
MTPInputReplyTo ReplyToForMTP( MTPInputReplyTo ReplyToForMTP(
not_null<Session*> owner, not_null<History*> history,
FullReplyTo replyTo) { FullReplyTo replyTo) {
const auto owner = &history->owner();
if (replyTo.storyId) { if (replyTo.storyId) {
if (const auto peer = owner->peerLoaded(replyTo.storyId.peer)) { if (const auto peer = owner->peerLoaded(replyTo.storyId.peer)) {
if (const auto user = peer->asUser()) { if (const auto user = peer->asUser()) {
@ -43,15 +44,19 @@ MTPInputReplyTo ReplyToForMTP(
MTP_int(replyTo.storyId.story)); MTP_int(replyTo.storyId.story));
} }
} }
} else if (replyTo.msgId || replyTo.topicRootId) { } else if (replyTo.messageId || replyTo.topicRootId) {
const auto external = (replyTo.messageId.peer != history->peer->id);
using Flag = MTPDinputReplyToMessage::Flag; using Flag = MTPDinputReplyToMessage::Flag;
return MTP_inputReplyToMessage( // #TODO replies return MTP_inputReplyToMessage(
(replyTo.topicRootId MTP_flags((replyTo.topicRootId ? Flag::f_top_msg_id : Flag())
? MTP_flags(Flag::f_top_msg_id) | (external ? Flag::f_reply_to_peer_id : Flag())),
: MTP_flags(0)), MTP_int(replyTo.messageId
MTP_int(replyTo.msgId ? replyTo.msgId : replyTo.topicRootId), ? replyTo.messageId.msg
: replyTo.topicRootId),
MTP_int(replyTo.topicRootId), MTP_int(replyTo.topicRootId),
MTPInputPeer(), // reply_to_peer_id (external
? owner->peer(replyTo.messageId.peer)->input
: MTPInputPeer()),
MTPstring(), // quote_text MTPstring(), // quote_text
MTPVector<MTPMessageEntity>()); // quote_entities MTPVector<MTPMessageEntity>()); // quote_entities
} }
@ -914,7 +919,7 @@ int Histories::sendPreparedMessage(
not_null<History*> history, not_null<History*> history,
FullReplyTo replyTo, FullReplyTo replyTo,
uint64 randomId, uint64 randomId,
Fn<PreparedMessage(not_null<Session*>, FullReplyTo)> message, Fn<PreparedMessage(not_null<History*>, FullReplyTo)> message,
Fn<void(const MTPUpdates&, const MTP::Response&)> done, Fn<void(const MTPUpdates&, const MTP::Response&)> done,
Fn<void(const MTP::Error&, const MTP::Response&)> fail) { Fn<void(const MTP::Error&, const MTP::Response&)> fail) {
if (isCreatingTopic(history, replyTo.topicRootId)) { if (isCreatingTopic(history, replyTo.topicRootId)) {
@ -929,7 +934,7 @@ int Histories::sendPreparedMessage(
} }
i->second.push_back({ i->second.push_back({
.randomId = randomId, .randomId = randomId,
.replyTo = replyTo.msgId, .replyTo = replyTo.messageId,
.message = std::move(message), .message = std::move(message),
.done = std::move(done), .done = std::move(done),
.fail = std::move(fail), .fail = std::move(fail),
@ -939,11 +944,11 @@ int Histories::sendPreparedMessage(
return id; return id;
} }
const auto realReplyTo = FullReplyTo{ const auto realReplyTo = FullReplyTo{
.msgId = convertTopicReplyToId(history, replyTo.msgId), .messageId = convertTopicReplyToId(history, replyTo.messageId),
.topicRootId = convertTopicReplyToId(history, replyTo.topicRootId),
.storyId = replyTo.storyId, .storyId = replyTo.storyId,
.topicRootId = convertTopicReplyToId(history, replyTo.topicRootId),
}; };
return v::match(message(_owner, realReplyTo), [&](const auto &request) { return v::match(message(history, realReplyTo), [&](const auto &request) {
const auto type = RequestType::Send; const auto type = RequestType::Send;
return sendRequest(history, type, [=](Fn<void()> finish) { return sendRequest(history, type, [=](Fn<void()> finish) {
const auto session = &_owner->session(); const auto session = &_owner->session();
@ -987,7 +992,7 @@ void Histories::checkTopicCreated(FullMsgId rootId, MsgId realRoot) {
sendPreparedMessage( sendPreparedMessage(
history, history,
FullReplyTo{ FullReplyTo{
.msgId = entry.replyTo, .messageId = entry.replyTo,
.topicRootId = realRoot, .topicRootId = realRoot,
}, },
entry.randomId, entry.randomId,
@ -1009,6 +1014,15 @@ void Histories::checkTopicCreated(FullMsgId rootId, MsgId realRoot) {
} }
} }
FullMsgId Histories::convertTopicReplyToId(
not_null<History*> history,
FullMsgId replyToId) const {
const auto id = (history->peer->id == replyToId.peer)
? convertTopicReplyToId(history, replyToId.msg)
: replyToId.msg;
return { replyToId.peer, id };
}
MsgId Histories::convertTopicReplyToId( MsgId Histories::convertTopicReplyToId(
not_null<History*> history, not_null<History*> history,
MsgId replyToId) const { MsgId replyToId) const {

View file

@ -27,7 +27,7 @@ class Session;
class Folder; class Folder;
[[nodiscard]] MTPInputReplyTo ReplyToForMTP( [[nodiscard]] MTPInputReplyTo ReplyToForMTP(
not_null<Session*> owner, not_null<History*> history,
FullReplyTo replyTo); FullReplyTo replyTo);
class Histories final { class Histories final {
@ -108,7 +108,7 @@ public:
not_null<History*> history, not_null<History*> history,
FullReplyTo replyTo, FullReplyTo replyTo,
uint64 randomId, uint64 randomId,
Fn<PreparedMessage(not_null<Session*>, FullReplyTo)> message, Fn<PreparedMessage(not_null<History*>, FullReplyTo)> message,
Fn<void(const MTPUpdates&, const MTP::Response&)> done, Fn<void(const MTPUpdates&, const MTP::Response&)> done,
Fn<void(const MTP::Error&, const MTP::Response&)> fail); Fn<void(const MTP::Error&, const MTP::Response&)> fail);
@ -116,14 +116,17 @@ public:
}; };
template <typename RequestType, typename ...Args> template <typename RequestType, typename ...Args>
static auto PrepareMessage(const Args &...args) static auto PrepareMessage(const Args &...args)
-> Fn<Histories::PreparedMessage(not_null<Session*>, FullReplyTo)> { -> Fn<Histories::PreparedMessage(not_null<History*>, FullReplyTo)> {
return [=](not_null<Session*> owner, FullReplyTo replyTo) return [=](not_null<History*> history, FullReplyTo replyTo)
-> RequestType { -> RequestType {
return { ReplaceReplyIds(owner, args, replyTo)... }; return { ReplaceReplyIds(history, args, replyTo)... };
}; };
} }
void checkTopicCreated(FullMsgId rootId, MsgId realRoot); void checkTopicCreated(FullMsgId rootId, MsgId realRoot);
[[nodiscard]] FullMsgId convertTopicReplyToId(
not_null<History*> history,
FullMsgId replyToId) const;
[[nodiscard]] MsgId convertTopicReplyToId( [[nodiscard]] MsgId convertTopicReplyToId(
not_null<History*> history, not_null<History*> history,
MsgId replyToId) const; MsgId replyToId) const;
@ -152,8 +155,8 @@ private:
}; };
struct DelayedByTopicMessage { struct DelayedByTopicMessage {
uint64 randomId = 0; uint64 randomId = 0;
MsgId replyTo = 0; FullMsgId replyTo;
Fn<PreparedMessage(not_null<Session*>, FullReplyTo)> message; Fn<PreparedMessage(not_null<History*>, FullReplyTo)> message;
Fn<void(const MTPUpdates&, const MTP::Response&)> done; Fn<void(const MTPUpdates&, const MTP::Response&)> done;
Fn<void(const MTP::Error&, const MTP::Response&)> fail; Fn<void(const MTP::Error&, const MTP::Response&)> fail;
int requestId = 0; int requestId = 0;
@ -169,11 +172,11 @@ private:
template <typename Arg> template <typename Arg>
static auto ReplaceReplyIds( static auto ReplaceReplyIds(
not_null<Session*> owner, not_null<History*> history,
Arg arg, Arg arg,
FullReplyTo replyTo) { FullReplyTo replyTo) {
if constexpr (std::is_same_v<Arg, ReplyToPlaceholder>) { if constexpr (std::is_same_v<Arg, ReplyToPlaceholder>) {
return ReplyToForMTP(owner, replyTo); return ReplyToForMTP(history, replyTo);
} else { } else {
return arg; return arg;
} }

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once #pragma once
#include "data/data_peer_id.h" #include "data/data_peer_id.h"
#include "ui/text/text_entity.h"
struct MsgId { struct MsgId {
constexpr MsgId() noexcept = default; constexpr MsgId() noexcept = default;
@ -67,21 +68,6 @@ struct FullStoryId {
friend inline bool operator==(FullStoryId, FullStoryId) = default; friend inline bool operator==(FullStoryId, FullStoryId) = default;
}; };
struct FullReplyTo {
MsgId msgId = 0;
MsgId topicRootId = 0;
FullStoryId storyId;
[[nodiscard]] bool valid() const {
return msgId || (storyId && peerIsUser(storyId.peer));
}
explicit operator bool() const {
return valid();
}
friend inline auto operator<=>(FullReplyTo, FullReplyTo) = default;
friend inline bool operator==(FullReplyTo, FullReplyTo) = default;
};
constexpr auto StartClientMsgId = MsgId(0x01 - (1LL << 58)); constexpr auto StartClientMsgId = MsgId(0x01 - (1LL << 58));
constexpr auto ClientMsgIds = (1LL << 31); constexpr auto ClientMsgIds = (1LL << 31);
constexpr auto EndClientMsgId = MsgId(StartClientMsgId.bare + ClientMsgIds); constexpr auto EndClientMsgId = MsgId(StartClientMsgId.bare + ClientMsgIds);
@ -169,6 +155,22 @@ struct FullMsgId {
Q_DECLARE_METATYPE(FullMsgId); Q_DECLARE_METATYPE(FullMsgId);
struct FullReplyTo {
FullMsgId messageId;
TextWithTags quote;
FullStoryId storyId;
MsgId topicRootId = 0;
[[nodiscard]] bool valid() const {
return messageId || (storyId && peerIsUser(storyId.peer));
}
explicit operator bool() const {
return valid();
}
friend inline auto operator<=>(FullReplyTo, FullReplyTo) = default;
friend inline bool operator==(FullReplyTo, FullReplyTo) = default;
};
struct GlobalMsgId { struct GlobalMsgId {
FullMsgId itemId; FullMsgId itemId;
uint64 sessionUniqueId = 0; uint64 sessionUniqueId = 0;

View file

@ -108,8 +108,7 @@ struct EntryState {
Key key; Key key;
Section section = Section::History; Section section = Section::History;
FilterId filterId = 0; FilterId filterId = 0;
MsgId rootId = 0; FullReplyTo currentReplyTo;
MsgId currentReplyToId = 0;
friend inline constexpr auto operator<=>(EntryState, EntryState) noexcept friend inline constexpr auto operator<=>(EntryState, EntryState) noexcept
= default; = default;

View file

@ -659,7 +659,7 @@ not_null<Ui::PathShiftGradient*> InnerWidget::elementPathShiftGradient() {
return _pathGradient.get(); return _pathGradient.get();
} }
void InnerWidget::elementReplyTo(const FullMsgId &to) { void InnerWidget::elementReplyTo(const FullReplyTo &to) {
} }
void InnerWidget::elementStartInteraction(not_null<const Element*> view) { void InnerWidget::elementStartInteraction(not_null<const Element*> view) {

View file

@ -127,7 +127,7 @@ public:
void elementHandleViaClick(not_null<UserData*> bot) override; void elementHandleViaClick(not_null<UserData*> bot) override;
bool elementIsChatWide() override; bool elementIsChatWide() override;
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override; not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
void elementReplyTo(const FullMsgId &to) override; void elementReplyTo(const FullReplyTo &to) override;
void elementStartInteraction( void elementStartInteraction(
not_null<const HistoryView::Element*> view) override; not_null<const HistoryView::Element*> view) override;
void elementStartPremium( void elementStartPremium(

View file

@ -177,8 +177,7 @@ void History::takeLocalDraft(not_null<History*> from) {
&& !_drafts.contains(Data::DraftKey::Local(topicRootId))) { && !_drafts.contains(Data::DraftKey::Local(topicRootId))) {
// Edit and reply to drafts can't migrate. // Edit and reply to drafts can't migrate.
// Cloud drafts do not migrate automatically. // Cloud drafts do not migrate automatically.
draft->msgId = 0; draft->reply = FullReplyTo();
setLocalDraft(std::move(draft)); setLocalDraft(std::move(draft));
} }
from->clearLocalDraft(topicRootId); from->clearLocalDraft(topicRootId);
@ -194,6 +193,7 @@ void History::createLocalDraftFromCloud(MsgId topicRootId) {
return; return;
} }
draft->reply.topicRootId = topicRootId;
auto existing = localDraft(topicRootId); auto existing = localDraft(topicRootId);
if (Data::DraftIsNull(existing) if (Data::DraftIsNull(existing)
|| !existing->date || !existing->date
@ -201,15 +201,13 @@ void History::createLocalDraftFromCloud(MsgId topicRootId) {
if (!existing) { if (!existing) {
setLocalDraft(std::make_unique<Data::Draft>( setLocalDraft(std::make_unique<Data::Draft>(
draft->textWithTags, draft->textWithTags,
draft->msgId, draft->reply,
topicRootId,
draft->cursor, draft->cursor,
draft->previewState)); draft->previewState));
existing = localDraft(topicRootId); existing = localDraft(topicRootId);
} else if (existing != draft) { } else if (existing != draft) {
existing->textWithTags = draft->textWithTags; existing->textWithTags = draft->textWithTags;
existing->msgId = draft->msgId; existing->reply = draft->reply;
existing->topicRootId = draft->topicRootId;
existing->cursor = draft->cursor; existing->cursor = draft->cursor;
existing->previewState = draft->previewState; existing->previewState = draft->previewState;
} }
@ -277,8 +275,7 @@ Data::Draft *History::createCloudDraft(
if (Data::DraftIsNull(fromDraft)) { if (Data::DraftIsNull(fromDraft)) {
setCloudDraft(std::make_unique<Data::Draft>( setCloudDraft(std::make_unique<Data::Draft>(
TextWithTags(), TextWithTags(),
0, FullReplyTo(),
topicRootId,
MessageCursor(), MessageCursor(),
Data::PreviewState::Allowed)); Data::PreviewState::Allowed));
cloudDraft(topicRootId)->date = TimeId(0); cloudDraft(topicRootId)->date = TimeId(0);
@ -287,18 +284,18 @@ Data::Draft *History::createCloudDraft(
if (!existing) { if (!existing) {
setCloudDraft(std::make_unique<Data::Draft>( setCloudDraft(std::make_unique<Data::Draft>(
fromDraft->textWithTags, fromDraft->textWithTags,
fromDraft->msgId, fromDraft->reply,
topicRootId,
fromDraft->cursor, fromDraft->cursor,
fromDraft->previewState)); fromDraft->previewState));
existing = cloudDraft(topicRootId); existing = cloudDraft(topicRootId);
} else if (existing != fromDraft) { } else if (existing != fromDraft) {
existing->textWithTags = fromDraft->textWithTags; existing->textWithTags = fromDraft->textWithTags;
existing->msgId = fromDraft->msgId; existing->reply = fromDraft->reply;
existing->cursor = fromDraft->cursor; existing->cursor = fromDraft->cursor;
existing->previewState = fromDraft->previewState; existing->previewState = fromDraft->previewState;
} }
existing->date = base::unixtime::now(); existing->date = base::unixtime::now();
existing->reply.topicRootId = topicRootId;
} }
if (const auto thread = threadFor(topicRootId)) { if (const auto thread = threadFor(topicRootId)) {

View file

@ -329,17 +329,17 @@ public:
} }
void setLocalDraft(std::unique_ptr<Data::Draft> &&draft) { void setLocalDraft(std::unique_ptr<Data::Draft> &&draft) {
setDraft( setDraft(
Data::DraftKey::Local(draft->topicRootId), Data::DraftKey::Local(draft->reply.topicRootId),
std::move(draft)); std::move(draft));
} }
void setLocalEditDraft(std::unique_ptr<Data::Draft> &&draft) { void setLocalEditDraft(std::unique_ptr<Data::Draft> &&draft) {
setDraft( setDraft(
Data::DraftKey::LocalEdit(draft->topicRootId), Data::DraftKey::LocalEdit(draft->reply.topicRootId),
std::move(draft)); std::move(draft));
} }
void setCloudDraft(std::unique_ptr<Data::Draft> &&draft) { void setCloudDraft(std::unique_ptr<Data::Draft> &&draft) {
setDraft( setDraft(
Data::DraftKey::Cloud(draft->topicRootId), Data::DraftKey::Cloud(draft->reply.topicRootId),
std::move(draft)); std::move(draft));
} }
void clearLocalDraft(MsgId topicRootId) { void clearLocalDraft(MsgId topicRootId) {

View file

@ -287,7 +287,7 @@ public:
return _widget->elementPathShiftGradient(); return _widget->elementPathShiftGradient();
} }
void elementReplyTo(const FullMsgId &to) override { void elementReplyTo(const FullReplyTo &to) override {
if (_widget) { if (_widget) {
_widget->elementReplyTo(to); _widget->elementReplyTo(to);
} }
@ -2206,7 +2206,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
}(); }();
if (canReply) { if (canReply) {
_menu->addAction(tr::lng_context_reply_msg(tr::now), [=] { _menu->addAction(tr::lng_context_reply_msg(tr::now), [=] {
_widget->replyToMessage(itemId); _widget->replyToMessage({ itemId });
}, &st::menuIconReply); }, &st::menuIconReply);
} }
const auto repliesCount = item->repliesCount(); const auto repliesCount = item->repliesCount();
@ -3481,7 +3481,7 @@ not_null<Ui::PathShiftGradient*> HistoryInner::elementPathShiftGradient() {
return _pathGradient.get(); return _pathGradient.get();
} }
void HistoryInner::elementReplyTo(const FullMsgId &to) { void HistoryInner::elementReplyTo(const FullReplyTo &to) {
return _widget->replyToMessage(to); return _widget->replyToMessage(to);
} }

View file

@ -159,7 +159,7 @@ public:
void elementHandleViaClick(not_null<UserData*> bot); void elementHandleViaClick(not_null<UserData*> bot);
bool elementIsChatWide(); bool elementIsChatWide();
not_null<Ui::PathShiftGradient*> elementPathShiftGradient(); not_null<Ui::PathShiftGradient*> elementPathShiftGradient();
void elementReplyTo(const FullMsgId &to); void elementReplyTo(const FullReplyTo &to);
void elementStartInteraction(not_null<const Element*> view); void elementStartInteraction(not_null<const Element*> view);
void elementStartPremium( void elementStartPremium(
not_null<const Element*> view, not_null<const Element*> view,

View file

@ -125,23 +125,23 @@ void HistoryItem::HistoryItem::Destroyer::operator()(HistoryItem *value) {
} }
struct HistoryItem::CreateConfig { struct HistoryItem::CreateConfig {
PeerId replyToPeer = 0; ReplyFields reply;
MsgId replyTo = 0;
MsgId replyToTop = 0;
StoryId replyToStory = 0;
bool replyIsTopicPost = false;
UserId viaBotId = 0; UserId viaBotId = 0;
int viewsCount = -1; int viewsCount = -1;
int forwardsCount = -1; int forwardsCount = -1;
QString author; QString postAuthor;
PeerId senderOriginal = 0;
QString senderNameOriginal;
QString forwardPsaType;
MsgId originalId = 0; MsgId originalId = 0;
TimeId originalDate = 0;
PeerId originalSenderId = 0;
QString originalSenderName;
QString originalPostAuthor;
QString forwardPsaType;
PeerId savedFromPeer = 0; PeerId savedFromPeer = 0;
MsgId savedFromMsgId = 0; MsgId savedFromMsgId = 0;
QString authorOriginal;
TimeId originalDate = 0;
TimeId editDate = 0; TimeId editDate = 0;
HistoryMessageMarkupData markup; HistoryMessageMarkupData markup;
HistoryMessageRepliesData replies; HistoryMessageRepliesData replies;
@ -154,14 +154,14 @@ struct HistoryItem::CreateConfig {
void HistoryItem::FillForwardedInfo( void HistoryItem::FillForwardedInfo(
CreateConfig &config, CreateConfig &config,
const MTPDmessageFwdHeader &data) { const MTPDmessageFwdHeader &data) {
if (const auto fromId = data.vfrom_id()) {
config.senderOriginal = peerFromMTP(*fromId);
}
config.originalDate = data.vdate().v;
config.senderNameOriginal = qs(data.vfrom_name().value_or_empty());
config.forwardPsaType = qs(data.vpsa_type().value_or_empty());
config.originalId = data.vchannel_post().value_or_empty(); config.originalId = data.vchannel_post().value_or_empty();
config.authorOriginal = qs(data.vpost_author().value_or_empty()); config.originalDate = data.vdate().v;
if (const auto fromId = data.vfrom_id()) {
config.originalSenderId = peerFromMTP(*fromId);
}
config.originalSenderName = qs(data.vfrom_name().value_or_empty());
config.originalPostAuthor = qs(data.vpost_author().value_or_empty());
config.forwardPsaType = qs(data.vpsa_type().value_or_empty());
const auto savedFromPeer = data.vsaved_from_peer(); const auto savedFromPeer = data.vsaved_from_peer();
const auto savedFromMsgId = data.vsaved_from_msg_id(); const auto savedFromMsgId = data.vsaved_from_msg_id();
if (savedFromPeer && savedFromMsgId) { if (savedFromPeer && savedFromMsgId) {
@ -435,21 +435,21 @@ HistoryItem::HistoryItem(
const auto originalMedia = original->media(); const auto originalMedia = original->media();
const auto dropForwardInfo = original->computeDropForwardedInfo(); const auto dropForwardInfo = original->computeDropForwardedInfo();
config.replyTo = config.replyToTop = topicRootId; config.reply.messageId = config.reply.topMessageId = topicRootId;
config.replyIsTopicPost = (topicRootId != 0); config.reply.topicPost = (topicRootId != 0);
if (!dropForwardInfo) { if (!dropForwardInfo) {
config.originalDate = original->dateOriginal(); config.originalDate = original->originalDate();
if (const auto info = original->hiddenSenderInfo()) { if (const auto info = original->hiddenSenderInfo()) {
config.senderNameOriginal = info->name; config.originalSenderName = info->name;
} else if (const auto senderOriginal = original->senderOriginal()) { } else if (const auto originalSender = original->originalSender()) {
config.senderOriginal = senderOriginal->id; config.originalSenderId = originalSender->id;
if (senderOriginal->isChannel()) { if (originalSender->isChannel()) {
config.originalId = original->idOriginal(); config.originalId = original->originalId();
} }
} else { } else {
Unexpected("Corrupt forwarded information in message."); Unexpected("Corrupt forwarded information in message.");
} }
config.authorOriginal = original->authorOriginal(); config.originalPostAuthor = original->originalPostAuthor();
} }
if (peer->isSelf()) { if (peer->isSelf()) {
// //
@ -465,12 +465,12 @@ HistoryItem::HistoryItem(
//} //}
} }
if (flags & MessageFlag::HasPostAuthor) { if (flags & MessageFlag::HasPostAuthor) {
config.author = postAuthor; config.postAuthor = postAuthor;
} }
if (const auto fwdViaBot = original->viaBot()) { if (const auto fwdViaBot = original->viaBot()) {
config.viaBotId = peerToUser(fwdViaBot->id); config.viaBotId = peerToUser(fwdViaBot->id);
} else if (originalMedia && originalMedia->game()) { } else if (originalMedia && originalMedia->game()) {
if (const auto sender = original->senderOriginal()) { if (const auto sender = original->originalSender()) {
if (const auto user = sender->asUser()) { if (const auto user = sender->asUser()) {
if (user->isBot()) { if (user->isBot()) {
config.viaBotId = peerToUser(user->id); config.viaBotId = peerToUser(user->id);
@ -482,8 +482,8 @@ HistoryItem::HistoryItem(
if (fwdViewsCount > 0) { if (fwdViewsCount > 0) {
config.viewsCount = fwdViewsCount; config.viewsCount = fwdViewsCount;
} else if ((isPost() && !isScheduled()) } else if ((isPost() && !isScheduled())
|| (original->senderOriginal() || (original->originalSender()
&& original->senderOriginal()->isChannel())) { && original->originalSender()->isChannel())) {
config.viewsCount = 1; config.viewsCount = 1;
} }
@ -1026,7 +1026,7 @@ void HistoryItem::setCommentsItemId(FullMsgId id) {
if (id.peer == _history->peer->id) { if (id.peer == _history->peer->id) {
if (id.msg != this->id) { if (id.msg != this->id) {
if (const auto reply = Get<HistoryMessageReply>()) { if (const auto reply = Get<HistoryMessageReply>()) {
reply->replyToMsgTop = id.msg; reply->setTopMessageId(id.msg);
} }
} }
} else if (const auto views = Get<HistoryMessageViews>()) { } else if (const auto views = Get<HistoryMessageViews>()) {
@ -1879,7 +1879,7 @@ void HistoryItem::changeReplyToTopCounter(
this, this,
Data::MessageUpdate::Flag::ReplyToTopAdded); Data::MessageUpdate::Flag::ReplyToTopAdded);
} }
const auto topId = reply->replyToTop(); const auto topId = reply->topMessageId();
if (!topId) { if (!topId) {
return; return;
} }
@ -1931,8 +1931,8 @@ void HistoryItem::setRealId(MsgId newId) {
_history->owner().requestItemResize(this); _history->owner().requestItemResize(this);
if (const auto reply = Get<HistoryMessageReply>()) { if (const auto reply = Get<HistoryMessageReply>()) {
if (reply->replyToLink()) { if (reply->link()) {
reply->setReplyToLinkFrom(this); reply->setLinkFrom(this);
} }
changeReplyToTopCounter(reply, 1); changeReplyToTopCounter(reply, 1);
} }
@ -2381,14 +2381,14 @@ not_null<PeerData*> HistoryItem::author() const {
return (isPost() && !isSponsored()) ? _history->peer : from(); return (isPost() && !isSponsored()) ? _history->peer : from();
} }
TimeId HistoryItem::dateOriginal() const { TimeId HistoryItem::originalDate() const {
if (const auto forwarded = Get<HistoryMessageForwarded>()) { if (const auto forwarded = Get<HistoryMessageForwarded>()) {
return forwarded->originalDate; return forwarded->originalDate;
} }
return date(); return date();
} }
PeerData *HistoryItem::senderOriginal() const { PeerData *HistoryItem::originalSender() const {
if (const auto forwarded = Get<HistoryMessageForwarded>()) { if (const auto forwarded = Get<HistoryMessageForwarded>()) {
return forwarded->originalSender; return forwarded->originalSender;
} }
@ -2416,18 +2416,18 @@ not_null<PeerData*> HistoryItem::fromOriginal() const {
return from(); return from();
} }
QString HistoryItem::authorOriginal() const { QString HistoryItem::originalPostAuthor() const {
if (const auto forwarded = Get<HistoryMessageForwarded>()) { if (const auto forwarded = Get<HistoryMessageForwarded>()) {
return forwarded->originalAuthor; return forwarded->originalPostAuthor;
} else if (const auto msgsigned = Get<HistoryMessageSigned>()) { } else if (const auto msgsigned = Get<HistoryMessageSigned>()) {
if (!msgsigned->isAnonymousRank) { if (!msgsigned->isAnonymousRank) {
return msgsigned->author; return msgsigned->postAuthor;
} }
} }
return QString(); return QString();
} }
MsgId HistoryItem::idOriginal() const { MsgId HistoryItem::originalId() const {
if (const auto forwarded = Get<HistoryMessageForwarded>()) { if (const auto forwarded = Get<HistoryMessageForwarded>()) {
return forwarded->originalId; return forwarded->originalId;
} }
@ -2491,9 +2491,9 @@ void HistoryItem::setForwardsCount(int count) {
history()->owner().notifyItemDataChange(this); history()->owner().notifyItemDataChange(this);
} }
void HistoryItem::setPostAuthor(const QString &author) { void HistoryItem::setPostAuthor(const QString &postAuthor) {
auto msgsigned = Get<HistoryMessageSigned>(); auto msgsigned = Get<HistoryMessageSigned>();
if (author.isEmpty()) { if (postAuthor.isEmpty()) {
if (!msgsigned) { if (!msgsigned) {
return; return;
} }
@ -2504,10 +2504,10 @@ void HistoryItem::setPostAuthor(const QString &author) {
if (!msgsigned) { if (!msgsigned) {
AddComponents(HistoryMessageSigned::Bit()); AddComponents(HistoryMessageSigned::Bit());
msgsigned = Get<HistoryMessageSigned>(); msgsigned = Get<HistoryMessageSigned>();
} else if (msgsigned->author == author) { } else if (msgsigned->postAuthor == postAuthor) {
return; return;
} }
msgsigned->author = author; msgsigned->postAuthor = postAuthor;
msgsigned->isAnonymousRank = !isDiscussionPost() msgsigned->isAnonymousRank = !isDiscussionPost()
&& this->author()->isMegagroup(); && this->author()->isMegagroup();
history()->owner().requestItemResize(this); history()->owner().requestItemResize(this);
@ -2643,20 +2643,10 @@ void HistoryItem::setReplyFields(
} }
} }
} else if (const auto reply = Get<HistoryMessageReply>()) { } else if (const auto reply = Get<HistoryMessageReply>()) {
reply->topicPost = isForumPost; const auto increment = (reply->topMessageId() != replyToTop)
if ((reply->replyToMsgId != replyTo) && !IsServerMsgId(reply->topMessageId());
&& !IsServerMsgId(reply->replyToMsgId)) { reply->updateFields(this, replyTo, replyToTop, isForumPost);
reply->replyToMsgId = replyTo; if (increment) {
if (!reply->updateData(this)) {
RequestDependentMessageItem(
this,
reply->replyToPeerId,
reply->replyToMsgId);
}
}
if ((reply->replyToMsgTop != replyToTop)
&& !IsServerMsgId(reply->replyToMsgTop)) {
reply->replyToMsgTop = replyToTop;
changeReplyToTopCounter(reply, 1); changeReplyToTopCounter(reply, 1);
} }
} }
@ -2819,14 +2809,22 @@ bool HistoryItem::unread(not_null<Data::Thread*> thread) const {
MsgId HistoryItem::replyToId() const { MsgId HistoryItem::replyToId() const {
if (const auto reply = Get<HistoryMessageReply>()) { if (const auto reply = Get<HistoryMessageReply>()) {
return reply->replyToId(); return reply->messageId();
} }
return 0; return 0;
} }
FullMsgId HistoryItem::replyToFullId() const {
if (const auto reply = Get<HistoryMessageReply>()) {
const auto peer = reply->externalPeerId();
return { peer ? peer : history()->peer->id, reply->messageId() };
}
return {};
}
MsgId HistoryItem::replyToTop() const { MsgId HistoryItem::replyToTop() const {
if (const auto reply = Get<HistoryMessageReply>()) { if (const auto reply = Get<HistoryMessageReply>()) {
return reply->replyToTop(); return reply->topMessageId();
} else if (const auto data = GetServiceDependentData()) { } else if (const auto data = GetServiceDependentData()) {
return data->topId; return data->topId;
} }
@ -2835,8 +2833,8 @@ MsgId HistoryItem::replyToTop() const {
MsgId HistoryItem::topicRootId() const { MsgId HistoryItem::topicRootId() const {
if (const auto reply = Get<HistoryMessageReply>() if (const auto reply = Get<HistoryMessageReply>()
; reply && reply->topicPost) { ; reply && reply->topicPost()) {
return reply->replyToTop(); return reply->topMessageId();
} else if (const auto data = GetServiceDependentData() } else if (const auto data = GetServiceDependentData()
; data && data->topicPost && data->topId) { ; data && data->topicPost && data->topId) {
return data->topId; return data->topId;
@ -2850,11 +2848,11 @@ MsgId HistoryItem::topicRootId() const {
FullStoryId HistoryItem::replyToStory() const { FullStoryId HistoryItem::replyToStory() const {
if (const auto reply = Get<HistoryMessageReply>()) { if (const auto reply = Get<HistoryMessageReply>()) {
if (reply->replyToStoryId) { if (reply->storyId()) {
const auto peerId = reply->replyToPeerId const auto peerId = reply->externalPeerId()
? reply->replyToPeerId ? reply->externalPeerId()
: _history->peer->id; : _history->peer->id;
return { .peer = peerId, .story = reply->replyToStoryId }; return { .peer = peerId, .story = reply->storyId() };
} }
} }
return {}; return {};
@ -2862,9 +2860,9 @@ FullStoryId HistoryItem::replyToStory() const {
FullReplyTo HistoryItem::replyTo() const { FullReplyTo HistoryItem::replyTo() const {
return { return {
.msgId = replyToId(), .messageId = replyToFullId(),
.topicRootId = topicRootId(),
.storyId = replyToStory(), .storyId = replyToStory(),
.topicRootId = topicRootId(),
}; };
} }
@ -3049,7 +3047,10 @@ const std::vector<ClickHandlerPtr> &HistoryItem::customTextLinks() const {
void HistoryItem::createComponents(CreateConfig &&config) { void HistoryItem::createComponents(CreateConfig &&config) {
uint64 mask = 0; uint64 mask = 0;
if (config.replyTo || config.replyToStory) { if (config.reply.messageId
|| config.reply.externalSenderId
|| !config.reply.externalSenderName.isEmpty()
|| config.reply.storyId) {
mask |= HistoryMessageReply::Bit(); mask |= HistoryMessageReply::Bit();
} }
if (config.viaBotId) { if (config.viaBotId) {
@ -3058,18 +3059,18 @@ void HistoryItem::createComponents(CreateConfig &&config) {
if (config.viewsCount >= 0 || !config.replies.isNull) { if (config.viewsCount >= 0 || !config.replies.isNull) {
mask |= HistoryMessageViews::Bit(); mask |= HistoryMessageViews::Bit();
} }
if (!config.author.isEmpty()) { if (!config.postAuthor.isEmpty()) {
mask |= HistoryMessageSigned::Bit(); mask |= HistoryMessageSigned::Bit();
} else if (_history->peer->isMegagroup() // Discussion posts signatures. } else if (_history->peer->isMegagroup() // Discussion posts signatures.
&& config.savedFromPeer && config.savedFromPeer
&& !config.authorOriginal.isEmpty()) { && !config.originalPostAuthor.isEmpty()) {
const auto savedFrom = _history->owner().peerLoaded( const auto savedFrom = _history->owner().peerLoaded(
config.savedFromPeer); config.savedFromPeer);
if (savedFrom && savedFrom->isChannel()) { if (savedFrom && savedFrom->isChannel()) {
mask |= HistoryMessageSigned::Bit(); mask |= HistoryMessageSigned::Bit();
} }
} else if ((_history->peer->isSelf() || _history->peer->isRepliesChat()) } else if ((_history->peer->isSelf() || _history->peer->isRepliesChat())
&& !config.authorOriginal.isEmpty()) { && !config.originalPostAuthor.isEmpty()) {
mask |= HistoryMessageSigned::Bit(); mask |= HistoryMessageSigned::Bit();
} }
if (config.editDate != TimeId(0)) { if (config.editDate != TimeId(0)) {
@ -3087,23 +3088,18 @@ void HistoryItem::createComponents(CreateConfig &&config) {
UpdateComponents(mask); UpdateComponents(mask);
if (const auto reply = Get<HistoryMessageReply>()) { if (const auto reply = Get<HistoryMessageReply>()) {
reply->replyToPeerId = config.replyToPeer; reply->set(config.reply);
reply->replyToMsgId = config.replyTo;
reply->replyToMsgTop = isScheduled() ? 0 : config.replyToTop;
reply->replyToStoryId = config.replyToStory;
reply->storyReply = (config.replyToStory != 0);
reply->topicPost = config.replyIsTopicPost;
if (!reply->updateData(this)) { if (!reply->updateData(this)) {
if (reply->replyToMsgId) { if (const auto messageId = reply->messageId()) {
RequestDependentMessageItem( RequestDependentMessageItem(
this, this,
reply->replyToPeerId, reply->externalPeerId(),
reply->replyToMsgId); reply->messageId());
} else if (reply->replyToStoryId) { } else if (reply->storyId()) {
RequestDependentMessageStory( RequestDependentMessageStory(
this, this,
reply->replyToPeerId, reply->externalPeerId(),
reply->replyToStoryId); reply->storyId());
} }
} }
} }
@ -3129,9 +3125,9 @@ void HistoryItem::createComponents(CreateConfig &&config) {
edited->date = config.editDate; edited->date = config.editDate;
} }
if (const auto msgsigned = Get<HistoryMessageSigned>()) { if (const auto msgsigned = Get<HistoryMessageSigned>()) {
msgsigned->author = config.author.isEmpty() msgsigned->postAuthor = config.postAuthor.isEmpty()
? config.authorOriginal ? config.originalPostAuthor
: config.author; : config.postAuthor;
msgsigned->isAnonymousRank = !isDiscussionPost() msgsigned->isAnonymousRank = !isDiscussionPost()
&& author()->isMegagroup(); && author()->isMegagroup();
} }
@ -3167,9 +3163,9 @@ void HistoryItem::setupForwardedComponent(const CreateConfig &config) {
return; return;
} }
forwarded->originalDate = config.originalDate; forwarded->originalDate = config.originalDate;
const auto originalSender = config.senderOriginal const auto originalSender = config.originalSenderId
? config.senderOriginal ? config.originalSenderId
: !config.senderNameOriginal.isEmpty() : !config.originalSenderName.isEmpty()
? PeerId() ? PeerId()
: from()->id; : from()->id;
forwarded->originalSender = originalSender forwarded->originalSender = originalSender
@ -3177,11 +3173,11 @@ void HistoryItem::setupForwardedComponent(const CreateConfig &config) {
: nullptr; : nullptr;
if (!forwarded->originalSender) { if (!forwarded->originalSender) {
forwarded->hiddenSenderInfo = std::make_unique<HiddenSenderInfo>( forwarded->hiddenSenderInfo = std::make_unique<HiddenSenderInfo>(
config.senderNameOriginal, config.originalSenderName,
config.imported); config.imported);
} }
forwarded->originalId = config.originalId; forwarded->originalId = config.originalId;
forwarded->originalAuthor = config.authorOriginal; forwarded->originalPostAuthor = config.originalPostAuthor;
forwarded->psaType = config.forwardPsaType; forwarded->psaType = config.forwardPsaType;
forwarded->savedFromPeer = _history->owner().peerLoaded( forwarded->savedFromPeer = _history->owner().peerLoaded(
config.savedFromPeer); config.savedFromPeer);
@ -3233,7 +3229,7 @@ TextWithEntities HistoryItem::withLocalEntities(
: nullptr; : nullptr;
if (document) { if (document) {
if (const auto duration = DurationForTimestampLinks(document)) { if (const auto duration = DurationForTimestampLinks(document)) {
const auto context = reply->replyToMsg->fullId(); const auto context = reply->resolvedMessage->fullId();
return AddTimestampLinks( return AddTimestampLinks(
textWithEntities, textWithEntities,
duration, duration,
@ -3241,7 +3237,7 @@ TextWithEntities HistoryItem::withLocalEntities(
} }
} else if (webpage) { } else if (webpage) {
if (const auto duration = DurationForTimestampLinks(webpage)) { if (const auto duration = DurationForTimestampLinks(webpage)) {
const auto context = reply->replyToMsg->fullId(); const auto context = reply->resolvedMessage->fullId();
return AddTimestampLinks( return AddTimestampLinks(
textWithEntities, textWithEntities,
duration, duration,
@ -3290,19 +3286,28 @@ void HistoryItem::createComponentsHelper(
auto config = CreateConfig(); auto config = CreateConfig();
config.viaBotId = viaBotId; config.viaBotId = viaBotId;
if (flags & MessageFlag::HasReplyInfo) { if (flags & MessageFlag::HasReplyInfo) {
config.replyTo = replyTo.msgId; config.reply.messageId = replyTo.messageId.msg;
config.replyToStory = replyTo.storyId.story; config.reply.storyId = replyTo.storyId.story;
config.replyToPeer = replyTo.storyId ? replyTo.storyId.peer : 0; config.reply.externalPeerId = replyTo.storyId
const auto to = LookupReplyTo(_history, replyTo.msgId); ? replyTo.storyId.peer
const auto replyToTop = LookupReplyToTop(to); : (replyTo.messageId && replyTo.messageId.peer
config.replyToTop = replyToTop ? replyToTop : replyTo.msgId; != history()->peer->id)
? replyTo.messageId.peer
: PeerId();
const auto to = LookupReplyTo(_history, replyTo.messageId);
const auto replyToTop = LookupReplyToTop(_history, to);
config.reply.topMessageId = replyToTop
? replyToTop
: (replyTo.messageId.peer == history()->peer->id)
? replyTo.messageId.msg
: MsgId();
const auto forum = _history->asForum(); const auto forum = _history->asForum();
config.replyIsTopicPost = LookupReplyIsTopicPost(to) config.reply.topicPost = LookupReplyIsTopicPost(to)
|| (to && to->Has<HistoryServiceTopicInfo>()) || (to && to->Has<HistoryServiceTopicInfo>())
|| (forum && forum->creating(config.replyToTop)); || (forum && forum->creating(config.reply.topMessageId));
} }
config.markup = std::move(markup); config.markup = std::move(markup);
if (flags & MessageFlag::HasPostAuthor) config.author = postAuthor; if (flags & MessageFlag::HasPostAuthor) config.postAuthor = postAuthor;
if (flags & MessageFlag::HasViews) config.viewsCount = 1; if (flags & MessageFlag::HasViews) config.viewsCount = 1;
createComponents(std::move(config)); createComponents(std::move(config));
@ -3392,26 +3397,7 @@ void HistoryItem::createComponents(const MTPDmessage &data) {
}); });
} }
if (const auto reply = data.vreply_to()) { if (const auto reply = data.vreply_to()) {
reply->match([&](const MTPDmessageReplyHeader &data) { config.reply = ReplyFieldsFromMTP(history(), *reply);
// #TODO replies
if (const auto id = data.vreply_to_msg_id().value_or_empty()) {
if (const auto peer = data.vreply_to_peer_id()) {
config.replyToPeer = peerFromMTP(*peer);
if (config.replyToPeer == _history->peer->id) {
config.replyToPeer = 0;
}
}
const auto owner = &_history->owner();
config.replyTo = data.is_reply_to_scheduled()
? owner->scheduledMessages().localMessageId(id)
: id;
config.replyToTop = data.vreply_to_top_id().value_or(id);
config.replyIsTopicPost = data.is_forum_topic();
}
}, [&](const MTPDmessageReplyStoryHeader &data) {
config.replyToPeer = peerFromUser(data.vuser_id());
config.replyToStory = data.vstory_id().v;
});
} }
config.viaBotId = data.vvia_bot_id().value_or_empty(); config.viaBotId = data.vvia_bot_id().value_or_empty();
config.viewsCount = data.vviews().value_or(-1); config.viewsCount = data.vviews().value_or(-1);
@ -3421,7 +3407,7 @@ void HistoryItem::createComponents(const MTPDmessage &data) {
: HistoryMessageRepliesData(data.vreplies()); : HistoryMessageRepliesData(data.vreplies());
config.markup = HistoryMessageMarkupData(data.vreply_markup()); config.markup = HistoryMessageMarkupData(data.vreply_markup());
config.editDate = data.vedit_date().value_or_empty(); config.editDate = data.vedit_date().value_or_empty();
config.author = qs(data.vpost_author().value_or_empty()); config.postAuthor = qs(data.vpost_author().value_or_empty());
createComponents(std::move(config)); createComponents(std::move(config));
} }

View file

@ -465,6 +465,7 @@ public:
void setText(const TextWithEntities &textWithEntities); void setText(const TextWithEntities &textWithEntities);
[[nodiscard]] MsgId replyToId() const; [[nodiscard]] MsgId replyToId() const;
[[nodiscard]] FullMsgId replyToFullId() const;
[[nodiscard]] MsgId replyToTop() const; [[nodiscard]] MsgId replyToTop() const;
[[nodiscard]] MsgId topicRootId() const; [[nodiscard]] MsgId topicRootId() const;
[[nodiscard]] FullStoryId replyToStory() const; [[nodiscard]] FullStoryId replyToStory() const;
@ -473,12 +474,12 @@ public:
[[nodiscard]] not_null<PeerData*> author() const; [[nodiscard]] not_null<PeerData*> author() const;
[[nodiscard]] TimeId dateOriginal() const; [[nodiscard]] TimeId originalDate() const;
[[nodiscard]] PeerData *senderOriginal() const; [[nodiscard]] PeerData *originalSender() const;
[[nodiscard]] const HiddenSenderInfo *hiddenSenderInfo() const; [[nodiscard]] const HiddenSenderInfo *hiddenSenderInfo() const;
[[nodiscard]] not_null<PeerData*> fromOriginal() const; [[nodiscard]] not_null<PeerData*> fromOriginal() const;
[[nodiscard]] QString authorOriginal() const; [[nodiscard]] QString originalPostAuthor() const;
[[nodiscard]] MsgId idOriginal() const; [[nodiscard]] MsgId originalId() const;
[[nodiscard]] bool isEmpty() const; [[nodiscard]] bool isEmpty() const;

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "history/history_item_components.h" #include "history/history_item_components.h"
#include "api/api_text_entities.h"
#include "base/qt/qt_key_modifiers.h" #include "base/qt/qt_key_modifiers.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "ui/effects/ripple_animation.h" #include "ui/effects/ripple_animation.h"
@ -38,6 +39,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_web_page.h" #include "data/data_web_page.h"
#include "data/data_file_click_handler.h" #include "data/data_file_click_handler.h"
#include "data/data_scheduled_messages.h"
#include "data/data_session.h"
#include "data/data_stories.h" #include "data/data_stories.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
@ -64,7 +67,7 @@ void HistoryMessageVia::create(
lt_inline_bot, lt_inline_bot,
'@' + bot->username())); '@' + bot->username()));
link = std::make_shared<LambdaClickHandler>([bot = this->bot]( link = std::make_shared<LambdaClickHandler>([bot = this->bot](
ClickContext context) { ClickContext context) {
const auto my = context.other.value<ClickHandlerContext>(); const auto my = context.other.value<ClickHandlerContext>();
if (const auto controller = my.sessionWindow.get()) { if (const auto controller = my.sessionWindow.get()) {
if (base::IsCtrlPressed()) { if (base::IsCtrlPressed()) {
@ -183,13 +186,13 @@ void HistoryMessageForwarded::create(const HistoryMessageVia *via) const {
? originalSender->name() ? originalSender->name()
: hiddenSenderInfo->name) : hiddenSenderInfo->name)
}; };
if (!originalAuthor.isEmpty()) { if (!originalPostAuthor.isEmpty()) {
phrase = tr::lng_forwarded_signed( phrase = tr::lng_forwarded_signed(
tr::now, tr::now,
lt_channel, lt_channel,
name, name,
lt_user, lt_user,
{ .text = originalAuthor }, { .text = originalPostAuthor },
Ui::Text::WithEntities); Ui::Text::WithEntities);
} else { } else {
phrase = name; phrase = name;
@ -261,6 +264,51 @@ void HistoryMessageForwarded::create(const HistoryMessageVia *via) const {
} }
} }
ReplyFields ReplyFieldsFromMTP(
not_null<History*> history,
const MTPMessageReplyHeader &reply) {
return reply.match([&](const MTPDmessageReplyHeader &data) {
auto result = ReplyFields();
if (const auto peer = data.vreply_to_peer_id()) {
result.externalPeerId = peerFromMTP(*peer);
if (result.externalPeerId == history->peer->id) {
result.externalPeerId = 0;
}
}
const auto owner = &history->owner();
if (const auto id = data.vreply_to_msg_id().value_or_empty()) {
result.messageId = data.is_reply_to_scheduled()
? owner->scheduledMessages().localMessageId(id)
: id;
result.topMessageId
= data.vreply_to_top_id().value_or(id);
result.topicPost = data.is_forum_topic();
}
if (const auto header = data.vreply_header()) {
const auto &data = header->data();
result.externalPostAuthor
= qs(data.vpost_author().value_or_empty());
result.externalSenderId = data.vfrom_id()
? peerFromMTP(*data.vfrom_id())
: PeerId();
result.externalSenderName
= qs(data.vfrom_name().value_or_empty());
}
result.quote = TextWithEntities{
qs(data.vquote_text().value_or_empty()),
Api::EntitiesFromMTP(
&owner->session(),
data.vquote_entities().value_or_empty()),
};
return result;
}, [&](const MTPDmessageReplyStoryHeader &data) {
return ReplyFields{
.externalPeerId = peerFromUser(data.vuser_id()),
.storyId = data.vstory_id().v,
};
});
}
HistoryMessageReply::HistoryMessageReply() = default; HistoryMessageReply::HistoryMessageReply() = default;
HistoryMessageReply &HistoryMessageReply::operator=( HistoryMessageReply &HistoryMessageReply::operator=(
@ -268,8 +316,8 @@ HistoryMessageReply &HistoryMessageReply::operator=(
HistoryMessageReply::~HistoryMessageReply() { HistoryMessageReply::~HistoryMessageReply() {
// clearData() should be called by holder. // clearData() should be called by holder.
Expects(replyToMsg.empty()); Expects(resolvedMessage.empty());
Expects(replyToVia == nullptr); Expects(originalVia == nullptr);
} }
bool HistoryMessageReply::updateData( bool HistoryMessageReply::updateData(
@ -277,166 +325,228 @@ bool HistoryMessageReply::updateData(
bool force) { bool force) {
const auto guard = gsl::finally([&] { refreshReplyToMedia(); }); const auto guard = gsl::finally([&] { refreshReplyToMedia(); });
if (!force) { if (!force) {
if ((replyToMsg || !replyToMsgId) if (resolvedMessage || resolvedStory || _deleted) {
&& (replyToStory || !replyToStoryId)) {
return true; return true;
} }
} }
const auto peerId = replyToPeerId const auto peerId = _fields.externalPeerId
? replyToPeerId ? _fields.externalPeerId
: holder->history()->peer->id; : holder->history()->peer->id;
if (!replyToMsg && replyToMsgId) { if (!resolvedMessage && _fields.messageId) {
replyToMsg = holder->history()->owner().message( resolvedMessage = holder->history()->owner().message(
peerId, peerId,
replyToMsgId); _fields.messageId);
if (replyToMsg) { if (resolvedMessage) {
if (replyToMsg->isEmpty()) { if (resolvedMessage->isEmpty()) {
// Really it is deleted. // Really it is deleted.
replyToMsg = nullptr; resolvedMessage = nullptr;
force = true; force = true;
} else { } else {
holder->history()->owner().registerDependentMessage( holder->history()->owner().registerDependentMessage(
holder, holder,
replyToMsg.get()); resolvedMessage.get());
} }
} }
} }
if (!replyToStory && replyToStoryId) { if (!resolvedStory && _fields.storyId) {
const auto maybe = holder->history()->owner().stories().lookup({ const auto maybe = holder->history()->owner().stories().lookup({
peerId, peerId,
replyToStoryId, _fields.storyId,
}); });
if (maybe) { if (maybe) {
replyToStory = *maybe; resolvedStory = *maybe;
holder->history()->owner().stories().registerDependentMessage( holder->history()->owner().stories().registerDependentMessage(
holder, holder,
replyToStory.get()); resolvedStory.get());
} else if (maybe.error() == Data::NoStory::Deleted) { } else if (maybe.error() == Data::NoStory::Deleted) {
force = true; force = true;
} }
} }
if (replyToMsg || replyToStory) { const auto external = _fields.externalSenderId
|| !_fields.externalSenderName.isEmpty();
if (resolvedMessage || resolvedStory || (external && force)) {
const auto repaint = [=] { holder->customEmojiRepaint(); }; const auto repaint = [=] { holder->customEmojiRepaint(); };
const auto context = Core::MarkedTextContext{ const auto context = Core::MarkedTextContext{
.session = &holder->history()->session(), .session = &holder->history()->session(),
.customEmojiRepaint = repaint, .customEmojiRepaint = repaint,
}; };
replyToText.setMarkedText( const auto text = !_fields.quote.empty()
? _fields.quote
: resolvedMessage
? resolvedMessage->inReplyText()
: resolvedStory
? resolvedStory->inReplyText()
: TextWithEntities{ u"..."_q };
_text.setMarkedText(
st::defaultTextStyle, st::defaultTextStyle,
(replyToMsg text,
? replyToMsg->inReplyText()
: replyToStory->inReplyText()),
Ui::DialogTextOptions(), Ui::DialogTextOptions(),
context); context);
updateName(holder); updateName(holder);
setLinkFrom(holder);
setReplyToLinkFrom(holder); if (resolvedMessage
if (replyToMsg && !replyToMsg->Has<HistoryMessageForwarded>()) { && !resolvedMessage->Has<HistoryMessageForwarded>()) {
if (auto bot = replyToMsg->viaBot()) { if (const auto bot = resolvedMessage->viaBot()) {
replyToVia = std::make_unique<HistoryMessageVia>(); originalVia = std::make_unique<HistoryMessageVia>();
replyToVia->create( originalVia->create(
&holder->history()->owner(), &holder->history()->owner(),
peerToUser(bot->id)); peerToUser(bot->id));
} }
} }
if (replyToMsg) { if (resolvedMessage) {
const auto peer = replyToMsg->history()->peer; const auto peer = resolvedMessage->history()->peer;
replyToColorKey = (!holder->out() _colorKey = (!holder->out()
&& (peer->isMegagroup() || peer->isChat()) && (peer->isMegagroup() || peer->isChat())
&& replyToMsg->from()->isUser()) && resolvedMessage->from()->isUser())
? replyToMsg->from()->id ? resolvedMessage->from()->id
: PeerId(0); : PeerId();
} else { } else {
replyToColorKey = PeerId(0); resolvedMessage = 0;
} }
const auto media = replyToMsg ? replyToMsg->media() : nullptr; const auto media = resolvedMessage
? resolvedMessage->media()
: nullptr;
if (!media || !media->hasReplyPreview() || !media->hasSpoiler()) { if (!media || !media->hasReplyPreview() || !media->hasSpoiler()) {
spoiler = nullptr; spoiler = nullptr;
} else if (!spoiler) { } else if (!spoiler) {
spoiler = std::make_unique<Ui::SpoilerAnimation>(repaint); spoiler = std::make_unique<Ui::SpoilerAnimation>(repaint);
} }
} else if (force) { } else if (force) {
replyToMsgId = 0; if (_fields.messageId || _fields.storyId) {
replyToStoryId = 0; _deleted = true;
replyToColorKey = PeerId(0); }
_colorKey = 0;
spoiler = nullptr; spoiler = nullptr;
} }
if (force) { if (force) {
holder->history()->owner().requestItemResize(holder); holder->history()->owner().requestItemResize(holder);
} }
return (replyToMsg || !replyToMsgId) return resolvedMessage
&& (replyToStory || !replyToStoryId); || resolvedStory
|| (external && !_fields.messageId)
|| _deleted;
} }
void HistoryMessageReply::setReplyToLinkFrom( void HistoryMessageReply::set(ReplyFields fields) {
_fields = std::move(fields);
}
void HistoryMessageReply::updateFields(
not_null<HistoryItem*> holder,
MsgId messageId,
MsgId topMessageId,
bool topicPost) {
_fields.topicPost = topicPost;
if ((_fields.messageId != messageId)
&& !IsServerMsgId(_fields.messageId)) {
_fields.messageId = messageId;
if (!updateData(holder)) {
RequestDependentMessageItem(
holder,
_fields.externalPeerId,
_fields.messageId);
}
}
if ((_fields.topMessageId != topMessageId)
&& !IsServerMsgId(_fields.topMessageId)) {
_fields.topMessageId = topMessageId;
}
}
void HistoryMessageReply::setLinkFrom(
not_null<HistoryItem*> holder) { not_null<HistoryItem*> holder) {
replyToLnk = replyToMsg const auto externalPeerId = _fields.externalSenderId;
? JumpToMessageClickHandler(replyToMsg.get(), holder->fullId()) const auto external = externalPeerId
: replyToStory || !_fields.externalSenderName.isEmpty();
? JumpToStoryClickHandler(replyToStory.get()) const auto externalLink = [=](ClickContext context) {
const auto my = context.other.value<ClickHandlerContext>();
if (const auto controller = my.sessionWindow.get()) {
if (externalPeerId) {
controller->showPeerInfo(
controller->session().data().peer(externalPeerId));
} else {
controller->showToast(u"External reply"_q);
}
}
};
_link = resolvedMessage
? JumpToMessageClickHandler(resolvedMessage.get(), holder->fullId())
: resolvedStory
? JumpToStoryClickHandler(resolvedStory.get())
: (external && !_fields.messageId)
? std::make_shared<LambdaClickHandler>(externalLink)
: nullptr; : nullptr;
} }
void HistoryMessageReply::setTopMessageId(MsgId topMessageId) {
_fields.topMessageId = topMessageId;
}
void HistoryMessageReply::clearData(not_null<HistoryItem*> holder) { void HistoryMessageReply::clearData(not_null<HistoryItem*> holder) {
replyToVia = nullptr; originalVia = nullptr;
if (replyToMsg) { if (resolvedMessage) {
holder->history()->owner().unregisterDependentMessage( holder->history()->owner().unregisterDependentMessage(
holder, holder,
replyToMsg.get()); resolvedMessage.get());
replyToMsg = nullptr; resolvedMessage = nullptr;
} }
if (replyToStory) { if (resolvedStory) {
holder->history()->owner().stories().unregisterDependentMessage( holder->history()->owner().stories().unregisterDependentMessage(
holder, holder,
replyToStory.get()); resolvedStory.get());
replyToStory = nullptr; resolvedStory = nullptr;
} }
replyToMsgId = 0; _deleted = true;
replyToStoryId = 0;
refreshReplyToMedia(); refreshReplyToMedia();
} }
PeerData *HistoryMessageReply::replyToFrom( PeerData *HistoryMessageReply::sender(not_null<HistoryItem*> holder) const {
not_null<HistoryItem*> holder) const { if (resolvedStory) {
if (!replyToMsg) { return resolvedStory->peer();
return nullptr; } else if (!resolvedMessage) {
if (!_externalSender && _fields.externalSenderId) {
_externalSender = holder->history()->owner().peer(
_fields.externalSenderId);
}
return _externalSender;
} else if (holder->Has<HistoryMessageForwarded>()) { } else if (holder->Has<HistoryMessageForwarded>()) {
if (const auto fwd = replyToMsg->Get<HistoryMessageForwarded>()) { // Forward of a reply. Show reply-to original sender.
return fwd->originalSender; const auto forwarded
= resolvedMessage->Get<HistoryMessageForwarded>();
if (forwarded) {
return forwarded->originalSender;
} }
} }
if (const auto from = replyToMsg->displayFrom()) { if (const auto from = resolvedMessage->displayFrom()) {
return from; return from;
} }
return replyToMsg->author().get(); return resolvedMessage->author().get();
} }
QString HistoryMessageReply::replyToFromName( QString HistoryMessageReply::senderName(
not_null<HistoryItem*> holder) const { not_null<HistoryItem*> holder) const {
if (replyToStory) { if (const auto peer = sender(holder)) {
return replyToFromName(replyToStory->peer()); return senderName(peer);
} else if (!replyToMsg) { } else if (!resolvedMessage) {
return QString(); return _fields.externalSenderName;
} else if (holder->Has<HistoryMessageForwarded>()) { } else if (holder->Has<HistoryMessageForwarded>()) {
if (const auto fwd = replyToMsg->Get<HistoryMessageForwarded>()) { // Forward of a reply. Show reply-to original sender.
return fwd->originalSender const auto forwarded
? replyToFromName(fwd->originalSender) = resolvedMessage->Get<HistoryMessageForwarded>();
: fwd->hiddenSenderInfo->name; if (forwarded) {
Assert(forwarded->hiddenSenderInfo != nullptr);
return forwarded->hiddenSenderInfo->name;
} }
} }
if (const auto from = replyToMsg->displayFrom()) { return QString();
return replyToFromName(from);
}
return replyToFromName(replyToMsg->author());
} }
QString HistoryMessageReply::replyToFromName( QString HistoryMessageReply::senderName(not_null<PeerData*> peer) const {
not_null<PeerData*> peer) const { if (const auto user = originalVia ? peer->asUser() : nullptr) {
if (const auto user = replyToVia ? peer->asUser() : nullptr) {
return user->firstName; return user->firstName;
} }
return peer->name(); return peer->name();
@ -444,9 +554,9 @@ QString HistoryMessageReply::replyToFromName(
bool HistoryMessageReply::isNameUpdated( bool HistoryMessageReply::isNameUpdated(
not_null<HistoryItem*> holder) const { not_null<HistoryItem*> holder) const {
if (const auto from = replyToFrom(holder)) { if (const auto from = sender(holder)) {
if (replyToVersion < from->nameVersion()) { if (_nameVersion < from->nameVersion()) {
updateName(holder); updateName(holder, from);
return true; return true;
} }
} }
@ -454,55 +564,73 @@ bool HistoryMessageReply::isNameUpdated(
} }
void HistoryMessageReply::updateName( void HistoryMessageReply::updateName(
not_null<HistoryItem*> holder) const { not_null<HistoryItem*> holder,
if (const auto name = replyToFromName(holder); !name.isEmpty()) { std::optional<PeerData*> resolvedSender) const {
replyToName.setText(st::fwdTextStyle, name, Ui::NameTextOptions()); const auto peer = resolvedSender.value_or(sender(holder));
if (const auto from = replyToFrom(holder)) { const auto name = peer ? senderName(peer) : senderName(holder);
replyToVersion = from->nameVersion(); if (!name.isEmpty()) {
} else if (replyToMsg) { _name.setText(st::fwdTextStyle, name, Ui::NameTextOptions());
replyToVersion = replyToMsg->author()->nameVersion(); if (peer) {
} else { _nameVersion = peer->nameVersion();
replyToVersion = replyToStory->peer()->nameVersion();
} }
bool hasPreview = (replyToStory && replyToStory->hasReplyPreview()) bool hasPreview = (resolvedStory
|| (replyToMsg && resolvedStory->hasReplyPreview())
&& replyToMsg->media() || (resolvedMessage
&& replyToMsg->media()->hasReplyPreview()); && resolvedMessage->media()
int32 previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0; && resolvedMessage->media()->hasReplyPreview());
int32 w = replyToName.maxWidth(); int32 previewSkip = hasPreview
if (replyToVia) { ? (st::msgReplyBarSize.height()
w += st::msgServiceFont->spacew + replyToVia->maxWidth; + st::msgReplyBarSkip
- st::msgReplyBarSize.width()
- st::msgReplyBarPos.x())
: 0;
int32 w = _name.maxWidth();
if (originalVia) {
w += st::msgServiceFont->spacew + originalVia->maxWidth;
} }
maxReplyWidth = previewSkip _maxWidth = previewSkip
+ std::max( + std::max(
w, w,
std::min(replyToText.maxWidth(), st::maxSignatureSize)) std::min(_text.maxWidth(), st::maxSignatureSize))
+ (storyReply + (_fields.storyId
? (st::dialogsMiniReplyStory.skipText ? (st::dialogsMiniReplyStory.skipText
+ st::dialogsMiniReplyStory.icon.icon.width()) + st::dialogsMiniReplyStory.icon.icon.width())
: 0); : 0);
} else { } else {
maxReplyWidth = st::msgDateFont->width(statePhrase()); _maxWidth = st::msgDateFont->width(statePhrase());
} }
maxReplyWidth = st::msgReplyPadding.left() + st::msgReplyBarSkip + maxReplyWidth + st::msgReplyPadding.right(); _maxWidth = st::msgReplyPadding.left()
+ st::msgReplyBarSkip
+ _maxWidth
+ st::msgReplyPadding.right();
} }
void HistoryMessageReply::resize(int width) const { void HistoryMessageReply::resize(int width) const {
if (replyToVia) { if (originalVia) {
bool hasPreview = (replyToStory && replyToStory->hasReplyPreview()) bool hasPreview = (resolvedStory
|| (replyToMsg && resolvedStory->hasReplyPreview())
&& replyToMsg->media() || (resolvedMessage
&& replyToMsg->media()->hasReplyPreview()); && resolvedMessage->media()
int previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0; && resolvedMessage->media()->hasReplyPreview());
replyToVia->resize(width - st::msgReplyBarSkip - previewSkip - replyToName.maxWidth() - st::msgServiceFont->spacew); int previewSkip = hasPreview
? (st::msgReplyBarSize.height()
+ st::msgReplyBarSkip
- st::msgReplyBarSize.width()
- st::msgReplyBarPos.x())
: 0;
originalVia->resize(width
- st::msgReplyBarSkip
- previewSkip
- _name.maxWidth()
- st::msgServiceFont->spacew);
} }
} }
void HistoryMessageReply::itemRemoved( void HistoryMessageReply::itemRemoved(
not_null<HistoryItem*> holder, not_null<HistoryItem*> holder,
not_null<HistoryItem*> removed) { not_null<HistoryItem*> removed) {
if (replyToMsg.get() == removed) { if (resolvedMessage.get() == removed) {
clearData(holder); clearData(holder);
holder->history()->owner().requestItemResize(holder); holder->history()->owner().requestItemResize(holder);
} }
@ -511,7 +639,7 @@ void HistoryMessageReply::itemRemoved(
void HistoryMessageReply::storyRemoved( void HistoryMessageReply::storyRemoved(
not_null<HistoryItem*> holder, not_null<HistoryItem*> holder,
not_null<Data::Story*> removed) { not_null<Data::Story*> removed) {
if (replyToStory.get() == removed) { if (resolvedStory.get() == removed) {
clearData(holder); clearData(holder);
holder->history()->owner().requestItemResize(holder); holder->history()->owner().requestItemResize(holder);
} }
@ -533,8 +661,8 @@ void HistoryMessageReply::paint(
const auto outerWidth = w + 2 * x; const auto outerWidth = w + 2 * x;
const auto &bar = !inBubble const auto &bar = !inBubble
? st->msgImgReplyBarColor() ? st->msgImgReplyBarColor()
: replyToColorKey : _colorKey
? HistoryView::FromNameFg(context, replyToColorKey) ? HistoryView::FromNameFg(context, _colorKey)
: stm->msgReplyBarColor; : stm->msgReplyBarColor;
const auto rbar = style::rtlrect( const auto rbar = style::rtlrect(
x + st::msgReplyBarPos.x(), x + st::msgReplyBarPos.x(),
@ -565,9 +693,10 @@ void HistoryMessageReply::paint(
const auto pausedSpoiler = context.paused const auto pausedSpoiler = context.paused
|| On(PowerSaving::kChatSpoiler); || On(PowerSaving::kChatSpoiler);
if (w > st::msgReplyBarSkip) { if (w > st::msgReplyBarSkip) {
if (replyToMsg || replyToStory) { if (resolvedMessage || resolvedStory) {
const auto media = replyToMsg ? replyToMsg->media() : nullptr; const auto media = resolvedMessage ? resolvedMessage->media() : nullptr;
auto hasPreview = (replyToStory && replyToStory->hasReplyPreview()) || (media && media->hasReplyPreview()); auto hasPreview = (media && media->hasReplyPreview())
|| (resolvedStory && resolvedStory->hasReplyPreview());
if (hasPreview && w < st::msgReplyBarSkip + st::msgReplyBarSize.height()) { if (hasPreview && w < st::msgReplyBarSkip + st::msgReplyBarSize.height()) {
hasPreview = false; hasPreview = false;
} }
@ -576,7 +705,7 @@ void HistoryMessageReply::paint(
if (hasPreview) { if (hasPreview) {
const auto image = media const auto image = media
? media->replyPreview() ? media->replyPreview()
: replyToStory->replyPreview(); : resolvedStory->replyPreview();
if (image) { if (image) {
auto to = style::rtlrect(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height(), w + 2 * x); auto to = style::rtlrect(x + st::msgReplyBarSkip, y + st::msgReplyPadding.top() + st::msgReplyBarPos.y(), st::msgReplyBarSize.height(), st::msgReplyBarSize.height(), w + 2 * x);
const auto preview = image->pixSingle( const auto preview = image->pixSingle(
@ -604,26 +733,26 @@ void HistoryMessageReply::paint(
if (w > st::msgReplyBarSkip + previewSkip) { if (w > st::msgReplyBarSkip + previewSkip) {
p.setPen(!inBubble p.setPen(!inBubble
? st->msgImgReplyBarColor() ? st->msgImgReplyBarColor()
: replyToColorKey : _colorKey
? HistoryView::FromNameFg(context, replyToColorKey) ? HistoryView::FromNameFg(context, _colorKey)
: stm->msgServiceFg); : stm->msgServiceFg);
replyToName.drawLeftElided(p, x + st::msgReplyBarSkip + previewSkip, y + st::msgReplyPadding.top(), w - st::msgReplyBarSkip - previewSkip, w + 2 * x); _name.drawLeftElided(p, x + st::msgReplyBarSkip + previewSkip, y + st::msgReplyPadding.top(), w - st::msgReplyBarSkip - previewSkip, w + 2 * x);
if (replyToVia && w > st::msgReplyBarSkip + previewSkip + replyToName.maxWidth() + st::msgServiceFont->spacew) { if (originalVia && w > st::msgReplyBarSkip + previewSkip + _name.maxWidth() + st::msgServiceFont->spacew) {
p.setFont(st::msgServiceFont); p.setFont(st::msgServiceFont);
p.drawText(x + st::msgReplyBarSkip + previewSkip + replyToName.maxWidth() + st::msgServiceFont->spacew, y + st::msgReplyPadding.top() + st::msgServiceFont->ascent, replyToVia->text); p.drawText(x + st::msgReplyBarSkip + previewSkip + _name.maxWidth() + st::msgServiceFont->spacew, y + st::msgReplyPadding.top() + st::msgServiceFont->ascent, originalVia->text);
} }
p.setPen(inBubble p.setPen(inBubble
? stm->historyTextFg ? stm->historyTextFg
: st->msgImgReplyBarColor()); : st->msgImgReplyBarColor());
holder->prepareCustomEmojiPaint(p, context, replyToText); holder->prepareCustomEmojiPaint(p, context, _text);
auto replyToTextPosition = QPoint( auto replyToTextPosition = QPoint(
x + st::msgReplyBarSkip + previewSkip, x + st::msgReplyBarSkip + previewSkip,
y + st::msgReplyPadding.top() + st::msgServiceNameFont->height); y + st::msgReplyPadding.top() + st::msgServiceNameFont->height);
const auto replyToTextPalette = &(inBubble const auto replyToTextPalette = &(inBubble
? stm->replyTextPalette ? stm->replyTextPalette
: st->imgReplyTextPalette()); : st->imgReplyTextPalette());
if (storyReply) { if (_fields.storyId) {
st::dialogsMiniReplyStory.icon.icon.paint( st::dialogsMiniReplyStory.icon.icon.paint(
p, p,
replyToTextPosition, replyToTextPosition,
@ -634,7 +763,7 @@ void HistoryMessageReply::paint(
+ st::dialogsMiniReplyStory.icon.icon.width(), + st::dialogsMiniReplyStory.icon.icon.width(),
0); 0);
} }
replyToText.draw(p, { _text.draw(p, {
.position = replyToTextPosition, .position = replyToTextPosition,
.availableWidth = w - st::msgReplyBarSkip - previewSkip, .availableWidth = w - st::msgReplyBarSkip - previewSkip,
.palette = replyToTextPalette, .palette = replyToTextPalette,
@ -657,10 +786,14 @@ void HistoryMessageReply::paint(
} }
} }
void HistoryMessageReply::unloadPersistentAnimation() {
_text.unloadPersistentAnimation();
}
QString HistoryMessageReply::statePhrase() const { QString HistoryMessageReply::statePhrase() const {
return (replyToMsgId || replyToStoryId) return ((_fields.messageId || _fields.storyId) && !_deleted)
? tr::lng_profile_loading(tr::now) ? tr::lng_profile_loading(tr::now)
: storyReply : _fields.storyId
? tr::lng_deleted_story(tr::now) ? tr::lng_deleted_story(tr::now)
: tr::lng_deleted_message(tr::now); : tr::lng_deleted_message(tr::now);
} }
@ -668,7 +801,7 @@ QString HistoryMessageReply::statePhrase() const {
void HistoryMessageReply::refreshReplyToMedia() { void HistoryMessageReply::refreshReplyToMedia() {
replyToDocumentId = 0; replyToDocumentId = 0;
replyToWebPageId = 0; replyToWebPageId = 0;
if (const auto media = replyToMsg ? replyToMsg->media() : nullptr) { if (const auto media = resolvedMessage ? resolvedMessage->media() : nullptr) {
if (const auto document = media->document()) { if (const auto document = media->document()) {
replyToDocumentId = document->id; replyToDocumentId = document->id;
} else if (const auto webpage = media->webpage()) { } else if (const auto webpage = media->webpage()) {

View file

@ -75,7 +75,7 @@ struct HistoryMessageViews : public RuntimeComponent<HistoryMessageViews, Histor
}; };
struct HistoryMessageSigned : public RuntimeComponent<HistoryMessageSigned, HistoryItem> { struct HistoryMessageSigned : public RuntimeComponent<HistoryMessageSigned, HistoryItem> {
QString author; QString postAuthor;
bool isAnonymousRank = false; bool isAnonymousRank = false;
}; };
@ -123,7 +123,7 @@ struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded
TimeId originalDate = 0; TimeId originalDate = 0;
PeerData *originalSender = nullptr; PeerData *originalSender = nullptr;
std::unique_ptr<HiddenSenderInfo> hiddenSenderInfo; std::unique_ptr<HiddenSenderInfo> hiddenSenderInfo;
QString originalAuthor; QString originalPostAuthor;
QString psaType; QString psaType;
MsgId originalId = 0; MsgId originalId = 0;
mutable Ui::Text::String text = { 1 }; mutable Ui::Text::String text = { 1 };
@ -226,6 +226,22 @@ private:
}; };
struct ReplyFields {
TextWithEntities quote;
PeerId externalSenderId = 0;
QString externalSenderName;
QString externalPostAuthor;
PeerId externalPeerId = 0;
MsgId messageId = 0;
MsgId topMessageId = 0;
StoryId storyId = 0;
bool topicPost = false;
};
[[nodiscard]] ReplyFields ReplyFieldsFromMTP(
not_null<History*> history,
const MTPMessageReplyHeader &reply);
struct HistoryMessageReply struct HistoryMessageReply
: public RuntimeComponent<HistoryMessageReply, HistoryItem> { : public RuntimeComponent<HistoryMessageReply, HistoryItem> {
HistoryMessageReply(); HistoryMessageReply();
@ -238,17 +254,25 @@ struct HistoryMessageReply
static constexpr auto kBarAlpha = 230. / 255.; static constexpr auto kBarAlpha = 230. / 255.;
void set(ReplyFields fields);
void updateFields(
not_null<HistoryItem*> holder,
MsgId messageId,
MsgId topMessageId,
bool topicPost);
bool updateData(not_null<HistoryItem*> holder, bool force = false); bool updateData(not_null<HistoryItem*> holder, bool force = false);
// Must be called before destructor. // Must be called before destructor.
void clearData(not_null<HistoryItem*> holder); void clearData(not_null<HistoryItem*> holder);
[[nodiscard]] PeerData *replyToFrom(not_null<HistoryItem*> holder) const; [[nodiscard]] PeerData *sender(not_null<HistoryItem*> holder) const;
[[nodiscard]] QString replyToFromName( [[nodiscard]] QString senderName(not_null<HistoryItem*> holder) const;
not_null<HistoryItem*> holder) const; [[nodiscard]] QString senderName(not_null<PeerData*> peer) const;
[[nodiscard]] QString replyToFromName(not_null<PeerData*> peer) const;
[[nodiscard]] bool isNameUpdated(not_null<HistoryItem*> holder) const; [[nodiscard]] bool isNameUpdated(not_null<HistoryItem*> holder) const;
void updateName(not_null<HistoryItem*> holder) const; void updateName(
not_null<HistoryItem*> holder,
std::optional<PeerData*> resolvedSender = std::nullopt) const;
void resize(int width) const; void resize(int width) const;
void itemRemoved( void itemRemoved(
not_null<HistoryItem*> holder, not_null<HistoryItem*> holder,
@ -265,52 +289,63 @@ struct HistoryMessageReply
int y, int y,
int w, int w,
bool inBubble) const; bool inBubble) const;
void unloadPersistentAnimation();
[[nodiscard]] PeerId replyToPeer() const { [[nodiscard]] PeerId colorKey() const {
return replyToPeerId; return _colorKey;
} }
[[nodiscard]] MsgId replyToId() const { [[nodiscard]] PeerId externalPeerId() const {
return replyToMsgId; return _fields.externalPeerId;
} }
[[nodiscard]] MsgId replyToTop() const { [[nodiscard]] MsgId messageId() const {
return replyToMsgTop; return _fields.messageId;
} }
[[nodiscard]] int replyToWidth() const { [[nodiscard]] StoryId storyId() const {
return maxReplyWidth; return _fields.storyId;
} }
[[nodiscard]] ClickHandlerPtr replyToLink() const { [[nodiscard]] MsgId topMessageId() const {
return replyToLnk; return _fields.topMessageId;
}
[[nodiscard]] int maxWidth() const {
return _maxWidth;
}
[[nodiscard]] ClickHandlerPtr link() const {
return _link;
}
[[nodiscard]] bool topicPost() const {
return _fields.topicPost;
} }
[[nodiscard]] QString statePhrase() const; [[nodiscard]] QString statePhrase() const;
void setReplyToLinkFrom(not_null<HistoryItem*> holder);
void setLinkFrom(not_null<HistoryItem*> holder);
void setTopMessageId(MsgId topMessageId);
void refreshReplyToMedia(); void refreshReplyToMedia();
PeerId replyToPeerId = 0;
MsgId replyToMsgId = 0;
MsgId replyToMsgTop = 0;
StoryId replyToStoryId = 0;
using ColorKey = PeerId;
ColorKey replyToColorKey = 0;
DocumentId replyToDocumentId = 0; DocumentId replyToDocumentId = 0;
WebPageId replyToWebPageId = 0; WebPageId replyToWebPageId = 0;
ReplyToMessagePointer replyToMsg; ReplyToMessagePointer resolvedMessage;
ReplyToStoryPointer replyToStory; ReplyToStoryPointer resolvedStory;
std::unique_ptr<HistoryMessageVia> replyToVia; std::unique_ptr<HistoryMessageVia> originalVia;
std::unique_ptr<Ui::SpoilerAnimation> spoiler; std::unique_ptr<Ui::SpoilerAnimation> spoiler;
ClickHandlerPtr replyToLnk;
mutable Ui::Text::String replyToName, replyToText;
mutable int replyToVersion = 0;
mutable int maxReplyWidth = 0;
int toWidth = 0; int toWidth = 0;
bool topicPost = false;
bool storyReply = false;
struct final { struct {
mutable std::unique_ptr<Ui::RippleAnimation> animation; mutable std::unique_ptr<Ui::RippleAnimation> animation;
QPoint lastPoint; QPoint lastPoint;
} ripple; } ripple;
private:
ReplyFields _fields;
PeerId _colorKey = 0;
ClickHandlerPtr _link;
mutable Ui::Text::String _name;
mutable Ui::Text::String _text;
mutable PeerData *_externalSender = nullptr;
mutable int _maxWidth = 0;
mutable int _nameVersion = 0;
bool _deleted = false;
}; };
struct HistoryMessageTranslation struct HistoryMessageTranslation

View file

@ -192,13 +192,14 @@ bool ShouldSendSilent(
&& peer->session().settings().supportAllSilent()); && peer->session().settings().supportAllSilent());
} }
HistoryItem *LookupReplyTo(not_null<History*> history, MsgId replyToId) { HistoryItem *LookupReplyTo(not_null<History*> history, FullMsgId replyTo) {
const auto &owner = history->owner(); return history->owner().message(replyTo);
return owner.message(history->peer, replyToId);
} }
MsgId LookupReplyToTop(HistoryItem *replyTo) { MsgId LookupReplyToTop(not_null<History*> history, HistoryItem *replyTo) {
return replyTo ? replyTo->replyToTop() : 0; return (replyTo && replyTo->history() == history)
? replyTo->replyToTop()
: 0;
} }
bool LookupReplyIsTopicPost(HistoryItem *replyTo) { bool LookupReplyIsTopicPost(HistoryItem *replyTo) {
@ -360,27 +361,21 @@ MTPMessageReplyHeader NewMessageReplyHeader(const Api::SendAction &action) {
MTP_long(peerToUser(replyTo.storyId.peer).bare), MTP_long(peerToUser(replyTo.storyId.peer).bare),
MTP_int(replyTo.storyId.story)); MTP_int(replyTo.storyId.story));
} }
const auto to = LookupReplyTo(action.history, replyTo.msgId); using Flag = MTPDmessageReplyHeader::Flag;
if (const auto replyToTop = LookupReplyToTop(to)) { const auto historyPeer = action.history->peer->id;
using Flag = MTPDmessageReplyHeader::Flag; const auto externalPeerId = (replyTo.messageId.peer == historyPeer)
return MTP_messageReplyHeader( ? PeerId()
MTP_flags(Flag::f_reply_to_top_id : replyTo.messageId.peer;
| (LookupReplyIsTopicPost(to) const auto to = LookupReplyTo(action.history, replyTo.messageId);
? Flag::f_forum_topic const auto replyToTop = LookupReplyToTop(action.history, to);
: Flag(0))),
MTP_int(replyTo.msgId),
MTPPeer(),
MTPMessageFwdHeader(), // reply_header
MTP_int(replyToTop),
MTPstring(), // quote_text
MTPVector<MTPMessageEntity>()); // quote_entities
}
return MTP_messageReplyHeader( return MTP_messageReplyHeader(
MTP_flags(0), MTP_flags(Flag::f_reply_to_msg_id
MTP_int(replyTo.msgId), | (replyToTop ? Flag::f_reply_to_top_id : Flag())
MTPPeer(), // reply_to_peer_id | (externalPeerId ? Flag::f_reply_to_peer_id : Flag())),
MTP_int(replyTo.messageId.msg),
peerToMTP(externalPeerId),
MTPMessageFwdHeader(), // reply_header MTPMessageFwdHeader(), // reply_header
MTPint(), // reply_to_top_id MTP_int(replyToTop), // reply_to_top_id
MTPstring(), // quote_text MTPstring(), // quote_text
MTPVector<MTPMessageEntity>()); // quote_entities MTPVector<MTPMessageEntity>()); // quote_entities
} }

View file

@ -85,8 +85,10 @@ void RequestDependentMessageStory(
const Api::SendOptions &options); const Api::SendOptions &options);
[[nodiscard]] HistoryItem *LookupReplyTo( [[nodiscard]] HistoryItem *LookupReplyTo(
not_null<History*> history, not_null<History*> history,
MsgId replyToId); FullMsgId replyToId);
[[nodiscard]] MsgId LookupReplyToTop(HistoryItem *replyTo); [[nodiscard]] MsgId LookupReplyToTop(
not_null<History*> history,
HistoryItem *replyTo);
[[nodiscard]] bool LookupReplyIsTopicPost(HistoryItem *replyTo); [[nodiscard]] bool LookupReplyIsTopicPost(HistoryItem *replyTo);
struct SendingErrorRequest { struct SendingErrorRequest {

View file

@ -517,7 +517,7 @@ HistoryWidget::HistoryWidget(
if (!_peer || isRecording()) { if (!_peer || isRecording()) {
return false; return false;
} }
const auto replyTo = (_replyToId && !_editMsgId) const auto replyTo = (_replyTo && !_editMsgId)
? _replyEditMsg ? _replyEditMsg
: 0; : 0;
const auto topic = replyTo ? replyTo->topic() : nullptr; const auto topic = replyTo ? replyTo->topic() : nullptr;
@ -848,9 +848,8 @@ HistoryWidget::HistoryWidget(
) | rpl::filter([=](const Api::SendAction &action) { ) | rpl::filter([=](const Api::SendAction &action) {
return (action.history == _history); return (action.history == _history);
}) | rpl::start_with_next([=](const Api::SendAction &action) { }) | rpl::start_with_next([=](const Api::SendAction &action) {
const auto lastKeyboardUsed = lastForceReplyReplied(FullMsgId( const auto lastKeyboardUsed = lastForceReplyReplied(
action.history->peer->id, action.replyTo.messageId);
action.replyTo.msgId));
if (action.replaceMediaOf) { if (action.replaceMediaOf) {
} else if (action.options.scheduled) { } else if (action.options.scheduled) {
cancelReply(lastKeyboardUsed); cancelReply(lastKeyboardUsed);
@ -909,7 +908,7 @@ Dialogs::EntryState HistoryWidget::computeDialogsEntryState() const {
return Dialogs::EntryState{ return Dialogs::EntryState{
.key = _history, .key = _history,
.section = Dialogs::EntryState::Section::History, .section = Dialogs::EntryState::Section::History,
.currentReplyToId = replyToId(), .currentReplyTo = replyTo(),
}; };
} }
@ -1354,7 +1353,7 @@ void HistoryWidget::insertHashtagOrBotCommand(
// Send bot command at once, if it was not inserted by pressing Tab. // Send bot command at once, if it was not inserted by pressing Tab.
if (str.at(0) == '/' && method != FieldAutocomplete::ChooseMethod::ByTab) { if (str.at(0) == '/' && method != FieldAutocomplete::ChooseMethod::ByTab) {
sendBotCommand({ _peer, str, FullMsgId(), replyToId() }); sendBotCommand({ _peer, str, FullMsgId(), replyTo() });
session().api().finishForwarding(prepareSendAction({})); session().api().finishForwarding(prepareSendAction({}));
setFieldText(_field->getTextWithTagsPart(_field->textCursor().position())); setFieldText(_field->getTextWithTagsPart(_field->textCursor().position()));
} else { } else {
@ -1663,21 +1662,22 @@ void HistoryWidget::saveFieldToHistoryLocalDraft() {
if (_editMsgId) { if (_editMsgId) {
_history->setLocalEditDraft(std::make_unique<Data::Draft>( _history->setLocalEditDraft(std::make_unique<Data::Draft>(
_field, _field,
_editMsgId, FullReplyTo{
topicRootId, .messageId = FullMsgId(_history->peer->id, _editMsgId),
.topicRootId = topicRootId,
},
_previewState, _previewState,
_saveEditMsgRequestId)); _saveEditMsgRequestId));
} else { } else {
if (_replyToId || !_field->empty()) { if (_replyTo || !_field->empty()) {
_history->setLocalDraft(std::make_unique<Data::Draft>( _history->setLocalDraft(std::make_unique<Data::Draft>(
_field, _field,
_replyToId, _replyTo,
topicRootId,
_previewState)); _previewState));
} else { } else {
_history->clearLocalDraft({}); _history->clearLocalDraft(topicRootId);
} }
_history->clearLocalEditDraft({}); _history->clearLocalEditDraft(topicRootId);
} }
} }
@ -1779,8 +1779,7 @@ bool HistoryWidget::notify_switchInlineBotButtonReceived(
}; };
_history->setLocalDraft(std::make_unique<Data::Draft>( _history->setLocalDraft(std::make_unique<Data::Draft>(
textWithTags, textWithTags,
0, // replyTo FullReplyTo(),
0, // topicRootId
cursor, cursor,
Data::PreviewState::Allowed)); Data::PreviewState::Allowed));
applyDraft(); applyDraft();
@ -1885,7 +1884,7 @@ bool HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
? _history->localDraft({}) ? _history->localDraft({})
: nullptr; : nullptr;
auto fieldAvailable = canWriteMessage(); auto fieldAvailable = canWriteMessage();
const auto editMsgId = editDraft ? editDraft->msgId : 0; const auto editMsgId = editDraft ? editDraft->reply.messageId.msg : 0;
if (_voiceRecordBar->isActive() || (!_canSendTexts && !editMsgId)) { if (_voiceRecordBar->isActive() || (!_canSendTexts && !editMsgId)) {
if (!_canSendTexts) { if (!_canSendTexts) {
clearFieldText(0, fieldHistoryAction); clearFieldText(0, fieldHistoryAction);
@ -1898,7 +1897,7 @@ bool HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
clearFieldText(0, fieldHistoryAction); clearFieldText(0, fieldHistoryAction);
setInnerFocus(); setInnerFocus();
_processingReplyItem = _replyEditMsg = nullptr; _processingReplyItem = _replyEditMsg = nullptr;
_processingReplyId = _replyToId = 0; _processingReplyTo = _replyTo = FullReplyTo();
setEditMsgId(0); setEditMsgId(0);
if (fieldWillBeHiddenAfterEdit) { if (fieldWillBeHiddenAfterEdit) {
updateControlsVisibility(); updateControlsVisibility();
@ -1916,7 +1915,7 @@ bool HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
| TextUpdateEvent::SendTyping; | TextUpdateEvent::SendTyping;
_processingReplyItem = _replyEditMsg = nullptr; _processingReplyItem = _replyEditMsg = nullptr;
_processingReplyId = _replyToId = 0; _processingReplyTo = _replyTo = FullReplyTo();
setEditMsgId(editMsgId); setEditMsgId(editMsgId);
updateCmdStartShown(); updateCmdStartShown();
updateControlsVisibility(); updateControlsVisibility();
@ -1929,7 +1928,7 @@ bool HistoryWidget::applyDraft(FieldHistoryAction fieldHistoryAction) {
} }
} else if (!readyToForward()) { } else if (!readyToForward()) {
const auto draft = _history->localDraft({}); const auto draft = _history->localDraft({});
_processingReplyId = draft ? draft->msgId : MsgId(); _processingReplyTo = draft ? draft->reply : FullReplyTo();
processReply(); processReply();
} }
@ -2147,7 +2146,8 @@ void HistoryWidget::showHistory(
_saveEditMsgRequestId = 0; _saveEditMsgRequestId = 0;
_processingReplyItem = _replyEditMsg = nullptr; _processingReplyItem = _replyEditMsg = nullptr;
_processingReplyId = _editMsgId = _replyToId = 0; _processingReplyTo = _replyTo = FullReplyTo();
_editMsgId = MsgId();
_canReplaceMedia = false; _canReplaceMedia = false;
_photoEditMedia = nullptr; _photoEditMedia = nullptr;
updateReplaceMediaButton(); updateReplaceMediaButton();
@ -2461,10 +2461,13 @@ void HistoryWidget::registerDraftSource() {
if (!_history) { if (!_history) {
return; return;
} }
const auto peerId = _history->peer->id;
const auto editMsgId = _editMsgId; const auto editMsgId = _editMsgId;
const auto draft = [=] { const auto draft = [=] {
return Storage::MessageDraft{ return Storage::MessageDraft{
editMsgId ? editMsgId : _replyToId, (editMsgId
? FullReplyTo{ FullMsgId(peerId, editMsgId) }
: _replyTo),
_field->getTextWithTags(), _field->getTextWithTags(),
_previewState, _previewState,
}; };
@ -2901,7 +2904,11 @@ void HistoryWidget::updateControlsVisibility() {
} }
updateFieldPlaceholder(); updateFieldPlaceholder();
if (_editMsgId || _replyToId || readyToForward() || (_previewData && _previewData->pendingTill >= 0) || _kbReplyTo) { if (_editMsgId
|| _replyTo
|| readyToForward()
|| (_previewData && _previewData->pendingTill >= 0)
|| _kbReplyTo) {
if (_fieldBarCancel->isHidden()) { if (_fieldBarCancel->isHidden()) {
_fieldBarCancel->show(); _fieldBarCancel->show();
updateControlsGeometry(); updateControlsGeometry();
@ -2938,7 +2945,7 @@ void HistoryWidget::updateControlsVisibility() {
_botMenuButton->hide(); _botMenuButton->hide();
} }
_kbScroll->hide(); _kbScroll->hide();
if (_replyToId || readyToForward() || _kbReplyTo) { if (_replyTo || readyToForward() || _kbReplyTo) {
if (_fieldBarCancel->isHidden()) { if (_fieldBarCancel->isHidden()) {
_fieldBarCancel->show(); _fieldBarCancel->show();
updateControlsGeometry(); updateControlsGeometry();
@ -3889,7 +3896,7 @@ void HistoryWidget::hideSelectorControlsAnimated() {
Api::SendAction HistoryWidget::prepareSendAction( Api::SendAction HistoryWidget::prepareSendAction(
Api::SendOptions options) const { Api::SendOptions options) const {
auto result = Api::SendAction(_history, options); auto result = Api::SendAction(_history, options);
result.replyTo = { .msgId = replyToId() }; result.replyTo = replyTo();
result.options.sendAs = _sendAs result.options.sendAs = _sendAs
? _history->session().sendAsPeers().resolveChosen( ? _history->session().sendAsPeers().resolveChosen(
_history->peer).get() _history->peer).get()
@ -4337,7 +4344,7 @@ void HistoryWidget::updateOverStates(QPoint pos) {
_field->y() - st::historySendPadding - st::historyReplyHeight, _field->y() - st::historySendPadding - st::historyReplyHeight,
width() - skip - _fieldBarCancel->width(), width() - skip - _fieldBarCancel->width(),
st::historyReplyHeight); st::historyReplyHeight);
auto inReplyEditForward = (_editMsgId || replyToId() || isReadyToForward) auto inReplyEditForward = (_editMsgId || replyTo() || isReadyToForward)
&& replyEditForwardInfoRect.contains(pos); && replyEditForwardInfoRect.contains(pos);
auto inPhotoEdit = inReplyEditForward auto inPhotoEdit = inReplyEditForward
&& _photoEditMedia && _photoEditMedia
@ -4387,9 +4394,9 @@ void HistoryWidget::sendBotCommand(const Bot::SendCommandRequest &request) {
return; return;
} }
const auto lastKeyboardUsed = (_keyboard->forMsgId() const auto forMsgId = _keyboard->forMsgId();
== FullMsgId(_peer->id, _history->lastKeyboardId)) const auto lastKeyboardUsed = (forMsgId == request.replyTo.messageId)
&& (_keyboard->forMsgId() == FullMsgId(_peer->id, request.replyTo)); && (forMsgId == FullMsgId(_peer->id, _history->lastKeyboardId));
// 'bot' may be nullptr in case of sending from FieldAutocomplete. // 'bot' may be nullptr in case of sending from FieldAutocomplete.
const auto toSend = (request.replyTo/* || !bot*/) const auto toSend = (request.replyTo/* || !bot*/)
@ -4398,14 +4405,14 @@ void HistoryWidget::sendBotCommand(const Bot::SendCommandRequest &request) {
auto message = Api::MessageToSend(prepareSendAction({})); auto message = Api::MessageToSend(prepareSendAction({}));
message.textWithTags = { toSend, TextWithTags::Tags() }; message.textWithTags = { toSend, TextWithTags::Tags() };
message.action.replyTo.msgId = request.replyTo message.action.replyTo = request.replyTo
? ((!_peer->isUser()/* && (botStatus == 0 || botStatus == 2)*/) ? ((!_peer->isUser()/* && (botStatus == 0 || botStatus == 2)*/)
? request.replyTo ? request.replyTo
: replyToId()) : replyTo())
: 0; : FullReplyTo();
session().api().sendMessage(std::move(message)); session().api().sendMessage(std::move(message));
if (request.replyTo) { if (request.replyTo) {
if (_replyToId == request.replyTo) { if (_replyTo == request.replyTo) {
cancelReply(); cancelReply();
saveCloudDraft(); saveCloudDraft();
} }
@ -4418,18 +4425,25 @@ void HistoryWidget::sendBotCommand(const Bot::SendCommandRequest &request) {
setInnerFocus(); setInnerFocus();
} }
void HistoryWidget::hideSingleUseKeyboard(PeerData *peer, MsgId replyTo) { void HistoryWidget::hideSingleUseKeyboard(FullMsgId replyToId) {
if (!_peer || _peer != peer) return; if (!_peer || _peer->id != replyToId.peer) {
return;
}
bool lastKeyboardUsed = (_keyboard->forMsgId() == FullMsgId(_peer->id, _history->lastKeyboardId)) bool lastKeyboardUsed = (_keyboard->forMsgId() == replyToId)
&& (_keyboard->forMsgId() == FullMsgId(_peer->id, replyTo)); && (_keyboard->forMsgId()
if (replyTo) { == FullMsgId(_peer->id, _history->lastKeyboardId));
if (_replyToId == replyTo) { if (replyToId) {
if (_replyTo.messageId == replyToId) {
cancelReply(); cancelReply();
saveCloudDraft(); saveCloudDraft();
} }
if (_keyboard->singleUse() && _keyboard->hasMarkup() && lastKeyboardUsed) { if (_keyboard->singleUse()
if (_kbShown) toggleKeyboard(false); && _keyboard->hasMarkup()
&& lastKeyboardUsed) {
if (_kbShown) {
toggleKeyboard(false);
}
_history->lastKeyboardUsed = true; _history->lastKeyboardUsed = true;
} }
} }
@ -4772,7 +4786,7 @@ void HistoryWidget::toggleKeyboard(bool manual) {
_field->setMaxHeight(computeMaxFieldHeight()); _field->setMaxHeight(computeMaxFieldHeight());
_kbReplyTo = nullptr; _kbReplyTo = nullptr;
if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_editMsgId && !_replyToId) { if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_editMsgId && !_replyTo) {
_fieldBarCancel->hide(); _fieldBarCancel->hide();
updateMouseTracking(); updateMouseTracking();
} }
@ -4797,7 +4811,7 @@ void HistoryWidget::toggleKeyboard(bool manual) {
_kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply())
? session().data().message(_keyboard->forMsgId()) ? session().data().message(_keyboard->forMsgId())
: nullptr; : nullptr;
if (_kbReplyTo && !_editMsgId && !_replyToId && fieldEnabled) { if (_kbReplyTo && !_editMsgId && !_replyTo && fieldEnabled) {
updateReplyToName(); updateReplyToName();
updateReplyEditText(_kbReplyTo); updateReplyEditText(_kbReplyTo);
} }
@ -4817,7 +4831,7 @@ void HistoryWidget::toggleKeyboard(bool manual) {
_kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply())
? session().data().message(_keyboard->forMsgId()) ? session().data().message(_keyboard->forMsgId())
: nullptr; : nullptr;
if (_kbReplyTo && !_editMsgId && !_replyToId) { if (_kbReplyTo && !_editMsgId && !_replyTo) {
updateReplyToName(); updateReplyToName();
updateReplyEditText(_kbReplyTo); updateReplyEditText(_kbReplyTo);
} }
@ -5589,11 +5603,11 @@ void HistoryWidget::itemRemoved(not_null<const HistoryItem*> item) {
if (item == _replyEditMsg && _editMsgId) { if (item == _replyEditMsg && _editMsgId) {
cancelEdit(); cancelEdit();
} }
if (item == _replyEditMsg && _replyToId) { if (item == _replyEditMsg && _replyTo) {
cancelReply(); cancelReply();
} }
if (item == _processingReplyItem) { if (item == _processingReplyItem) {
_processingReplyId = 0; _processingReplyTo = {};
_processingReplyItem = nullptr; _processingReplyItem = nullptr;
} }
if (_kbReplyTo && item == _kbReplyTo) { if (_kbReplyTo && item == _kbReplyTo) {
@ -5617,8 +5631,12 @@ void HistoryWidget::itemEdited(not_null<HistoryItem*> item) {
} }
} }
MsgId HistoryWidget::replyToId() const { FullReplyTo HistoryWidget::replyTo() const {
return _replyToId ? _replyToId : (_kbReplyTo ? _kbReplyTo->id : 0); return _replyTo
? _replyTo
: _kbReplyTo
? FullReplyTo{ _kbReplyTo->fullId() }
: FullReplyTo();
} }
bool HistoryWidget::hasSavedScroll() const { bool HistoryWidget::hasSavedScroll() const {
@ -5753,7 +5771,7 @@ void HistoryWidget::updateHistoryGeometry(
} else if (writeRestriction().has_value()) { } else if (writeRestriction().has_value()) {
newScrollHeight -= _unblock->height(); newScrollHeight -= _unblock->height();
} }
if (_editMsgId || replyToId() || readyToForward() || (_previewData && _previewData->pendingTill >= 0)) { if (_editMsgId || replyTo() || readyToForward() || (_previewData && _previewData->pendingTill >= 0)) {
newScrollHeight -= st::historyReplyHeight; newScrollHeight -= st::historyReplyHeight;
} }
if (_kbShown) { if (_kbShown) {
@ -5991,9 +6009,9 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
const auto wasVisible = _kbShown || _kbReplyTo; const auto wasVisible = _kbShown || _kbReplyTo;
const auto wasMsgId = _keyboard->forMsgId(); const auto wasMsgId = _keyboard->forMsgId();
auto changed = false; auto changed = false;
if ((_replyToId && !_replyEditMsg) || _editMsgId || !_history) { if ((_replyTo && !_replyEditMsg) || _editMsgId || !_history) {
changed = _keyboard->updateMarkup(nullptr, force); changed = _keyboard->updateMarkup(nullptr, force);
} else if (_replyToId && _replyEditMsg) { } else if (_replyTo && _replyEditMsg) {
changed = _keyboard->updateMarkup(_replyEditMsg, force); changed = _keyboard->updateMarkup(_replyEditMsg, force);
} else { } else {
const auto keyboardItem = _history->lastKeyboardId const auto keyboardItem = _history->lastKeyboardId
@ -6010,7 +6028,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
_kbScroll->scrollTo({ 0, 0 }); _kbScroll->scrollTo({ 0, 0 });
} }
bool hasMarkup = _keyboard->hasMarkup(), forceReply = _keyboard->forceReply() && (!_replyToId || !_replyEditMsg); bool hasMarkup = _keyboard->hasMarkup(), forceReply = _keyboard->forceReply() && (!_replyTo || !_replyEditMsg);
if (hasMarkup || forceReply) { if (hasMarkup || forceReply) {
if (_keyboard->singleUse() if (_keyboard->singleUse()
&& _keyboard->hasMarkup() && _keyboard->hasMarkup()
@ -6019,7 +6037,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
&& _history->lastKeyboardUsed) { && _history->lastKeyboardUsed) {
_history->lastKeyboardHiddenId = _history->lastKeyboardId; _history->lastKeyboardHiddenId = _history->lastKeyboardId;
} }
if (!isSearching() && !isBotStart() && !isBlocked() && _canSendMessages && (wasVisible || (_replyToId && _replyEditMsg) || (!HasSendText(_field) && !kbWasHidden()))) { if (!isSearching() && !isBotStart() && !isBlocked() && _canSendMessages && (wasVisible || (_replyTo && _replyEditMsg) || (!HasSendText(_field) && !kbWasHidden()))) {
if (!_showAnimation) { if (!_showAnimation) {
if (hasMarkup) { if (hasMarkup) {
_kbScroll->show(); _kbScroll->show();
@ -6040,7 +6058,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
_kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply()) _kbReplyTo = (_peer->isChat() || _peer->isChannel() || _keyboard->forceReply())
? session().data().message(_keyboard->forMsgId()) ? session().data().message(_keyboard->forMsgId())
: nullptr; : nullptr;
if (_kbReplyTo && !_replyToId) { if (_kbReplyTo && !_replyTo) {
updateReplyToName(); updateReplyToName();
updateReplyEditText(_kbReplyTo); updateReplyEditText(_kbReplyTo);
} }
@ -6055,7 +6073,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
_field->setMaxHeight(computeMaxFieldHeight()); _field->setMaxHeight(computeMaxFieldHeight());
_kbShown = false; _kbShown = false;
_kbReplyTo = nullptr; _kbReplyTo = nullptr;
if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId) { if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyTo) {
_fieldBarCancel->hide(); _fieldBarCancel->hide();
updateMouseTracking(); updateMouseTracking();
} }
@ -6071,7 +6089,7 @@ void HistoryWidget::updateBotKeyboard(History *h, bool force) {
_field->setMaxHeight(computeMaxFieldHeight()); _field->setMaxHeight(computeMaxFieldHeight());
_kbShown = false; _kbShown = false;
_kbReplyTo = nullptr; _kbReplyTo = nullptr;
if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyToId && !_editMsgId) { if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_replyTo && !_editMsgId) {
_fieldBarCancel->hide(); _fieldBarCancel->hide();
updateMouseTracking(); updateMouseTracking();
} }
@ -6093,7 +6111,7 @@ void HistoryWidget::botCallbackSent(not_null<HistoryItem*> item) {
session().data().requestItemRepaint(item); session().data().requestItemRepaint(item);
if (_replyToId == item->id) { if (_replyTo.messageId == item->fullId()) {
cancelReply(); cancelReply();
} }
if (_keyboard->singleUse() if (_keyboard->singleUse()
@ -6114,7 +6132,7 @@ int HistoryWidget::computeMaxFieldHeight() const {
- (_groupCallBar ? _groupCallBar->height() : 0) - (_groupCallBar ? _groupCallBar->height() : 0)
- (_requestsBar ? _requestsBar->height() : 0) - (_requestsBar ? _requestsBar->height() : 0)
- ((_editMsgId - ((_editMsgId
|| replyToId() || replyTo()
|| readyToForward() || readyToForward()
|| (_previewData && _previewData->pendingTill >= 0)) || (_previewData && _previewData->pendingTill >= 0))
? st::historyReplyHeight ? st::historyReplyHeight
@ -6175,7 +6193,7 @@ bool HistoryWidget::cornerButtonsHas(HistoryView::CornerButtonType type) {
void HistoryWidget::mousePressEvent(QMouseEvent *e) { void HistoryWidget::mousePressEvent(QMouseEvent *e) {
const auto isReadyToForward = readyToForward(); const auto isReadyToForward = readyToForward();
const auto hasSecondLayer = (_editMsgId const auto hasSecondLayer = (_editMsgId
|| _replyToId || _replyTo
|| isReadyToForward || isReadyToForward
|| _kbReplyTo); || _kbReplyTo);
_replyForwardPressed = hasSecondLayer && QRect( _replyForwardPressed = hasSecondLayer && QRect(
@ -6201,11 +6219,13 @@ void HistoryWidget::mousePressEvent(QMouseEvent *e) {
} else { } else {
_forwardPanel->editOptions(controller()->uiShow()); _forwardPanel->editOptions(controller()->uiShow());
} }
} else if (replyTo() && replyTo().messageId.peer != _peer->id) {
// edit options
} else { } else {
controller()->showPeerHistory( controller()->showPeerHistory(
_peer, _peer,
Window::SectionShow::Way::Forward, Window::SectionShow::Way::Forward,
_editMsgId ? _editMsgId : replyToId()); _editMsgId ? _editMsgId : replyTo().messageId.msg);
} }
} }
} }
@ -6235,7 +6255,7 @@ void HistoryWidget::keyPressEvent(QKeyEvent *e) {
if (item if (item
&& _field->empty() && _field->empty()
&& !_editMsgId && !_editMsgId
&& !_replyToId) { && !_replyTo) {
editMessage(item); editMessage(item);
return; return;
} }
@ -6303,14 +6323,17 @@ void HistoryWidget::handlePeerMigration() {
} }
bool HistoryWidget::replyToPreviousMessage() { bool HistoryWidget::replyToPreviousMessage() {
if (!_history || _editMsgId || _history->isForum()) { if (!_history
|| _editMsgId
|| _history->isForum()
|| (_replyTo && _replyTo.messageId.peer != _history->peer->id)) {
return false; return false;
} }
const auto fullId = FullMsgId( const auto fullId = FullMsgId(
_history->peer->id, _history->peer->id,
_field->isVisible() (_field->isVisible()
? _replyToId ? _replyTo.messageId.msg
: _highlighter.latestSingleHighlightedMsgId()); : _highlighter.latestSingleHighlightedMsgId()));
if (const auto item = session().data().message(fullId)) { if (const auto item = session().data().message(fullId)) {
if (const auto view = item->mainView()) { if (const auto view = item->mainView()) {
if (const auto previousView = view->previousDisplayedInBlocks()) { if (const auto previousView = view->previousDisplayedInBlocks()) {
@ -6334,14 +6357,17 @@ bool HistoryWidget::replyToPreviousMessage() {
} }
bool HistoryWidget::replyToNextMessage() { bool HistoryWidget::replyToNextMessage() {
if (!_history || _editMsgId || _history->isForum()) { if (!_history
|| _editMsgId
|| _history->isForum()
|| (_replyTo && _replyTo.messageId.peer != _history->peer->id)) {
return false; return false;
} }
const auto fullId = FullMsgId( const auto fullId = FullMsgId(
_history->peer->id, _history->peer->id,
_field->isVisible() (_field->isVisible()
? _replyToId ? _replyTo.messageId.msg
: _highlighter.latestSingleHighlightedMsgId()); : _highlighter.latestSingleHighlightedMsgId()));
if (const auto item = session().data().message(fullId)) { if (const auto item = session().data().message(fullId)) {
if (const auto view = item->mainView()) { if (const auto view = item->mainView()) {
if (const auto nextView = view->nextDisplayedInBlocks()) { if (const auto nextView = view->nextDisplayedInBlocks()) {
@ -7024,17 +7050,19 @@ void HistoryWidget::clearFieldText(
setFieldText(TextWithTags(), events, fieldHistoryAction); setFieldText(TextWithTags(), events, fieldHistoryAction);
} }
void HistoryWidget::replyToMessage(FullMsgId itemId) { void HistoryWidget::replyToMessage(FullReplyTo id) {
if (const auto item = session().data().message(itemId)) { if (const auto item = session().data().message(id.messageId)) {
replyToMessage(item); replyToMessage(item, id.quote);
} }
} }
void HistoryWidget::replyToMessage(not_null<HistoryItem*> item) { void HistoryWidget::replyToMessage(
not_null<HistoryItem*> item,
TextWithTags quote) {
if (isJoinChannel()) { if (isJoinChannel()) {
return; return;
} }
_processingReplyId = item->id; _processingReplyTo = { .messageId = item->fullId(), .quote = quote};
_processingReplyItem = item; _processingReplyItem = item;
processReply(); processReply();
} }
@ -7042,14 +7070,13 @@ void HistoryWidget::replyToMessage(not_null<HistoryItem*> item) {
void HistoryWidget::processReply() { void HistoryWidget::processReply() {
const auto processContinue = [=] { const auto processContinue = [=] {
return crl::guard(_list, [=] { return crl::guard(_list, [=] {
if (!_peer || !_processingReplyId) { if (!_peer || !_processingReplyTo) {
return; return;
} else if (!_processingReplyItem) { } else if (!_processingReplyItem) {
_processingReplyItem = _peer->owner().message( _processingReplyItem = _peer->owner().message(
_peer, _processingReplyTo.messageId);
_processingReplyId);
if (!_processingReplyItem) { if (!_processingReplyItem) {
_processingReplyId = 0; _processingReplyTo = {};
} else { } else {
processReply(); processReply();
} }
@ -7057,16 +7084,16 @@ void HistoryWidget::processReply() {
}); });
}; };
const auto processCancel = [=] { const auto processCancel = [=] {
_processingReplyId = 0; _processingReplyTo = {};
_processingReplyItem = nullptr; _processingReplyItem = nullptr;
}; };
if (!_peer || !_processingReplyId) { if (!_peer || !_processingReplyTo) {
return processCancel(); return processCancel();
} else if (!_processingReplyItem) { } else if (!_processingReplyItem) {
session().api().requestMessageData( session().api().requestMessageData(
_peer, session().data().peer(_processingReplyTo.messageId.peer),
_processingReplyId, _processingReplyTo.messageId.msg,
processContinue()); processContinue());
return; return;
} else if (_processingReplyItem->history() == _migrated) { } else if (_processingReplyItem->history() == _migrated) {
@ -7107,7 +7134,7 @@ void HistoryWidget::processReply() {
} }
void HistoryWidget::setReplyFieldsFromProcessing() { void HistoryWidget::setReplyFieldsFromProcessing() {
if (!_processingReplyId || !_processingReplyItem) { if (!_processingReplyTo || !_processingReplyItem) {
return; return;
} }
@ -7116,22 +7143,21 @@ void HistoryWidget::setReplyFieldsFromProcessing() {
_composeSearch->hideAnimated(); _composeSearch->hideAnimated();
} }
const auto id = base::take(_processingReplyId); const auto id = base::take(_processingReplyTo);
const auto item = base::take(_processingReplyItem); const auto item = base::take(_processingReplyItem);
if (_editMsgId) { if (_editMsgId) {
if (const auto localDraft = _history->localDraft({})) { if (const auto localDraft = _history->localDraft({})) {
localDraft->msgId = id; localDraft->reply = id;
} else { } else {
_history->setLocalDraft(std::make_unique<Data::Draft>( _history->setLocalDraft(std::make_unique<Data::Draft>(
TextWithTags(), TextWithTags(),
id, id,
MsgId(),
MessageCursor(), MessageCursor(),
Data::PreviewState::Allowed)); Data::PreviewState::Allowed));
} }
} else { } else {
_replyEditMsg = item; _replyEditMsg = item;
_replyToId = id; _replyTo = id;
updateReplyEditText(_replyEditMsg); updateReplyEditText(_replyEditMsg);
updateCanSendMessage(); updateCanSendMessage();
updateBotKeyboard(); updateBotKeyboard();
@ -7170,11 +7196,10 @@ void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
_send->clearState(); _send->clearState();
} }
if (!_editMsgId) { if (!_editMsgId) {
if (_replyToId || !_field->empty()) { if (_replyTo || !_field->empty()) {
_history->setLocalDraft(std::make_unique<Data::Draft>( _history->setLocalDraft(std::make_unique<Data::Draft>(
_field, _field,
_replyToId, _replyTo,
MsgId(), // topicRootId
_previewState)); _previewState));
} else { } else {
_history->clearLocalDraft({}); _history->clearLocalDraft({});
@ -7198,8 +7223,7 @@ void HistoryWidget::editMessage(not_null<HistoryItem*> item) {
: Data::PreviewState::EmptyOnEdit; : Data::PreviewState::EmptyOnEdit;
_history->setLocalEditDraft(std::make_unique<Data::Draft>( _history->setLocalEditDraft(std::make_unique<Data::Draft>(
editData, editData,
item->id, FullReplyTo{ item->fullId() },
MsgId(), // topicRootId
cursor, cursor,
previewState)); previewState));
applyDraft(); applyDraft();
@ -7261,17 +7285,18 @@ bool HistoryWidget::lastForceReplyReplied(const FullMsgId &replyTo) const {
bool HistoryWidget::lastForceReplyReplied() const { bool HistoryWidget::lastForceReplyReplied() const {
return _peer return _peer
&& _keyboard->forceReply() && _keyboard->forceReply()
&& _keyboard->forMsgId() == FullMsgId(_peer->id, _history->lastKeyboardId) && _keyboard->forMsgId() == replyTo().messageId
&& _keyboard->forMsgId().msg == replyToId(); && (_keyboard->forMsgId()
== FullMsgId(_peer->id, _history->lastKeyboardId));
} }
bool HistoryWidget::cancelReply(bool lastKeyboardUsed) { bool HistoryWidget::cancelReply(bool lastKeyboardUsed) {
bool wasReply = false; bool wasReply = false;
if (_replyToId) { if (_replyTo) {
wasReply = true; wasReply = true;
_processingReplyItem = _replyEditMsg = nullptr; _processingReplyItem = _replyEditMsg = nullptr;
_processingReplyId = _replyToId = 0; _processingReplyTo = _replyTo = FullReplyTo();
mouseMoveEvent(0); mouseMoveEvent(0);
if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_kbReplyTo) { if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !_kbReplyTo) {
_fieldBarCancel->hide(); _fieldBarCancel->hide();
@ -7284,11 +7309,11 @@ bool HistoryWidget::cancelReply(bool lastKeyboardUsed) {
updateControlsGeometry(); updateControlsGeometry();
update(); update();
} else if (const auto localDraft = (_history ? _history->localDraft({}) : nullptr)) { } else if (const auto localDraft = (_history ? _history->localDraft({}) : nullptr)) {
if (localDraft->msgId) { if (localDraft->reply) {
if (localDraft->textWithTags.text.isEmpty()) { if (localDraft->textWithTags.text.isEmpty()) {
_history->clearLocalDraft({}); _history->clearLocalDraft({});
} else { } else {
localDraft->msgId = 0; localDraft->reply = {};
} }
} }
} }
@ -7344,7 +7369,7 @@ void HistoryWidget::cancelEdit() {
saveDraft(); saveDraft();
mouseMoveEvent(nullptr); mouseMoveEvent(nullptr);
if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !replyToId()) { if (!readyToForward() && (!_previewData || _previewData->pendingTill < 0) && !replyTo()) {
_fieldBarCancel->hide(); _fieldBarCancel->hide();
updateMouseTracking(); updateMouseTracking();
} }
@ -7376,7 +7401,7 @@ void HistoryWidget::cancelFieldAreaState() {
cancelEdit(); cancelEdit();
} else if (readyToForward()) { } else if (readyToForward()) {
_history->setForwardDraft(MsgId(), {}); _history->setForwardDraft(MsgId(), {});
} else if (_replyToId) { } else if (_replyTo) {
cancelReply(); cancelReply();
} else if (_kbReplyTo) { } else if (_kbReplyTo) {
toggleKeyboard(); toggleKeyboard();
@ -7512,7 +7537,7 @@ void HistoryWidget::updatePreview() {
preview.description, preview.description,
Ui::DialogTextOptions()); Ui::DialogTextOptions());
} }
} else if (!readyToForward() && !replyToId() && !_editMsgId) { } else if (!readyToForward() && !replyTo() && !_editMsgId) {
_fieldBarCancel->hide(); _fieldBarCancel->hide();
updateMouseTracking(); updateMouseTracking();
} }
@ -7579,7 +7604,7 @@ bool HistoryWidget::updateCanSendMessage() {
if (!_peer) { if (!_peer) {
return false; return false;
} }
const auto replyTo = (_replyToId && !_editMsgId) ? _replyEditMsg : 0; const auto replyTo = (_replyTo && !_editMsgId) ? _replyEditMsg : 0;
const auto topic = replyTo ? replyTo->topic() : nullptr; const auto topic = replyTo ? replyTo->topic() : nullptr;
const auto allWithoutPolls = Data::AllSendRestrictions() const auto allWithoutPolls = Data::AllSendRestrictions()
& ~ChatRestriction::SendPolls; & ~ChatRestriction::SendPolls;
@ -7657,7 +7682,7 @@ void HistoryWidget::escape() {
} }
} else if (!_fieldAutocomplete->isHidden()) { } else if (!_fieldAutocomplete->isHidden()) {
_fieldAutocomplete->hideAnimated(); _fieldAutocomplete->hideAnimated();
} else if (_replyToId && _field->getTextWithTags().text.isEmpty()) { } else if (_replyTo && _field->getTextWithTags().text.isEmpty()) {
cancelReply(); cancelReply();
} else if (auto &voice = _voiceRecordBar; voice->isActive()) { } else if (auto &voice = _voiceRecordBar; voice->isActive()) {
voice->showDiscardBox(nullptr, anim::type::normal); voice->showDiscardBox(nullptr, anim::type::normal);
@ -7752,7 +7777,8 @@ void HistoryWidget::messageDataReceived(
MsgId msgId) { MsgId msgId) {
if (!_peer || _peer != peer || !msgId) { if (!_peer || _peer != peer || !msgId) {
return; return;
} else if (_editMsgId == msgId || _replyToId == msgId) { } else if (_editMsgId == msgId
|| (_replyTo.messageId == FullMsgId(peer->id, msgId))) {
updateReplyEditTexts(true); updateReplyEditTexts(true);
} }
} }
@ -7775,14 +7801,14 @@ void HistoryWidget::updateReplyEditText(not_null<HistoryItem*> item) {
void HistoryWidget::updateReplyEditTexts(bool force) { void HistoryWidget::updateReplyEditTexts(bool force) {
if (!force) { if (!force) {
if (_replyEditMsg || (!_editMsgId && !_replyToId)) { if (_replyEditMsg || (!_editMsgId && !_replyTo)) {
return; return;
} }
} }
if (!_replyEditMsg && _peer) { if (!_replyEditMsg && _peer) {
_replyEditMsg = session().data().message( _replyEditMsg = session().data().message(
_peer->id, _editMsgId ? _peer->id : _replyTo.messageId.peer,
_editMsgId ? _editMsgId : _replyToId); _editMsgId ? _editMsgId : _replyTo.messageId.msg);
} }
if (_replyEditMsg) { if (_replyEditMsg) {
const auto media = _replyEditMsg->media(); const auto media = _replyEditMsg->media();
@ -7828,7 +7854,7 @@ void HistoryWidget::updateForwarding() {
void HistoryWidget::updateReplyToName() { void HistoryWidget::updateReplyToName() {
if (_editMsgId) { if (_editMsgId) {
return; return;
} else if (!_replyEditMsg && (_replyToId || !_kbReplyTo)) { } else if (!_replyEditMsg && (_replyTo || !_kbReplyTo)) {
return; return;
} }
const auto from = [&] { const auto from = [&] {
@ -7862,8 +7888,8 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) {
auto backy = _field->y() - st::historySendPadding; auto backy = _field->y() - st::historySendPadding;
auto backh = fieldHeight() + 2 * st::historySendPadding; auto backh = fieldHeight() + 2 * st::historySendPadding;
auto hasForward = readyToForward(); auto hasForward = readyToForward();
auto drawMsgText = (_editMsgId || _replyToId) ? _replyEditMsg : _kbReplyTo; auto drawMsgText = (_editMsgId || _replyTo) ? _replyEditMsg : _kbReplyTo;
if (_editMsgId || _replyToId || (!hasForward && _kbReplyTo)) { if (_editMsgId || _replyTo || (!hasForward && _kbReplyTo)) {
if (!_editMsgId if (!_editMsgId
&& drawMsgText && drawMsgText
&& (_replyToNameVersion && (_replyToNameVersion
@ -7898,7 +7924,7 @@ void HistoryWidget::drawField(Painter &p, const QRect &rect) {
}); });
} }
if (_editMsgId || _replyToId || (!hasForward && _kbReplyTo)) { if (_editMsgId || _replyTo || (!hasForward && _kbReplyTo)) {
const auto now = crl::now(); const auto now = crl::now();
const auto paused = p.inactive(); const auto paused = p.inactive();
const auto pausedSpoiler = paused || On(PowerSaving::kChatSpoiler); const auto pausedSpoiler = paused || On(PowerSaving::kChatSpoiler);
@ -8097,7 +8123,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) {
const auto restrictionHidden = fieldOrDisabledShown() const auto restrictionHidden = fieldOrDisabledShown()
|| isRecording(); || isRecording();
if (restrictionHidden if (restrictionHidden
|| replyToId() || replyTo()
|| readyToForward() || readyToForward()
|| _kbShown) { || _kbShown) {
drawField(p, clip); drawField(p, clip);

View file

@ -182,12 +182,14 @@ public:
MessageIdsList getSelectedItems() const; MessageIdsList getSelectedItems() const;
void itemEdited(not_null<HistoryItem*> item); void itemEdited(not_null<HistoryItem*> item);
void replyToMessage(FullMsgId itemId); void replyToMessage(FullReplyTo id);
void replyToMessage(not_null<HistoryItem*> item); void replyToMessage(
not_null<HistoryItem*> item,
TextWithTags quote = {});
void editMessage(FullMsgId itemId); void editMessage(FullMsgId itemId);
void editMessage(not_null<HistoryItem*> item); void editMessage(not_null<HistoryItem*> item);
MsgId replyToId() const; [[nodiscard]] FullReplyTo replyTo() const;
bool lastForceReplyReplied(const FullMsgId &replyTo) const; bool lastForceReplyReplied(const FullMsgId &replyTo) const;
bool lastForceReplyReplied() const; bool lastForceReplyReplied() const;
bool cancelReply(bool lastKeyboardUsed = false); bool cancelReply(bool lastKeyboardUsed = false);
@ -204,7 +206,7 @@ public:
void escape(); void escape();
void sendBotCommand(const Bot::SendCommandRequest &request); void sendBotCommand(const Bot::SendCommandRequest &request);
void hideSingleUseKeyboard(PeerData *peer, MsgId replyTo); void hideSingleUseKeyboard(FullMsgId replyToId);
bool insertBotCommand(const QString &cmd); bool insertBotCommand(const QString &cmd);
bool eventFilter(QObject *obj, QEvent *e) override; bool eventFilter(QObject *obj, QEvent *e) override;
@ -633,11 +635,11 @@ private:
void searchInChat(); void searchInChat();
MTP::Sender _api; MTP::Sender _api;
MsgId _replyToId = 0; FullReplyTo _replyTo;
Ui::Text::String _replyToName; Ui::Text::String _replyToName;
int _replyToNameVersion = 0; int _replyToNameVersion = 0;
MsgId _processingReplyId = 0; FullReplyTo _processingReplyTo;
HistoryItem *_processingReplyItem = nullptr; HistoryItem *_processingReplyItem = nullptr;
MsgId _editMsgId = 0; MsgId _editMsgId = 0;

View file

@ -353,7 +353,7 @@ public:
void init(); void init();
void editMessage(FullMsgId id, bool photoEditAllowed = false); void editMessage(FullMsgId id, bool photoEditAllowed = false);
void replyToMessage(FullMsgId id); void replyToMessage(FullReplyTo id);
void updateForwarding( void updateForwarding(
Data::Thread *thread, Data::Thread *thread,
Data::ResolvedForwardDraft items); Data::ResolvedForwardDraft items);
@ -367,7 +367,7 @@ public:
[[nodiscard]] bool isEditingMessage() const; [[nodiscard]] bool isEditingMessage() const;
[[nodiscard]] bool readyToForward() const; [[nodiscard]] bool readyToForward() const;
[[nodiscard]] const HistoryItemsList &forwardItems() const; [[nodiscard]] const HistoryItemsList &forwardItems() const;
[[nodiscard]] FullMsgId replyingToMessage() const; [[nodiscard]] FullReplyTo replyingToMessage() const;
[[nodiscard]] FullMsgId editMsgId() const; [[nodiscard]] FullMsgId editMsgId() const;
[[nodiscard]] rpl::producer<FullMsgId> editMsgIdValue() const; [[nodiscard]] rpl::producer<FullMsgId> editMsgIdValue() const;
[[nodiscard]] rpl::producer<FullMsgId> scrollToItemRequests() const; [[nodiscard]] rpl::producer<FullMsgId> scrollToItemRequests() const;
@ -375,7 +375,7 @@ public:
[[nodiscard]] MessageToEdit queryToEdit(); [[nodiscard]] MessageToEdit queryToEdit();
[[nodiscard]] WebPageId webPageId() const; [[nodiscard]] WebPageId webPageId() const;
[[nodiscard]] MsgId getDraftMessageId() const; [[nodiscard]] FullReplyTo getDraftReply() const;
[[nodiscard]] rpl::producer<> editCancelled() const { [[nodiscard]] rpl::producer<> editCancelled() const {
return _editCancelled.events(); return _editCancelled.events();
} }
@ -425,7 +425,7 @@ private:
rpl::lifetime _previewLifetime; rpl::lifetime _previewLifetime;
rpl::variable<FullMsgId> _editMsgId; rpl::variable<FullMsgId> _editMsgId;
rpl::variable<FullMsgId> _replyToId; rpl::variable<FullReplyTo> _replyTo;
std::unique_ptr<ForwardPanel> _forwardPanel; std::unique_ptr<ForwardPanel> _forwardPanel;
rpl::producer<> _toForwardUpdated; rpl::producer<> _toForwardUpdated;
@ -508,14 +508,14 @@ void FieldHeader::init() {
_editMsgId.value( _editMsgId.value(
) | rpl::start_with_next([=](FullMsgId value) { ) | rpl::start_with_next([=](FullMsgId value) {
const auto shown = value ? value : _replyToId.current(); const auto shown = value ? value : _replyTo.current().messageId;
setShownMessage(_data->message(shown)); setShownMessage(_data->message(shown));
}, lifetime()); }, lifetime());
_replyToId.value( _replyTo.value(
) | rpl::start_with_next([=](FullMsgId value) { ) | rpl::start_with_next([=](const FullReplyTo &value) {
if (!_editMsgId.current()) { if (!_editMsgId.current()) {
setShownMessage(_data->message(value)); setShownMessage(_data->message(value.messageId));
} }
}, lifetime()); }, lifetime());
@ -529,7 +529,7 @@ void FieldHeader::init() {
if (_editMsgId.current() == update.item->fullId()) { if (_editMsgId.current() == update.item->fullId()) {
_editCancelled.fire({}); _editCancelled.fire({});
} }
if (_replyToId.current() == update.item->fullId()) { if (_replyTo.current().messageId == update.item->fullId()) {
_replyCancelled.fire({}); _replyCancelled.fire({});
} }
} else { } else {
@ -545,7 +545,7 @@ void FieldHeader::init() {
_editCancelled.fire({}); _editCancelled.fire({});
} else if (readyToForward()) { } else if (readyToForward()) {
_forwardCancelled.fire({}); _forwardCancelled.fire({});
} else if (_replyToId.current()) { } else if (_replyTo.current()) {
_replyCancelled.fire({}); _replyCancelled.fire({});
} }
updateVisible(); updateVisible();
@ -624,7 +624,7 @@ void FieldHeader::init() {
} else { } else {
auto id = isEditingMessage() auto id = isEditingMessage()
? _editMsgId.current() ? _editMsgId.current()
: replyingToMessage(); : replyingToMessage().messageId;
_scrollToItemRequests.fire(std::move(id)); _scrollToItemRequests.fire(std::move(id));
} }
} }
@ -692,16 +692,18 @@ void FieldHeader::setShownMessage(HistoryItem *item) {
} }
void FieldHeader::resolveMessageData() { void FieldHeader::resolveMessageData() {
const auto id = (isEditingMessage() ? _editMsgId : _replyToId).current(); const auto id = isEditingMessage()
? _editMsgId.current()
: _replyTo.current().messageId;
if (!id) { if (!id) {
return; return;
} }
const auto peer = _data->peer(id.peer); const auto peer = _data->peer(id.peer);
const auto itemId = id.msg; const auto itemId = id.msg;
const auto callback = crl::guard(this, [=] { const auto callback = crl::guard(this, [=] {
const auto now = (isEditingMessage() const auto now = isEditingMessage()
? _editMsgId ? _editMsgId.current()
: _replyToId).current(); : _replyTo.current().messageId;
if (now == id && !_shownMessage) { if (now == id && !_shownMessage) {
if (const auto message = _data->message(peer, itemId)) { if (const auto message = _data->message(peer, itemId)) {
setShownMessage(message); setShownMessage(message);
@ -950,8 +952,8 @@ const HistoryItemsList &FieldHeader::forwardItems() const {
return _forwardPanel->items(); return _forwardPanel->items();
} }
FullMsgId FieldHeader::replyingToMessage() const { FullReplyTo FieldHeader::replyingToMessage() const {
return _replyToId.current(); return _replyTo.current();
} }
bool FieldHeader::hasPreview() const { bool FieldHeader::hasPreview() const {
@ -962,8 +964,10 @@ WebPageId FieldHeader::webPageId() const {
return hasPreview() ? _preview.data->id : CancelledWebPageId; return hasPreview() ? _preview.data->id : CancelledWebPageId;
} }
MsgId FieldHeader::getDraftMessageId() const { FullReplyTo FieldHeader::getDraftReply() const {
return (isEditingMessage() ? _editMsgId : _replyToId).current().msg; return isEditingMessage()
? FullReplyTo{ _editMsgId.current() }
: _replyTo.current();
} }
void FieldHeader::updateControlsGeometry(QSize size) { void FieldHeader::updateControlsGeometry(QSize size) {
@ -992,8 +996,8 @@ void FieldHeader::editMessage(FullMsgId id, bool photoEditAllowed) {
update(); update();
} }
void FieldHeader::replyToMessage(FullMsgId id) { void FieldHeader::replyToMessage(FullReplyTo id) {
_replyToId = id; _replyTo = id;
} }
void FieldHeader::updateForwarding( void FieldHeader::updateForwarding(
@ -1156,6 +1160,7 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) {
} }
unregisterDraftSources(); unregisterDraftSources();
_history = history; _history = history;
_topicRootId = args.topicRootId;
_historyLifetime.destroy(); _historyLifetime.destroy();
_header->setHistory(args); _header->setHistory(args);
registerDraftSource(); registerDraftSource();
@ -1193,8 +1198,10 @@ void ComposeControls::setHistory(SetHistoryArgs &&args) {
orderControls(); orderControls();
} }
void ComposeControls::setCurrentDialogsEntryState(Dialogs::EntryState state) { void ComposeControls::setCurrentDialogsEntryState(
Dialogs::EntryState state) {
unregisterDraftSources(); unregisterDraftSources();
state.currentReplyTo.topicRootId = _topicRootId;
_currentDialogsEntryState = state; _currentDialogsEntryState = state;
updateForwarding(); updateForwarding();
registerDraftSource(); registerDraftSource();
@ -1472,16 +1479,12 @@ void ComposeControls::saveFieldToHistoryLocalDraft() {
if (!_history || !key) { if (!_history || !key) {
return; return;
} }
const auto id = _header->getDraftMessageId(); const auto id = _header->getDraftReply();
if (_preview && (id || !_field->empty())) { if (_preview && (id || !_field->empty())) {
const auto key = draftKeyCurrent(); const auto key = draftKeyCurrent();
_history->setDraft( _history->setDraft(
key, key,
std::make_unique<Data::Draft>( std::make_unique<Data::Draft>(_field, id, _preview->state()));
_field,
_header->getDraftMessageId(),
key.topicRootId(),
_preview->state()));
} else { } else {
_history->clearDraft(draftKeyCurrent()); _history->clearDraft(draftKeyCurrent());
} }
@ -1757,7 +1760,7 @@ void ComposeControls::initKeyHandler() {
} }
} }
_replyNextRequests.fire({ _replyNextRequests.fire({
.replyId = replyingToMessage(), .replyId = replyingToMessage().messageId,
.direction = (isDown .direction = (isDown
? ReplyNextRequest::Direction::Next ? ReplyNextRequest::Direction::Next
: ReplyNextRequest::Direction::Previous) : ReplyNextRequest::Direction::Previous)
@ -2037,8 +2040,8 @@ Data::DraftKey ComposeControls::draftKey(DraftType type) const {
case Section::History: case Section::History:
case Section::Replies: case Section::Replies:
return (type == DraftType::Edit) return (type == DraftType::Edit)
? Key::LocalEdit(_currentDialogsEntryState.rootId) ? Key::LocalEdit(_topicRootId)
: Key::Local(_currentDialogsEntryState.rootId); : Key::Local(_topicRootId);
case Section::Scheduled: case Section::Scheduled:
return (type == DraftType::Edit) return (type == DraftType::Edit)
? Key::ScheduledEdit() ? Key::ScheduledEdit()
@ -2102,7 +2105,7 @@ void ComposeControls::registerDraftSource() {
if (key != Data::DraftKey::None()) { if (key != Data::DraftKey::None()) {
const auto draft = [=] { const auto draft = [=] {
return Storage::MessageDraft{ return Storage::MessageDraft{
_header->getDraftMessageId(), _header->getDraftReply(),
_field->getTextWithTags(), _field->getTextWithTags(),
_preview->state(), _preview->state(),
}; };
@ -2150,8 +2153,8 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
const auto draft = editDraft const auto draft = editDraft
? editDraft ? editDraft
: _history->draft(draftKey(DraftType::Normal)); : _history->draft(draftKey(DraftType::Normal));
const auto editingId = (draft == editDraft) const auto editingId = (draft && draft == editDraft)
? FullMsgId{ _history->peer->id, draft ? draft->msgId : 0 } ? draft->reply.messageId
: FullMsgId(); : FullMsgId();
InvokeQueued(_autocomplete.get(), [=] { updateStickersByEmoji(); }); InvokeQueued(_autocomplete.get(), [=] { updateStickersByEmoji(); });
@ -2232,7 +2235,7 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
} else { } else {
_canReplaceMedia = false; _canReplaceMedia = false;
_photoEditMedia = nullptr; _photoEditMedia = nullptr;
_header->replyToMessage({ _history->peer->id, draft->msgId }); _header->replyToMessage(draft->reply);
if (_header->replyingToMessage()) { if (_header->replyingToMessage()) {
cancelForward(); cancelForward();
} }
@ -2241,9 +2244,7 @@ void ComposeControls::applyDraft(FieldHistoryAction fieldHistoryAction) {
} }
void ComposeControls::cancelForward() { void ComposeControls::cancelForward() {
_history->setForwardDraft( _history->setForwardDraft(_topicRootId, {});
_currentDialogsEntryState.rootId,
{});
updateForwarding(); updateForwarding();
} }
@ -2840,8 +2841,7 @@ void ComposeControls::toggleTabbedSelectorMode() {
&& !_regularWindow->adaptive().isOneColumn()) { && !_regularWindow->adaptive().isOneColumn()) {
Core::App().settings().setTabbedSelectorSectionEnabled(true); Core::App().settings().setTabbedSelectorSectionEnabled(true);
Core::App().saveSettingsDelayed(); Core::App().saveSettingsDelayed();
const auto topic = _history->peer->forumTopicFor( const auto topic = _history->peer->forumTopicFor(_topicRootId);
_currentDialogsEntryState.rootId);
pushTabbedSelectorToThirdSection( pushTabbedSelectorToThirdSection(
(topic ? topic : (Data::Thread*)_history), (topic ? topic : (Data::Thread*)_history),
Window::SectionShow::Way::ClearStack); Window::SectionShow::Way::ClearStack);
@ -2900,8 +2900,10 @@ void ComposeControls::editMessage(not_null<HistoryItem*> item) {
key, key,
std::make_unique<Data::Draft>( std::make_unique<Data::Draft>(
editData, editData,
item->id, FullReplyTo{
key.topicRootId(), .messageId = item->fullId(),
.topicRootId = key.topicRootId(),
},
cursor, cursor,
previewState)); previewState));
applyDraft(); applyDraft();
@ -2968,25 +2970,26 @@ void ComposeControls::maybeCancelEditMessage() {
} }
} }
void ComposeControls::replyToMessage(FullMsgId id) { void ComposeControls::replyToMessage(FullReplyTo id) {
Expects(_history != nullptr); Expects(_history != nullptr);
Expects(draftKeyCurrent() != Data::DraftKey::None()); Expects(draftKeyCurrent() != Data::DraftKey::None());
id.topicRootId = _topicRootId;
if (!id) { if (!id) {
cancelReplyMessage(); cancelReplyMessage();
return; return;
} }
if (isEditingMessage()) { if (isEditingMessage()) {
const auto key = draftKey(DraftType::Normal); const auto key = draftKey(DraftType::Normal);
Assert(key.topicRootId() == id.topicRootId);
if (const auto localDraft = _history->draft(key)) { if (const auto localDraft = _history->draft(key)) {
localDraft->msgId = id.msg; localDraft->reply = id;
} else { } else {
_history->setDraft( _history->setDraft(
key, key,
std::make_unique<Data::Draft>( std::make_unique<Data::Draft>(
TextWithTags(), TextWithTags(),
id.msg, id,
key.topicRootId(),
MessageCursor(), MessageCursor(),
Data::PreviewState::Allowed)); Data::PreviewState::Allowed));
} }
@ -3008,11 +3011,11 @@ void ComposeControls::cancelReplyMessage() {
if (_history) { if (_history) {
const auto key = draftKey(DraftType::Normal); const auto key = draftKey(DraftType::Normal);
if (const auto localDraft = _history->draft(key)) { if (const auto localDraft = _history->draft(key)) {
if (localDraft->msgId) { if (localDraft->reply.messageId) {
if (localDraft->textWithTags.text.isEmpty()) { if (localDraft->textWithTags.text.isEmpty()) {
_history->clearDraft(key); _history->clearDraft(key);
} else { } else {
localDraft->msgId = 0; localDraft->reply = {};
} }
} }
} }
@ -3025,7 +3028,7 @@ void ComposeControls::cancelReplyMessage() {
} }
void ComposeControls::updateForwarding() { void ComposeControls::updateForwarding() {
const auto rootId = _currentDialogsEntryState.rootId; const auto rootId = _topicRootId;
const auto thread = (_history && rootId) const auto thread = (_history && rootId)
? _history->peer->forumTopicFor(rootId) ? _history->peer->forumTopicFor(rootId)
: (Data::Thread*)_history; : (Data::Thread*)_history;
@ -3118,7 +3121,7 @@ void ComposeControls::initForwardProcess() {
) | rpl::start_with_next([=](const Data::EntryUpdate &update) { ) | rpl::start_with_next([=](const Data::EntryUpdate &update) {
if (const auto topic = update.entry->asTopic()) { if (const auto topic = update.entry->asTopic()) {
if (topic->history() == _history if (topic->history() == _history
&& topic->rootId() == _currentDialogsEntryState.rootId) { && topic->rootId() == _topicRootId) {
updateForwarding(); updateForwarding();
} }
} }
@ -3145,8 +3148,10 @@ bool ComposeControls::isEditingMessage() const {
return _header->isEditingMessage(); return _header->isEditingMessage();
} }
FullMsgId ComposeControls::replyingToMessage() const { FullReplyTo ComposeControls::replyingToMessage() const {
return _header->replyingToMessage(); auto result = _header->replyingToMessage();
result.topicRootId = _topicRootId;
return result;
} }
bool ComposeControls::readyToForward() const { bool ComposeControls::readyToForward() const {

View file

@ -185,7 +185,7 @@ public:
[[nodiscard]] bool isEditingMessage() const; [[nodiscard]] bool isEditingMessage() const;
[[nodiscard]] bool readyToForward() const; [[nodiscard]] bool readyToForward() const;
[[nodiscard]] const HistoryItemsList &forwardItems() const; [[nodiscard]] const HistoryItemsList &forwardItems() const;
[[nodiscard]] FullMsgId replyingToMessage() const; [[nodiscard]] FullReplyTo replyingToMessage() const;
[[nodiscard]] bool preventsClose(Fn<void()> &&continueCallback) const; [[nodiscard]] bool preventsClose(Fn<void()> &&continueCallback) const;
@ -198,7 +198,7 @@ public:
void cancelEditMessage(); void cancelEditMessage();
void maybeCancelEditMessage(); // Confirm if changed and cancel. void maybeCancelEditMessage(); // Confirm if changed and cancel.
void replyToMessage(FullMsgId id); void replyToMessage(FullReplyTo id);
void cancelReplyMessage(); void cancelReplyMessage();
void updateForwarding(); void updateForwarding();
@ -345,6 +345,7 @@ private:
rpl::event_stream<ChatHelpers::FileChosen> _stickerOrEmojiChosen; rpl::event_stream<ChatHelpers::FileChosen> _stickerOrEmojiChosen;
History *_history = nullptr; History *_history = nullptr;
MsgId _topicRootId = 0;
Fn<bool()> _showSlowmodeError; Fn<bool()> _showSlowmodeError;
Fn<Api::SendAction()> _sendActionFactory; Fn<Api::SendAction()> _sendActionFactory;
rpl::variable<int> _slowmodeSecondsLeft; rpl::variable<int> _slowmodeSecondsLeft;

View file

@ -117,7 +117,7 @@ void ForwardPanel::checkTexts() {
: kNameNoCaptionsVersion; : kNameNoCaptionsVersion;
if (keepNames) { if (keepNames) {
for (const auto item : _data.items) { for (const auto item : _data.items) {
if (const auto from = item->senderOriginal()) { if (const auto from = item->originalSender()) {
version += from->nameVersion(); version += from->nameVersion();
} else if (const auto info = item->hiddenSenderInfo()) { } else if (const auto info = item->hiddenSenderInfo()) {
++version; ++version;
@ -154,7 +154,7 @@ void ForwardPanel::updateTexts() {
auto names = std::vector<QString>(); auto names = std::vector<QString>();
names.reserve(_data.items.size()); names.reserve(_data.items.size());
for (const auto item : _data.items) { for (const auto item : _data.items) {
if (const auto from = item->senderOriginal()) { if (const auto from = item->originalSender()) {
if (!insertedPeers.contains(from)) { if (!insertedPeers.contains(from)) {
insertedPeers.emplace(from); insertedPeers.emplace(from);
names.push_back(from->shortName()); names.push_back(from->shortName());

View file

@ -655,7 +655,7 @@ BottomInfo::Data BottomInfoDataFromMessage(not_null<Message*> message) {
} }
if (const auto msgsigned = item->Get<HistoryMessageSigned>()) { if (const auto msgsigned = item->Get<HistoryMessageSigned>()) {
if (!msgsigned->isAnonymousRank) { if (!msgsigned->isAnonymousRank) {
result.author = msgsigned->author; result.author = msgsigned->postAuthor;
} }
} }
if (message->displayedEditDate()) { if (message->displayedEditDate()) {

View file

@ -601,7 +601,7 @@ bool AddReplyToMessageAction(
if (!item) { if (!item) {
return; return;
} }
list->replyToMessageRequestNotify(item->fullId()); list->replyToMessageRequestNotify({ item->fullId() });
}, &st::menuIconReply); }, &st::menuIconReply);
return true; return true;
} }

View file

@ -68,10 +68,10 @@ Element *MousedElement/* = nullptr*/;
HistoryMessageForwarded *prevForwarded, HistoryMessageForwarded *prevForwarded,
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
HistoryMessageForwarded *forwarded) { HistoryMessageForwarded *forwarded) {
const auto sender = previous->senderOriginal(); const auto sender = previous->originalSender();
if ((prevForwarded != nullptr) != (forwarded != nullptr)) { if ((prevForwarded != nullptr) != (forwarded != nullptr)) {
return false; return false;
} else if (sender != item->senderOriginal()) { } else if (sender != item->originalSender()) {
return false; return false;
} else if (!prevForwarded || sender) { } else if (!prevForwarded || sender) {
return true; return true;
@ -178,7 +178,7 @@ bool DefaultElementDelegate::elementIsChatWide() {
return false; return false;
} }
void DefaultElementDelegate::elementReplyTo(const FullMsgId &to) { void DefaultElementDelegate::elementReplyTo(const FullReplyTo &to) {
} }
void DefaultElementDelegate::elementStartInteraction( void DefaultElementDelegate::elementStartInteraction(
@ -275,8 +275,10 @@ QString DateTooltipText(not_null<Element*> view) {
} }
if (view->isSignedAuthorElided()) { if (view->isSignedAuthorElided()) {
if (const auto msgsigned = item->Get<HistoryMessageSigned>()) { if (const auto msgsigned = item->Get<HistoryMessageSigned>()) {
dateText += '\n' dateText += '\n' + tr::lng_signed_author(
+ tr::lng_signed_author(tr::now, lt_user, msgsigned->author); tr::now,
lt_user,
msgsigned->postAuthor);
} }
} }
return dateText; return dateText;
@ -1447,7 +1449,7 @@ void Element::unloadHeavyPart() {
_heavyCustomEmoji = false; _heavyCustomEmoji = false;
_text.unloadPersistentAnimation(); _text.unloadPersistentAnimation();
if (const auto reply = data()->Get<HistoryMessageReply>()) { if (const auto reply = data()->Get<HistoryMessageReply>()) {
reply->replyToText.unloadPersistentAnimation(); reply->unloadPersistentAnimation();
} }
} }
} }

View file

@ -100,7 +100,7 @@ public:
virtual void elementHandleViaClick(not_null<UserData*> bot) = 0; virtual void elementHandleViaClick(not_null<UserData*> bot) = 0;
virtual bool elementIsChatWide() = 0; virtual bool elementIsChatWide() = 0;
virtual not_null<Ui::PathShiftGradient*> elementPathShiftGradient() = 0; virtual not_null<Ui::PathShiftGradient*> elementPathShiftGradient() = 0;
virtual void elementReplyTo(const FullMsgId &to) = 0; virtual void elementReplyTo(const FullReplyTo &to) = 0;
virtual void elementStartInteraction(not_null<const Element*> view) = 0; virtual void elementStartInteraction(not_null<const Element*> view) = 0;
virtual void elementStartPremium( virtual void elementStartPremium(
not_null<const Element*> view, not_null<const Element*> view,
@ -149,7 +149,7 @@ public:
const FullMsgId &context) override; const FullMsgId &context) override;
void elementHandleViaClick(not_null<UserData*> bot) override; void elementHandleViaClick(not_null<UserData*> bot) override;
bool elementIsChatWide() override; bool elementIsChatWide() override;
void elementReplyTo(const FullMsgId &to) override; void elementReplyTo(const FullReplyTo &to) override;
void elementStartInteraction(not_null<const Element*> view) override; void elementStartInteraction(not_null<const Element*> view) override;
void elementStartPremium( void elementStartPremium(
not_null<const Element*> view, not_null<const Element*> view,

View file

@ -1735,7 +1735,7 @@ not_null<Ui::PathShiftGradient*> ListWidget::elementPathShiftGradient() {
return _pathGradient.get(); return _pathGradient.get();
} }
void ListWidget::elementReplyTo(const FullMsgId &to) { void ListWidget::elementReplyTo(const FullReplyTo &to) {
replyToMessageRequestNotify(to); replyToMessageRequestNotify(to);
} }
@ -2474,7 +2474,7 @@ void ListWidget::mouseDoubleClickEvent(QMouseEvent *e) {
mouseActionCancel(); mouseActionCancel();
switch (CurrentQuickAction()) { switch (CurrentQuickAction()) {
case DoubleClickQuickAction::Reply: { case DoubleClickQuickAction::Reply: {
replyToMessageRequestNotify(_overElement->data()->fullId()); replyToMessageRequestNotify({ _overElement->data()->fullId() });
} break; } break;
case DoubleClickQuickAction::React: { case DoubleClickQuickAction::React: {
toggleFavoriteReaction(_overElement); toggleFavoriteReaction(_overElement);
@ -3855,12 +3855,12 @@ bool ListWidget::lastMessageEditRequestNotify() const {
} }
} }
rpl::producer<FullMsgId> ListWidget::replyToMessageRequested() const { rpl::producer<FullReplyTo> ListWidget::replyToMessageRequested() const {
return _requestedToReplyToMessage.events(); return _requestedToReplyToMessage.events();
} }
void ListWidget::replyToMessageRequestNotify(FullMsgId item) { void ListWidget::replyToMessageRequestNotify(FullReplyTo id) {
_requestedToReplyToMessage.fire(std::move(item)); _requestedToReplyToMessage.fire(std::move(id));
} }
rpl::producer<FullMsgId> ListWidget::readMessageRequested() const { rpl::producer<FullMsgId> ListWidget::readMessageRequested() const {
@ -3878,10 +3878,10 @@ void ListWidget::replyNextMessage(FullMsgId fullId, bool next) {
if (!view->data()->isRegular()) { if (!view->data()->isRegular()) {
return replyNextMessage(newFullId, next); return replyNextMessage(newFullId, next);
} }
replyToMessageRequestNotify(newFullId); replyToMessageRequestNotify({ newFullId });
_requestedToShowMessage.fire_copy(newFullId); _requestedToShowMessage.fire_copy(newFullId);
} else { } else {
replyToMessageRequestNotify(FullMsgId()); replyToMessageRequestNotify({});
_highlighter.clear(); _highlighter.clear();
} }
}; };

View file

@ -277,8 +277,8 @@ public:
[[nodiscard]] rpl::producer<FullMsgId> editMessageRequested() const; [[nodiscard]] rpl::producer<FullMsgId> editMessageRequested() const;
void editMessageRequestNotify(FullMsgId item) const; void editMessageRequestNotify(FullMsgId item) const;
[[nodiscard]] bool lastMessageEditRequestNotify() const; [[nodiscard]] bool lastMessageEditRequestNotify() const;
[[nodiscard]] rpl::producer<FullMsgId> replyToMessageRequested() const; [[nodiscard]] rpl::producer<FullReplyTo> replyToMessageRequested() const;
void replyToMessageRequestNotify(FullMsgId item); void replyToMessageRequestNotify(FullReplyTo id);
[[nodiscard]] rpl::producer<FullMsgId> readMessageRequested() const; [[nodiscard]] rpl::producer<FullMsgId> readMessageRequested() const;
[[nodiscard]] rpl::producer<FullMsgId> showMessageRequested() const; [[nodiscard]] rpl::producer<FullMsgId> showMessageRequested() const;
void replyNextMessage(FullMsgId fullId, bool next = true); void replyNextMessage(FullMsgId fullId, bool next = true);
@ -323,7 +323,7 @@ public:
void elementHandleViaClick(not_null<UserData*> bot) override; void elementHandleViaClick(not_null<UserData*> bot) override;
bool elementIsChatWide() override; bool elementIsChatWide() override;
not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override; not_null<Ui::PathShiftGradient*> elementPathShiftGradient() override;
void elementReplyTo(const FullMsgId &to) override; void elementReplyTo(const FullReplyTo &to) override;
void elementStartInteraction(not_null<const Element*> view) override; void elementStartInteraction(not_null<const Element*> view) override;
void elementStartPremium( void elementStartPremium(
not_null<const Element*> view, not_null<const Element*> view,
@ -735,7 +735,7 @@ private:
base::Timer _touchScrollTimer; base::Timer _touchScrollTimer;
rpl::event_stream<FullMsgId> _requestedToEditMessage; rpl::event_stream<FullMsgId> _requestedToEditMessage;
rpl::event_stream<FullMsgId> _requestedToReplyToMessage; rpl::event_stream<FullReplyTo> _requestedToReplyToMessage;
rpl::event_stream<FullMsgId> _requestedToReadMessage; rpl::event_stream<FullMsgId> _requestedToReadMessage;
rpl::event_stream<FullMsgId> _requestedToShowMessage; rpl::event_stream<FullMsgId> _requestedToShowMessage;

View file

@ -477,7 +477,7 @@ void Message::refreshRightBadge() {
} else if (data()->author()->isMegagroup()) { } else if (data()->author()->isMegagroup()) {
if (const auto msgsigned = data()->Get<HistoryMessageSigned>()) { if (const auto msgsigned = data()->Get<HistoryMessageSigned>()) {
Assert(msgsigned->isAnonymousRank); Assert(msgsigned->isAnonymousRank);
return msgsigned->author; return msgsigned->postAuthor;
} }
} }
const auto channel = data()->history()->peer->asMegagroup(); const auto channel = data()->history()->peer->asMegagroup();
@ -801,9 +801,9 @@ QSize Message::performCountOptimalSize() {
accumulate_max(maxWidth, namew); accumulate_max(maxWidth, namew);
} }
if (reply) { if (reply) {
auto replyw = st::msgPadding.left() + reply->maxReplyWidth - st::msgReplyPadding.left() - st::msgReplyPadding.right() + st::msgPadding.right(); auto replyw = st::msgPadding.left() + reply->maxWidth() - st::msgReplyPadding.left() - st::msgReplyPadding.right() + st::msgPadding.right();
if (reply->replyToVia) { if (reply->originalVia) {
replyw += st::msgServiceFont->spacew + reply->replyToVia->maxWidth; replyw += st::msgServiceFont->spacew + reply->originalVia->maxWidth;
} }
accumulate_max(maxWidth, replyw); accumulate_max(maxWidth, replyw);
} }
@ -1736,8 +1736,8 @@ void Message::clickHandlerPressedChanged(
toggleTopicButtonRipple(pressed); toggleTopicButtonRipple(pressed);
} else if (_viewButton) { } else if (_viewButton) {
_viewButton->checkLink(handler, pressed); _viewButton->checkLink(handler, pressed);
} else if (const auto reply = displayedReply(); } else if (const auto reply = displayedReply()
reply && (handler == reply->replyToLink())) { ; reply && (handler == reply->link())) {
toggleReplyRipple(pressed); toggleReplyRipple(pressed);
} }
} }
@ -2469,10 +2469,11 @@ bool Message::getStateReplyInfo(
trect.y() + st::msgReplyPadding.top(), trect.y() + st::msgReplyPadding.top(),
trect.width(), trect.width(),
st::msgReplyBarSize.height()); st::msgReplyBarSize.height());
if ((reply->replyToMsg || reply->replyToStory) if (g.contains(point)) {
&& g.contains(point)) { if (const auto link = reply->link()) {
outResult->link = reply->replyToLink(); outResult->link = reply->link();
reply->ripple.lastPoint = point - g.topLeft(); reply->ripple.lastPoint = point - g.topLeft();
}
} }
return true; return true;
} }
@ -3439,7 +3440,7 @@ ClickHandlerPtr Message::fastReplyLink() const {
} }
const auto itemId = data()->fullId(); const auto itemId = data()->fullId();
_fastReplyLink = std::make_shared<LambdaClickHandler>([=] { _fastReplyLink = std::make_shared<LambdaClickHandler>([=] {
delegate()->elementReplyTo(itemId); delegate()->elementReplyTo({ itemId });
}); });
return _fastReplyLink; return _fastReplyLink;
} }

View file

@ -1002,7 +1002,7 @@ void RepliesWidget::sendingFilesConfirmed(
album, album,
action); action);
} }
if (_composeControls->replyingToMessage().msg == action.replyTo.msgId) { if (_composeControls->replyingToMessage() == action.replyTo) {
_composeControls->cancelReplyMessage(); _composeControls->cancelReplyMessage();
refreshTopBarActiveChat(); refreshTopBarActiveChat();
} }
@ -1123,9 +1123,9 @@ bool RepliesWidget::showSendingFilesError(
} }
Api::SendAction RepliesWidget::prepareSendAction( Api::SendAction RepliesWidget::prepareSendAction(
Api::SendOptions options) const { Api::SendOptions options) const {
auto result = Api::SendAction(_history, options); auto result = Api::SendAction(_history, options);
result.replyTo = { .msgId = replyToId(), .topicRootId = _rootId }; result.replyTo = replyTo();
result.options.sendAs = _composeControls->sendAsPeer(); result.options.sendAs = _composeControls->sendAsPeer();
return result; return result;
} }
@ -1444,28 +1444,29 @@ SendMenu::Type RepliesWidget::sendMenuType() const {
: SendMenu::Type::Scheduled; : SendMenu::Type::Scheduled;
} }
FullReplyTo RepliesWidget::replyTo() const {
if (auto custom = _composeControls->replyingToMessage()) {
custom.topicRootId = _rootId;
return custom;
}
return FullReplyTo{
.messageId = FullMsgId(_history->peer->id, _rootId),
.topicRootId = _rootId,
};
}
void RepliesWidget::refreshTopBarActiveChat() { void RepliesWidget::refreshTopBarActiveChat() {
using namespace Dialogs; using namespace Dialogs;
const auto state = EntryState{ const auto state = EntryState{
.key = (_topic ? Key{ _topic } : Key{ _history }), .key = (_topic ? Key{ _topic } : Key{ _history }),
.section = EntryState::Section::Replies, .section = EntryState::Section::Replies,
.rootId = _rootId, .currentReplyTo = replyTo(),
.currentReplyToId = _composeControls->replyingToMessage().msg,
}; };
_topBar->setActiveChat(state, _sendAction.get()); _topBar->setActiveChat(state, _sendAction.get());
_composeControls->setCurrentDialogsEntryState(state); _composeControls->setCurrentDialogsEntryState(state);
controller()->setCurrentDialogsEntryState(state); controller()->setCurrentDialogsEntryState(state);
} }
MsgId RepliesWidget::replyToId() const {
const auto custom = _composeControls->replyingToMessage().msg;
return custom
? custom
: (_rootId == Data::ForumTopic::kGeneralId)
? MsgId()
: _rootId;
}
void RepliesWidget::refreshUnreadCountBadge(std::optional<int> count) { void RepliesWidget::refreshUnreadCountBadge(std::optional<int> count) {
if (count.has_value()) { if (count.has_value()) {
_cornerButtons.updateJumpDownVisibility(count); _cornerButtons.updateJumpDownVisibility(count);
@ -2052,8 +2053,8 @@ bool RepliesWidget::confirmSendingFiles(
insertTextOnCancel); insertTextOnCancel);
} }
void RepliesWidget::replyToMessage(FullMsgId itemId) { void RepliesWidget::replyToMessage(FullReplyTo id) {
_composeControls->replyToMessage(itemId); _composeControls->replyToMessage(std::move(id));
refreshTopBarActiveChat(); refreshTopBarActiveChat();
} }

View file

@ -243,7 +243,7 @@ private:
mtpRequestId *const saveEditMsgRequestId); mtpRequestId *const saveEditMsgRequestId);
void chooseAttach(std::optional<bool> overrideSendImagesAsPhotos); void chooseAttach(std::optional<bool> overrideSendImagesAsPhotos);
[[nodiscard]] SendMenu::Type sendMenuType() const; [[nodiscard]] SendMenu::Type sendMenuType() const;
[[nodiscard]] MsgId replyToId() const; [[nodiscard]] FullReplyTo replyTo() const;
[[nodiscard]] HistoryItem *lookupRoot() const; [[nodiscard]] HistoryItem *lookupRoot() const;
[[nodiscard]] Data::ForumTopic *lookupTopic(); [[nodiscard]] Data::ForumTopic *lookupTopic();
[[nodiscard]] bool computeAreComments() const; [[nodiscard]] bool computeAreComments() const;
@ -252,7 +252,7 @@ private:
void pushReplyReturn(not_null<HistoryItem*> item); void pushReplyReturn(not_null<HistoryItem*> item);
void checkReplyReturns(); void checkReplyReturns();
void recountChatWidth(); void recountChatWidth();
void replyToMessage(FullMsgId itemId); void replyToMessage(FullReplyTo id);
void refreshTopBarActiveChat(); void refreshTopBarActiveChat();
void refreshUnreadCountBadge(std::optional<int> count); void refreshUnreadCountBadge(std::optional<int> count);

View file

@ -112,7 +112,7 @@ bool TranslateTracker::add(
} }
if (!skipDependencies) { if (!skipDependencies) {
if (const auto reply = item->Get<HistoryMessageReply>()) { if (const auto reply = item->Get<HistoryMessageReply>()) {
if (const auto to = reply->replyToMsg.get()) { if (const auto to = reply->resolvedMessage.get()) {
add(to, true); add(to, true);
} }
} }

View file

@ -1069,7 +1069,7 @@ TextState Gif::textState(QPoint point, StateRequest request) const {
if (reply) { if (reply) {
const auto replyRect = QRect(rectx, recty, rectw, recth); const auto replyRect = QRect(rectx, recty, rectw, recth);
if (replyRect.contains(point)) { if (replyRect.contains(point)) {
result.link = reply->replyToLink(); result.link = reply->link();
reply->ripple.lastPoint = point - replyRect.topLeft(); reply->ripple.lastPoint = point - replyRect.topLeft();
if (!reply->ripple.animation) { if (!reply->ripple.animation) {
reply->ripple.animation = std::make_unique<Ui::RippleAnimation>( reply->ripple.animation = std::make_unique<Ui::RippleAnimation>(
@ -1737,7 +1737,7 @@ int Gif::additionalWidth(const HistoryMessageVia *via, const HistoryMessageReply
accumulate_max(result, st::msgReplyPadding.left() + st::msgReplyPadding.left() + via->maxWidth + st::msgReplyPadding.left()); accumulate_max(result, st::msgReplyPadding.left() + st::msgReplyPadding.left() + via->maxWidth + st::msgReplyPadding.left());
} }
if (reply) { if (reply) {
accumulate_max(result, st::msgReplyPadding.left() + reply->replyToWidth()); accumulate_max(result, st::msgReplyPadding.left() + reply->maxWidth());
} }
return result; return result;
} }

View file

@ -464,7 +464,7 @@ TextState UnwrappedMedia::textState(QPoint point, StateRequest request) const {
if (reply) { if (reply) {
const auto replyRect = QRect(rectx, recty, rectw, recth); const auto replyRect = QRect(rectx, recty, rectw, recth);
if (replyRect.contains(point)) { if (replyRect.contains(point)) {
result.link = reply->replyToLink(); result.link = reply->link();
reply->ripple.lastPoint = point - replyRect.topLeft(); reply->ripple.lastPoint = point - replyRect.topLeft();
if (!reply->ripple.animation) { if (!reply->ripple.animation) {
reply->ripple.animation = std::make_unique<Ui::RippleAnimation>( reply->ripple.animation = std::make_unique<Ui::RippleAnimation>(
@ -519,10 +519,9 @@ bool UnwrappedMedia::hasTextForCopy() const {
return _content->hasTextForCopy(); return _content->hasTextForCopy();
} }
bool UnwrappedMedia::dragItemByHandler( bool UnwrappedMedia::dragItemByHandler(const ClickHandlerPtr &p) const {
const ClickHandlerPtr &p) const {
const auto reply = _parent->displayedReply(); const auto reply = _parent->displayedReply();
return !(reply && (reply->replyToLink() == p)); return !reply || (reply->link() != p);
} }
QRect UnwrappedMedia::contentRectForReactions() const { QRect UnwrappedMedia::contentRectForReactions() const {
@ -642,7 +641,7 @@ int UnwrappedMedia::additionalWidth(
accumulate_max(result, 2 * st::msgReplyPadding.left() + via->maxWidth + st::msgReplyPadding.right()); accumulate_max(result, 2 * st::msgReplyPadding.left() + via->maxWidth + st::msgReplyPadding.right());
} }
if (reply) { if (reply) {
accumulate_max(result, st::msgReplyPadding.left() + reply->replyToWidth()); accumulate_max(result, st::msgReplyPadding.left() + reply->maxWidth());
} }
return result; return result;
} }

View file

@ -602,8 +602,7 @@ bool MainWidget::shareUrl(
const auto topicRootId = thread->topicRootId(); const auto topicRootId = thread->topicRootId();
history->setLocalDraft(std::make_unique<Data::Draft>( history->setLocalDraft(std::make_unique<Data::Draft>(
textWithTags, textWithTags,
0, // replyTo FullReplyTo{ .topicRootId = topicRootId },
topicRootId,
cursor, cursor,
Data::PreviewState::Allowed)); Data::PreviewState::Allowed));
history->clearLocalEditDraft(topicRootId); history->clearLocalEditDraft(topicRootId);
@ -746,8 +745,8 @@ void MainWidget::sendBotCommand(Bot::SendCommandRequest request) {
} }
} }
void MainWidget::hideSingleUseKeyboard(PeerData *peer, MsgId replyTo) { void MainWidget::hideSingleUseKeyboard(FullMsgId replyToId) {
_history->hideSingleUseKeyboard(peer, replyTo); _history->hideSingleUseKeyboard(replyToId);
} }
void MainWidget::searchMessages(const QString &query, Dialogs::Key inChat) { void MainWidget::searchMessages(const QString &query, Dialogs::Key inChat) {

View file

@ -188,7 +188,7 @@ public:
not_null<const QMimeData*> data); not_null<const QMimeData*> data);
void sendBotCommand(Bot::SendCommandRequest request); void sendBotCommand(Bot::SendCommandRequest request);
void hideSingleUseKeyboard(PeerData *peer, MsgId replyTo); void hideSingleUseKeyboard(FullMsgId replyToId);
void searchMessages(const QString &query, Dialogs::Key inChat); void searchMessages(const QString &query, Dialogs::Key inChat);

View file

@ -3037,7 +3037,7 @@ void OverlayWidget::refreshMediaViewer() {
void OverlayWidget::refreshFromLabel() { void OverlayWidget::refreshFromLabel() {
if (_message) { if (_message) {
_from = _message->senderOriginal(); _from = _message->originalSender();
if (const auto info = _message->hiddenSenderInfo()) { if (const auto info = _message->hiddenSenderInfo()) {
_fromName = info->name; _fromName = info->name;
} else { } else {

View file

@ -59,6 +59,7 @@ constexpr auto kMultiDraftTagOld = quint64(0xFFFF'FFFF'FFFF'FF01ULL);
constexpr auto kMultiDraftCursorsTagOld = quint64(0xFFFF'FFFF'FFFF'FF02ULL); constexpr auto kMultiDraftCursorsTagOld = quint64(0xFFFF'FFFF'FFFF'FF02ULL);
constexpr auto kMultiDraftTag = quint64(0xFFFF'FFFF'FFFF'FF03ULL); constexpr auto kMultiDraftTag = quint64(0xFFFF'FFFF'FFFF'FF03ULL);
constexpr auto kMultiDraftCursorsTag = quint64(0xFFFF'FFFF'FFFF'FF04ULL); constexpr auto kMultiDraftCursorsTag = quint64(0xFFFF'FFFF'FFFF'FF04ULL);
constexpr auto kRichDraftsTag = quint64(0xFFFF'FFFF'FFFF'FF05ULL);
enum { // Local Storage Keys enum { // Local Storage Keys
lskUserMap = 0x00, lskUserMap = 0x00,
@ -1041,7 +1042,7 @@ void EnumerateDrafts(
} }
callback( callback(
key, key,
draft->msgId, draft->reply,
draft->textWithTags, draft->textWithTags,
draft->previewState, draft->previewState,
draft->cursor); draft->cursor);
@ -1049,12 +1050,12 @@ void EnumerateDrafts(
for (const auto &[key, source] : sources) { for (const auto &[key, source] : sources) {
const auto draft = source.draft(); const auto draft = source.draft();
const auto cursor = source.cursor(); const auto cursor = source.cursor();
if (draft.msgId if (draft.reply.messageId
|| !draft.textWithTags.text.isEmpty() || !draft.textWithTags.text.isEmpty()
|| cursor != MessageCursor()) { || cursor != MessageCursor()) {
callback( callback(
key, key,
draft.msgId, draft.reply,
draft.textWithTags, draft.textWithTags,
draft.previewState, draft.previewState,
cursor); cursor);
@ -1119,14 +1120,18 @@ void Account::writeDrafts(not_null<History*> history) {
auto size = int(sizeof(quint64) * 2 + sizeof(quint32)); auto size = int(sizeof(quint64) * 2 + sizeof(quint32));
const auto sizeCallback = [&]( const auto sizeCallback = [&](
auto&&, // key auto&&, // key
MsgId, // msgId const FullReplyTo &reply,
const TextWithTags &text, const TextWithTags &text,
Data::PreviewState, Data::PreviewState,
auto&&) { // cursor auto&&) { // cursor
size += sizeof(qint64) // key size += sizeof(qint64) // key
+ Serialize::stringSize(text.text) + Serialize::stringSize(text.text)
+ sizeof(qint64) + TextUtilities::SerializeTagsSize(text.tags) + sizeof(qint64) + TextUtilities::SerializeTagsSize(text.tags)
+ sizeof(qint64) + sizeof(qint32); // msgId, previewState + sizeof(qint64) + sizeof(qint64) // messageId
+ Serialize::stringSize(reply.quote.text)
+ sizeof(qint64)
+ TextUtilities::SerializeTagsSize(reply.quote.tags)
+ sizeof(qint32); // previewState
}; };
EnumerateDrafts( EnumerateDrafts(
map, map,
@ -1136,13 +1141,13 @@ void Account::writeDrafts(not_null<History*> history) {
EncryptedDescriptor data(size); EncryptedDescriptor data(size);
data.stream data.stream
<< quint64(kMultiDraftTag) << quint64(kRichDraftsTag)
<< SerializePeerId(peerId) << SerializePeerId(peerId)
<< quint32(count); << quint32(count);
const auto writeCallback = [&]( const auto writeCallback = [&](
const Data::DraftKey &key, const Data::DraftKey &key,
MsgId msgId, const FullReplyTo &reply,
const TextWithTags &text, const TextWithTags &text,
Data::PreviewState previewState, Data::PreviewState previewState,
auto&&) { // cursor auto&&) { // cursor
@ -1150,7 +1155,10 @@ void Account::writeDrafts(not_null<History*> history) {
<< key.serialize() << key.serialize()
<< text.text << text.text
<< TextUtilities::SerializeTags(text.tags) << TextUtilities::SerializeTags(text.tags)
<< qint64(msgId.bare) << qint64(reply.messageId.peer.value)
<< qint64(reply.messageId.msg.bare)
<< reply.quote.text
<< TextUtilities::SerializeTags(reply.quote.tags)
<< qint32(previewState); << qint32(previewState);
}; };
EnumerateDrafts( EnumerateDrafts(
@ -1201,7 +1209,7 @@ void Account::writeDraftCursors(not_null<History*> history) {
const auto writeCallback = [&]( const auto writeCallback = [&](
const Data::DraftKey &key, const Data::DraftKey &key,
MsgId, // msgId auto&&, // reply
auto&&, // text auto&&, // text
Data::PreviewState, Data::PreviewState,
const MessageCursor &cursor) { // cursor const MessageCursor &cursor) { // cursor
@ -1343,7 +1351,9 @@ void Account::readDraftsWithCursors(not_null<History*> history) {
quint64 tag = 0; quint64 tag = 0;
draft.stream >> tag; draft.stream >> tag;
if (tag != kMultiDraftTag && tag != kMultiDraftTagOld) { if (tag != kRichDraftsTag
&& tag != kMultiDraftTag
&& tag != kMultiDraftTagOld) {
readDraftsWithCursorsLegacy(history, draft, tag); readDraftsWithCursorsLegacy(history, draft, tag);
return; return;
} }
@ -1359,24 +1369,43 @@ void Account::readDraftsWithCursors(not_null<History*> history) {
} }
auto map = Data::HistoryDrafts(); auto map = Data::HistoryDrafts();
const auto keysOld = (tag == kMultiDraftTagOld); const auto keysOld = (tag == kMultiDraftTagOld);
const auto rich = (tag == kRichDraftsTag);
for (auto i = 0; i != count; ++i) { for (auto i = 0; i != count; ++i) {
TextWithTags data; TextWithTags quote;
QByteArray tagsSerialized; TextWithTags text;
qint64 keyValue = 0, messageId = 0; QByteArray textTagsSerialized;
QByteArray quoteTagsSerialized;
qint64 keyValue = 0;
qint64 messageIdPeer = 0, messageIdMsg = 0;
qint32 keyValueOld = 0, uncheckedPreviewState = 0; qint32 keyValueOld = 0, uncheckedPreviewState = 0;
if (keysOld) { if (keysOld) {
draft.stream >> keyValueOld; draft.stream >> keyValueOld;
} else { } else {
draft.stream >> keyValue; draft.stream >> keyValue;
} }
draft.stream if (!rich) {
>> data.text draft.stream
>> tagsSerialized >> text.text
>> messageId >> textTagsSerialized
>> uncheckedPreviewState; >> messageIdMsg
data.tags = TextUtilities::DeserializeTags( >> uncheckedPreviewState;
tagsSerialized, messageIdPeer = peerId.value;
data.text.size()); } else {
draft.stream
>> text.text
>> textTagsSerialized
>> messageIdPeer
>> messageIdMsg
>> quote.text
>> quoteTagsSerialized
>> uncheckedPreviewState;
quote.tags = TextUtilities::DeserializeTags(
quoteTagsSerialized,
quote.text.size());
}
text.tags = TextUtilities::DeserializeTags(
textTagsSerialized,
text.text.size());
auto previewState = Data::PreviewState::Allowed; auto previewState = Data::PreviewState::Allowed;
switch (static_cast<Data::PreviewState>(uncheckedPreviewState)) { switch (static_cast<Data::PreviewState>(uncheckedPreviewState)) {
case Data::PreviewState::Cancelled: case Data::PreviewState::Cancelled:
@ -1388,9 +1417,14 @@ void Account::readDraftsWithCursors(not_null<History*> history) {
: Data::DraftKey::FromSerialized(keyValue); : Data::DraftKey::FromSerialized(keyValue);
if (key && !key.isCloud()) { if (key && !key.isCloud()) {
map.emplace(key, std::make_unique<Data::Draft>( map.emplace(key, std::make_unique<Data::Draft>(
data, text,
messageId, FullReplyTo{
key.topicRootId(), .messageId = FullMsgId(
PeerId(messageIdPeer),
MsgId(messageIdMsg)),
.quote = quote,
.topicRootId = key.topicRootId(),
},
MessageCursor(), MessageCursor(),
previewState)); previewState));
} }
@ -1455,8 +1489,7 @@ void Account::readDraftsWithCursorsLegacy(
Data::DraftKey::Local(topicRootId), Data::DraftKey::Local(topicRootId),
std::make_unique<Data::Draft>( std::make_unique<Data::Draft>(
msgData, msgData,
msgReplyTo, FullReplyTo{ FullMsgId(peerId, MsgId(msgReplyTo)) },
topicRootId,
MessageCursor(), MessageCursor(),
(msgPreviewCancelled (msgPreviewCancelled
? Data::PreviewState::Cancelled ? Data::PreviewState::Cancelled
@ -1467,8 +1500,7 @@ void Account::readDraftsWithCursorsLegacy(
Data::DraftKey::LocalEdit(topicRootId), Data::DraftKey::LocalEdit(topicRootId),
std::make_unique<Data::Draft>( std::make_unique<Data::Draft>(
editData, editData,
editMsgId, FullReplyTo{ FullMsgId(peerId, editMsgId) },
topicRootId,
MessageCursor(), MessageCursor(),
(editPreviewCancelled (editPreviewCancelled
? Data::PreviewState::Cancelled ? Data::PreviewState::Cancelled

View file

@ -51,7 +51,7 @@ using FileKey = quint64;
enum class StartResult : uchar; enum class StartResult : uchar;
struct MessageDraft { struct MessageDraft {
MsgId msgId = 0; FullReplyTo reply;
TextWithTags textWithTags; TextWithTags textWithTags;
Data::PreviewState previewState = Data::PreviewState::Allowed; Data::PreviewState previewState = Data::PreviewState::Allowed;
}; };

View file

@ -159,8 +159,7 @@ Data::Draft OccupiedDraft(const QString &normalizedName) {
+ QString::number(OccupationTag()) + QString::number(OccupationTag())
+ ";n:" + ";n:"
+ normalizedName }, + normalizedName },
MsgId(0), // replyTo FullReplyTo(),
kTopicRootId,
MessageCursor(), MessageCursor(),
Data::PreviewState::Allowed Data::PreviewState::Allowed
}; };

View file

@ -1060,12 +1060,14 @@ void Manager::notificationActivated(
const auto replyToId = (id.msgId > 0 const auto replyToId = (id.msgId > 0
&& !history->peer->isUser() && !history->peer->isUser()
&& id.msgId != topicRootId) && id.msgId != topicRootId)
? id.msgId ? FullMsgId(history->peer->id, id.msgId)
: 0; : FullMsgId();
auto draft = std::make_unique<Data::Draft>( auto draft = std::make_unique<Data::Draft>(
reply, reply,
replyToId, FullReplyTo{
topicRootId, .messageId = replyToId,
.topicRootId = topicRootId,
},
MessageCursor{ MessageCursor{
int(reply.text.size()), int(reply.text.size()),
int(reply.text.size()), int(reply.text.size()),
@ -1150,7 +1152,7 @@ void Manager::notificationReplied(
? topicRootId ? topicRootId
: MsgId(0); : MsgId(0);
message.action.replyTo = { message.action.replyTo = {
.msgId = replyToId, .messageId = { replyToId ? history->peer->id : 0, replyToId },
.topicRootId = topic ? topic->rootId() : 0, .topicRootId = topic ? topic->rootId() : 0,
}; };
message.action.clearDraft = false; message.action.clearDraft = false;

View file

@ -112,7 +112,10 @@ void ShareBotGame(
} }
histories.sendPreparedMessage( histories.sendPreparedMessage(
history, history,
FullReplyTo{ .msgId = replyTo, .topicRootId = topicRootId }, FullReplyTo{
.messageId = { replyTo ? history->peer->id : 0, replyTo },
.topicRootId = topicRootId,
},
randomId, randomId,
Data::Histories::PrepareMessage<MTPmessages_SendMedia>( Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
MTP_flags(flags), MTP_flags(flags),
@ -1036,16 +1039,12 @@ void Filler::addCreatePoll() {
? SendMenu::Type::SilentOnly ? SendMenu::Type::SilentOnly
: SendMenu::Type::Scheduled; : SendMenu::Type::Scheduled;
const auto flag = PollData::Flags(); const auto flag = PollData::Flags();
const auto topicRootId = _request.rootId; const auto replyTo = _request.currentReplyTo;
const auto replyToId = _request.currentReplyToId
? _request.currentReplyToId
: topicRootId;
auto callback = [=] { auto callback = [=] {
PeerMenuCreatePoll( PeerMenuCreatePoll(
controller, controller,
peer, peer,
replyToId, replyTo,
topicRootId,
flag, flag,
flag, flag,
source, source,
@ -1464,8 +1463,7 @@ void PeerMenuShareContactBox(
void PeerMenuCreatePoll( void PeerMenuCreatePoll(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId replyToId, FullReplyTo replyTo,
MsgId topicRootId,
PollData::Flags chosen, PollData::Flags chosen,
PollData::Flags disabled, PollData::Flags disabled,
Api::SendType sendType, Api::SendType sendType,
@ -1490,10 +1488,12 @@ void PeerMenuCreatePoll(
auto action = Api::SendAction( auto action = Api::SendAction(
peer->owner().history(peer), peer->owner().history(peer),
result.options); result.options);
action.clearDraft = false; action.replyTo = replyTo;
action.replyTo = { .msgId = replyToId, .topicRootId = topicRootId }; const auto topicRootId = replyTo.topicRootId;
if (const auto local = action.history->localDraft(topicRootId)) { if (const auto local = action.history->localDraft(topicRootId)) {
action.clearDraft = local->textWithTags.text.isEmpty(); action.clearDraft = local->textWithTags.text.isEmpty();
} else {
action.clearDraft = false;
} }
const auto api = &peer->session().api(); const auto api = &peer->session().api();
api->polls().create(result.poll, action, crl::guard(weak, [=] { api->polls().create(result.poll, action, crl::guard(weak, [=] {
@ -1637,7 +1637,7 @@ void BlockSenderFromRepliesBox(
PeerMenuBlockUserBox( PeerMenuBlockUserBox(
box, box,
&controller->window(), &controller->window(),
item->senderOriginal(), item->originalSender(),
true, true,
Window::ClearReply{ id }); Window::ClearReply{ id });
} }

View file

@ -86,8 +86,7 @@ void PeerMenuAddChannelMembers(
void PeerMenuCreatePoll( void PeerMenuCreatePoll(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<PeerData*> peer, not_null<PeerData*> peer,
MsgId replyToId = 0, FullReplyTo replyTo = FullReplyTo(),
MsgId topicRootId = 0,
PollData::Flags chosen = PollData::Flags(), PollData::Flags chosen = PollData::Flags(),
PollData::Flags disabled = PollData::Flags(), PollData::Flags disabled = PollData::Flags(),
Api::SendType sendType = Api::SendType::Normal, Api::SendType sendType = Api::SendType::Normal,

View file

@ -1526,8 +1526,7 @@ bool SessionController::switchInlineQuery(
}; };
auto draft = std::make_unique<Data::Draft>( auto draft = std::make_unique<Data::Draft>(
textWithTags, textWithTags,
to.currentReplyToId, to.currentReplyTo,
to.rootId,
cursor, cursor,
Data::PreviewState::Allowed); Data::PreviewState::Allowed);
@ -1539,11 +1538,12 @@ bool SessionController::switchInlineQuery(
std::make_shared<HistoryView::ScheduledMemento>(history), std::make_shared<HistoryView::ScheduledMemento>(history),
params); params);
} else { } else {
const auto topicRootId = to.currentReplyTo.topicRootId;
history->setLocalDraft(std::move(draft)); history->setLocalDraft(std::move(draft));
history->clearLocalEditDraft(to.rootId); history->clearLocalEditDraft(topicRootId);
if (to.section == Section::Replies) { if (to.section == Section::Replies) {
const auto commentId = MsgId(); const auto commentId = MsgId();
showRepliesForMessage(history, to.rootId, commentId, params); showRepliesForMessage(history, topicRootId, commentId, params);
} else { } else {
showPeerHistory(history->peer, params); showPeerHistory(history->peer, params);
} }
@ -1560,7 +1560,7 @@ bool SessionController::switchInlineQuery(
.section = (thread->asTopic() .section = (thread->asTopic()
? Dialogs::EntryState::Section::Replies ? Dialogs::EntryState::Section::Replies
: Dialogs::EntryState::Section::History), : Dialogs::EntryState::Section::History),
.rootId = thread->topicRootId(), .currentReplyTo = { .topicRootId = thread->topicRootId() },
}; };
return switchInlineQuery(entryState, bot, query); return switchInlineQuery(entryState, bot, query);
} }