| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963 |
- /*
- 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_changes.h"
- #include "data/data_forum.h"
- #include "data/data_histories.h"
- #include "data/data_replies_list.h"
- #include "data/data_send_action.h"
- #include "data/notify/data_notify_settings.h"
- #include "data/data_session.h"
- #include "data/stickers/data_custom_emoji.h"
- #include "dialogs/dialogs_main_list.h"
- #include "dialogs/ui/dialogs_layout.h"
- #include "core/application.h"
- #include "core/core_settings.h"
- #include "apiwrap.h"
- #include "api/api_unread_things.h"
- #include "history/history.h"
- #include "history/history_item.h"
- #include "history/history_unread_things.h"
- #include "history/view/history_view_item_preview.h"
- #include "history/view/history_view_replies_section.h"
- #include "main/main_session.h"
- #include "base/unixtime.h"
- #include "ui/painter.h"
- #include "ui/color_int_conversion.h"
- #include "ui/text/text_custom_emoji.h"
- #include "styles/style_dialogs.h"
- #include "styles/style_chat_helpers.h"
- #include <QtSvg/QSvgRenderer>
- namespace Data {
- namespace {
- using UpdateFlag = TopicUpdate::Flag;
- constexpr auto kUserpicLoopsCount = 1;
- } // namespace
- const base::flat_map<int32, QString> &ForumTopicIcons() {
- static const auto Result = base::flat_map<int32, QString>{
- { 0x6FB9F0, u"blue"_q },
- { 0xFFD67E, u"yellow"_q },
- { 0xCB86DB, u"violet"_q },
- { 0x8EEE98, u"green"_q },
- { 0xFF93B2, u"rose"_q },
- { 0xFB6F5F, u"red"_q },
- };
- return Result;
- }
- const std::vector<int32> &ForumTopicColorIds() {
- static const auto Result = ForumTopicIcons(
- ) | ranges::views::transform([](const auto &pair) {
- return pair.first;
- }) | ranges::to_vector;
- return Result;
- }
- const QString &ForumTopicDefaultIcon() {
- static const auto Result = u"gray"_q;
- return Result;
- }
- const QString &ForumTopicIcon(int32 colorId) {
- const auto &icons = ForumTopicIcons();
- const auto i = icons.find(colorId);
- return (i != end(icons)) ? i->second : ForumTopicDefaultIcon();
- }
- QString ForumTopicIconPath(const QString &name) {
- return u":/gui/topic_icons/%1.svg"_q.arg(name);
- }
- QImage ForumTopicIconBackground(int32 colorId, int size) {
- const auto ratio = style::DevicePixelRatio();
- auto svg = QSvgRenderer(ForumTopicIconPath(ForumTopicIcon(colorId)));
- auto result = QImage(
- QSize(size, size) * ratio,
- QImage::Format_ARGB32_Premultiplied);
- result.setDevicePixelRatio(ratio);
- result.fill(Qt::transparent);
- auto p = QPainter(&result);
- svg.render(&p, QRect(0, 0, size, size));
- p.end();
- return result;
- }
- QString ExtractNonEmojiLetter(const QString &title) {
- const auto begin = title.data();
- const auto end = begin + title.size();
- for (auto ch = begin; ch != end;) {
- auto length = 0;
- if (Ui::Emoji::Find(ch, end, &length)) {
- ch += length;
- continue;
- }
- uint ucs4 = ch->unicode();
- length = 1;
- if (QChar::isHighSurrogate(ucs4) && ch + 1 != end) {
- ushort low = ch[1].unicode();
- if (QChar::isLowSurrogate(low)) {
- ucs4 = QChar::surrogateToUcs4(ucs4, low);
- length = 2;
- }
- }
- if (!QChar::isLetterOrNumber(ucs4)) {
- ch += length;
- continue;
- }
- return QString(ch, length);
- }
- return QString();
- }
- QImage ForumTopicIconFrame(
- int32 colorId,
- const QString &title,
- const style::ForumTopicIcon &st) {
- auto background = ForumTopicIconBackground(colorId, st.size);
- if (const auto one = ExtractNonEmojiLetter(title); !one.isEmpty()) {
- auto p = QPainter(&background);
- p.setPen(Qt::white);
- p.setFont(st.font);
- p.drawText(
- QRect(0, st.textTop, st.size, st.font->height * 2),
- one,
- style::al_top);
- }
- return background;
- }
- QImage ForumTopicGeneralIconFrame(int size, const QColor &color) {
- const auto ratio = style::DevicePixelRatio();
- auto svg = QSvgRenderer(ForumTopicIconPath(u"general"_q));
- auto result = QImage(
- QSize(size, size) * ratio,
- QImage::Format_ARGB32_Premultiplied);
- result.setDevicePixelRatio(ratio);
- result.fill(Qt::transparent);
- const auto use = size * 0.8;
- const auto skip = size * 0.1;
- auto p = QPainter(&result);
- svg.render(&p, QRectF(skip, 0, use, use));
- p.end();
- return style::colorizeImage(result, color);
- }
- TextWithEntities ForumTopicIconWithTitle(
- MsgId rootId,
- DocumentId iconId,
- const QString &title) {
- const auto wrapped = st::wrap_rtl(title);
- return (rootId == ForumTopic::kGeneralId)
- ? TextWithEntities{ u"# "_q + wrapped }
- : iconId
- ? Data::SingleCustomEmoji(iconId).append(' ').append(wrapped)
- : TextWithEntities{ wrapped };
- }
- QString ForumGeneralIconTitle() {
- return QChar(0) + u"general"_q;
- }
- bool IsForumGeneralIconTitle(const QString &title) {
- return !title.isEmpty() && !title[0].unicode();
- }
- int32 ForumGeneralIconColor(const QColor &color) {
- return int32(uint32(color.red()) << 16
- | uint32(color.green()) << 8
- | uint32(color.blue())
- | (uint32(color.alpha() == 255 ? 0 : color.alpha()) << 24));
- }
- QColor ParseForumGeneralIconColor(int32 value) {
- const auto alpha = uint32(value) >> 24;
- return QColor(
- (value >> 16) & 0xFF,
- (value >> 8) & 0xFF,
- value & 0xFF,
- alpha ? alpha : 255);
- }
- QString TopicIconEmojiEntity(TopicIconDescriptor descriptor) {
- return IsForumGeneralIconTitle(descriptor.title)
- ? u"topic_general:"_q + QString::number(uint32(descriptor.colorId))
- : (u"topic_icon:"_q
- + QString::number(uint32(descriptor.colorId))
- + ' '
- + ExtractNonEmojiLetter(descriptor.title));
- }
- TopicIconDescriptor ParseTopicIconEmojiEntity(QStringView entity) {
- if (!entity.startsWith(u"topic_")) {
- return {};
- }
- const auto general = u"topic_general:"_q;
- const auto normal = u"topic_icon:"_q;
- if (entity.startsWith(general)) {
- return {
- .title = ForumGeneralIconTitle(),
- .colorId = int32(entity.mid(general.size()).toUInt()),
- };
- } else if (entity.startsWith(normal)) {
- const auto parts = entity.mid(normal.size()).split(' ');
- if (parts.size() == 2) {
- return {
- .title = parts[1].toString(),
- .colorId = int32(parts[0].toUInt()),
- };
- }
- }
- return {};
- }
- ForumTopic::ForumTopic(not_null<Forum*> forum, MsgId rootId)
- : Thread(&forum->history()->owner(), Type::ForumTopic)
- , _forum(forum)
- , _list(_forum->topicsList())
- , _replies(std::make_shared<RepliesList>(history(), rootId, this))
- , _sendActionPainter(owner().sendActionManager().repliesPainter(
- history(),
- rootId))
- , _rootId(rootId)
- , _lastKnownServerMessageId(rootId)
- , _creatorId(creating() ? forum->session().userPeerId() : 0)
- , _creationDate(creating() ? base::unixtime::now() : 0)
- , _flags(creating() ? Flag::My : Flag()) {
- Thread::setMuted(owner().notifySettings().isMuted(this));
- _sendActionPainter->setTopic(this);
- subscribeToUnreadChanges();
- if (isGeneral()) {
- style::PaletteChanged(
- ) | rpl::start_with_next([=] {
- _defaultIcon = QImage();
- }, _lifetime);
- }
- }
- ForumTopic::~ForumTopic() {
- _sendActionPainter->setTopic(nullptr);
- session().api().unreadThings().cancelRequests(this);
- }
- std::shared_ptr<Data::RepliesList> ForumTopic::replies() const {
- return _replies;
- }
- not_null<ChannelData*> ForumTopic::channel() const {
- return _forum->channel();
- }
- not_null<History*> ForumTopic::history() const {
- return _forum->history();
- }
- not_null<Forum*> ForumTopic::forum() const {
- return _forum;
- }
- rpl::producer<> ForumTopic::destroyed() const {
- using namespace rpl::mappers;
- return rpl::merge(
- _forum->destroyed(),
- _forum->topicDestroyed() | rpl::filter(_1 == this) | rpl::to_empty);
- }
- MsgId ForumTopic::rootId() const {
- return _rootId;
- }
- PeerId ForumTopic::creatorId() const {
- return _creatorId;
- }
- TimeId ForumTopic::creationDate() const {
- return _creationDate;
- }
- not_null<HistoryView::ListMemento*> ForumTopic::listMemento() {
- if (!_listMemento) {
- _listMemento = std::make_unique<HistoryView::ListMemento>();
- }
- return _listMemento.get();
- }
- bool ForumTopic::my() const {
- return (_flags & Flag::My);
- }
- bool ForumTopic::canEdit() const {
- return my() || channel()->canManageTopics();
- }
- bool ForumTopic::canDelete() const {
- if (creating() || isGeneral()) {
- return false;
- } else if (channel()->canDeleteMessages()) {
- return true;
- }
- return my() && replies()->canDeleteMyTopic();
- }
- bool ForumTopic::canToggleClosed() const {
- return !creating() && canEdit();
- }
- bool ForumTopic::canTogglePinned() const {
- return !creating() && channel()->canManageTopics();
- }
- bool ForumTopic::creating() const {
- return _forum->creating(_rootId);
- }
- void ForumTopic::discard() {
- Expects(creating());
- _forum->discardCreatingId(_rootId);
- }
- void ForumTopic::setRealRootId(MsgId realId) {
- if (_rootId != realId) {
- _rootId = realId;
- _lastKnownServerMessageId = realId;
- _replies = std::make_shared<RepliesList>(history(), _rootId);
- if (_sendActionPainter) {
- _sendActionPainter->setTopic(nullptr);
- }
- _sendActionPainter = owner().sendActionManager().repliesPainter(
- history(),
- _rootId);
- _sendActionPainter->setTopic(this);
- subscribeToUnreadChanges();
- }
- }
- void ForumTopic::subscribeToUnreadChanges() {
- _replies->unreadCountValue(
- ) | rpl::map([=](std::optional<int> value) {
- return value ? _replies->displayedUnreadCount() : value;
- }) | rpl::distinct_until_changed(
- ) | rpl::combine_previous(
- ) | rpl::filter([=] {
- return inChatList();
- }) | rpl::start_with_next([=](
- std::optional<int> previous,
- std::optional<int> now) {
- if (previous.value_or(0) != now.value_or(0)) {
- _forum->recentTopicsInvalidate(this);
- }
- notifyUnreadStateChange(unreadStateFor(
- previous.value_or(0),
- previous.has_value()));
- }, _lifetime);
- }
- void ForumTopic::readTillEnd() {
- _replies->readTill(_lastKnownServerMessageId);
- }
- void ForumTopic::applyTopic(const MTPDforumTopic &data) {
- Expects(_rootId == data.vid().v);
- const auto min = data.is_short();
- applyCreator(peerFromMTP(data.vfrom_id()));
- applyCreationDate(data.vdate().v);
- applyTitle(qs(data.vtitle()));
- if (const auto iconId = data.vicon_emoji_id()) {
- applyIconId(iconId->v);
- } else {
- applyIconId(0);
- }
- applyColorId(data.vicon_color().v);
- applyIsMy(data.is_my());
- setClosed(data.is_closed());
- if (!min) {
- owner().setPinnedFromEntryList(this, data.is_pinned());
- owner().notifySettings().apply(this, data.vnotify_settings());
- if (const auto draft = data.vdraft()) {
- draft->match([&](const MTPDdraftMessage &data) {
- Data::ApplyPeerCloudDraft(
- &session(),
- channel()->id,
- _rootId,
- data);
- }, [](const MTPDdraftMessageEmpty&) {});
- }
- _replies->setInboxReadTill(
- data.vread_inbox_max_id().v,
- data.vunread_count().v);
- _replies->setOutboxReadTill(data.vread_outbox_max_id().v);
- applyTopicTopMessage(data.vtop_message().v);
- unreadMentions().setCount(data.vunread_mentions_count().v);
- unreadReactions().setCount(data.vunread_reactions_count().v);
- }
- }
- void ForumTopic::applyCreator(PeerId creatorId) {
- if (_creatorId != creatorId) {
- _creatorId = creatorId;
- session().changes().topicUpdated(this, UpdateFlag::Creator);
- }
- }
- void ForumTopic::applyCreationDate(TimeId date) {
- _creationDate = date;
- }
- void ForumTopic::applyIsMy(bool my) {
- if (my != this->my()) {
- if (my) {
- _flags |= Flag::My;
- } else {
- _flags &= ~Flag::My;
- }
- }
- }
- bool ForumTopic::closed() const {
- return _flags & Flag::Closed;
- }
- void ForumTopic::setClosed(bool closed) {
- if (this->closed() == closed) {
- return;
- } else if (closed) {
- _flags |= Flag::Closed;
- } else {
- _flags &= ~Flag::Closed;
- }
- session().changes().topicUpdated(this, UpdateFlag::Closed);
- }
- void ForumTopic::setClosedAndSave(bool closed) {
- setClosed(closed);
- const auto api = &session().api();
- const auto weak = base::make_weak(this);
- api->request(MTPchannels_EditForumTopic(
- MTP_flags(MTPchannels_EditForumTopic::Flag::f_closed),
- channel()->inputChannel,
- MTP_int(_rootId),
- MTPstring(), // title
- MTPlong(), // icon_emoji_id
- MTP_bool(closed),
- MTPBool() // hidden
- )).done([=](const MTPUpdates &result) {
- api->applyUpdates(result);
- }).fail([=](const MTP::Error &error) {
- if (error.type() != u"TOPIC_NOT_MODIFIED") {
- if (const auto topic = weak.get()) {
- topic->forum()->requestTopic(topic->rootId());
- }
- }
- }).send();
- }
- bool ForumTopic::hidden() const {
- return (_flags & Flag::Hidden);
- }
- void ForumTopic::setHidden(bool hidden) {
- if (hidden) {
- _flags |= Flag::Hidden;
- } else {
- _flags &= ~Flag::Hidden;
- }
- }
- 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::applyTopicTopMessage(MsgId topMessageId) {
- if (topMessageId) {
- growLastKnownServerMessageId(topMessageId);
- const auto itemId = FullMsgId(channel()->id, topMessageId);
- if (const auto item = owner().message(itemId)) {
- setLastServerMessage(item);
- resolveChatListMessageGroup();
- } else {
- setLastServerMessage(nullptr);
- }
- } else {
- setLastServerMessage(nullptr);
- }
- }
- void ForumTopic::resolveChatListMessageGroup() {
- if (!(_flags & Flag::ResolveChatListMessage)) {
- return;
- }
- // If we set a single album part, request the full album.
- const auto item = _lastServerMessage.value_or(nullptr);
- if (item && item->groupId() != MessageGroupId()) {
- if (owner().groups().isGroupOfOne(item)
- && !item->toPreview({
- .hideSender = true,
- .hideCaption = true }).images.empty()
- && _requestedGroups.emplace(item->fullId()).second) {
- owner().histories().requestGroupAround(item);
- }
- }
- }
- void ForumTopic::growLastKnownServerMessageId(MsgId id) {
- _lastKnownServerMessageId = std::max(_lastKnownServerMessageId, id);
- }
- void ForumTopic::setLastServerMessage(HistoryItem *item) {
- if (item) {
- growLastKnownServerMessageId(item->id);
- }
- _lastServerMessage = item;
- if (_lastMessage
- && *_lastMessage
- && !(*_lastMessage)->isRegular()
- && (!item
- || (*_lastMessage)->date() > item->date()
- || (*_lastMessage)->isSending())) {
- return;
- }
- setLastMessage(item);
- }
- void ForumTopic::setLastMessage(HistoryItem *item) {
- if (_lastMessage && *_lastMessage == item) {
- return;
- }
- _lastMessage = item;
- if (!item || item->isRegular()) {
- _lastServerMessage = item;
- if (item) {
- growLastKnownServerMessageId(item->id);
- }
- }
- 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());
- } else if (!_chatListMessage || *_chatListMessage) {
- _chatListMessage = nullptr;
- updateChatListEntry();
- }
- _forum->listMessageChanged(was, item);
- }
- void ForumTopic::chatListPreloadData() {
- if (_icon) {
- [[maybe_unused]] const auto preload = _icon->ready();
- }
- allowChatListMessageResolve();
- }
- void ForumTopic::paintUserpic(
- Painter &p,
- Ui::PeerUserpicView &view,
- const Dialogs::Ui::PaintContext &context) const {
- const auto &st = context.st;
- auto position = QPoint(st->padding.left(), st->padding.top());
- if (_icon) {
- if (context.narrow) {
- const auto ratio = style::DevicePixelRatio();
- const auto tag = Data::CustomEmojiManager::SizeTag::Normal;
- const auto size = Data::FrameSizeFromTag(tag) / ratio;
- position = QPoint(
- (context.width - size) / 2,
- (st->height - size) / 2);
- }
- _icon->paint(p, {
- .textColor = (context.active
- ? st::dialogsNameFgActive
- : context.selected
- ? st::dialogsNameFgOver
- : st::dialogsNameFg)->c,
- .now = context.now,
- .position = position,
- .paused = context.paused,
- });
- } else {
- if (isGeneral()) {
- validateGeneralIcon(context);
- } else {
- validateDefaultIcon();
- }
- const auto size = st::defaultForumTopicIcon.size;
- if (context.narrow) {
- position = QPoint(
- (context.width - size) / 2,
- (st->height - size) / 2);
- } else {
- const auto esize = st::emojiSize;
- const auto shift = (esize - size) / 2;
- position += st::forumTopicIconPosition + QPoint(shift, 0);
- }
- p.drawImage(position, _defaultIcon);
- }
- }
- void ForumTopic::clearUserpicLoops() {
- if (_icon) {
- _icon->unload();
- }
- }
- void ForumTopic::validateDefaultIcon() const {
- if (!_defaultIcon.isNull()) {
- return;
- }
- _defaultIcon = ForumTopicIconFrame(
- _colorId,
- _title,
- st::defaultForumTopicIcon);
- }
- void ForumTopic::validateGeneralIcon(
- const Dialogs::Ui::PaintContext &context) const {
- const auto mask = Flag::GeneralIconActive | Flag::GeneralIconSelected;
- const auto flags = context.active
- ? Flag::GeneralIconActive
- : context.selected
- ? Flag::GeneralIconSelected
- : Flag(0);
- if (!_defaultIcon.isNull() && ((_flags & mask) == flags)) {
- return;
- }
- const auto size = st::defaultForumTopicIcon.size;
- const auto &color = context.active
- ? st::dialogsTextFgActive
- : context.selected
- ? st::dialogsTextFgOver
- : st::dialogsTextFg;
- _defaultIcon = ForumTopicGeneralIconFrame(size, color->c);
- _flags = (_flags & ~mask) | flags;
- }
- void ForumTopic::requestChatListMessage() {
- if (!chatListMessageKnown() && !forum()->creating(_rootId)) {
- forum()->requestTopic(_rootId);
- }
- }
- TimeId ForumTopic::adjustedChatListTimeId() const {
- const auto result = chatListTimeId();
- if (const auto draft = history()->cloudDraft(_rootId)) {
- if (!Data::DraftIsNull(draft) && !session().supportMode()) {
- return std::max(result, draft->date);
- }
- }
- return result;
- }
- int ForumTopic::fixedOnTopIndex() const {
- return 0;
- }
- 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();
- }
- MsgId ForumTopic::lastKnownServerMessageId() const {
- return _lastKnownServerMessageId;
- }
- QString ForumTopic::title() const {
- return _title;
- }
- TextWithEntities ForumTopic::titleWithIcon() const {
- return ForumTopicIconWithTitle(_rootId, _iconId, _title);
- }
- int ForumTopic::titleVersion() const {
- return _titleVersion;
- }
- void ForumTopic::applyTitle(const QString &title) {
- if (_title == title) {
- return;
- }
- _title = title;
- invalidateTitleWithIcon();
- _defaultIcon = QImage();
- indexTitleParts();
- updateChatListEntry();
- session().changes().topicUpdated(this, UpdateFlag::Title);
- }
- DocumentId ForumTopic::iconId() const {
- return _iconId;
- }
- void ForumTopic::applyIconId(DocumentId iconId) {
- if (_iconId == iconId) {
- return;
- }
- _iconId = iconId;
- invalidateTitleWithIcon();
- _icon = iconId
- ? std::make_unique<Ui::Text::LimitedLoopsEmoji>(
- owner().customEmojiManager().create(
- _iconId,
- [=] { updateChatListEntry(); },
- Data::CustomEmojiManager::SizeTag::Normal),
- kUserpicLoopsCount)
- : nullptr;
- if (iconId) {
- _defaultIcon = QImage();
- }
- updateChatListEntry();
- session().changes().topicUpdated(this, UpdateFlag::IconId);
- }
- void ForumTopic::invalidateTitleWithIcon() {
- ++_titleVersion;
- _forum->recentTopicsInvalidate(this);
- }
- int32 ForumTopic::colorId() const {
- return _colorId;
- }
- void ForumTopic::applyColorId(int32 colorId) {
- if (_colorId != colorId) {
- _colorId = colorId;
- session().changes().topicUpdated(this, UpdateFlag::ColorId);
- }
- }
- void ForumTopic::applyItemAdded(not_null<HistoryItem*> item) {
- if (item->isRegular()) {
- setLastServerMessage(item);
- } else {
- setLastMessage(item);
- }
- }
- void ForumTopic::maybeSetLastMessage(not_null<HistoryItem*> item) {
- Expects(item->topicRootId() == _rootId);
- if (!_lastMessage
- || !(*_lastMessage)
- || ((*_lastMessage)->date() < item->date())
- || ((*_lastMessage)->date() == item->date()
- && (*_lastMessage)->id < item->id)) {
- setLastMessage(item);
- }
- }
- void ForumTopic::applyItemRemoved(MsgId id) {
- if (const auto lastItem = lastMessage()) {
- if (lastItem->id == id) {
- _lastMessage = std::nullopt;
- }
- }
- if (const auto lastServerItem = lastServerMessage()) {
- if (lastServerItem->id == id) {
- _lastServerMessage = std::nullopt;
- }
- }
- if (const auto chatListItem = _chatListMessage.value_or(nullptr)) {
- if (chatListItem->id == id) {
- _chatListMessage = std::nullopt;
- requestChatListMessage();
- }
- }
- }
- bool ForumTopic::isServerSideUnread(
- not_null<const HistoryItem*> item) const {
- return _replies->isServerSideUnread(item);
- }
- void ForumTopic::setMuted(bool muted) {
- if (this->muted() == muted) {
- return;
- }
- const auto state = chatListBadgesState();
- const auto notify = state.unread || state.reaction;
- const auto notifier = unreadStateChangeNotifier(notify);
- Thread::setMuted(muted);
- session().changes().topicUpdated(this, UpdateFlag::Notifications);
- }
- not_null<HistoryView::SendActionPainter*> ForumTopic::sendActionPainter() {
- return _sendActionPainter.get();
- }
- Dialogs::UnreadState ForumTopic::chatListUnreadState() const {
- return unreadStateFor(
- _replies->displayedUnreadCount(),
- _replies->unreadCountKnown());
- }
- Dialogs::BadgesState ForumTopic::chatListBadgesState() const {
- auto result = Dialogs::BadgesForUnread(
- chatListUnreadState(),
- Dialogs::CountInBadge::Messages,
- Dialogs::IncludeInBadge::All);
- if (!result.unread && _replies->inboxReadTillId() < 2) {
- result.unread = channel()->amIn()
- && (_lastKnownServerMessageId > history()->inboxReadTillId());
- result.unreadMuted = muted();
- }
- return result;
- }
- Dialogs::UnreadState ForumTopic::unreadStateFor(
- int count,
- bool known) const {
- auto result = Dialogs::UnreadState();
- const auto muted = this->muted();
- result.messages = count;
- result.chats = count ? 1 : 0;
- result.mentions = unreadMentions().has() ? 1 : 0;
- result.reactions = unreadReactions().has() ? 1 : 0;
- result.messagesMuted = muted ? result.messages : 0;
- result.chatsMuted = muted ? result.chats : 0;
- result.reactionsMuted = muted ? result.reactions : 0;
- result.known = known;
- return result;
- }
- void ForumTopic::allowChatListMessageResolve() {
- if (_flags & Flag::ResolveChatListMessage) {
- return;
- }
- _flags |= Flag::ResolveChatListMessage;
- resolveChatListMessageGroup();
- }
- 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;
- }
- void ForumTopic::hasUnreadMentionChanged(bool has) {
- auto was = chatListUnreadState();
- if (has) {
- was.mentions = 0;
- } else {
- was.mentions = 1;
- }
- notifyUnreadStateChange(was);
- }
- void ForumTopic::hasUnreadReactionChanged(bool has) {
- auto was = chatListUnreadState();
- if (has) {
- was.reactions = was.reactionsMuted = 0;
- } else {
- was.reactions = 1;
- was.reactionsMuted = muted() ? was.reactions : 0;
- }
- notifyUnreadStateChange(was);
- }
- const QString &ForumTopic::chatListNameSortKey() const {
- static const auto empty = QString();
- return empty;
- }
- } // namespace Data
|