mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
Support topic on-the-fly creation.
This commit is contained in:
parent
065d2e2ac9
commit
3722e55b67
32 changed files with 447 additions and 130 deletions
|
@ -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),
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 = [=] {
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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());
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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, [=] {
|
||||||
|
|
Loading…
Add table
Reference in a new issue