2
0
mirror of https://github.com/kotatogram/kotatogram-desktop synced 2025-08-31 06:35:14 +00:00

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

@@ -124,20 +124,18 @@ void Forum::applyTopicAdded(
MsgId rootId,
const QString &title,
DocumentId iconId) {
if (const auto i = _topics.find(rootId); i != end(_topics)) {
i->second->applyTitle(title);
i->second->applyIconId(iconId);
} else {
const auto raw = _topics.emplace(
const auto i = _topics.find(rootId);
const auto raw = (i != end(_topics))
? i->second.get()
: _topics.emplace(
rootId,
std::make_unique<ForumTopic>(_history, rootId)
).first->second.get();
raw->applyTitle(title);
raw->applyIconId(iconId);
if (!creating(rootId)) {
raw->addToChatList(FilterId(), topicsList());
_chatsListChanges.fire({});
}
raw->applyTitle(title);
raw->applyIconId(iconId);
if (!creating(rootId)) {
raw->addToChatList(FilterId(), topicsList());
_chatsListChanges.fire({});
}
}
@@ -171,6 +169,25 @@ bool Forum::creating(MsgId rootId) const {
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) {
const auto maybe = topicFor(item->replyToTop());
return maybe ? maybe : topicFor(item->topicRootId());

View File

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

View File

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

View File

@@ -30,6 +30,7 @@ public:
static constexpr auto kGeneralId = 1;
ForumTopic(not_null<History*> history, MsgId rootId);
~ForumTopic();
ForumTopic(const ForumTopic &) = delete;
ForumTopic &operator=(const ForumTopic &) = delete;
@@ -42,6 +43,8 @@ public:
return (_rootId == kGeneralId);
}
void setRealRootId(MsgId realId);
void applyTopic(const MTPForumTopic &topic);
TimeId adjustedChatListTimeId() const override;
@@ -109,7 +112,7 @@ private:
const not_null<History*> _history;
const not_null<Dialogs::MainList*> _list;
const MsgId _rootId = 0;
MsgId _rootId = 0;
QString _title;
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_chat.h"
#include "data/data_folder.h"
#include "data/data_forum.h"
#include "data/data_forum_topic.h"
#include "data/data_scheduled_messages.h"
#include "base/unixtime.h"
#include "base/random.h"
#include "main/main_session.h"
#include "window/notifications_manager.h"
#include "history/history.h"
@@ -841,14 +844,74 @@ int Histories::sendRequest(
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(
not_null<History*> history,
MsgId replyTo,
uint64 randomId,
PreparedMessage message,
Fn<PreparedMessage(MsgId replyTo)> message,
Fn<void(const MTPUpdates&, const MTP::Response&)> done,
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;
return sendRequest(history, type, [=](Fn<void()> finish) {
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) {
if (const auto state = lookup(history)) {
finishSentRequest(history, state, id);
@@ -883,6 +994,9 @@ void Histories::checkPostponed(not_null<History*> history, int id) {
void Histories::cancelRequest(int id) {
if (!id) {
return;
} else if (_creatingTopicRequests.contains(id)) {
cancelDelayedByTopicRequest(id);
return;
}
const auto history = _historyByRequest.take(id);
if (!history) {
@@ -896,6 +1010,15 @@ void Histories::cancelRequest(int 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(
not_null<History*> history,
not_null<State*> state,

View File

@@ -104,10 +104,25 @@ public:
not_null<History*> history,
MsgId replyTo,
uint64 randomId,
PreparedMessage message,
Fn<void(const MTPUpdates&, const MTP::Response &)> done,
Fn<PreparedMessage(MsgId replyTo)> message,
Fn<void(const MTPUpdates&, const MTP::Response&)> done,
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:
struct PostponedHistoryRequest {
Fn<mtpRequestId(Fn<void()> finish)> generator;
@@ -130,6 +145,22 @@ private:
MsgId aroundId = 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 sendReadRequests();
@@ -147,6 +178,12 @@ private:
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;
std::unordered_map<PeerId, std::unique_ptr<History>> _map;
@@ -169,6 +206,12 @@ private:
not_null<History*>,
ChatListGroupRequest> _chatListGroupRequests;
base::flat_map<
FullMsgId,
std::vector<DelayedByTopicMessage>> _creatingTopics;
base::flat_map<FullMsgId, MsgId> _createdTopicIds;
base::flat_set<mtpRequestId> _creatingTopicRequests;
};
} // namespace Data

View File

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

View File

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

View File

@@ -243,7 +243,7 @@ public:
void itemVisibilitiesUpdated();
struct IdChange {
not_null<HistoryItem*> item;
FullMsgId newId;
MsgId oldId = 0;
};
void notifyItemIdChange(IdChange event);
@@ -728,7 +728,7 @@ private:
not_null<Messages*> messagesListForInsert(PeerId peerId);
not_null<HistoryItem*> registerMessage(
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 photoApplyFields(