Support topic on-the-fly creation.

This commit is contained in:
John Preston 2022-10-04 19:34:45 +04:00
parent 065d2e2ac9
commit 3722e55b67
32 changed files with 447 additions and 130 deletions

View file

@ -71,10 +71,10 @@ void Polls::create(
history, history,
replyTo, replyTo,
randomId, randomId,
MTPmessages_SendMedia( Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
MTP_flags(sendFlags), MTP_flags(sendFlags),
peer->input, peer->input,
MTP_int(replyTo), Data::Histories::ReplyToPlaceholder(),
PollDataToInputMedia(&data), PollDataToInputMedia(&data),
MTP_string(), MTP_string(),
MTP_long(randomId), MTP_long(randomId),

View file

@ -148,10 +148,10 @@ void SendExistingMedia(
history, history,
replyTo, replyTo,
randomId, randomId,
MTPmessages_SendMedia( Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
MTP_flags(sendFlags), MTP_flags(sendFlags),
peer->input, peer->input,
MTP_int(replyTo), Data::Histories::ReplyToPlaceholder(),
inputMedia(), inputMedia(),
MTP_string(captionText), MTP_string(captionText),
MTP_long(randomId), MTP_long(randomId),
@ -314,10 +314,10 @@ bool SendDice(MessageToSend &message) {
history, history,
replyTo, replyTo,
randomId, randomId,
MTPmessages_SendMedia( Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
MTP_flags(sendFlags), MTP_flags(sendFlags),
peer->input, peer->input,
MTP_int(replyTo), Data::Histories::ReplyToPlaceholder(),
MTP_inputMediaDice(MTP_string(emoji)), MTP_inputMediaDice(MTP_string(emoji)),
MTP_string(), MTP_string(),
MTP_long(randomId), MTP_long(randomId),
@ -347,9 +347,9 @@ void SendConfirmedFile(
&& (file->to.replaceMediaOf != 0); && (file->to.replaceMediaOf != 0);
const auto newId = FullMsgId( const auto newId = FullMsgId(
file->to.peer, file->to.peer,
isEditing (isEditing
? file->to.replaceMediaOf ? file->to.replaceMediaOf
: session->data().nextLocalMessageId()); : session->data().nextLocalMessageId()));
const auto groupId = file->album ? file->album->groupId : uint64(0); const auto groupId = file->album ? file->album->groupId : uint64(0);
if (file->album) { if (file->album) {
const auto proj = [](const SendingAlbum::Item &item) { const auto proj = [](const SendingAlbum::Item &item) {
@ -360,15 +360,21 @@ void SendConfirmedFile(
it->msgId = newId; it->msgId = newId;
} }
session->uploader().upload(newId, file);
const auto itemToEdit = isEditing const auto itemToEdit = isEditing
? session->data().message(newId) ? session->data().message(newId)
: nullptr; : nullptr;
const auto history = session->data().history(file->to.peer); const auto history = session->data().history(file->to.peer);
const auto peer = history->peer; const auto peer = history->peer;
if (!isEditing) {
file->to.replyTo = session->data().histories().convertTopicReplyTo(
history,
file->to.replyTo);
}
session->uploader().upload(newId, file);
auto action = SendAction(history, file->to.options); auto action = SendAction(history, file->to.options);
action.clearDraft = false; action.clearDraft = false;
action.replyTo = file->to.replyTo; action.replyTo = file->to.replyTo;

View file

@ -1534,7 +1534,8 @@ void Updates::feedUpdate(const MTPUpdate &update) {
const auto randomId = d.vrandom_id().v; const auto randomId = d.vrandom_id().v;
if (const auto id = session().data().messageIdByRandomId(randomId)) { if (const auto id = session().data().messageIdByRandomId(randomId)) {
const auto newId = d.vid().v; const auto newId = d.vid().v;
if (const auto local = session().data().message(id)) { auto &owner = session().data();
if (const auto local = owner.message(id)) {
if (local->isScheduled()) { if (local->isScheduled()) {
session().data().scheduledMessages().apply(d, local); session().data().scheduledMessages().apply(d, local);
} else { } else {
@ -1552,6 +1553,8 @@ void Updates::feedUpdate(const MTPUpdate &update) {
local->setRealId(d.vid().v); local->setRealId(d.vid().v);
} }
} }
} else {
owner.histories().checkTopicCreated(id, newId);
} }
session().data().unregisterMessageRandomId(randomId); session().data().unregisterMessageRandomId(randomId);
} }

View file

@ -3494,10 +3494,10 @@ void ApiWrap::sendMessage(MessageToSend &&message) {
history, history,
replyTo, replyTo,
randomId, randomId,
MTPmessages_SendMessage( Data::Histories::PrepareMessage<MTPmessages_SendMessage>(
MTP_flags(sendFlags), MTP_flags(sendFlags),
peer->input, peer->input,
MTP_int(replyTo), Data::Histories::ReplyToPlaceholder(),
msgText, msgText,
MTP_long(randomId), MTP_long(randomId),
MTPReplyMarkup(), MTPReplyMarkup(),
@ -3637,10 +3637,10 @@ void ApiWrap::sendInlineResult(
history, history,
replyTo, replyTo,
randomId, randomId,
MTPmessages_SendInlineBotResult( Data::Histories::PrepareMessage<MTPmessages_SendInlineBotResult>(
MTP_flags(sendFlags), MTP_flags(sendFlags),
peer->input, peer->input,
MTP_int(replyTo), Data::Histories::ReplyToPlaceholder(),
MTP_long(randomId), MTP_long(randomId),
MTP_long(data->getQueryId()), MTP_long(data->getQueryId()),
MTP_string(data->getId()), MTP_string(data->getId()),
@ -3784,10 +3784,10 @@ void ApiWrap::sendMediaWithRandomId(
history, history,
replyTo, replyTo,
randomId, randomId,
MTPmessages_SendMedia( Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
MTP_flags(flags), MTP_flags(flags),
peer->input, peer->input,
MTP_int(replyTo), Data::Histories::ReplyToPlaceholder(),
media, media,
MTP_string(caption.text), MTP_string(caption.text),
MTP_long(randomId), MTP_long(randomId),
@ -3889,10 +3889,10 @@ void ApiWrap::sendAlbumIfReady(not_null<SendingAlbum*> album) {
history, history,
replyTo, replyTo,
uint64(0), // randomId uint64(0), // randomId
MTPmessages_SendMultiMedia( Data::Histories::PrepareMessage<MTPmessages_SendMultiMedia>(
MTP_flags(flags), MTP_flags(flags),
peer->input, peer->input,
MTP_int(replyTo), Data::Histories::ReplyToPlaceholder(),
MTP_vector<MTPInputSingleMedia>(medias), MTP_vector<MTPInputSingleMedia>(medias),
MTP_int(album->options.scheduled), MTP_int(album->options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()) (sendAs ? sendAs->input : MTP_inputPeerEmpty())

View file

@ -69,10 +69,10 @@ void ShareBotGame(
history, history,
replyTo, replyTo,
randomId, randomId,
MTPmessages_SendMedia( Data::Histories::PrepareMessage<MTPmessages_SendMedia>(
MTP_flags(0), MTP_flags(0),
chat->input, chat->input,
MTP_int(0), Data::Histories::ReplyToPlaceholder(),
MTP_inputMediaGame( MTP_inputMediaGame(
MTP_inputGameShortName( MTP_inputGameShortName(
bot->inputUser, bot->inputUser,

View file

@ -139,7 +139,8 @@ void EditForumTopicBox(
const auto requestId = std::make_shared<mtpRequestId>(); const auto requestId = std::make_shared<mtpRequestId>();
const auto create = [=] { const auto create = [=] {
if (!forum->peer->isForum()) { const auto channel = forum->peer->asChannel();
if (!channel || !channel->isForum()) {
box->closeBox(); box->closeBox();
return; return;
} else if (title->getLastText().trimmed().isEmpty()) { } else if (title->getLastText().trimmed().isEmpty()) {
@ -149,30 +150,10 @@ void EditForumTopicBox(
controller->showSection( controller->showSection(
std::make_shared<HistoryView::RepliesMemento>( std::make_shared<HistoryView::RepliesMemento>(
forum, forum,
forum->peer->forum()->reserveCreatingId( channel->forum()->reserveCreatingId(
title->getLastText().trimmed(), title->getLastText().trimmed(),
state->iconId)), state->iconId)),
Window::SectionShow::Way::ClearStack); Window::SectionShow::Way::ClearStack);
#if 0 // #TODO forum create
const auto randomId = base::RandomValue<uint64>();
const auto api = &forum->session().api();
api->request(MTPchannels_CreateForumTopic(
MTP_flags(0),
forum->inputChannel,
MTP_string(title->getLastText().trimmed()),
MTPlong(), // icon_emoji_id
MTPInputMedia(),
MTP_string(message->getLastText().trimmed()),
MTP_long(randomId),
MTPVector<MTPMessageEntity>(),
MTPInputPeer() // send_as
)).done([=](const MTPUpdates &result) {
api->applyUpdates(result, randomId);
box->closeBox();
}).fail([=](const MTP::Error &error) {
api->sendMessageFail(error, forum, randomId);
}).send();
#endif
}; };
const auto save = [=] { const auto save = [=] {

View file

@ -124,20 +124,18 @@ void Forum::applyTopicAdded(
MsgId rootId, MsgId rootId,
const QString &title, const QString &title,
DocumentId iconId) { DocumentId iconId) {
if (const auto i = _topics.find(rootId); i != end(_topics)) { const auto i = _topics.find(rootId);
i->second->applyTitle(title); const auto raw = (i != end(_topics))
i->second->applyIconId(iconId); ? i->second.get()
} else { : _topics.emplace(
const auto raw = _topics.emplace(
rootId, rootId,
std::make_unique<ForumTopic>(_history, rootId) std::make_unique<ForumTopic>(_history, rootId)
).first->second.get(); ).first->second.get();
raw->applyTitle(title); raw->applyTitle(title);
raw->applyIconId(iconId); raw->applyIconId(iconId);
if (!creating(rootId)) { if (!creating(rootId)) {
raw->addToChatList(FilterId(), topicsList()); raw->addToChatList(FilterId(), topicsList());
_chatsListChanges.fire({}); _chatsListChanges.fire({});
}
} }
} }
@ -171,6 +169,25 @@ bool Forum::creating(MsgId rootId) const {
return _creatingRootIds.contains(rootId); return _creatingRootIds.contains(rootId);
} }
void Forum::created(MsgId rootId, MsgId realId) {
if (rootId == realId) {
return;
}
_creatingRootIds.remove(rootId);
const auto i = _topics.find(rootId);
Assert(i != end(_topics));
auto topic = std::move(i->second);
_topics.erase(i);
const auto id = FullMsgId(_history->peer->id, realId);
if (!_topics.contains(realId)) {
_topics.emplace(
realId,
std::move(topic)
).first->second->setRealRootId(realId);
}
_history->owner().notifyItemIdChange({ id, rootId });
}
ForumTopic *Forum::topicFor(not_null<HistoryItem*> item) { ForumTopic *Forum::topicFor(not_null<HistoryItem*> item) {
const auto maybe = topicFor(item->replyToTop()); const auto maybe = topicFor(item->replyToTop());
return maybe ? maybe : topicFor(item->topicRootId()); return maybe ? maybe : topicFor(item->topicRootId());

View file

@ -36,6 +36,7 @@ public:
const QString &title, const QString &title,
DocumentId iconId); DocumentId iconId);
void applyTopicRemoved(MsgId rootId); void applyTopicRemoved(MsgId rootId);
void applyTopicCreated(MsgId rootId, MsgId realId);
[[nodiscard]] ForumTopic *topicFor(not_null<HistoryItem*> item); [[nodiscard]] ForumTopic *topicFor(not_null<HistoryItem*> item);
[[nodiscard]] ForumTopic *topicFor(MsgId rootId); [[nodiscard]] ForumTopic *topicFor(MsgId rootId);
@ -46,6 +47,7 @@ public:
DocumentId iconId); DocumentId iconId);
void discardCreatingId(MsgId rootId); void discardCreatingId(MsgId rootId);
[[nodiscard]] bool creating(MsgId rootId) const; [[nodiscard]] bool creating(MsgId rootId) const;
void created(MsgId rootId, MsgId realId);
private: private:
void applyReceivedTopics( void applyReceivedTopics(

View file

@ -29,6 +29,8 @@ ForumTopic::ForumTopic(not_null<History*> history, MsgId rootId)
, _rootId(rootId) { , _rootId(rootId) {
} }
ForumTopic::~ForumTopic() = default;
not_null<ChannelData*> ForumTopic::channel() const { not_null<ChannelData*> ForumTopic::channel() const {
return _history->peer->asChannel(); return _history->peer->asChannel();
} }
@ -45,6 +47,10 @@ MsgId ForumTopic::rootId() const {
return _rootId; return _rootId;
} }
void ForumTopic::setRealRootId(MsgId realId) {
_rootId = realId;
}
void ForumTopic::applyTopic(const MTPForumTopic &topic) { void ForumTopic::applyTopic(const MTPForumTopic &topic) {
Expects(_rootId == topic.data().vid().v); Expects(_rootId == topic.data().vid().v);

View file

@ -30,6 +30,7 @@ public:
static constexpr auto kGeneralId = 1; static constexpr auto kGeneralId = 1;
ForumTopic(not_null<History*> history, MsgId rootId); ForumTopic(not_null<History*> history, MsgId rootId);
~ForumTopic();
ForumTopic(const ForumTopic &) = delete; ForumTopic(const ForumTopic &) = delete;
ForumTopic &operator=(const ForumTopic &) = delete; ForumTopic &operator=(const ForumTopic &) = delete;
@ -42,6 +43,8 @@ public:
return (_rootId == kGeneralId); return (_rootId == kGeneralId);
} }
void setRealRootId(MsgId realId);
void applyTopic(const MTPForumTopic &topic); void applyTopic(const MTPForumTopic &topic);
TimeId adjustedChatListTimeId() const override; TimeId adjustedChatListTimeId() const override;
@ -109,7 +112,7 @@ private:
const not_null<History*> _history; const not_null<History*> _history;
const not_null<Dialogs::MainList*> _list; const not_null<Dialogs::MainList*> _list;
const MsgId _rootId = 0; MsgId _rootId = 0;
QString _title; QString _title;
DocumentId _iconId = 0; DocumentId _iconId = 0;

View file

@ -11,8 +11,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_chat.h" #include "data/data_chat.h"
#include "data/data_folder.h" #include "data/data_folder.h"
#include "data/data_forum.h"
#include "data/data_forum_topic.h"
#include "data/data_scheduled_messages.h" #include "data/data_scheduled_messages.h"
#include "base/unixtime.h" #include "base/unixtime.h"
#include "base/random.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "window/notifications_manager.h" #include "window/notifications_manager.h"
#include "history/history.h" #include "history/history.h"
@ -841,14 +844,74 @@ int Histories::sendRequest(
return id; return id;
} }
void Histories::sendCreateTopicRequest(
not_null<History*> history,
MsgId rootId) {
Expects(history->peer->isChannel());
const auto forum = history->peer->forum();
Assert(forum != nullptr);
const auto topic = forum->topicFor(rootId);
Assert(topic != nullptr);
const auto randomId = base::RandomValue<uint64>();
session().data().registerMessageRandomId(
randomId,
{ history->peer->id, rootId });
const auto api = &session().api();
using Flag = MTPchannels_CreateForumTopic::Flag;
api->request(MTPchannels_CreateForumTopic(
MTP_flags(topic->iconId() ? Flag::f_icon_emoji_id : Flag(0)),
history->peer->asChannel()->inputChannel,
MTP_string(topic->title()),
MTP_long(topic->iconId()),
MTP_long(randomId),
MTPInputPeer() // send_as
)).done([=](const MTPUpdates &result) {
//AssertIsDebug();
//const auto id = result.c_updates().vupdates().v.front().c_updateMessageID().vrandom_id().v;
//session().data().registerMessageRandomId(
// id,
// { history->peer->id, rootId });
api->applyUpdates(result, randomId);
}).fail([=](const MTP::Error &error) {
api->sendMessageFail(error, history->peer, randomId);
}).send();
}
bool Histories::isCreatingTopic(
not_null<History*> history,
MsgId rootId) const {
const auto forum = history->peer->forum();
return forum && forum->creating(rootId);
}
int Histories::sendPreparedMessage( int Histories::sendPreparedMessage(
not_null<History*> history, not_null<History*> history,
MsgId replyTo, MsgId replyTo,
uint64 randomId, uint64 randomId,
PreparedMessage message, Fn<PreparedMessage(MsgId replyTo)> message,
Fn<void(const MTPUpdates&, const MTP::Response&)> done, Fn<void(const MTPUpdates&, const MTP::Response&)> done,
Fn<void(const MTP::Error&, const MTP::Response&)> fail) { Fn<void(const MTP::Error&, const MTP::Response&)> fail) {
return v::match(message, [&](const auto &request) { if (isCreatingTopic(history, replyTo)) {
const auto id = ++_requestAutoincrement;
const auto creatingId = FullMsgId(history->peer->id, replyTo);
auto i = _creatingTopics.find(creatingId);
if (i == end(_creatingTopics)) {
sendCreateTopicRequest(history, replyTo);
i = _creatingTopics.emplace(creatingId).first;
}
i->second.push_back({
.randomId = randomId,
.message = std::move(message),
.done = std::move(done),
.fail = std::move(fail),
.requestId = id,
});
_creatingTopicRequests.emplace(id);
return id;
}
const auto realTo = convertTopicReplyTo(history, replyTo);
return v::match(message(realTo), [&](const auto &request) {
const auto type = RequestType::Send; const auto type = RequestType::Send;
return sendRequest(history, type, [=](Fn<void()> finish) { return sendRequest(history, type, [=](Fn<void()> finish) {
const auto session = &_owner->session(); const auto session = &_owner->session();
@ -874,6 +937,54 @@ int Histories::sendPreparedMessage(
}); });
} }
void Histories::checkTopicCreated(FullMsgId rootId, MsgId realId) {
const auto i = _creatingTopics.find(rootId);
if (i != end(_creatingTopics)) {
auto scheduled = base::take(i->second);
_creatingTopics.erase(i);
_createdTopicIds.emplace(rootId, realId);
if (const auto forum = _owner->peer(rootId.peer)->forum()) {
forum->created(rootId.msg, realId);
}
const auto history = _owner->history(rootId.peer);
for (auto &entry : scheduled) {
_creatingTopicRequests.erase(entry.requestId);
//AssertIsDebug();
sendPreparedMessage(
history,
realId,
entry.randomId,
std::move(entry.message),
std::move(entry.done),
std::move(entry.fail));
}
for (const auto &item : history->clientSideMessages()) {
const auto replace = [&](MsgId nowId) {
return (nowId == rootId.msg) ? realId : nowId;
};
if (item->replyToTop() == rootId.msg) {
item->setReplyFields(
replace(item->replyToId()),
realId,
true);
}
}
}
}
MsgId Histories::convertTopicReplyTo(
not_null<History*> history,
MsgId replyTo) const {
if (!replyTo) {
return {};
}
const auto i = _createdTopicIds.find({ history->peer->id, replyTo });
return (i != end(_createdTopicIds)) ? i->second : replyTo;
}
void Histories::checkPostponed(not_null<History*> history, int id) { void Histories::checkPostponed(not_null<History*> history, int id) {
if (const auto state = lookup(history)) { if (const auto state = lookup(history)) {
finishSentRequest(history, state, id); finishSentRequest(history, state, id);
@ -883,6 +994,9 @@ void Histories::checkPostponed(not_null<History*> history, int id) {
void Histories::cancelRequest(int id) { void Histories::cancelRequest(int id) {
if (!id) { if (!id) {
return; return;
} else if (_creatingTopicRequests.contains(id)) {
cancelDelayedByTopicRequest(id);
return;
} }
const auto history = _historyByRequest.take(id); const auto history = _historyByRequest.take(id);
if (!history) { if (!history) {
@ -896,6 +1010,15 @@ void Histories::cancelRequest(int id) {
finishSentRequest(*history, state, id); finishSentRequest(*history, state, id);
} }
void Histories::cancelDelayedByTopicRequest(int id) {
for (auto &[rootId, messages] : _creatingTopics) {
messages.erase(
ranges::remove(messages, id, &DelayedByTopicMessage::requestId),
end(messages));
}
_creatingTopicRequests.remove(id);
}
void Histories::finishSentRequest( void Histories::finishSentRequest(
not_null<History*> history, not_null<History*> history,
not_null<State*> state, not_null<State*> state,

View file

@ -104,10 +104,25 @@ public:
not_null<History*> history, not_null<History*> history,
MsgId replyTo, MsgId replyTo,
uint64 randomId, uint64 randomId,
PreparedMessage message, Fn<PreparedMessage(MsgId replyTo)> message,
Fn<void(const MTPUpdates&, const MTP::Response &)> done, Fn<void(const MTPUpdates&, const MTP::Response&)> done,
Fn<void(const MTP::Error&, const MTP::Response&)> fail); Fn<void(const MTP::Error&, const MTP::Response&)> fail);
struct ReplyToPlaceholder {
};
template <typename RequestType, typename ...Args>
static Fn<Histories::PreparedMessage(MsgId)> PrepareMessage(
const Args &...args) {
return [=](MsgId replyTo) {
return RequestType(ReplaceReplyTo(args, replyTo)...);
};
}
void checkTopicCreated(FullMsgId rootId, MsgId realId);
[[nodiscard]] MsgId convertTopicReplyTo(
not_null<History*> history,
MsgId replyTo) const;
private: private:
struct PostponedHistoryRequest { struct PostponedHistoryRequest {
Fn<mtpRequestId(Fn<void()> finish)> generator; Fn<mtpRequestId(Fn<void()> finish)> generator;
@ -130,6 +145,22 @@ private:
MsgId aroundId = 0; MsgId aroundId = 0;
mtpRequestId requestId = 0; mtpRequestId requestId = 0;
}; };
struct DelayedByTopicMessage {
uint64 randomId = 0;
Fn<PreparedMessage(MsgId replyTo)> message;
Fn<void(const MTPUpdates&, const MTP::Response&)> done;
Fn<void(const MTP::Error&, const MTP::Response&)> fail;
int requestId = 0;
};
template <typename Arg>
static auto ReplaceReplyTo(Arg arg, MsgId replyTo) {
return arg;
}
template <>
static auto ReplaceReplyTo(ReplyToPlaceholder, MsgId replyTo) {
return MTP_int(replyTo);
}
void readInboxTill(not_null<History*> history, MsgId tillId, bool force); void readInboxTill(not_null<History*> history, MsgId tillId, bool force);
void sendReadRequests(); void sendReadRequests();
@ -147,6 +178,12 @@ private:
void sendDialogRequests(); void sendDialogRequests();
[[nodiscard]] bool isCreatingTopic(
not_null<History*> history,
MsgId rootId) const;
void sendCreateTopicRequest(not_null<History*> history, MsgId rootId);
void cancelDelayedByTopicRequest(int id);
const not_null<Session*> _owner; const not_null<Session*> _owner;
std::unordered_map<PeerId, std::unique_ptr<History>> _map; std::unordered_map<PeerId, std::unique_ptr<History>> _map;
@ -169,6 +206,12 @@ private:
not_null<History*>, not_null<History*>,
ChatListGroupRequest> _chatListGroupRequests; ChatListGroupRequest> _chatListGroupRequests;
base::flat_map<
FullMsgId,
std::vector<DelayedByTopicMessage>> _creatingTopics;
base::flat_map<FullMsgId, MsgId> _createdTopicIds;
base::flat_set<mtpRequestId> _creatingTopicRequests;
}; };
} // namespace Data } // namespace Data

View file

@ -137,9 +137,6 @@ void RepliesList::appendClientSideMessages(MessagesSlice &slice) {
} }
slice.ids.reserve(messages.size()); slice.ids.reserve(messages.size());
for (const auto &item : messages) { for (const auto &item : messages) {
const auto checkId = (_rootId == ForumTopic::kGeneralId)
? item->topicRootId()
: item->replyToTop();
if (!item->inThread(_rootId)) { if (!item->inThread(_rootId)) {
continue; continue;
} }

View file

@ -1407,10 +1407,12 @@ rpl::producer<not_null<HistoryItem*>> Session::newItemAdded() const {
return _newItemAdded.events(); return _newItemAdded.events();
} }
void Session::changeMessageId(PeerId peerId, MsgId wasId, MsgId nowId) { HistoryItem *Session::changeMessageId(PeerId peerId, MsgId wasId, MsgId nowId) {
const auto list = messagesListForInsert(peerId); const auto list = messagesListForInsert(peerId);
auto i = list->find(wasId); const auto i = list->find(wasId);
Assert(i != list->end()); if (i == list->end()) {
return nullptr;
}
const auto item = i->second; const auto item = i->second;
list->erase(i); list->erase(i);
const auto [j, ok] = list->emplace(nowId, item); const auto [j, ok] = list->emplace(nowId, item);
@ -1427,6 +1429,7 @@ void Session::changeMessageId(PeerId peerId, MsgId wasId, MsgId nowId) {
} }
Ensures(ok); Ensures(ok);
return item;
} }
bool Session::queryItemVisibility(not_null<HistoryItem*> item) const { bool Session::queryItemVisibility(not_null<HistoryItem*> item) const {
@ -1448,19 +1451,23 @@ void Session::itemVisibilitiesUpdated() {
} }
void Session::notifyItemIdChange(IdChange event) { void Session::notifyItemIdChange(IdChange event) {
const auto item = event.item; const auto item = changeMessageId(
changeMessageId(item->history()->peer->id, event.oldId, item->id); event.newId.peer,
event.oldId,
event.newId.msg);
_itemIdChanges.fire_copy(event); _itemIdChanges.fire_copy(event);
const auto refreshViewDataId = [](not_null<ViewElement*> view) { if (item) {
view->refreshDataId(); const auto refreshViewDataId = [](not_null<ViewElement*> view) {
}; view->refreshDataId();
enumerateItemViews(item, refreshViewDataId); };
if (const auto group = groups().find(item)) { enumerateItemViews(item, refreshViewDataId);
const auto leader = group->items.front(); if (const auto group = groups().find(item)) {
if (leader != item) { const auto leader = group->items.front();
enumerateItemViews(leader, refreshViewDataId); if (leader != item) {
enumerateItemViews(leader, refreshViewDataId);
}
} }
} }
} }

View file

@ -243,7 +243,7 @@ public:
void itemVisibilitiesUpdated(); void itemVisibilitiesUpdated();
struct IdChange { struct IdChange {
not_null<HistoryItem*> item; FullMsgId newId;
MsgId oldId = 0; MsgId oldId = 0;
}; };
void notifyItemIdChange(IdChange event); void notifyItemIdChange(IdChange event);
@ -728,7 +728,7 @@ private:
not_null<Messages*> messagesListForInsert(PeerId peerId); not_null<Messages*> messagesListForInsert(PeerId peerId);
not_null<HistoryItem*> registerMessage( not_null<HistoryItem*> registerMessage(
std::unique_ptr<HistoryItem> item); std::unique_ptr<HistoryItem> item);
void changeMessageId(PeerId peerId, MsgId wasId, MsgId nowId); HistoryItem *changeMessageId(PeerId peerId, MsgId wasId, MsgId nowId);
void removeDependencyMessage(not_null<HistoryItem*> item); void removeDependencyMessage(not_null<HistoryItem*> item);
void photoApplyFields( void photoApplyFields(

View file

@ -660,9 +660,11 @@ void HistoryItem::applySentMessage(const MTPDmessage &data) {
setForwardsCount(data.vforwards().value_or(-1)); setForwardsCount(data.vforwards().value_or(-1));
if (const auto reply = data.vreply_to()) { if (const auto reply = data.vreply_to()) {
reply->match([&](const MTPDmessageReplyHeader &data) { reply->match([&](const MTPDmessageReplyHeader &data) {
setReplyToTop( setReplyFields(
data.vreply_to_msg_id().v,
data.vreply_to_top_id().value_or( data.vreply_to_top_id().value_or(
data.vreply_to_msg_id().v)); data.vreply_to_msg_id().v),
data.is_forum_topic());
}); });
} }
setPostAuthor(data.vpost_author().value_or_empty()); setPostAuthor(data.vpost_author().value_or_empty());
@ -715,7 +717,7 @@ void HistoryItem::setRealId(MsgId newId) {
if (isRegular()) { if (isRegular()) {
_history->unregisterClientSideMessage(this); _history->unregisterClientSideMessage(this);
} }
_history->owner().notifyItemIdChange({ this, oldId }); _history->owner().notifyItemIdChange({ fullId(), oldId });
// We don't fire MessageUpdate::Flag::ReplyMarkup and update keyboard // We don't fire MessageUpdate::Flag::ReplyMarkup and update keyboard
// in history widget, because it can't exist for an outgoing message. // in history widget, because it can't exist for an outgoing message.

View file

@ -341,7 +341,10 @@ public:
PeerId replier, PeerId replier,
std::optional<bool> unread) { std::optional<bool> unread) {
} }
virtual void setReplyToTop(MsgId replyToTop) = 0; virtual void setReplyFields(
MsgId replyTo,
MsgId replyToTop,
bool isForumPost) = 0;
virtual void setPostAuthor(const QString &author) { virtual void setPostAuthor(const QString &author) {
} }
virtual void setRealId(MsgId newId); virtual void setRealId(MsgId newId);

View file

@ -32,6 +32,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_media_types.h" #include "data/data_media_types.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_forum_topic.h" #include "data/data_forum_topic.h"
#include "data/data_forum.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_web_page.h" #include "data/data_web_page.h"
#include "data/data_sponsored_messages.h" #include "data/data_sponsored_messages.h"
@ -173,6 +174,9 @@ void RequestDependentMessageData(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
PeerId peerId, PeerId peerId,
MsgId msgId) { MsgId msgId) {
if (!IsServerMsgId(msgId)) {
return;
}
const auto fullId = item->fullId(); const auto fullId = item->fullId();
const auto history = item->history(); const auto history = item->history();
const auto session = &history->session(); const auto session = &history->session();
@ -332,6 +336,7 @@ HistoryMessage::HistoryMessage(
: id; : id;
config.replyToTop = data.vreply_to_top_id().value_or( config.replyToTop = data.vreply_to_top_id().value_or(
data.vreply_to_msg_id().v); data.vreply_to_msg_id().v);
config.replyIsTopicPost = data.is_forum_topic();
}); });
} }
config.viaBotId = data.vvia_bot_id().value_or_empty(); config.viaBotId = data.vvia_bot_id().value_or_empty();
@ -654,8 +659,10 @@ void HistoryMessage::createComponentsHelper(
const auto to = LookupReplyTo(history(), replyTo); const auto to = LookupReplyTo(history(), replyTo);
const auto replyToTop = LookupReplyToTop(to); const auto replyToTop = LookupReplyToTop(to);
config.replyToTop = replyToTop ? replyToTop : replyTo; config.replyToTop = replyToTop ? replyToTop : replyTo;
const auto forum = history()->peer->forum();
config.replyIsTopicPost = LookupReplyIsTopicPost(to) config.replyIsTopicPost = LookupReplyIsTopicPost(to)
|| to->Has<HistoryServiceTopicInfo>(); || (to && to->Has<HistoryServiceTopicInfo>())
|| (forum && forum->creating(replyToTop));
} }
config.markup = std::move(markup); config.markup = std::move(markup);
if (flags & MessageFlag::HasPostAuthor) config.author = postAuthor; if (flags & MessageFlag::HasPostAuthor) config.author = postAuthor;
@ -1811,16 +1818,30 @@ void HistoryMessage::setSponsoredFrom(const Data::SponsoredFrom &from) {
: Type::Group; : Type::Group;
} }
void HistoryMessage::setReplyToTop(MsgId replyToTop) { void HistoryMessage::setReplyFields(
MsgId replyTo,
MsgId replyToTop,
bool isForumPost) {
const auto reply = Get<HistoryMessageReply>(); const auto reply = Get<HistoryMessageReply>();
if (!reply if (!reply || isScheduled()) {
|| (reply->replyToMsgTop == replyToTop)
|| (reply->replyToMsgTop != 0)
|| isScheduled()) {
return; return;
} }
reply->replyToMsgTop = replyToTop; reply->topicPost = isForumPost;
changeReplyToTopCounter(reply, 1); if ((reply->replyToMsgId != replyTo)
&& !IsServerMsgId(reply->replyToMsgId)) {
reply->replyToMsgId = replyTo;
if (!reply->updateData(this)) {
RequestDependentMessageData(
this,
reply->replyToPeerId,
reply->replyToMsgId);
}
}
if ((reply->replyToMsgTop != replyToTop)
&& !IsServerMsgId(reply->replyToMsgTop)) {
reply->replyToMsgTop = replyToTop;
changeReplyToTopCounter(reply, 1);
}
} }
void HistoryMessage::setRealId(MsgId newId) { void HistoryMessage::setRealId(MsgId newId) {

View file

@ -146,7 +146,10 @@ public:
int delta, int delta,
PeerId replier, PeerId replier,
std::optional<bool> unread) override; std::optional<bool> unread) override;
void setReplyToTop(MsgId replyToTop) override; void setReplyFields(
MsgId replyTo,
MsgId replyToTop,
bool isForumPost) override;
void setPostAuthor(const QString &author) override; void setPostAuthor(const QString &author) override;
void setRealId(MsgId newId) override; void setRealId(MsgId newId) override;
void incrementReplyToTopCounter() override; void incrementReplyToTopCounter() override;

View file

@ -1318,12 +1318,12 @@ MsgId HistoryService::topicRootId() const {
return Data::ForumTopic::kGeneralId; return Data::ForumTopic::kGeneralId;
} }
void HistoryService::setReplyToTop(MsgId replyToTop) { void HistoryService::setReplyFields(
MsgId replyTo,
MsgId replyToTop,
bool isForumPost) {
const auto data = GetDependentData(); const auto data = GetDependentData();
if (!data if (!data || IsServerMsgId(data->topId) || isScheduled()) {
|| (data->topId == replyToTop)
|| (data->topId != 0)
|| isScheduled()) {
return; return;
} }
data->topId = replyToTop; data->topId = replyToTop;

View file

@ -139,7 +139,10 @@ public:
MsgId replyToId() const override; MsgId replyToId() const override;
MsgId replyToTop() const override; MsgId replyToTop() const override;
MsgId topicRootId() const override; MsgId topicRootId() const override;
void setReplyToTop(MsgId replyToTop) override; void setReplyFields(
MsgId replyTo,
MsgId replyToTop,
bool isForumPost) override;
std::unique_ptr<HistoryView::Element> createView( std::unique_ptr<HistoryView::Element> createView(
not_null<HistoryView::ElementDelegate*> delegate, not_null<HistoryView::ElementDelegate*> delegate,

View file

@ -154,6 +154,16 @@ object_ptr<Window::SectionWidget> RepliesMemento::createWidget(
return result; return result;
} }
void RepliesMemento::setupTopicViewer() {
_history->owner().itemIdChanged(
) | rpl::start_with_next([=](const Data::Session::IdChange &change) {
if (_rootId == change.oldId) {
_rootId = change.newId.msg;
_replies = nullptr;
}
}, _lifetime);
}
RepliesWidget::RepliesWidget( RepliesWidget::RepliesWidget(
QWidget *parent, QWidget *parent,
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
@ -199,6 +209,7 @@ RepliesWidget::RepliesWidget(
setupRoot(); setupRoot();
setupRootView(); setupRootView();
setupTopicViewer();
session().api().requestFullPeer(_history->peer); session().api().requestFullPeer(_history->peer);
@ -442,6 +453,27 @@ void RepliesWidget::setupRootView() {
}, _rootView->lifetime()); }, _rootView->lifetime());
} }
void RepliesWidget::setupTopicViewer() {
_history->owner().itemIdChanged(
) | rpl::start_with_next([=](const Data::Session::IdChange &change) {
if (_rootId == change.oldId) {
_rootId = change.newId.msg;
_root = lookupRoot();
createReplies();
if (_topic && _topic->rootId() == change.oldId) {
setTopic(_topic->forum()->topicFor(change.newId.msg));
}
_inner->update();
}
}, lifetime());
}
void RepliesWidget::setTopic(Data::ForumTopic *topic) {
if ((_topic = topic)) {
refreshTopBarActiveChat();
}
}
HistoryItem *RepliesWidget::lookupRoot() const { HistoryItem *RepliesWidget::lookupRoot() const {
return _history->owner().message(_history->peer, _rootId); return _history->owner().message(_history->peer, _rootId);
} }
@ -458,9 +490,7 @@ Data::ForumTopic *RepliesWidget::lookupTopic() {
).done([=](const MTPmessages_ForumTopics &result) { ).done([=](const MTPmessages_ForumTopics &result) {
if (const auto forum = _history->peer->forum()) { if (const auto forum = _history->peer->forum()) {
forum->applyReceivedTopics(result); forum->applyReceivedTopics(result);
if ((_topic = forum->topicFor(_rootId))) { setTopic(forum->topicFor(_rootId));
refreshTopBarActiveChat();
}
} }
_resolveTopicRequestId = 0; _resolveTopicRequestId = 0;
}).fail([=] { }).fail([=] {
@ -1699,32 +1729,44 @@ void RepliesWidget::saveState(not_null<RepliesMemento*> memento) {
_inner->saveState(memento->list()); _inner->saveState(memento->list());
} }
void RepliesWidget::restoreState(not_null<RepliesMemento*> memento) { void RepliesWidget::createReplies() {
const auto setReplies = [&](std::shared_ptr<Data::RepliesList> replies) { auto old = base::take(_replies);
_replies = std::move(replies); setReplies(std::make_shared<Data::RepliesList>(_history, _rootId));
if (old) {
_inner->showAroundPosition(Data::UnreadMessagePosition, nullptr);
}
}
rpl::combine( void RepliesWidget::setReplies(std::shared_ptr<Data::RepliesList> replies) {
rpl::single(0) | rpl::then(_replies->fullCount()), _replies = std::move(replies);
_areComments.value() _repliesLifetime.destroy();
) | rpl::map([=](int count, bool areComments) { if (_topic) {
return count return;
? (areComments }
? tr::lng_comments_header rpl::combine(
: tr::lng_replies_header)( rpl::single(0) | rpl::then(_replies->fullCount()),
lt_count_decimal, _areComments.value()
rpl::single(count) | tr::to_count()) ) | rpl::map([=](int count, bool areComments) {
: (areComments return count
? tr::lng_comments_header_none ? (areComments
: tr::lng_replies_header_none)(); ? tr::lng_comments_header
}) | rpl::flatten_latest( : tr::lng_replies_header)(
) | rpl::start_with_next([=](const QString &text) { lt_count_decimal,
_topBar->setCustomTitle(text); rpl::single(count) | tr::to_count())
}, lifetime()); : (areComments
}; ? tr::lng_comments_header_none
: tr::lng_replies_header_none)();
}) | rpl::flatten_latest(
) | rpl::start_with_next([=](const QString &text) {
_topBar->setCustomTitle(text);
}, _repliesLifetime);
}
void RepliesWidget::restoreState(not_null<RepliesMemento*> memento) {
if (auto replies = memento->getReplies()) { if (auto replies = memento->getReplies()) {
setReplies(std::move(replies)); setReplies(std::move(replies));
} else if (!_replies) { } else if (!_replies) {
setReplies(std::make_shared<Data::RepliesList>(_history, _rootId)); createReplies();
} }
restoreReplyReturns(memento->replyReturns()); restoreReplyReturns(memento->replyReturns());
_inner->restoreState(memento->list()); _inner->restoreState(memento->list());

View file

@ -163,6 +163,8 @@ private:
void updateAdaptiveLayout(); void updateAdaptiveLayout();
void saveState(not_null<RepliesMemento*> memento); void saveState(not_null<RepliesMemento*> memento);
void restoreState(not_null<RepliesMemento*> memento); void restoreState(not_null<RepliesMemento*> memento);
void setReplies(std::shared_ptr<Data::RepliesList> replies);
void createReplies();
void showAtStart(); void showAtStart();
void showAtEnd(); void showAtEnd();
void showAtPosition( void showAtPosition(
@ -178,6 +180,8 @@ private:
void setupRoot(); void setupRoot();
void setupRootView(); void setupRootView();
void setupTopicViewer();
void setTopic(Data::ForumTopic *topic);
void setupDragArea(); void setupDragArea();
void sendReadTillRequest(); void sendReadTillRequest();
void readTill(not_null<HistoryItem*> item); void readTill(not_null<HistoryItem*> item);
@ -276,6 +280,7 @@ private:
mutable bool _newTopicDiscarded = false; mutable bool _newTopicDiscarded = false;
std::shared_ptr<Data::RepliesList> _replies; std::shared_ptr<Data::RepliesList> _replies;
rpl::lifetime _repliesLifetime;
rpl::variable<bool> _areComments = false; rpl::variable<bool> _areComments = false;
std::shared_ptr<SendActionPainter> _sendAction; std::shared_ptr<SendActionPainter> _sendAction;
QPointer<ListWidget> _inner; QPointer<ListWidget> _inner;
@ -363,13 +368,17 @@ public:
} }
private: private:
void setupTopicViewer();
const not_null<History*> _history; const not_null<History*> _history;
const MsgId _rootId = 0; MsgId _rootId = 0;
const MsgId _highlightId = 0; const MsgId _highlightId = 0;
ListMemento _list; ListMemento _list;
std::shared_ptr<Data::RepliesList> _replies; std::shared_ptr<Data::RepliesList> _replies;
std::vector<MsgId> _replyReturns; std::vector<MsgId> _replyReturns;
rpl::lifetime _lifetime;
}; };
} // namespace HistoryView } // namespace HistoryView

View file

@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_forum_topic.h" #include "data/data_forum_topic.h"
#include "data/data_forum.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "styles/style_info.h" #include "styles/style_info.h"
#include "styles/style_profile.h" #include "styles/style_profile.h"
@ -340,6 +341,12 @@ ContentMemento::ContentMemento(not_null<Data::ForumTopic*> topic)
: _peer(topic->channel()) : _peer(topic->channel())
, _migratedPeerId(_peer->migrateFrom() ? _peer->migrateFrom()->id : 0) , _migratedPeerId(_peer->migrateFrom() ? _peer->migrateFrom()->id : 0)
, _topic(topic) { , _topic(topic) {
_peer->owner().itemIdChanged(
) | rpl::start_with_next([=](const Data::Session::IdChange &change) {
if (_topic->rootId() == change.oldId) {
_topic = _topic->forum()->topicFor(change.newId.msg);
}
}, _lifetime);
} }
ContentMemento::ContentMemento(Settings::Tag settings) ContentMemento::ContentMemento(Settings::Tag settings)

View file

@ -213,7 +213,7 @@ public:
private: private:
PeerData * const _peer = nullptr; PeerData * const _peer = nullptr;
const PeerId _migratedPeerId = 0; const PeerId _migratedPeerId = 0;
Data::ForumTopic * const _topic = nullptr; Data::ForumTopic *_topic = nullptr;
UserData * const _settingsSelf = nullptr; UserData * const _settingsSelf = nullptr;
PollData * const _poll = nullptr; PollData * const _poll = nullptr;
const FullMsgId _pollContextId; const FullMsgId _pollContextId;
@ -223,6 +223,8 @@ private:
bool _searchEnabledByContent = false; bool _searchEnabledByContent = false;
bool _searchStartsFocused = false; bool _searchStartsFocused = false;
rpl::lifetime _lifetime;
}; };
} // namespace Info } // namespace Info

View file

@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_chat.h" #include "data/data_chat.h"
#include "data/data_forum_topic.h" #include "data/data_forum_topic.h"
#include "data/data_forum.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_media_types.h" #include "data/data_media_types.h"
#include "data/data_download_manager.h" #include "data/data_download_manager.h"
@ -180,6 +181,7 @@ Controller::Controller(
, _section(memento->section()) { , _section(memento->section()) {
updateSearchControllers(memento); updateSearchControllers(memento);
setupMigrationViewer(); setupMigrationViewer();
setupTopicViewer();
} }
void Controller::setupMigrationViewer() { void Controller::setupMigrationViewer() {
@ -210,6 +212,17 @@ void Controller::setupMigrationViewer() {
}, lifetime()); }, lifetime());
} }
void Controller::setupTopicViewer() {
session().data().itemIdChanged(
) | rpl::start_with_next([=](const Data::Session::IdChange &change) {
if (const auto topic = _key.topic()) {
if (topic->rootId() == change.oldId) {
_key = Key(topic->forum()->topicFor(change.newId.msg));
}
}
}, _lifetime);
}
Wrap Controller::wrap() const { Wrap Controller::wrap() const {
return _widget->wrap(); return _widget->wrap();
} }

View file

@ -235,6 +235,7 @@ private:
void updateSearchControllers(not_null<ContentMemento*> memento); void updateSearchControllers(not_null<ContentMemento*> memento);
SearchQuery produceSearchQuery(const QString &query) const; SearchQuery produceSearchQuery(const QString &query) const;
void setupMigrationViewer(); void setupMigrationViewer();
void setupTopicViewer();
not_null<WrapWidget*> _widget; not_null<WrapWidget*> _widget;
Key _key; Key _key;

View file

@ -28,7 +28,7 @@ class Memento final : public ContentMemento {
public: public:
explicit Memento(not_null<Controller*> controller); explicit Memento(not_null<Controller*> controller);
Memento(not_null<PeerData*> peer, PeerId migratedPeerId, Type type); Memento(not_null<PeerData*> peer, PeerId migratedPeerId, Type type);
Memento(not_null<Data::ForumTopic*> peer, Type type); Memento(not_null<Data::ForumTopic*> topic, Type type);
using SearchState = Api::DelayedSearchController::SavedState; using SearchState = Api::DelayedSearchController::SavedState;

View file

@ -702,8 +702,8 @@ void OverlayWidget::documentUpdated(not_null<DocumentData*> document) {
} }
} }
void OverlayWidget::changingMsgId(not_null<HistoryItem*> row, MsgId oldId) { void OverlayWidget::changingMsgId(FullMsgId newId, MsgId oldId) {
if (row == _message) { if (_message && _message->fullId() == newId) {
refreshMediaViewer(); refreshMediaViewer();
} }
} }
@ -4181,7 +4181,7 @@ void OverlayWidget::setSession(not_null<Main::Session*> session) {
session->data().itemIdChanged( session->data().itemIdChanged(
) | rpl::start_with_next([=](const Data::Session::IdChange &change) { ) | rpl::start_with_next([=](const Data::Session::IdChange &change) {
changingMsgId(change.item, change.oldId); changingMsgId(change.newId, change.oldId);
}, _sessionLifetime); }, _sessionLifetime);
session->data().itemRemoved( session->data().itemRemoved(

View file

@ -330,7 +330,7 @@ private:
void updateThemePreviewGeometry(); void updateThemePreviewGeometry();
void documentUpdated(not_null<DocumentData*> document); void documentUpdated(not_null<DocumentData*> document);
void changingMsgId(not_null<HistoryItem*> row, MsgId oldId); void changingMsgId(FullMsgId newId, MsgId oldId);
[[nodiscard]] int finalContentRotation() const; [[nodiscard]] int finalContentRotation() const;
[[nodiscard]] QRect finalContentRect() const; [[nodiscard]] QRect finalContentRect() const;

View file

@ -68,7 +68,7 @@ AlbumThumbnail::AlbumThumbnail(
const auto availableFileWidth = st::sendMediaPreviewSize const auto availableFileWidth = st::sendMediaPreviewSize
- st.thumbSize - st.thumbSize
- st.padding.right() - st.thumbSkip
// Right buttons. // Right buttons.
- st::sendBoxAlbumGroupButtonFile.width * 2 - st::sendBoxAlbumGroupButtonFile.width * 2
- st::sendBoxAlbumGroupEditInternalSkip * 2 - st::sendBoxAlbumGroupEditInternalSkip * 2
@ -392,7 +392,7 @@ void AlbumThumbnail::paintFile(
int top, int top,
int outerWidth) { int outerWidth) {
const auto &st = st::attachPreviewThumbLayout; const auto &st = st::attachPreviewThumbLayout;
const auto textLeft = left + st.thumbSize + st.padding.right(); const auto textLeft = left + st.thumbSize + st.thumbSkip;
p.drawPixmap(left, top, _fileThumb); p.drawPixmap(left, top, _fileThumb);
p.setFont(st::semiboldFont); p.setFont(st::semiboldFont);

View file

@ -39,6 +39,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document_resolver.h" #include "data/data_document_resolver.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_group_call.h" #include "data/data_group_call.h"
#include "data/data_forum.h"
#include "data/data_forum_topic.h"
#include "data/data_chat_filters.h" #include "data/data_chat_filters.h"
#include "data/data_peer_values.h" #include "data/data_peer_values.h"
#include "passport/passport_form_controller.h" #include "passport/passport_form_controller.h"
@ -674,6 +676,27 @@ SessionController::SessionController(
}); });
}, lifetime()); }, lifetime());
session->data().itemIdChanged(
) | rpl::start_with_next([=](Data::Session::IdChange change) {
const auto current = _activeChatEntry.current();
if (const auto topic = current.key.topic()) {
if (topic->rootId() == change.oldId) {
setActiveChatEntry({
Dialogs::Key(topic->forum()->topicFor(change.newId.msg)),
current.fullId,
});
}
}
for (auto &entry : _chatEntryHistory) {
if (const auto topic = entry.key.topic()) {
if (topic->rootId() == change.oldId) {
entry.key = Dialogs::Key(
topic->forum()->topicFor(change.newId.msg));
}
}
}
}, lifetime());
session->api().globalPrivacy().suggestArchiveAndMute( session->api().globalPrivacy().suggestArchiveAndMute(
) | rpl::take(1) | rpl::start_with_next([=] { ) | rpl::take(1) | rpl::start_with_next([=] {
session->api().globalPrivacy().reload(crl::guard(this, [=] { session->api().globalPrivacy().reload(crl::guard(this, [=] {