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

Support default General topic in forums.

This commit is contained in:
John Preston
2022-09-26 17:37:32 +04:00
parent 2201159da5
commit 73e56b0340
23 changed files with 385 additions and 183 deletions

View File

@@ -60,13 +60,9 @@ Data::ChatBotCommands::Changed MegagroupInfo::setBotCommands(
return _botCommands.update(list);
}
void MegagroupInfo::setIsForum(not_null<ChannelData*> that, bool is) {
if (is == (_forum != nullptr)) {
return;
} else if (is) {
void MegagroupInfo::ensureForum(not_null<ChannelData*> that) {
if (!_forum) {
_forum = std::make_unique<Data::Forum>(that->owner().history(that));
} else {
_forum = nullptr;
}
}
@@ -74,35 +70,15 @@ Data::Forum *MegagroupInfo::forum() const {
return _forum.get();
}
std::unique_ptr<Data::Forum> MegagroupInfo::takeForumData() {
return std::move(_forum);
}
ChannelData::ChannelData(not_null<Data::Session*> owner, PeerId id)
: PeerData(owner, id)
, inputChannel(
MTP_inputChannel(MTP_long(peerToChannel(id).bare), MTP_long(0)))
, _ptsWaiter(&owner->session().updates()) {
_flags.changes(
) | rpl::start_with_next([=](const Flags::Change &change) {
if (change.diff
& (Flag::Left | Flag::Forbidden)) {
if (const auto chat = getMigrateFromChat()) {
session().changes().peerUpdated(chat, UpdateFlag::Migration);
session().changes().peerUpdated(this, UpdateFlag::Migration);
}
}
if (change.diff & Flag::Megagroup) {
if (change.value & Flag::Megagroup) {
if (!mgInfo) {
mgInfo = std::make_unique<MegagroupInfo>();
}
} else if (mgInfo) {
mgInfo = nullptr;
}
}
if (change.diff & Flag::CallNotEmpty) {
if (const auto history = this->owner().historyLoaded(this)) {
history->updateChatListEntry();
}
}
}, _lifetime);
}
void ChannelData::setPhoto(const MTPChatPhoto &photo) {
@@ -132,6 +108,44 @@ void ChannelData::setAccessHash(uint64 accessHash) {
MTP_long(accessHash));
}
void ChannelData::setFlags(ChannelDataFlags which) {
const auto diff = flags() ^ which;
if ((which & Flag::Megagroup) && !mgInfo) {
mgInfo = std::make_unique<MegagroupInfo>();
}
// Let Data::Forum live till the end of _flags.set.
// That way the data can be used in changes handler.
// Example: render frame for forum auto-closing animation.
const auto taken = ((diff & Flag::Forum) && !(which & Flag::Forum))
? mgInfo->takeForumData()
: nullptr;
if ((diff & Flag::Forum) && (which & Flag::Forum)) {
mgInfo->ensureForum(this);
}
_flags.set(which);
if (diff & (Flag::Left | Flag::Forbidden)) {
if (const auto chat = getMigrateFromChat()) {
session().changes().peerUpdated(chat, UpdateFlag::Migration);
session().changes().peerUpdated(this, UpdateFlag::Migration);
}
}
if (diff & Flag::CallNotEmpty) {
if (const auto history = this->owner().historyLoaded(this)) {
history->updateChatListEntry();
}
}
}
void ChannelData::addFlags(ChannelDataFlags which) {
setFlags(flags() | which);
}
void ChannelData::removeFlags(ChannelDataFlags which) {
setFlags(flags() & ~which);
}
void ChannelData::setInviteLink(const QString &newInviteLink) {
_inviteLink = newInviteLink;
}

View File

@@ -96,8 +96,9 @@ public:
return _botCommands;
}
void setIsForum(not_null<ChannelData*> that, bool is);
void ensureForum(not_null<ChannelData*> that);
[[nodiscard]] Data::Forum *forum() const;
[[nodiscard]] std::unique_ptr<Data::Forum> takeForumData();
std::deque<not_null<UserData*>> lastParticipants;
base::flat_map<not_null<UserData*>, Admin> lastAdmins;
@@ -148,15 +149,9 @@ public:
void setPhoto(const MTPChatPhoto &photo);
void setAccessHash(uint64 accessHash);
void setFlags(ChannelDataFlags which) {
_flags.set(which);
}
void addFlags(ChannelDataFlags which) {
_flags.add(which);
}
void removeFlags(ChannelDataFlags which) {
_flags.remove(which);
}
void setFlags(ChannelDataFlags which);
void addFlags(ChannelDataFlags which);
void removeFlags(ChannelDataFlags which);
[[nodiscard]] auto flags() const {
return _flags.current();
}
@@ -490,8 +485,6 @@ private:
int _slowmodeSeconds = 0;
TimeId _slowmodeLastMessage = 0;
rpl::lifetime _lifetime;
};
namespace Data {

View File

@@ -29,12 +29,25 @@ constexpr auto kTopicsPerPage = 500;
} // namespace
Forum::Forum(not_null<History*> forum)
: _forum(forum)
, _topicsList(&forum->session(), FilterId(0), rpl::single(1)) {
Forum::Forum(not_null<History*> history)
: _history(history)
, _topicsList(&_history->session(), FilterId(0), rpl::single(1)) {
Expects(_history->peer->isChannel());
}
Forum::~Forum() = default;
Forum::~Forum() {
if (_requestId) {
_history->session().api().request(_requestId).cancel();
}
}
not_null<History*> Forum::history() const {
return _history;
}
not_null<ChannelData*> Forum::channel() const {
return _history->peer->asChannel();
}
not_null<Dialogs::MainList*> Forum::topicsList() {
return &_topicsList;
@@ -44,28 +57,24 @@ void Forum::requestTopics() {
if (_allLoaded || _requestId) {
return;
}
const auto forum = _forum;
const auto firstLoad = !_offsetDate;
const auto loadCount = firstLoad ? kTopicsFirstLoad : kTopicsPerPage;
const auto api = &forum->session().api();
const auto api = &_history->session().api();
_requestId = api->request(MTPchannels_GetForumTopics(
MTP_flags(0),
forum->peer->asChannel()->inputChannel,
channel()->inputChannel,
MTPstring(), // q
MTP_int(_offsetDate),
MTP_int(_offsetId),
MTP_int(_offsetTopicId),
MTP_int(loadCount)
)).done([=](const MTPmessages_ForumTopics &result) {
if (!forum->peer->isForum()) {
return;
}
const auto &data = result.data();
const auto owner = &forum->owner();
const auto owner = &channel()->owner();
owner->processUsers(data.vusers());
owner->processChats(data.vchats());
owner->processMessages(data.vmessages(), NewMessageType::Existing);
forum->peer->asChannel()->ptsReceived(data.vpts().v);
channel()->ptsReceived(data.vpts().v);
const auto &list = data.vtopics().v;
for (const auto &topic : list) {
const auto rootId = MsgId(topic.data().vid().v);
@@ -74,7 +83,7 @@ void Forum::requestTopics() {
const auto raw = creating
? _topics.emplace(
rootId,
std::make_unique<ForumTopic>(forum, rootId)
std::make_unique<ForumTopic>(_history, rootId)
).first->second.get()
: i->second.get();
raw->applyTopic(topic);
@@ -107,7 +116,7 @@ void Forum::applyTopicAdded(MsgId rootId, const QString &title) {
} else {
const auto raw = _topics.emplace(
rootId,
std::make_unique<ForumTopic>(_forum, rootId)
std::make_unique<ForumTopic>(_history, rootId)
).first->second.get();
raw->applyTitle(title);
raw->addToChatList(FilterId(), topicsList());
@@ -122,10 +131,18 @@ void Forum::applyTopicRemoved(MsgId rootId) {
}
ForumTopic *Forum::topicFor(not_null<HistoryItem*> item) {
if (const auto rootId = item->replyToTop()) {
return topicFor(item->topicRootId());
}
ForumTopic *Forum::topicFor(MsgId rootId) {
if (rootId != ForumTopic::kGeneralId) {
if (const auto i = _topics.find(rootId); i != end(_topics)) {
return i->second.get();
}
} else {
// #TODO forum lang
applyTopicAdded(rootId, "General! Created.");
return _topics.find(rootId)->second.get();
}
return nullptr;
}
@@ -148,13 +165,13 @@ void ShowAddForumTopic(
object_ptr<Ui::InputField>(
box,
st::defaultInputField,
rpl::single(u"Topic Title"_q))); // #TODO forum
rpl::single(u"Topic Title"_q))); // #TODO forum lang
const auto message = box->addRow(
object_ptr<Ui::InputField>(
box,
st::newGroupDescription,
Ui::InputField::Mode::MultiLine,
rpl::single(u"Message"_q))); // #TODO forum
rpl::single(u"Message"_q))); // #TODO forum lang
box->setFocusCallback([=] {
title->setFocusFast();
});

View File

@@ -20,9 +20,11 @@ namespace Data {
class Forum final {
public:
explicit Forum(not_null<History*> forum);
explicit Forum(not_null<History*> history);
~Forum();
[[nodiscard]] not_null<History*> history() const;
[[nodiscard]] not_null<ChannelData*> channel() const;
[[nodiscard]] not_null<Dialogs::MainList*> topicsList();
void requestTopics();
@@ -32,9 +34,10 @@ public:
void applyTopicAdded(MsgId rootId, const QString &title);
void applyTopicRemoved(MsgId rootId);
[[nodiscard]] ForumTopic *topicFor(not_null<HistoryItem*> item);
[[nodiscard]] ForumTopic *topicFor(MsgId rootId);
private:
const not_null<History*> _forum;
const not_null<History*> _history;
base::flat_map<MsgId, std::unique_ptr<ForumTopic>> _topics;
Dialogs::MainList _topicsList;

View File

@@ -198,6 +198,9 @@ void ForumTopic::requestChatListMessage() {
}
TimeId ForumTopic::adjustedChatListTimeId() const {
if (isGeneral()) {
return TimeId(1);
}
const auto result = chatListTimeId();
#if 0 // #TODO forum
if (const auto draft = cloudDraft()) {
@@ -236,10 +239,10 @@ bool ForumTopic::lastServerMessageKnown() const {
}
void ForumTopic::applyTitle(const QString &title) {
if (_title == title) {
if (_title == title || (isGeneral() && !_title.isEmpty())) {
return;
}
_title = title;
_title = isGeneral() ? "General! Topic." : title; // #TODO forum lang
++_titleVersion;
indexTitleParts();
updateChatListEntry();

View File

@@ -26,6 +26,8 @@ class Session;
class ForumTopic final : public Dialogs::Entry {
public:
static constexpr auto kGeneralId = 1;
ForumTopic(not_null<History*> forum, MsgId rootId);
ForumTopic(const ForumTopic &) = delete;
@@ -33,6 +35,9 @@ public:
[[nodiscard]] not_null<History*> forum() const;
[[nodiscard]] MsgId rootId() const;
[[nodiscard]] bool isGeneral() const {
return (_rootId == kGeneralId);
}
void applyTopic(const MTPForumTopic &topic);
@@ -88,7 +93,6 @@ private:
int unreadCount,
MsgId maxInboxRead,
MsgId maxOutboxRead);
void applyChatListMessage(HistoryItem *item);
void setLastMessage(HistoryItem *item);
void setLastServerMessage(HistoryItem *item);

View File

@@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h"
#include "data/data_channel.h"
#include "data/data_messages.h"
#include "data/data_forum_topic.h"
#include "lang/lang_keys.h"
#include "apiwrap.h"
@@ -125,7 +126,10 @@ void RepliesList::appendClientSideMessages(MessagesSlice &slice) {
}
slice.ids.reserve(messages.size());
for (const auto &item : messages) {
if (item->replyToTop() != _rootId) {
const auto checkId = (_rootId == ForumTopic::kGeneralId)
? item->topicRootId()
: item->replyToTop();
if (!item->inThread(_rootId)) {
continue;
}
slice.ids.push_back(item->fullId());
@@ -143,7 +147,7 @@ void RepliesList::appendClientSideMessages(MessagesSlice &slice) {
dates.push_back(message->date());
}
for (const auto &item : messages) {
if (item->replyToTop() != _rootId) {
if (!item->inThread(_rootId)) {
continue;
}
const auto date = item->date();
@@ -341,12 +345,15 @@ bool RepliesList::buildFromData(not_null<Viewer*> viewer) {
auto nearestToAround = std::optional<MsgId>();
slice->ids.reserve(useAfter + useBefore);
for (auto j = i - useAfter, e = i + useBefore; j != e; ++j) {
if (!nearestToAround && *j < around) {
const auto id = *j;
if (id == _rootId) {
continue;
} else if (!nearestToAround && id < around) {
nearestToAround = (j == i - useAfter)
? *j
? id
: *(j - 1);
}
slice->ids.emplace_back(peerId, *j);
slice->ids.emplace_back(peerId, id);
}
slice->nearestToAround = FullMsgId(
peerId,
@@ -380,7 +387,7 @@ bool RepliesList::applyUpdate(
}
}
}
if (update.item->replyToTop() != _rootId) {
if (!update.item->inThread(_rootId)) {
return false;
}
const auto id = update.item->id;
@@ -613,7 +620,7 @@ bool RepliesList::processMessagesIsEmpty(const MTPmessages_Messages &result) {
auto skipped = 0;
for (const auto &message : list) {
if (const auto item = owner.addNewMessage(message, localFlags, type)) {
if (item->replyToTop() == _rootId) {
if (item->inThread(_rootId)) {
if (toFront && item->id > _list.front()) {
refreshed.push_back(item->id);
} else if (_list.empty() || item->id < _list.back()) {

View File

@@ -807,12 +807,7 @@ not_null<PeerData*> Session::processChat(const MTPChat &data) {
| ((data.is_forum() && data.is_megagroup())
? Flag::Forum
: Flag());
const auto wasForum = channel->isForum();
channel->setFlags((channel->flags() & ~flagsMask) | flagsSet);
if (const auto nowForum = channel->isForum(); nowForum != wasForum) {
Assert(channel->mgInfo != nullptr);
channel->mgInfo->setIsForum(channel, nowForum);
}
channel->setName(
qs(data.vtitle()),