mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-18 15:17:07 +02:00
Allow enabling forum, creating topics.
This commit is contained in:
parent
c88140e256
commit
388fe6adfb
21 changed files with 781 additions and 49 deletions
Telegram
CMakeLists.txt
SourceFiles
|
@ -469,6 +469,8 @@ PRIVATE
|
|||
data/data_folder.h
|
||||
data/data_forum.cpp
|
||||
data/data_forum.h
|
||||
data/data_forum_topic.cpp
|
||||
data/data_forum_topic.h
|
||||
data/data_file_click_handler.cpp
|
||||
data/data_file_click_handler.h
|
||||
data/data_file_origin.cpp
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_changes.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_chat_filters.h"
|
||||
#include "mainwidget.h"
|
||||
#include "main/main_session.h"
|
||||
|
@ -45,7 +46,7 @@ uint64 PinnedDialogPos(int pinnedIndex) {
|
|||
|
||||
Entry::Entry(not_null<Data::Session*> owner, Type type)
|
||||
: _owner(owner)
|
||||
, _isFolder(type == Type::Folder) {
|
||||
, _type(type) {
|
||||
}
|
||||
|
||||
Data::Session &Entry::owner() const {
|
||||
|
@ -57,11 +58,19 @@ Main::Session &Entry::session() const {
|
|||
}
|
||||
|
||||
History *Entry::asHistory() {
|
||||
return _isFolder ? nullptr : static_cast<History*>(this);
|
||||
return (_type == Type::History) ? static_cast<History*>(this) : nullptr;
|
||||
}
|
||||
|
||||
Data::Folder *Entry::asFolder() {
|
||||
return _isFolder ? static_cast<Data::Folder*>(this) : nullptr;
|
||||
return (_type == Type::Folder)
|
||||
? static_cast<Data::Folder*>(this)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
Data::ForumTopic *Entry::asForumTopic() {
|
||||
return (_type == Type::ForumTopic)
|
||||
? static_cast<Data::ForumTopic*>(this)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
void Entry::pinnedIndexChanged(FilterId filterId, int was, int now) {
|
||||
|
@ -177,6 +186,9 @@ const Ui::Text::String &Entry::chatListNameText() const {
|
|||
}
|
||||
|
||||
void Entry::setChatListExistence(bool exists) {
|
||||
if (asForumTopic()) {
|
||||
return;
|
||||
}
|
||||
if (exists && _sortKeyInChatList) {
|
||||
owner().refreshChatListEntry(this);
|
||||
updateChatListEntry();
|
||||
|
@ -251,7 +263,7 @@ not_null<Row*> Entry::addToChatList(
|
|||
void Entry::removeFromChatList(
|
||||
FilterId filterId,
|
||||
not_null<MainList*> list) {
|
||||
if (isPinnedDialog(filterId)) {
|
||||
if (!asForumTopic() && isPinnedDialog(filterId)) {
|
||||
owner().setChatPinned(this, filterId, false);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ class Session;
|
|||
namespace Data {
|
||||
class Session;
|
||||
class Folder;
|
||||
class ForumTopic;
|
||||
class CloudImageView;
|
||||
} // namespace Data
|
||||
|
||||
|
@ -91,9 +92,10 @@ inline UnreadState operator-(const UnreadState &a, const UnreadState &b) {
|
|||
|
||||
class Entry {
|
||||
public:
|
||||
enum class Type {
|
||||
enum class Type : uchar {
|
||||
History,
|
||||
Folder,
|
||||
ForumTopic,
|
||||
};
|
||||
Entry(not_null<Data::Session*> owner, Type type);
|
||||
Entry(const Entry &other) = delete;
|
||||
|
@ -105,6 +107,7 @@ public:
|
|||
|
||||
History *asHistory();
|
||||
Data::Folder *asFolder();
|
||||
Data::ForumTopic *asForumTopic();
|
||||
|
||||
PositionChange adjustByPosInChatList(
|
||||
FilterId filterId,
|
||||
|
@ -230,7 +233,7 @@ private:
|
|||
mutable int _chatListNameVersion = 0;
|
||||
TimeId _timeId = 0;
|
||||
bool _isTopPromoted = false;
|
||||
const bool _isFolder = false;
|
||||
const Type _type;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/ui_utility.h"
|
||||
#include "data/data_drafts.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
|
@ -409,6 +410,17 @@ void InnerWidget::changeOpenedForum(ChannelData *forum) {
|
|||
stopReorderPinned();
|
||||
clearSelection();
|
||||
_openedForum = forum;
|
||||
|
||||
_openedForumLifetime.destroy();
|
||||
if (forum) {
|
||||
rpl::merge(
|
||||
forum->forum()->chatsListChanges(),
|
||||
forum->forum()->chatsListLoadedEvents()
|
||||
) | rpl::start_with_next([=] {
|
||||
refresh();
|
||||
}, _openedForumLifetime);
|
||||
}
|
||||
|
||||
refreshWithCollapsedRows(true);
|
||||
if (_loadMoreCallback) {
|
||||
_loadMoreCallback();
|
||||
|
@ -1778,6 +1790,8 @@ void InnerWidget::updateSelectedRow(Key key) {
|
|||
not_null<IndexedList*> InnerWidget::shownDialogs() const {
|
||||
return _filterId
|
||||
? session().data().chatsFilters().chatsList(_filterId)->indexed()
|
||||
: _openedForum
|
||||
? _openedForum->forum()->topicsList()->indexed()
|
||||
: session().data().chatsList(_openedFolder)->indexed();
|
||||
}
|
||||
|
||||
|
@ -2346,6 +2360,8 @@ void InnerWidget::refreshEmptyLabel() {
|
|||
const auto data = &session().data();
|
||||
const auto state = !shownDialogs()->empty()
|
||||
? EmptyState::None
|
||||
: _openedForum
|
||||
? EmptyState::EmptyForum
|
||||
: (!_filterId && data->contactsLoaded().current())
|
||||
? EmptyState::NoContacts
|
||||
: (_filterId > 0) && data->chatsList()->loaded()
|
||||
|
@ -2364,11 +2380,17 @@ void InnerWidget::refreshEmptyLabel() {
|
|||
? tr::lng_no_chats()
|
||||
: (state == EmptyState::EmptyFolder)
|
||||
? tr::lng_no_chats_filter()
|
||||
: (state == EmptyState::EmptyForum)
|
||||
// #TODO forum
|
||||
? rpl::single(u"No chats currently created in this forum."_q)
|
||||
: tr::lng_contacts_loading();
|
||||
auto link = (state == EmptyState::NoContacts)
|
||||
? tr::lng_add_contact_button()
|
||||
: (state == EmptyState::EmptyFolder)
|
||||
? tr::lng_filters_context_edit()
|
||||
: (state == EmptyState::EmptyForum)
|
||||
// #TODO forum
|
||||
? rpl::single(u"Create topic"_q)
|
||||
: rpl::single(QString());
|
||||
auto full = rpl::combine(
|
||||
std::move(phrase),
|
||||
|
@ -2387,6 +2409,8 @@ void InnerWidget::refreshEmptyLabel() {
|
|||
_controller->showAddContact();
|
||||
} else if (_emptyState == EmptyState::EmptyFolder) {
|
||||
editOpenedFilter();
|
||||
} else if (_emptyState == EmptyState::EmptyForum) {
|
||||
Data::ShowAddForumTopic(_controller, _openedForum);
|
||||
}
|
||||
});
|
||||
_empty->setVisible(_state == WidgetState::Default);
|
||||
|
@ -3261,9 +3285,13 @@ void InnerWidget::setupShortcuts() {
|
|||
return jumpToDialogRow(last);
|
||||
});
|
||||
request->check(Command::ChatSelf) && request->handle([=] {
|
||||
_controller->content()->choosePeer(
|
||||
session().userPeerId(),
|
||||
ShowAtUnreadMsgId);
|
||||
if (_openedForum) {
|
||||
Data::ShowAddForumTopic(_controller, _openedForum);
|
||||
} else {
|
||||
_controller->content()->choosePeer(
|
||||
session().userPeerId(),
|
||||
ShowAtUnreadMsgId);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
request->check(Command::ShowArchive) && request->handle([=] {
|
||||
|
|
|
@ -331,7 +331,7 @@ private:
|
|||
void clearSearchResults(bool clearPeerSearchResults = true);
|
||||
void updateSelectedRow(Key key = Key());
|
||||
|
||||
not_null<IndexedList*> shownDialogs() const;
|
||||
[[nodiscard]] not_null<IndexedList*> shownDialogs() const;
|
||||
|
||||
void checkReorderPinnedStart(QPoint localPosition);
|
||||
int updateReorderIndexGetCount();
|
||||
|
@ -343,7 +343,7 @@ private:
|
|||
bool pinnedShiftAnimationCallback(crl::time now);
|
||||
void handleChatListEntryRefreshes();
|
||||
|
||||
not_null<Window::SessionController*> _controller;
|
||||
const not_null<Window::SessionController*> _controller;
|
||||
|
||||
FilterId _filterId = 0;
|
||||
bool _mouseSelection = false;
|
||||
|
@ -352,6 +352,7 @@ private:
|
|||
|
||||
Data::Folder *_openedFolder = nullptr;
|
||||
ChannelData *_openedForum = nullptr;
|
||||
rpl::lifetime _openedForumLifetime;
|
||||
|
||||
std::vector<std::unique_ptr<CollapsedRow>> _collapsedRows;
|
||||
int _collapsedSelected = -1;
|
||||
|
|
|
@ -8,12 +8,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "dialogs/dialogs_key.h"
|
||||
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "history/history.h"
|
||||
|
||||
namespace Dialogs {
|
||||
namespace {
|
||||
|
||||
using Folder = Data::Folder;
|
||||
using ForumTopic = Data::ForumTopic;
|
||||
|
||||
} // namespace
|
||||
|
||||
|
@ -23,12 +25,18 @@ Key::Key(History *history) : _value(history) {
|
|||
Key::Key(Data::Folder *folder) : _value(folder) {
|
||||
}
|
||||
|
||||
Key::Key(Data::ForumTopic *forumTopic) : _value(forumTopic) {
|
||||
}
|
||||
|
||||
Key::Key(not_null<History*> history) : _value(history) {
|
||||
}
|
||||
|
||||
Key::Key(not_null<Data::Folder*> folder) : _value(folder) {
|
||||
}
|
||||
|
||||
Key::Key(not_null<Data::ForumTopic*> forumTopic) : _value(forumTopic) {
|
||||
}
|
||||
|
||||
not_null<Entry*> Key::entry() const {
|
||||
Expects(_value != nullptr);
|
||||
|
||||
|
@ -43,6 +51,10 @@ Folder *Key::folder() const {
|
|||
return _value ? _value->asFolder() : nullptr;
|
||||
}
|
||||
|
||||
ForumTopic *Key::forumTopic() const {
|
||||
return _value ? _value->asForumTopic() : nullptr;
|
||||
}
|
||||
|
||||
PeerData *Key::peer() const {
|
||||
if (const auto history = this->history()) {
|
||||
return history->peer;
|
||||
|
|
|
@ -14,6 +14,7 @@ class PeerData;
|
|||
|
||||
namespace Data {
|
||||
class Folder;
|
||||
class ForumTopic;
|
||||
} // namespace Data
|
||||
|
||||
namespace Dialogs {
|
||||
|
@ -27,10 +28,12 @@ public:
|
|||
}
|
||||
Key(History *history);
|
||||
Key(Data::Folder *folder);
|
||||
Key(Data::ForumTopic *forumTopic);
|
||||
Key(not_null<Entry*> entry) : _value(entry) {
|
||||
}
|
||||
Key(not_null<History*> history);
|
||||
Key(not_null<Data::Folder*> folder);
|
||||
Key(not_null<Data::ForumTopic*> forumTopic);
|
||||
|
||||
explicit operator bool() const {
|
||||
return (_value != nullptr);
|
||||
|
@ -38,6 +41,7 @@ public:
|
|||
not_null<Entry*> entry() const;
|
||||
History *history() const;
|
||||
Data::Folder *folder() const;
|
||||
Data::ForumTopic *forumTopic() const;
|
||||
PeerData *peer() const;
|
||||
|
||||
inline bool operator<(const Key &other) const {
|
||||
|
|
|
@ -46,6 +46,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_chat.h"
|
||||
#include "data/data_user.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_download_manager.h"
|
||||
|
@ -261,7 +263,11 @@ Widget::Widget(
|
|||
const auto openSearchResult = !controller->selectingPeer()
|
||||
&& row.filteredRow;
|
||||
const auto history = row.key.history();
|
||||
if (history && history->peer->isForum()) {
|
||||
if (const auto forumTopic = row.key.forumTopic()) {
|
||||
controller->showRepliesForMessage(
|
||||
forumTopic->forum(),
|
||||
forumTopic->rootId());
|
||||
} else if (history && history->peer->isForum()) {
|
||||
controller->openForum(history->peer->asChannel());
|
||||
} else if (history) {
|
||||
const auto peer = history->peer;
|
||||
|
@ -372,6 +378,8 @@ Widget::Widget(
|
|||
&& _searchFull
|
||||
&& !_searchFullMigrated))) {
|
||||
searchMore();
|
||||
} else if (_openedForum) {
|
||||
_openedForum->forum()->requestTopics();
|
||||
} else {
|
||||
const auto folder = _inner->shownFolder();
|
||||
if (!folder || !folder->chatsList()->loaded()) {
|
||||
|
|
|
@ -27,6 +27,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_sponsored_messages.h"
|
||||
#include "data/data_send_action.h"
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_photo.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat.h"
|
||||
|
@ -1123,6 +1124,11 @@ void History::newItemAdded(not_null<HistoryItem*> item) {
|
|||
if (!folderKnown()) {
|
||||
owner().histories().requestDialogEntry(this);
|
||||
}
|
||||
if (item->isTopicStart() && peer->isForum()) {
|
||||
if (const auto forum = peer->asChannel()->forum()) {
|
||||
forum->topicAdded(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void History::registerClientSideMessage(not_null<HistoryItem*> item) {
|
||||
|
@ -2019,12 +2025,13 @@ Dialogs::UnreadState History::chatListUnreadState() const {
|
|||
auto result = Dialogs::UnreadState();
|
||||
const auto count = _unreadCount.value_or(0);
|
||||
const auto mark = !count && _unreadMark;
|
||||
const auto muted = mute();
|
||||
result.messages = count;
|
||||
result.messagesMuted = mute() ? count : 0;
|
||||
result.messagesMuted = muted ? count : 0;
|
||||
result.chats = count ? 1 : 0;
|
||||
result.chatsMuted = (count && mute()) ? 1 : 0;
|
||||
result.chatsMuted = (count && muted) ? 1 : 0;
|
||||
result.marks = mark ? 1 : 0;
|
||||
result.marksMuted = (mark && mute()) ? 1 : 0;
|
||||
result.marksMuted = (mark && muted) ? 1 : 0;
|
||||
result.known = _unreadCount.has_value();
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1424,7 +1424,8 @@ MessageFlags FlagsFromMTP(
|
|||
| ((flags & MTP::f_reply_markup) ? Flag::HasReplyMarkup : Flag())
|
||||
| ((flags & MTP::f_from_scheduled) ? Flag::IsOrWasScheduled : Flag())
|
||||
| ((flags & MTP::f_views) ? Flag::HasViews : Flag())
|
||||
| ((flags & MTP::f_noforwards) ? Flag::NoForwards : Flag());
|
||||
| ((flags & MTP::f_noforwards) ? Flag::NoForwards : Flag())
|
||||
| ((flags & MTP::f_topic_start) ? Flag::TopicStart : Flag());
|
||||
}
|
||||
|
||||
MessageFlags FlagsFromMTP(
|
||||
|
|
|
@ -145,6 +145,9 @@ public:
|
|||
[[nodiscard]] bool isPinned() const {
|
||||
return _flags & MessageFlag::Pinned;
|
||||
}
|
||||
[[nodiscard]] bool isTopicStart() const {
|
||||
return _flags & MessageFlag::TopicStart;
|
||||
}
|
||||
[[nodiscard]] bool unread() const;
|
||||
[[nodiscard]] bool showNotification() const;
|
||||
void markClientSideAsRead();
|
||||
|
|
Loading…
Add table
Reference in a new issue