mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-08-31 14:38:15 +00:00
Allow enabling forum, creating topics.
This commit is contained in:
@@ -60,11 +60,11 @@ Data::ChatBotCommands::Changed MegagroupInfo::setBotCommands(
|
||||
return _botCommands.update(list);
|
||||
}
|
||||
|
||||
void MegagroupInfo::setIsForum(bool is) {
|
||||
void MegagroupInfo::setIsForum(not_null<ChannelData*> that, bool is) {
|
||||
if (is == (_forum != nullptr)) {
|
||||
return;
|
||||
} else if (is) {
|
||||
_forum = std::make_unique<Data::Forum>();
|
||||
_forum = std::make_unique<Data::Forum>(that->owner().history(that));
|
||||
} else {
|
||||
_forum = nullptr;
|
||||
}
|
||||
@@ -97,10 +97,6 @@ ChannelData::ChannelData(not_null<Data::Session*> owner, PeerId id)
|
||||
mgInfo = nullptr;
|
||||
}
|
||||
}
|
||||
if (change.diff & Flag::Forum) {
|
||||
Assert(mgInfo != nullptr);
|
||||
mgInfo->setIsForum(change.value & Flag::Forum);
|
||||
}
|
||||
if (change.diff & Flag::CallNotEmpty) {
|
||||
if (const auto history = this->owner().historyLoaded(this)) {
|
||||
history->updateChatListEntry();
|
||||
|
@@ -100,7 +100,7 @@ public:
|
||||
return _botCommands;
|
||||
}
|
||||
|
||||
void setIsForum(bool is);
|
||||
void setIsForum(not_null<ChannelData*> that, bool is);
|
||||
[[nodiscard]] Data::Forum *forum() const;
|
||||
|
||||
std::deque<not_null<UserData*>> lastParticipants;
|
||||
|
@@ -41,8 +41,7 @@ Folder::Folder(not_null<Data::Session*> owner, FolderId id)
|
||||
&owner->session(),
|
||||
FilterId(),
|
||||
owner->maxPinnedChatsLimitValue(this, FilterId()))
|
||||
, _name(tr::lng_archived_name(tr::now))
|
||||
, _chatListNameSortKey(owner->nameSortKey(_name)) {
|
||||
, _name(tr::lng_archived_name(tr::now)) {
|
||||
indexNameParts();
|
||||
|
||||
session().changes().peerUpdates(
|
||||
@@ -374,7 +373,8 @@ const base::flat_set<QChar> &Folder::chatListFirstLetters() const {
|
||||
}
|
||||
|
||||
const QString &Folder::chatListNameSortKey() const {
|
||||
return _chatListNameSortKey;
|
||||
static const auto empty = QString();
|
||||
return empty;
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
@@ -21,7 +21,6 @@ class Session;
|
||||
namespace Data {
|
||||
|
||||
class Session;
|
||||
class Folder;
|
||||
|
||||
class Folder final : public Dialogs::Entry, public base::has_weak_ptr {
|
||||
public:
|
||||
@@ -31,12 +30,12 @@ public:
|
||||
Folder(const Folder &) = delete;
|
||||
Folder &operator=(const Folder &) = delete;
|
||||
|
||||
FolderId id() const;
|
||||
[[nodiscard]] FolderId id() const;
|
||||
void registerOne(not_null<History*> history);
|
||||
void unregisterOne(not_null<History*> history);
|
||||
void oneListMessageChanged(HistoryItem *from, HistoryItem *to);
|
||||
|
||||
not_null<Dialogs::MainList*> chatsList();
|
||||
[[nodiscard]] not_null<Dialogs::MainList*> chatsList();
|
||||
|
||||
void applyDialog(const MTPDdialogFolder &data);
|
||||
void applyPinnedUpdate(const MTPDupdateDialogPinned &data);
|
||||
@@ -94,13 +93,12 @@ private:
|
||||
const style::color *overrideBg,
|
||||
const style::color *overrideFg) const;
|
||||
|
||||
FolderId _id = 0;
|
||||
const FolderId _id = 0;
|
||||
Dialogs::MainList _chatsList;
|
||||
|
||||
QString _name;
|
||||
base::flat_set<QString> _nameWords;
|
||||
base::flat_set<QChar> _nameFirstLetters;
|
||||
QString _chatListNameSortKey;
|
||||
|
||||
std::vector<not_null<History*>> _lastHistories;
|
||||
HistoryItem *_chatListMessage = nullptr;
|
||||
|
@@ -7,10 +7,172 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "data/data_forum.h"
|
||||
|
||||
namespace Data {
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "main/main_session.h"
|
||||
#include "base/random.h"
|
||||
#include "apiwrap.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "window/window_session_controller.h"
|
||||
#include "styles/style_boxes.h"
|
||||
|
||||
Forum::Forum() = default;
|
||||
namespace Data {
|
||||
namespace {
|
||||
|
||||
constexpr auto kTopicsFirstLoad = 20;
|
||||
constexpr auto kTopicsPerPage = 500;
|
||||
|
||||
} // namespace
|
||||
|
||||
Forum::Forum(not_null<History*> forum)
|
||||
: _forum(forum)
|
||||
, _topicsList(&forum->session(), FilterId(0), rpl::single(1)) {
|
||||
}
|
||||
|
||||
Forum::~Forum() = default;
|
||||
|
||||
not_null<Dialogs::MainList*> Forum::topicsList() {
|
||||
return &_topicsList;
|
||||
}
|
||||
|
||||
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();
|
||||
_requestId = api->request(MTPchannels_GetForumTopics(
|
||||
MTP_flags(0),
|
||||
forum->peer->asChannel()->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();
|
||||
owner->processUsers(data.vusers());
|
||||
owner->processChats(data.vchats());
|
||||
owner->processMessages(data.vmessages(), NewMessageType::Existing);
|
||||
forum->peer->asChannel()->ptsReceived(data.vpts().v);
|
||||
const auto &list = data.vtopics().v;
|
||||
for (const auto &topic : list) {
|
||||
const auto rootId = MsgId(topic.data().vid().v);
|
||||
if (const auto i = _topics.find(rootId); i != end(_topics)) {
|
||||
i->second->applyTopic(topic);
|
||||
} else {
|
||||
const auto raw = _topics.emplace(
|
||||
rootId,
|
||||
std::make_unique<ForumTopic>(forum, rootId)
|
||||
).first->second.get();
|
||||
raw->applyTopic(topic);
|
||||
raw->addToChatList(FilterId(), topicsList());
|
||||
}
|
||||
}
|
||||
if (list.isEmpty() || list.size() == data.vcount().v) {
|
||||
_allLoaded = true;
|
||||
}
|
||||
if (const auto date = data.vnext_date()) {
|
||||
_offsetDate = date->v;
|
||||
}
|
||||
_requestId = 0;
|
||||
_chatsListChanges.fire({});
|
||||
if (_allLoaded) {
|
||||
_chatsListLoadedEvents.fire({});
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_allLoaded = true;
|
||||
_requestId = 0;
|
||||
}).send();
|
||||
}
|
||||
|
||||
void Forum::topicAdded(not_null<HistoryItem*> root) {
|
||||
const auto rootId = root->id;
|
||||
if (const auto i = _topics.find(rootId); i != end(_topics)) {
|
||||
//i->second->applyTopic(topic);
|
||||
} else {
|
||||
const auto raw = _topics.emplace(
|
||||
rootId,
|
||||
std::make_unique<ForumTopic>(_forum, rootId)
|
||||
).first->second.get();
|
||||
//raw->applyTopic(topic);
|
||||
raw->addToChatList(FilterId(), topicsList());
|
||||
_chatsListChanges.fire({});
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<> Forum::chatsListChanges() const {
|
||||
return _chatsListChanges.events();
|
||||
}
|
||||
|
||||
rpl::producer<> Forum::chatsListLoadedEvents() const {
|
||||
return _chatsListLoadedEvents.events();
|
||||
}
|
||||
|
||||
void ShowAddForumTopic(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<ChannelData*> forum) {
|
||||
controller->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
box->setTitle(rpl::single(u"New Topic"_q));
|
||||
|
||||
const auto title = box->addRow(
|
||||
object_ptr<Ui::InputField>(
|
||||
box,
|
||||
st::defaultInputField,
|
||||
rpl::single(u"Topic Title"_q))); // #TODO forum
|
||||
const auto message = box->addRow(
|
||||
object_ptr<Ui::InputField>(
|
||||
box,
|
||||
st::newGroupDescription,
|
||||
Ui::InputField::Mode::MultiLine,
|
||||
rpl::single(u"Message"_q))); // #TODO forum
|
||||
box->setFocusCallback([=] {
|
||||
title->setFocusFast();
|
||||
});
|
||||
box->addButton(tr::lng_create_group_create(), [=] {
|
||||
if (!forum->isForum()) {
|
||||
box->closeBox();
|
||||
return;
|
||||
} else if (title->getLastText().trimmed().isEmpty()) {
|
||||
title->setFocus();
|
||||
return;
|
||||
} else if (message->getLastText().trimmed().isEmpty()) {
|
||||
message->setFocus();
|
||||
return;
|
||||
}
|
||||
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()),
|
||||
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();
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
}), Ui::LayerOption::KeepOther);
|
||||
}
|
||||
|
||||
} // namespace Data
|
||||
|
@@ -7,15 +7,49 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "dialogs/dialogs_main_list.h"
|
||||
|
||||
class History;
|
||||
class ChannelData;
|
||||
|
||||
namespace Window {
|
||||
class SessionController;
|
||||
} // namespace Window;
|
||||
|
||||
namespace Data {
|
||||
|
||||
class Forum final {
|
||||
public:
|
||||
Forum();
|
||||
explicit Forum(not_null<History*> forum);
|
||||
~Forum();
|
||||
|
||||
[[nodiscard]] not_null<Dialogs::MainList*> topicsList();
|
||||
|
||||
void requestTopics();
|
||||
[[nodiscard]] rpl::producer<> chatsListChanges() const;
|
||||
[[nodiscard]] rpl::producer<> chatsListLoadedEvents() const;
|
||||
|
||||
void topicAdded(not_null<HistoryItem*> root);
|
||||
|
||||
private:
|
||||
const not_null<History*> _forum;
|
||||
|
||||
base::flat_map<MsgId, std::unique_ptr<ForumTopic>> _topics;
|
||||
Dialogs::MainList _topicsList;
|
||||
|
||||
mtpRequestId _requestId = 0;
|
||||
TimeId _offsetDate = 0;
|
||||
MsgId _offsetId = 0;
|
||||
MsgId _offsetTopicId = 0;
|
||||
bool _allLoaded = false;
|
||||
|
||||
rpl::event_stream<> _chatsListChanges;
|
||||
rpl::event_stream<> _chatsListLoadedEvents;
|
||||
|
||||
};
|
||||
|
||||
void ShowAddForumTopic(
|
||||
not_null<Window::SessionController*> controller,
|
||||
not_null<ChannelData*> forum);
|
||||
|
||||
} // namespace Data
|
||||
|
340
Telegram/SourceFiles/data/data_forum_topic.cpp
Normal file
340
Telegram/SourceFiles/data/data_forum_topic.cpp
Normal file
@@ -0,0 +1,340 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "data/data_forum_topic.h"
|
||||
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_session.h"
|
||||
#include "dialogs/dialogs_main_list.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
|
||||
namespace Data {
|
||||
|
||||
ForumTopic::ForumTopic(not_null<History*> forum, MsgId rootId)
|
||||
: Entry(&forum->owner(), Type::ForumTopic)
|
||||
, _forum(forum)
|
||||
, _list(forum->peer->asChannel()->forum()->topicsList())
|
||||
, _rootId(rootId) {
|
||||
}
|
||||
|
||||
not_null<History*> ForumTopic::forum() const {
|
||||
return _forum;
|
||||
}
|
||||
|
||||
MsgId ForumTopic::rootId() const {
|
||||
return _rootId;
|
||||
}
|
||||
|
||||
void ForumTopic::applyTopic(const MTPForumTopic &topic) {
|
||||
Expects(_rootId == topic.data().vid().v);
|
||||
|
||||
const auto &data = topic.data();
|
||||
const auto title = qs(data.vtitle());
|
||||
if (_title != title) {
|
||||
_title = title;
|
||||
++_titleVersion;
|
||||
indexTitleParts();
|
||||
updateChatListEntry();
|
||||
}
|
||||
|
||||
const auto pinned = _list->pinned();
|
||||
if (data.is_pinned()) {
|
||||
pinned->addPinned(Dialogs::Key(this));
|
||||
} else {
|
||||
pinned->setPinned(Dialogs::Key(this), false);
|
||||
}
|
||||
|
||||
applyTopicFields(
|
||||
data.vunread_count().v,
|
||||
data.vread_inbox_max_id().v,
|
||||
data.vread_outbox_max_id().v);
|
||||
applyTopicTopMessage(data.vtop_message().v);
|
||||
//setUnreadMark(data.is_unread_mark());
|
||||
}
|
||||
|
||||
void ForumTopic::indexTitleParts() {
|
||||
_titleWords.clear();
|
||||
_titleFirstLetters.clear();
|
||||
auto toIndexList = QStringList();
|
||||
auto appendToIndex = [&](const QString &value) {
|
||||
if (!value.isEmpty()) {
|
||||
toIndexList.push_back(TextUtilities::RemoveAccents(value));
|
||||
}
|
||||
};
|
||||
|
||||
appendToIndex(_title);
|
||||
const auto appendTranslit = !toIndexList.isEmpty()
|
||||
&& cRussianLetters().match(toIndexList.front()).hasMatch();
|
||||
if (appendTranslit) {
|
||||
appendToIndex(translitRusEng(toIndexList.front()));
|
||||
}
|
||||
auto toIndex = toIndexList.join(' ');
|
||||
toIndex += ' ' + rusKeyboardLayoutSwitch(toIndex);
|
||||
|
||||
const auto namesList = TextUtilities::PrepareSearchWords(toIndex);
|
||||
for (const auto &name : namesList) {
|
||||
_titleWords.insert(name);
|
||||
_titleFirstLetters.insert(name[0]);
|
||||
}
|
||||
}
|
||||
|
||||
int ForumTopic::chatListNameVersion() const {
|
||||
return _titleVersion;
|
||||
}
|
||||
|
||||
void ForumTopic::applyTopicFields(
|
||||
int unreadCount,
|
||||
MsgId maxInboxRead,
|
||||
MsgId maxOutboxRead) {
|
||||
if (maxInboxRead + 1 >= _inboxReadBefore.value_or(1)) {
|
||||
setUnreadCount(unreadCount);
|
||||
setInboxReadTill(maxInboxRead);
|
||||
}
|
||||
setOutboxReadTill(maxOutboxRead);
|
||||
}
|
||||
|
||||
void ForumTopic::applyTopicTopMessage(MsgId topMessageId) {
|
||||
if (topMessageId) {
|
||||
const auto itemId = FullMsgId(_forum->peer->id, topMessageId);
|
||||
if (const auto item = owner().message(itemId)) {
|
||||
setLastServerMessage(item);
|
||||
} else {
|
||||
setLastServerMessage(nullptr);
|
||||
}
|
||||
} else {
|
||||
setLastServerMessage(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void ForumTopic::setLastServerMessage(HistoryItem *item) {
|
||||
_lastServerMessage = item;
|
||||
if (_lastMessage
|
||||
&& *_lastMessage
|
||||
&& !(*_lastMessage)->isRegular()
|
||||
&& (!item || (*_lastMessage)->date() > item->date())) {
|
||||
return;
|
||||
}
|
||||
setLastMessage(item);
|
||||
}
|
||||
|
||||
void ForumTopic::setLastMessage(HistoryItem *item) {
|
||||
if (_lastMessage && *_lastMessage == item) {
|
||||
return;
|
||||
}
|
||||
_lastMessage = item;
|
||||
if (!item || item->isRegular()) {
|
||||
_lastServerMessage = item;
|
||||
}
|
||||
setChatListMessage(item);
|
||||
}
|
||||
|
||||
void ForumTopic::setChatListMessage(HistoryItem *item) {
|
||||
if (_chatListMessage && *_chatListMessage == item) {
|
||||
return;
|
||||
}
|
||||
const auto was = _chatListMessage.value_or(nullptr);
|
||||
if (item) {
|
||||
if (item->isSponsored()) {
|
||||
return;
|
||||
}
|
||||
if (_chatListMessage
|
||||
&& *_chatListMessage
|
||||
&& !(*_chatListMessage)->isRegular()
|
||||
&& (*_chatListMessage)->date() > item->date()) {
|
||||
return;
|
||||
}
|
||||
_chatListMessage = item;
|
||||
setChatListTimeId(item->date());
|
||||
|
||||
#if 0 // #TODO forum
|
||||
// If we have a single message from a group, request the full album.
|
||||
if (hasOrphanMediaGroupPart()
|
||||
&& !item->toPreview({
|
||||
.hideSender = true,
|
||||
.hideCaption = true }).images.empty()) {
|
||||
owner().histories().requestGroupAround(item);
|
||||
}
|
||||
#endif
|
||||
} else if (!_chatListMessage || *_chatListMessage) {
|
||||
_chatListMessage = nullptr;
|
||||
updateChatListEntry();
|
||||
}
|
||||
}
|
||||
|
||||
void ForumTopic::setInboxReadTill(MsgId upTo) {
|
||||
if (_inboxReadBefore) {
|
||||
accumulate_max(*_inboxReadBefore, upTo + 1);
|
||||
} else {
|
||||
_inboxReadBefore = upTo + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void ForumTopic::setOutboxReadTill(MsgId upTo) {
|
||||
if (_outboxReadBefore) {
|
||||
accumulate_max(*_outboxReadBefore, upTo + 1);
|
||||
} else {
|
||||
_outboxReadBefore = upTo + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void ForumTopic::loadUserpic() {
|
||||
}
|
||||
|
||||
void ForumTopic::paintUserpic(
|
||||
Painter &p,
|
||||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
int x,
|
||||
int y,
|
||||
int size) const {
|
||||
// #TODO forum
|
||||
}
|
||||
|
||||
void ForumTopic::requestChatListMessage() {
|
||||
if (!chatListMessageKnown()) {
|
||||
// #TODO forum
|
||||
}
|
||||
}
|
||||
|
||||
TimeId ForumTopic::adjustedChatListTimeId() const {
|
||||
const auto result = chatListTimeId();
|
||||
#if 0 // #TODO forum
|
||||
if (const auto draft = cloudDraft()) {
|
||||
if (!Data::draftIsNull(draft) && !session().supportMode()) {
|
||||
return std::max(result, draft->date);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
|
||||
int ForumTopic::fixedOnTopIndex() const {
|
||||
return kArchiveFixOnTopIndex;
|
||||
}
|
||||
|
||||
bool ForumTopic::shouldBeInChatList() const {
|
||||
return isPinnedDialog(FilterId())
|
||||
|| !lastMessageKnown()
|
||||
|| (lastMessage() != nullptr);
|
||||
}
|
||||
|
||||
HistoryItem *ForumTopic::lastMessage() const {
|
||||
return _lastMessage.value_or(nullptr);
|
||||
}
|
||||
|
||||
bool ForumTopic::lastMessageKnown() const {
|
||||
return _lastMessage.has_value();
|
||||
}
|
||||
|
||||
HistoryItem *ForumTopic::lastServerMessage() const {
|
||||
return _lastServerMessage.value_or(nullptr);
|
||||
}
|
||||
|
||||
bool ForumTopic::lastServerMessageKnown() const {
|
||||
return _lastServerMessage.has_value();
|
||||
}
|
||||
|
||||
int ForumTopic::unreadCount() const {
|
||||
return _unreadCount ? *_unreadCount : 0;
|
||||
}
|
||||
|
||||
int ForumTopic::unreadCountForBadge() const {
|
||||
const auto result = unreadCount();
|
||||
return (!result && unreadMark()) ? 1 : result;
|
||||
}
|
||||
|
||||
bool ForumTopic::unreadCountKnown() const {
|
||||
return _unreadCount.has_value();
|
||||
}
|
||||
|
||||
void ForumTopic::setUnreadCount(int newUnreadCount) {
|
||||
if (_unreadCount == newUnreadCount) {
|
||||
return;
|
||||
}
|
||||
const auto wasForBadge = (unreadCountForBadge() > 0);
|
||||
const auto notifier = unreadStateChangeNotifier(true);
|
||||
_unreadCount = newUnreadCount;
|
||||
}
|
||||
|
||||
void ForumTopic::setUnreadMark(bool unread) {
|
||||
if (_unreadMark == unread) {
|
||||
return;
|
||||
}
|
||||
const auto noUnreadMessages = !unreadCount();
|
||||
const auto refresher = gsl::finally([&] {
|
||||
if (inChatList() && noUnreadMessages) {
|
||||
updateChatListEntry();
|
||||
}
|
||||
});
|
||||
const auto notifier = unreadStateChangeNotifier(noUnreadMessages);
|
||||
_unreadMark = unread;
|
||||
}
|
||||
|
||||
bool ForumTopic::unreadMark() const {
|
||||
return _unreadMark;
|
||||
}
|
||||
|
||||
int ForumTopic::chatListUnreadCount() const {
|
||||
const auto state = chatListUnreadState();
|
||||
return state.marks
|
||||
+ (Core::App().settings().countUnreadMessages()
|
||||
? state.messages
|
||||
: state.chats);
|
||||
}
|
||||
|
||||
Dialogs::UnreadState ForumTopic::chatListUnreadState() const {
|
||||
auto result = Dialogs::UnreadState();
|
||||
const auto count = _unreadCount.value_or(0);
|
||||
const auto mark = !count && _unreadMark;
|
||||
const auto muted = _forum->mute();
|
||||
result.messages = count;
|
||||
result.messagesMuted = muted ? count : 0;
|
||||
result.chats = count ? 1 : 0;
|
||||
result.chatsMuted = (count && muted) ? 1 : 0;
|
||||
result.marks = mark ? 1 : 0;
|
||||
result.marksMuted = (mark && muted) ? 1 : 0;
|
||||
result.known = _unreadCount.has_value();
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ForumTopic::chatListUnreadMark() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ForumTopic::chatListMutedBadge() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
HistoryItem *ForumTopic::chatListMessage() const {
|
||||
return _lastMessage.value_or(nullptr);
|
||||
}
|
||||
|
||||
bool ForumTopic::chatListMessageKnown() const {
|
||||
return _lastMessage.has_value();
|
||||
}
|
||||
|
||||
const QString &ForumTopic::chatListName() const {
|
||||
return _title;
|
||||
}
|
||||
|
||||
const base::flat_set<QString> &ForumTopic::chatListNameWords() const {
|
||||
return _titleWords;
|
||||
}
|
||||
|
||||
const base::flat_set<QChar> &ForumTopic::chatListFirstLetters() const {
|
||||
return _titleFirstLetters;
|
||||
}
|
||||
|
||||
const QString &ForumTopic::chatListNameSortKey() const {
|
||||
static const auto empty = QString();
|
||||
return empty;
|
||||
}
|
||||
|
||||
} // namespace Data
|
115
Telegram/SourceFiles/data/data_forum_topic.h
Normal file
115
Telegram/SourceFiles/data/data_forum_topic.h
Normal file
@@ -0,0 +1,115 @@
|
||||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "dialogs/dialogs_entry.h"
|
||||
|
||||
class ChannelData;
|
||||
|
||||
namespace Dialogs {
|
||||
class MainList;
|
||||
} // namespace Dialogs
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Data {
|
||||
|
||||
class Session;
|
||||
|
||||
class ForumTopic final : public Dialogs::Entry {
|
||||
public:
|
||||
ForumTopic(not_null<History*> forum, MsgId rootId);
|
||||
|
||||
ForumTopic(const ForumTopic &) = delete;
|
||||
ForumTopic &operator=(const ForumTopic &) = delete;
|
||||
|
||||
[[nodiscard]] not_null<History*> forum() const;
|
||||
[[nodiscard]] MsgId rootId() const;
|
||||
|
||||
void applyTopic(const MTPForumTopic &topic);
|
||||
|
||||
TimeId adjustedChatListTimeId() const override;
|
||||
|
||||
int fixedOnTopIndex() const override;
|
||||
bool shouldBeInChatList() const override;
|
||||
int chatListUnreadCount() const override;
|
||||
bool chatListUnreadMark() const override;
|
||||
bool chatListMutedBadge() const override;
|
||||
Dialogs::UnreadState chatListUnreadState() const override;
|
||||
HistoryItem *chatListMessage() const override;
|
||||
bool chatListMessageKnown() const override;
|
||||
void requestChatListMessage() override;
|
||||
const QString &chatListName() const override;
|
||||
const QString &chatListNameSortKey() const override;
|
||||
const base::flat_set<QString> &chatListNameWords() const override;
|
||||
const base::flat_set<QChar> &chatListFirstLetters() const override;
|
||||
|
||||
[[nodiscard]] HistoryItem *lastMessage() const;
|
||||
[[nodiscard]] HistoryItem *lastServerMessage() const;
|
||||
[[nodiscard]] bool lastMessageKnown() const;
|
||||
[[nodiscard]] bool lastServerMessageKnown() const;
|
||||
|
||||
void loadUserpic() override;
|
||||
void paintUserpic(
|
||||
Painter &p,
|
||||
std::shared_ptr<Data::CloudImageView> &view,
|
||||
int x,
|
||||
int y,
|
||||
int size) const override;
|
||||
|
||||
[[nodiscard]] int unreadCount() const;
|
||||
[[nodiscard]] bool unreadCountKnown() const;
|
||||
|
||||
[[nodiscard]] int unreadCountForBadge() const; // unreadCount || unreadMark ? 1 : 0.
|
||||
|
||||
void setUnreadCount(int newUnreadCount);
|
||||
void setUnreadMark(bool unread);
|
||||
[[nodiscard]] bool unreadMark() const;
|
||||
|
||||
private:
|
||||
void indexTitleParts();
|
||||
void applyTopicTopMessage(MsgId topMessageId);
|
||||
void applyTopicFields(
|
||||
int unreadCount,
|
||||
MsgId maxInboxRead,
|
||||
MsgId maxOutboxRead);
|
||||
void applyChatListMessage(HistoryItem *item);
|
||||
|
||||
void setLastMessage(HistoryItem *item);
|
||||
void setLastServerMessage(HistoryItem *item);
|
||||
void setChatListMessage(HistoryItem *item);
|
||||
|
||||
void setInboxReadTill(MsgId upTo);
|
||||
void setOutboxReadTill(MsgId upTo);
|
||||
|
||||
int chatListNameVersion() const override;
|
||||
|
||||
const not_null<History*> _forum;
|
||||
const not_null<Dialogs::MainList*> _list;
|
||||
const MsgId _rootId = 0;
|
||||
|
||||
QString _title;
|
||||
base::flat_set<QString> _titleWords;
|
||||
base::flat_set<QChar> _titleFirstLetters;
|
||||
int _titleVersion = 0;
|
||||
|
||||
std::optional<MsgId> _inboxReadBefore;
|
||||
std::optional<MsgId> _outboxReadBefore;
|
||||
std::optional<int> _unreadCount;
|
||||
std::optional<HistoryItem*> _lastMessage;
|
||||
std::optional<HistoryItem*> _lastServerMessage;
|
||||
std::optional<HistoryItem*> _chatListMessage;
|
||||
bool _unreadMark = false;
|
||||
|
||||
rpl::lifetime _lifetime;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Data
|
@@ -805,7 +805,12 @@ 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()),
|
||||
|
@@ -239,52 +239,53 @@ enum class MessageFlag : uint64 {
|
||||
MentionsMe = (1ULL << 15),
|
||||
IsOrWasScheduled = (1ULL << 16),
|
||||
NoForwards = (1ULL << 17),
|
||||
TopicStart = (1ULL << 18),
|
||||
|
||||
// Needs to return back to inline mode.
|
||||
HasSwitchInlineButton = (1ULL << 18),
|
||||
HasSwitchInlineButton = (1ULL << 19),
|
||||
|
||||
// For "shared links" indexing.
|
||||
HasTextLinks = (1ULL << 19),
|
||||
HasTextLinks = (1ULL << 20),
|
||||
|
||||
// Group / channel create or migrate service message.
|
||||
IsGroupEssential = (1ULL << 20),
|
||||
IsGroupEssential = (1ULL << 21),
|
||||
|
||||
// Edited media is generated on the client
|
||||
// and should not update media from server.
|
||||
IsLocalUpdateMedia = (1ULL << 21),
|
||||
IsLocalUpdateMedia = (1ULL << 22),
|
||||
|
||||
// Sent from inline bot, need to re-set media when sent.
|
||||
FromInlineBot = (1ULL << 22),
|
||||
FromInlineBot = (1ULL << 23),
|
||||
|
||||
// Generated on the client side and should be unread.
|
||||
ClientSideUnread = (1ULL << 23),
|
||||
ClientSideUnread = (1ULL << 24),
|
||||
|
||||
// In a supergroup.
|
||||
HasAdminBadge = (1ULL << 24),
|
||||
HasAdminBadge = (1ULL << 25),
|
||||
|
||||
// Outgoing message that is being sent.
|
||||
BeingSent = (1ULL << 25),
|
||||
BeingSent = (1ULL << 26),
|
||||
|
||||
// Outgoing message and failed to be sent.
|
||||
SendingFailed = (1ULL << 26),
|
||||
SendingFailed = (1ULL << 27),
|
||||
|
||||
// No media and only a several emoji or an only custom emoji text.
|
||||
SpecialOnlyEmoji = (1ULL << 27),
|
||||
SpecialOnlyEmoji = (1ULL << 28),
|
||||
|
||||
// Message existing in the message history.
|
||||
HistoryEntry = (1ULL << 28),
|
||||
HistoryEntry = (1ULL << 29),
|
||||
|
||||
// Local message, not existing on the server.
|
||||
Local = (1ULL << 29),
|
||||
Local = (1ULL << 30),
|
||||
|
||||
// Fake message for some UI element.
|
||||
FakeHistoryItem = (1ULL << 30),
|
||||
FakeHistoryItem = (1ULL << 31),
|
||||
|
||||
// Contact sign-up message, notification should be skipped for Silent.
|
||||
IsContactSignUp = (1ULL << 31),
|
||||
IsContactSignUp = (1ULL << 32),
|
||||
|
||||
// Optimization for item text custom emoji repainting.
|
||||
CustomEmojiRepainting = (1ULL << 32),
|
||||
CustomEmojiRepainting = (1ULL << 33),
|
||||
};
|
||||
inline constexpr bool is_flag_type(MessageFlag) { return true; }
|
||||
using MessageFlags = base::flags<MessageFlag>;
|
||||
|
Reference in New Issue
Block a user