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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -308,7 +308,6 @@ void BotCommandClickHandler::onClick(ClickContext context) const {
.peer = peer,
.command = _cmd,
.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 "history/history.h"
#include "history/history_widget.h"
#include "history/history_item_components.h"
#include "main/main_session.h"
#include "data/data_session.h"
#include "mainwidget.h"
@ -21,14 +22,12 @@ namespace Data {
Draft::Draft(
const TextWithTags &textWithTags,
MsgId msgId,
MsgId topicRootId,
FullReplyTo reply,
const MessageCursor &cursor,
PreviewState previewState,
mtpRequestId saveRequestId)
: textWithTags(textWithTags)
, msgId(msgId)
, topicRootId(topicRootId)
, reply(std::move(reply))
, cursor(cursor)
, previewState(previewState)
, saveRequestId(saveRequestId) {
@ -36,13 +35,11 @@ Draft::Draft(
Draft::Draft(
not_null<const Ui::InputField*> field,
MsgId msgId,
MsgId topicRootId,
FullReplyTo reply,
PreviewState previewState,
mtpRequestId saveRequestId)
: textWithTags(field->getTextWithTags())
, msgId(msgId)
, topicRootId(topicRootId)
, reply(std::move(reply))
, cursor(field)
, previewState(previewState) {
}
@ -64,22 +61,26 @@ void ApplyPeerCloudDraft(
session,
draft.ventities().value_or_empty()))
};
auto replyTo = MsgId();
if (const auto reply = draft.vreply_to()) {
reply->match([&](const MTPDmessageReplyHeader &data) {
if (!data.vreply_to_peer_id()
|| (peerFromMTP(*data.vreply_to_peer_id()) == peerId)) {
replyTo = data.vreply_to_msg_id().value_or_empty();
} else {
// #TODO replies
}
}, [&](const MTPDmessageReplyStoryHeader &data) {
});
}
const auto reply = draft.vreply_to()
? ReplyFieldsFromMTP(history, *draft.vreply_to())
: ReplyFields();
const auto replyPeerId = reply.externalPeerId
? reply.externalPeerId
: peerId;
auto cloudDraft = std::make_unique<Draft>(
textWithTags,
replyTo,
topicRootId,
FullReplyTo{
.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),
(draft.is_no_webpage()
? Data::PreviewState::Cancelled

View File

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

View File

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

View File

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

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#pragma once
#include "data/data_peer_id.h"
#include "ui/text/text_entity.h"
struct MsgId {
constexpr MsgId() noexcept = default;
@ -67,21 +68,6 @@ struct FullStoryId {
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 ClientMsgIds = (1LL << 31);
constexpr auto EndClientMsgId = MsgId(StartClientMsgId.bare + ClientMsgIds);
@ -169,6 +155,22 @@ struct 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 {
FullMsgId itemId;
uint64 sessionUniqueId = 0;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -465,6 +465,7 @@ public:
void setText(const TextWithEntities &textWithEntities);
[[nodiscard]] MsgId replyToId() const;
[[nodiscard]] FullMsgId replyToFullId() const;
[[nodiscard]] MsgId replyToTop() const;
[[nodiscard]] MsgId topicRootId() const;
[[nodiscard]] FullStoryId replyToStory() const;
@ -473,12 +474,12 @@ public:
[[nodiscard]] not_null<PeerData*> author() const;
[[nodiscard]] TimeId dateOriginal() const;
[[nodiscard]] PeerData *senderOriginal() const;
[[nodiscard]] TimeId originalDate() const;
[[nodiscard]] PeerData *originalSender() const;
[[nodiscard]] const HiddenSenderInfo *hiddenSenderInfo() const;
[[nodiscard]] not_null<PeerData*> fromOriginal() const;
[[nodiscard]] QString authorOriginal() const;
[[nodiscard]] MsgId idOriginal() const;
[[nodiscard]] QString originalPostAuthor() const;
[[nodiscard]] MsgId originalId() 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 "api/api_text_entities.h"
#include "base/qt/qt_key_modifiers.h"
#include "lang/lang_keys.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_web_page.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 "main/main_session.h"
#include "window/window_session_controller.h"
@ -64,7 +67,7 @@ void HistoryMessageVia::create(
lt_inline_bot,
'@' + bot->username()));
link = std::make_shared<LambdaClickHandler>([bot = this->bot](
ClickContext context) {
ClickContext context) {
const auto my = context.other.value<ClickHandlerContext>();
if (const auto controller = my.sessionWindow.get()) {
if (base::IsCtrlPressed()) {
@ -183,13 +186,13 @@ void HistoryMessageForwarded::create(const HistoryMessageVia *via) const {
? originalSender->name()
: hiddenSenderInfo->name)
};
if (!originalAuthor.isEmpty()) {
if (!originalPostAuthor.isEmpty()) {
phrase = tr::lng_forwarded_signed(
tr::now,
lt_channel,
name,
lt_user,
{ .text = originalAuthor },
{ .text = originalPostAuthor },
Ui::Text::WithEntities);
} else {
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::operator=(
@ -268,8 +316,8 @@ HistoryMessageReply &HistoryMessageReply::operator=(
HistoryMessageReply::~HistoryMessageReply() {
// clearData() should be called by holder.
Expects(replyToMsg.empty());
Expects(replyToVia == nullptr);
Expects(resolvedMessage.empty());
Expects(originalVia == nullptr);
}
bool HistoryMessageReply::updateData(
@ -277,166 +325,228 @@ bool HistoryMessageReply::updateData(
bool force) {
const auto guard = gsl::finally([&] { refreshReplyToMedia(); });
if (!force) {
if ((replyToMsg || !replyToMsgId)
&& (replyToStory || !replyToStoryId)) {
if (resolvedMessage || resolvedStory || _deleted) {
return true;
}
}
const auto peerId = replyToPeerId
? replyToPeerId
const auto peerId = _fields.externalPeerId
? _fields.externalPeerId
: holder->history()->peer->id;
if (!replyToMsg && replyToMsgId) {
replyToMsg = holder->history()->owner().message(
if (!resolvedMessage && _fields.messageId) {
resolvedMessage = holder->history()->owner().message(
peerId,
replyToMsgId);
if (replyToMsg) {
if (replyToMsg->isEmpty()) {
_fields.messageId);
if (resolvedMessage) {
if (resolvedMessage->isEmpty()) {
// Really it is deleted.
replyToMsg = nullptr;
resolvedMessage = nullptr;
force = true;
} else {
holder->history()->owner().registerDependentMessage(
holder,
replyToMsg.get());
resolvedMessage.get());
}
}
}
if (!replyToStory && replyToStoryId) {
if (!resolvedStory && _fields.storyId) {
const auto maybe = holder->history()->owner().stories().lookup({
peerId,
replyToStoryId,
_fields.storyId,
});
if (maybe) {
replyToStory = *maybe;
resolvedStory = *maybe;
holder->history()->owner().stories().registerDependentMessage(
holder,
replyToStory.get());
resolvedStory.get());
} else if (maybe.error() == Data::NoStory::Deleted) {
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 context = Core::MarkedTextContext{
.session = &holder->history()->session(),
.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,
(replyToMsg
? replyToMsg->inReplyText()
: replyToStory->inReplyText()),
text,
Ui::DialogTextOptions(),
context);
updateName(holder);
setReplyToLinkFrom(holder);
if (replyToMsg && !replyToMsg->Has<HistoryMessageForwarded>()) {
if (auto bot = replyToMsg->viaBot()) {
replyToVia = std::make_unique<HistoryMessageVia>();
replyToVia->create(
setLinkFrom(holder);
if (resolvedMessage
&& !resolvedMessage->Has<HistoryMessageForwarded>()) {
if (const auto bot = resolvedMessage->viaBot()) {
originalVia = std::make_unique<HistoryMessageVia>();
originalVia->create(
&holder->history()->owner(),
peerToUser(bot->id));
}
}
if (replyToMsg) {
const auto peer = replyToMsg->history()->peer;
replyToColorKey = (!holder->out()
if (resolvedMessage) {
const auto peer = resolvedMessage->history()->peer;
_colorKey = (!holder->out()
&& (peer->isMegagroup() || peer->isChat())
&& replyToMsg->from()->isUser())
? replyToMsg->from()->id
: PeerId(0);
&& resolvedMessage->from()->isUser())
? resolvedMessage->from()->id
: PeerId();
} 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()) {
spoiler = nullptr;
} else if (!spoiler) {
spoiler = std::make_unique<Ui::SpoilerAnimation>(repaint);
}
} else if (force) {
replyToMsgId = 0;
replyToStoryId = 0;
replyToColorKey = PeerId(0);
if (_fields.messageId || _fields.storyId) {
_deleted = true;
}
_colorKey = 0;
spoiler = nullptr;
}
if (force) {
holder->history()->owner().requestItemResize(holder);
}
return (replyToMsg || !replyToMsgId)
&& (replyToStory || !replyToStoryId);
return resolvedMessage
|| 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) {
replyToLnk = replyToMsg
? JumpToMessageClickHandler(replyToMsg.get(), holder->fullId())
: replyToStory
? JumpToStoryClickHandler(replyToStory.get())
const auto externalPeerId = _fields.externalSenderId;
const auto external = externalPeerId
|| !_fields.externalSenderName.isEmpty();
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;
}
void HistoryMessageReply::setTopMessageId(MsgId topMessageId) {
_fields.topMessageId = topMessageId;
}
void HistoryMessageReply::clearData(not_null<HistoryItem*> holder) {
replyToVia = nullptr;
if (replyToMsg) {
originalVia = nullptr;
if (resolvedMessage) {
holder->history()->owner().unregisterDependentMessage(
holder,
replyToMsg.get());
replyToMsg = nullptr;
resolvedMessage.get());
resolvedMessage = nullptr;
}
if (replyToStory) {
if (resolvedStory) {
holder->history()->owner().stories().unregisterDependentMessage(
holder,
replyToStory.get());
replyToStory = nullptr;
resolvedStory.get());
resolvedStory = nullptr;
}
replyToMsgId = 0;
replyToStoryId = 0;
_deleted = true;
refreshReplyToMedia();
}
PeerData *HistoryMessageReply::replyToFrom(
not_null<HistoryItem*> holder) const {
if (!replyToMsg) {
return nullptr;
PeerData *HistoryMessageReply::sender(not_null<HistoryItem*> holder) const {
if (resolvedStory) {
return resolvedStory->peer();
} else if (!resolvedMessage) {
if (!_externalSender && _fields.externalSenderId) {
_externalSender = holder->history()->owner().peer(
_fields.externalSenderId);
}
return _externalSender;
} else if (holder->Has<HistoryMessageForwarded>()) {
if (const auto fwd = replyToMsg->Get<HistoryMessageForwarded>()) {
return fwd->originalSender;
// Forward of a reply. Show reply-to original sender.
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 replyToMsg->author().get();
return resolvedMessage->author().get();
}
QString HistoryMessageReply::replyToFromName(
QString HistoryMessageReply::senderName(
not_null<HistoryItem*> holder) const {
if (replyToStory) {
return replyToFromName(replyToStory->peer());
} else if (!replyToMsg) {
return QString();
if (const auto peer = sender(holder)) {
return senderName(peer);
} else if (!resolvedMessage) {
return _fields.externalSenderName;
} else if (holder->Has<HistoryMessageForwarded>()) {
if (const auto fwd = replyToMsg->Get<HistoryMessageForwarded>()) {
return fwd->originalSender
? replyToFromName(fwd->originalSender)
: fwd->hiddenSenderInfo->name;
// Forward of a reply. Show reply-to original sender.
const auto forwarded
= resolvedMessage->Get<HistoryMessageForwarded>();
if (forwarded) {
Assert(forwarded->hiddenSenderInfo != nullptr);
return forwarded->hiddenSenderInfo->name;
}
}
if (const auto from = replyToMsg->displayFrom()) {
return replyToFromName(from);
}
return replyToFromName(replyToMsg->author());
return QString();
}
QString HistoryMessageReply::replyToFromName(
not_null<PeerData*> peer) const {
if (const auto user = replyToVia ? peer->asUser() : nullptr) {
QString HistoryMessageReply::senderName(not_null<PeerData*> peer) const {
if (const auto user = originalVia ? peer->asUser() : nullptr) {
return user->firstName;
}
return peer->name();
@ -444,9 +554,9 @@ QString HistoryMessageReply::replyToFromName(
bool HistoryMessageReply::isNameUpdated(
not_null<HistoryItem*> holder) const {
if (const auto from = replyToFrom(holder)) {
if (replyToVersion < from->nameVersion()) {
updateName(holder);
if (const auto from = sender(holder)) {
if (_nameVersion < from->nameVersion()) {
updateName(holder, from);
return true;
}
}
@ -454,55 +564,73 @@ bool HistoryMessageReply::isNameUpdated(
}
void HistoryMessageReply::updateName(
not_null<HistoryItem*> holder) const {
if (const auto name = replyToFromName(holder); !name.isEmpty()) {
replyToName.setText(st::fwdTextStyle, name, Ui::NameTextOptions());
if (const auto from = replyToFrom(holder)) {
replyToVersion = from->nameVersion();
} else if (replyToMsg) {
replyToVersion = replyToMsg->author()->nameVersion();
} else {
replyToVersion = replyToStory->peer()->nameVersion();
not_null<HistoryItem*> holder,
std::optional<PeerData*> resolvedSender) const {
const auto peer = resolvedSender.value_or(sender(holder));
const auto name = peer ? senderName(peer) : senderName(holder);
if (!name.isEmpty()) {
_name.setText(st::fwdTextStyle, name, Ui::NameTextOptions());
if (peer) {
_nameVersion = peer->nameVersion();
}
bool hasPreview = (replyToStory && replyToStory->hasReplyPreview())
|| (replyToMsg
&& replyToMsg->media()
&& replyToMsg->media()->hasReplyPreview());
int32 previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
int32 w = replyToName.maxWidth();
if (replyToVia) {
w += st::msgServiceFont->spacew + replyToVia->maxWidth;
bool hasPreview = (resolvedStory
&& resolvedStory->hasReplyPreview())
|| (resolvedMessage
&& resolvedMessage->media()
&& resolvedMessage->media()->hasReplyPreview());
int32 previewSkip = hasPreview
? (st::msgReplyBarSize.height()
+ 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(
w,
std::min(replyToText.maxWidth(), st::maxSignatureSize))
+ (storyReply
std::min(_text.maxWidth(), st::maxSignatureSize))
+ (_fields.storyId
? (st::dialogsMiniReplyStory.skipText
+ st::dialogsMiniReplyStory.icon.icon.width())
: 0);
} 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 {
if (replyToVia) {
bool hasPreview = (replyToStory && replyToStory->hasReplyPreview())
|| (replyToMsg
&& replyToMsg->media()
&& replyToMsg->media()->hasReplyPreview());
int previewSkip = hasPreview ? (st::msgReplyBarSize.height() + st::msgReplyBarSkip - st::msgReplyBarSize.width() - st::msgReplyBarPos.x()) : 0;
replyToVia->resize(width - st::msgReplyBarSkip - previewSkip - replyToName.maxWidth() - st::msgServiceFont->spacew);
if (originalVia) {
bool hasPreview = (resolvedStory
&& resolvedStory->hasReplyPreview())
|| (resolvedMessage
&& resolvedMessage->media()
&& resolvedMessage->media()->hasReplyPreview());
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(
not_null<HistoryItem*> holder,
not_null<HistoryItem*> removed) {
if (replyToMsg.get() == removed) {
if (resolvedMessage.get() == removed) {
clearData(holder);
holder->history()->owner().requestItemResize(holder);
}
@ -511,7 +639,7 @@ void HistoryMessageReply::itemRemoved(
void HistoryMessageReply::storyRemoved(
not_null<HistoryItem*> holder,
not_null<Data::Story*> removed) {
if (replyToStory.get() == removed) {
if (resolvedStory.get() == removed) {
clearData(holder);
holder->history()->owner().requestItemResize(holder);
}
@ -533,8 +661,8 @@ void HistoryMessageReply::paint(
const auto outerWidth = w + 2 * x;
const auto &bar = !inBubble
? st->msgImgReplyBarColor()
: replyToColorKey
? HistoryView::FromNameFg(context, replyToColorKey)
: _colorKey
? HistoryView::FromNameFg(context, _colorKey)
: stm->msgReplyBarColor;
const auto rbar = style::rtlrect(
x + st::msgReplyBarPos.x(),
@ -565,9 +693,10 @@ void HistoryMessageReply::paint(
const auto pausedSpoiler = context.paused
|| On(PowerSaving::kChatSpoiler);
if (w > st::msgReplyBarSkip) {
if (replyToMsg || replyToStory) {
const auto media = replyToMsg ? replyToMsg->media() : nullptr;
auto hasPreview = (replyToStory && replyToStory->hasReplyPreview()) || (media && media->hasReplyPreview());
if (resolvedMessage || resolvedStory) {
const auto media = resolvedMessage ? resolvedMessage->media() : nullptr;
auto hasPreview = (media && media->hasReplyPreview())
|| (resolvedStory && resolvedStory->hasReplyPreview());
if (hasPreview && w < st::msgReplyBarSkip + st::msgReplyBarSize.height()) {
hasPreview = false;
}
@ -576,7 +705,7 @@ void HistoryMessageReply::paint(
if (hasPreview) {
const auto image = media
? media->replyPreview()
: replyToStory->replyPreview();
: resolvedStory->replyPreview();
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);
const auto preview = image->pixSingle(
@ -604,26 +733,26 @@ void HistoryMessageReply::paint(
if (w > st::msgReplyBarSkip + previewSkip) {
p.setPen(!inBubble
? st->msgImgReplyBarColor()
: replyToColorKey
? HistoryView::FromNameFg(context, replyToColorKey)
: _colorKey
? HistoryView::FromNameFg(context, _colorKey)
: stm->msgServiceFg);
replyToName.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) {
_name.drawLeftElided(p, x + st::msgReplyBarSkip + previewSkip, y + st::msgReplyPadding.top(), w - st::msgReplyBarSkip - previewSkip, w + 2 * x);
if (originalVia && w > st::msgReplyBarSkip + previewSkip + _name.maxWidth() + st::msgServiceFont->spacew) {
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
? stm->historyTextFg
: st->msgImgReplyBarColor());
holder->prepareCustomEmojiPaint(p, context, replyToText);
holder->prepareCustomEmojiPaint(p, context, _text);
auto replyToTextPosition = QPoint(
x + st::msgReplyBarSkip + previewSkip,
y + st::msgReplyPadding.top() + st::msgServiceNameFont->height);
const auto replyToTextPalette = &(inBubble
? stm->replyTextPalette
: st->imgReplyTextPalette());
if (storyReply) {
if (_fields.storyId) {
st::dialogsMiniReplyStory.icon.icon.paint(
p,
replyToTextPosition,
@ -634,7 +763,7 @@ void HistoryMessageReply::paint(
+ st::dialogsMiniReplyStory.icon.icon.width(),
0);
}
replyToText.draw(p, {
_text.draw(p, {
.position = replyToTextPosition,
.availableWidth = w - st::msgReplyBarSkip - previewSkip,
.palette = replyToTextPalette,
@ -657,10 +786,14 @@ void HistoryMessageReply::paint(
}
}
void HistoryMessageReply::unloadPersistentAnimation() {
_text.unloadPersistentAnimation();
}
QString HistoryMessageReply::statePhrase() const {
return (replyToMsgId || replyToStoryId)
return ((_fields.messageId || _fields.storyId) && !_deleted)
? tr::lng_profile_loading(tr::now)
: storyReply
: _fields.storyId
? tr::lng_deleted_story(tr::now)
: tr::lng_deleted_message(tr::now);
}
@ -668,7 +801,7 @@ QString HistoryMessageReply::statePhrase() const {
void HistoryMessageReply::refreshReplyToMedia() {
replyToDocumentId = 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()) {
replyToDocumentId = document->id;
} 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> {
QString author;
QString postAuthor;
bool isAnonymousRank = false;
};
@ -123,7 +123,7 @@ struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded
TimeId originalDate = 0;
PeerData *originalSender = nullptr;
std::unique_ptr<HiddenSenderInfo> hiddenSenderInfo;
QString originalAuthor;
QString originalPostAuthor;
QString psaType;
MsgId originalId = 0;
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
: public RuntimeComponent<HistoryMessageReply, HistoryItem> {
HistoryMessageReply();
@ -238,17 +254,25 @@ struct HistoryMessageReply
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);
// Must be called before destructor.
void clearData(not_null<HistoryItem*> holder);
[[nodiscard]] PeerData *replyToFrom(not_null<HistoryItem*> holder) const;
[[nodiscard]] QString replyToFromName(
not_null<HistoryItem*> holder) const;
[[nodiscard]] QString replyToFromName(not_null<PeerData*> peer) const;
[[nodiscard]] PeerData *sender(not_null<HistoryItem*> holder) const;
[[nodiscard]] QString senderName(not_null<HistoryItem*> holder) const;
[[nodiscard]] QString senderName(not_null<PeerData*> peer) 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 itemRemoved(
not_null<HistoryItem*> holder,
@ -265,52 +289,63 @@ struct HistoryMessageReply
int y,
int w,
bool inBubble) const;
void unloadPersistentAnimation();
[[nodiscard]] PeerId replyToPeer() const {
return replyToPeerId;
[[nodiscard]] PeerId colorKey() const {
return _colorKey;
}
[[nodiscard]] MsgId replyToId() const {
return replyToMsgId;
[[nodiscard]] PeerId externalPeerId() const {
return _fields.externalPeerId;
}
[[nodiscard]] MsgId replyToTop() const {
return replyToMsgTop;
[[nodiscard]] MsgId messageId() const {
return _fields.messageId;
}
[[nodiscard]] int replyToWidth() const {
return maxReplyWidth;
[[nodiscard]] StoryId storyId() const {
return _fields.storyId;
}
[[nodiscard]] ClickHandlerPtr replyToLink() const {
return replyToLnk;
[[nodiscard]] MsgId topMessageId() const {
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;
void setReplyToLinkFrom(not_null<HistoryItem*> holder);
void setLinkFrom(not_null<HistoryItem*> holder);
void setTopMessageId(MsgId topMessageId);
void refreshReplyToMedia();
PeerId replyToPeerId = 0;
MsgId replyToMsgId = 0;
MsgId replyToMsgTop = 0;
StoryId replyToStoryId = 0;
using ColorKey = PeerId;
ColorKey replyToColorKey = 0;
DocumentId replyToDocumentId = 0;
WebPageId replyToWebPageId = 0;
ReplyToMessagePointer replyToMsg;
ReplyToStoryPointer replyToStory;
std::unique_ptr<HistoryMessageVia> replyToVia;
ReplyToMessagePointer resolvedMessage;
ReplyToStoryPointer resolvedStory;
std::unique_ptr<HistoryMessageVia> originalVia;
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;
bool topicPost = false;
bool storyReply = false;
struct final {
struct {
mutable std::unique_ptr<Ui::RippleAnimation> animation;
QPoint lastPoint;
} 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -117,7 +117,7 @@ void ForwardPanel::checkTexts() {
: kNameNoCaptionsVersion;
if (keepNames) {
for (const auto item : _data.items) {
if (const auto from = item->senderOriginal()) {
if (const auto from = item->originalSender()) {
version += from->nameVersion();
} else if (const auto info = item->hiddenSenderInfo()) {
++version;
@ -154,7 +154,7 @@ void ForwardPanel::updateTexts() {
auto names = std::vector<QString>();
names.reserve(_data.items.size());
for (const auto item : _data.items) {
if (const auto from = item->senderOriginal()) {
if (const auto from = item->originalSender()) {
if (!insertedPeers.contains(from)) {
insertedPeers.emplace(from);
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 (!msgsigned->isAnonymousRank) {
result.author = msgsigned->author;
result.author = msgsigned->postAuthor;
}
}
if (message->displayedEditDate()) {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1069,7 +1069,7 @@ TextState Gif::textState(QPoint point, StateRequest request) const {
if (reply) {
const auto replyRect = QRect(rectx, recty, rectw, recth);
if (replyRect.contains(point)) {
result.link = reply->replyToLink();
result.link = reply->link();
reply->ripple.lastPoint = point - replyRect.topLeft();
if (!reply->ripple.animation) {
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());
}
if (reply) {
accumulate_max(result, st::msgReplyPadding.left() + reply->replyToWidth());
accumulate_max(result, st::msgReplyPadding.left() + reply->maxWidth());
}
return result;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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