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:
@@ -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;
|
||||
}
|
||||
|
@@ -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 {
|
||||
|
@@ -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();
|
||||
});
|
||||
|
@@ -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;
|
||||
|
@@ -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();
|
||||
|
@@ -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);
|
||||
|
@@ -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()) {
|
||||
|
@@ -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()),
|
||||
|
Reference in New Issue
Block a user