From e209737b1af7d38999ea3d67a12dad0300eeda9b Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 11 Aug 2017 09:16:07 +0200 Subject: [PATCH] Display and follow unread mentions in history. --- .../icons/history_unread_mention.png | Bin 0 -> 583 bytes .../icons/history_unread_mention@2x.png | Bin 0 -> 995 bytes Telegram/SourceFiles/apiwrap.cpp | 49 ++- Telegram/SourceFiles/apiwrap.h | 7 + Telegram/SourceFiles/base/variant.h | 47 ++- .../chat_helpers/stickers_list_widget.cpp | 8 +- .../chat_helpers/stickers_list_widget.h | 4 +- .../dialogs/dialogs_inner_widget.cpp | 1 + .../SourceFiles/dialogs/dialogs_layout.cpp | 26 +- Telegram/SourceFiles/history/history.cpp | 193 ++++++----- Telegram/SourceFiles/history/history.h | 64 ++-- Telegram/SourceFiles/history/history.style | 6 + .../history/history_inner_widget.cpp | 12 + Telegram/SourceFiles/history/history_item.h | 4 +- .../SourceFiles/history/history_message.cpp | 16 + .../SourceFiles/history/history_message.h | 2 + .../SourceFiles/history/history_widget.cpp | 84 ++++- Telegram/SourceFiles/history/history_widget.h | 11 +- Telegram/SourceFiles/mainwidget.cpp | 316 +++++++++++------- Telegram/SourceFiles/mainwidget.h | 4 +- Telegram/SourceFiles/mainwindow.cpp | 5 + Telegram/SourceFiles/mainwindow.h | 1 + .../media/player/media_player_instance.cpp | 4 +- Telegram/SourceFiles/mediaview.cpp | 120 ++++--- Telegram/SourceFiles/mediaview.h | 4 +- Telegram/SourceFiles/observer_peer.h | 1 + Telegram/SourceFiles/overviewwidget.cpp | 103 +++--- Telegram/SourceFiles/overviewwidget.h | 2 +- Telegram/SourceFiles/shortcuts.cpp | 2 +- Telegram/SourceFiles/ui/special_buttons.cpp | 6 +- Telegram/gyp/telegram_sources.txt | 2 + 31 files changed, 747 insertions(+), 357 deletions(-) create mode 100644 Telegram/Resources/icons/history_unread_mention.png create mode 100644 Telegram/Resources/icons/history_unread_mention@2x.png diff --git a/Telegram/Resources/icons/history_unread_mention.png b/Telegram/Resources/icons/history_unread_mention.png new file mode 100644 index 0000000000000000000000000000000000000000..1ecd0546b5739cfbe268694e8d3e84f463cb4284 GIT binary patch literal 583 zcmV-N0=WH&P)Q+sWBLgIgTVj*u;1?i073|KyIlal zbUMXkGO->r#yGya-EQza56xy1p8(KkGynkC>lFYn9*=GCd_FIb+wFFhmn}X4*lM)a zPC$7gJkN7t@+6nbzXJgJ{k}cO!5FjOVHi4MLI|5=yWIkKu}qRA)|0Od;hdB2 z`-Mj3qnA73#oQpJl+)!TNh<9krF86MjIrMR0pM4s)4^yoLcLzc^ZCSjy~bj(K&@6o ztJOl5WjLSDn9XJgf&j~=eQ4F%sBV{(K3?}9TK#u$=PzMW^^ z_bEw|cZvV~=_>r3ATyqw7IRq8% z;5dX8tk!m3@!p=ho5^lg_v!l}#b#zFUy?~OGcy2y@E_w>6Gk}au(`Pj`}_N(yw~f& z!@~pI-rj!7K7OW2DN!kfNs^!|;yA`AijZ?&XDoM8j4=$uuofvBwAQXf-B+v$Vnu{u zSUT=Z%CZbfDL6Pdu*yfH5j;OX!(=jnUavRpUszaxPNxGK8ym2_y$y47bL9VbcXx1d zasp3JPo%tbONYZDIrBJ@hNXBtoPLMHX{FEd~DLKwL20?&IDb!l$S`MN7@L2z*r35_w5A3Ff@MfJkPoUHmA3Na+5fY4IL}N;_CJLeUhB-`?ZK=Q6xy& z%G?g+iqxU_3OPhH>@mg+oy<0WDE9mPoDy|6an8xpr?sX|AbA^5J5qF&D2hyiMFc@m zX$aAJe_4InMNeyu0D!ab7oO)q5jQtCxo^%nY;A4j`d(jO;qdU#__rxc3GVFd7{cr8 z>)iM8@v)(EadFWIk@0v;_D>16TCJR5Fc=uhdwY9EU$@(>MTD|jhAF|-)zwPn`T6Y-Mj$D6cXyqLtgWq){ZoP~D=Rs{%galp(IiQn2LB3XZGy>U(#QbqcH2p>xYGCc z_W)2r&}D0umzPWBLI`V+T10UoT5ABv5PW=mtTa;8Sz1~G&bd|p^z_t;obUU&iM+qR z=WY=4q9no|G7Pi5)j|kEH$^t8VT>8ULgC`KzpqqyR5X^7Od}r7d1dCOltRuqa?XwC z6%mVRgnh<^xXikWRfwX6 z%?wB>PzUBm2m;WVR_eq}q@IkiIu!|8KpmK@L)R>!p%4OtAef$J*Xl%R@hFNSI66AA ziE~9Gtu?&8y#c^S(WupG!TS0-wA*dtO}@OmgtN0VtL-dGXYf;Hw@h>MnWhSth=1Iq z#-Xd{+`jKSy$VIdag0hSGBqu0P6bRGgb=W}xM(e&r+%rFs!R=R_!7bIz;E#@s4Ac# R07d`+002ovPDHLkV1gCG*iZlf literal 0 HcmV?d00001 diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 4253fffbd..bf89e9f2d 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -43,6 +43,9 @@ constexpr auto kSaveCloudDraftTimeout = 1000; // save draft to the cloud with 1 constexpr auto kSaveDraftBeforeQuitTimeout = 1500; // give the app 1.5 secs to save drafts to cloud when quitting constexpr auto kSmallDelayMs = 5; constexpr auto kStickersUpdateTimeout = 3600000; // update not more than once in an hour +constexpr auto kUnreadMentionsPreloadIfLess = 5; +constexpr auto kUnreadMentionsFirstRequestLimit = 10; +constexpr auto kUnreadMentionsNextRequestLimit = 100; } // namespace @@ -1644,9 +1647,9 @@ void ApiWrap::applyUpdateNoPtsCheck(const MTPUpdate &update) { case mtpc_updateReadMessagesContents: { auto &d = update.c_updateReadMessagesContents(); - auto &v = d.vmessages.v; - for (auto i = 0, l = v.size(); i < l; ++i) { - if (auto item = App::histItemById(NoChannel, v.at(i).v)) { + auto possiblyReadMentions = base::flat_set(); + for_const (auto &msgId, d.vmessages.v) { + if (auto item = App::histItemById(NoChannel, msgId.v)) { if (item->isMediaUnread()) { item->markMediaRead(); Ui::repaintHistoryItem(item); @@ -1656,8 +1659,12 @@ void ApiWrap::applyUpdateNoPtsCheck(const MTPUpdate &update) { item->history()->peer->asUser()->madeAction(when); } } + } else { + // Perhaps it was an unread mention! + possiblyReadMentions.insert(msgId.v); } } + checkForUnreadMentions(possiblyReadMentions); } break; case mtpc_updateReadHistoryInbox: { @@ -1762,4 +1769,40 @@ void ApiWrap::jumpToDate(gsl::not_null peer, const QDate &date) { }).send(); } +void ApiWrap::preloadEnoughUnreadMentions(gsl::not_null history) { + auto fullCount = history->getUnreadMentionsCount(); + auto loadedCount = history->getUnreadMentionsLoadedCount(); + auto allLoaded = (fullCount >= 0) ? (loadedCount >= fullCount) : false; + if (fullCount < 0 || loadedCount >= kUnreadMentionsPreloadIfLess || allLoaded) { + return; + } + if (_unreadMentionsRequests.contains(history)) { + return; + } + auto offsetId = loadedCount ? history->getMaxLoadedUnreadMention() : 1; + auto limit = loadedCount ? kUnreadMentionsNextRequestLimit : kUnreadMentionsFirstRequestLimit; + auto addOffset = loadedCount ? -(limit + 1) : -limit; + auto maxId = 0; + auto minId = 0; + auto requestId = request(MTPmessages_GetUnreadMentions(history->peer->input, MTP_int(offsetId), MTP_int(addOffset), MTP_int(limit), MTP_int(maxId), MTP_int(minId))).done([this, history](const MTPmessages_Messages &result) { + _unreadMentionsRequests.remove(history); + history->addUnreadMentionsSlice(result); + }).fail([this, history](const RPCError &error) { + _unreadMentionsRequests.remove(history); + }).send(); + _unreadMentionsRequests.emplace(history, requestId); +} + +void ApiWrap::checkForUnreadMentions(const base::flat_set &possiblyReadMentions, ChannelData *channel) { + for (auto msgId : possiblyReadMentions) { + requestMessageData(channel, msgId, [](ChannelData *channel, MsgId msgId) { + if (auto item = App::histItemById(channel, msgId)) { + if (item->mentionsMe()) { + item->markMediaRead(); + } + } + }); + } +} + ApiWrap::~ApiWrap() = default; diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index df6f58e62..a526deff5 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -23,6 +23,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "base/timer.h" #include "core/single_timer.h" #include "mtproto/sender.h" +#include "base/flat_map.h" +#include "base/flat_set.h" class AuthSession; @@ -98,6 +100,9 @@ public: void jumpToDate(gsl::not_null peer, const QDate &date); + void preloadEnoughUnreadMentions(gsl::not_null history); + void checkForUnreadMentions(const base::flat_set &possiblyReadMentions, ChannelData *channel = nullptr); + ~ApiWrap(); private: @@ -189,6 +194,8 @@ private: mtpRequestId _contactsStatusesRequestId = 0; + base::flat_map, mtpRequestId> _unreadMentionsRequests; + base::Observable _fullPeerUpdated; }; diff --git a/Telegram/SourceFiles/base/variant.h b/Telegram/SourceFiles/base/variant.h index 963f10117..7e40bba4c 100644 --- a/Telegram/SourceFiles/base/variant.h +++ b/Telegram/SourceFiles/base/variant.h @@ -25,11 +25,23 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org // We use base::variant<> alias and base::get_if() helper while we don't have std::variant<>. namespace base { -template -using variant = mapbox::util::variant; +struct null_variant_type { +}; + +inline constexpr null_variant_type null_variant() { + return null_variant_type {}; +} + +inline bool operator<(null_variant_type a, null_variant_type b) { + return false; +} + +inline bool operator==(null_variant_type a, null_variant_type b) { + return true; +} template -using optional_variant = variant; +using variant = mapbox::util::variant; template inline T *get_if(variant *v) { @@ -41,9 +53,36 @@ inline const T *get_if(const variant *v) { return (v && v->template is()) ? &v->template get_unchecked() : nullptr; } +template +using optional_variant = variant; + template inline bool is_null_variant(const optional_variant &variant) { - return get_if(&variant) != nullptr; + return get_if(&variant) != nullptr; +} + +template +using optional = optional_variant; + +using null_optional_type = null_variant_type; + +template +inline Type *get_if(optional *v) { + return (v && v->template is()) ? &v->template get_unchecked() : nullptr; +} + +template +inline const Type *get_if(const optional *v) { + return (v && v->template is()) ? &v->template get_unchecked() : nullptr; +} + +template +inline bool is_null_optional(const optional &optional) { + return is_null_variant(optional); +} + +inline constexpr null_optional_type null_optional() { + return null_optional_type {}; } } // namespace base diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index ad81a0c15..65ae4869f 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -979,7 +979,7 @@ void StickersListWidget::mouseReleaseEvent(QMouseEvent *e) { _previewTimer.stop(); auto pressed = _pressed; - setPressed(nullptr); + setPressed(base::null_variant()); if (pressed != _selected) { update(); } @@ -1104,8 +1104,8 @@ void StickersListWidget::enterFromChildEvent(QEvent *e, QWidget *child) { } void StickersListWidget::clearSelection() { - setPressed(nullptr); - setSelected(nullptr); + setPressed(base::null_variant()); + setSelected(base::null_variant()); update(); } @@ -1398,7 +1398,7 @@ void StickersListWidget::updateSelected() { return; } - auto newSelected = OverState { nullptr }; + auto newSelected = OverState { base::null_variant() }; auto p = mapFromGlobal(_lastMousePosition); if (!rect().contains(p) || p.y() < getVisibleTop() || p.y() >= getVisibleBottom() diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h index b25a1829c..62c502994 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.h @@ -226,8 +226,8 @@ private: Footer *_footer = nullptr; - OverState _selected = nullptr; - OverState _pressed = nullptr; + OverState _selected = base::null_variant(); + OverState _pressed = base::null_variant(); QPoint _lastMousePosition; Text _megagroupSetAbout; diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index a64ab6513..f8df92a08 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -1458,6 +1458,7 @@ void DialogsInner::dialogsReceived(const QVector &added) { } auto history = App::historyFromDialog(peerId, d.vunread_count.v, d.vread_inbox_max_id.v, d.vread_outbox_max_id.v); + history->setUnreadMentionsCount(d.vunread_mentions_count.v); auto peer = history->peer; if (auto channel = peer->asChannel()) { if (d.has_pts()) { diff --git a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp index 998d10bad..464af8158 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_layout.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_layout.cpp @@ -293,8 +293,9 @@ void RowPainter::paint(Painter &p, const Row *row, int fullWidth, bool active, b cloudDraft = nullptr; // Draw item, if draft is older. } paintRow(p, row, history, item, cloudDraft, displayDate(), fullWidth, active, selected, onlyBackground, ms, [&p, fullWidth, active, selected, ms, history, unreadCount](int nameleft, int namewidth, HistoryItem *item) { - int availableWidth = namewidth; - int texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip; + auto availableWidth = namewidth; + auto texttop = st::dialogsPadding.y() + st::msgNameFont->height + st::dialogsSkip; + auto hadOneBadge = false; if (unreadCount) { auto counter = QString::number(unreadCount); auto mutedCounter = history->mute(); @@ -307,10 +308,31 @@ void RowPainter::paint(Painter &p, const Row *row, int fullWidth, bool active, b st.muted = history->mute(); paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth); availableWidth -= unreadWidth + st.padding; + + hadOneBadge = true; } else if (history->isPinnedDialog()) { auto &icon = (active ? st::dialogsPinnedIconActive : (selected ? st::dialogsPinnedIconOver : st::dialogsPinnedIcon)); icon.paint(p, fullWidth - st::dialogsPadding.x() - icon.width(), texttop, fullWidth); availableWidth -= icon.width() + st::dialogsUnreadPadding; + + hadOneBadge = true; + } + if (history->hasUnreadMentions()) { + auto counter = qsl("@"); + auto unreadRight = fullWidth - st::dialogsPadding.x() - (namewidth - availableWidth); + if (hadOneBadge) { + unreadRight -= st::dialogsUnreadPadding; + } + auto unreadTop = texttop + st::dialogsTextFont->ascent - st::dialogsUnreadFont->ascent - (st::dialogsUnreadHeight - st::dialogsUnreadFont->height) / 2; + auto unreadWidth = 0; + + UnreadBadgeStyle st; + st.active = active; + st.muted = history->mute(); + st.padding = 0; + st.textTop = 0; + paintUnreadCount(p, counter, unreadRight, unreadTop, st, &unreadWidth); + availableWidth -= unreadWidth + st.padding + (hadOneBadge ? st::dialogsUnreadPadding : 0); } auto &color = active ? st::dialogsTextFgServiceActive : (selected ? st::dialogsTextFgServiceOver : st::dialogsTextFgService); if (!history->paintSendAction(p, nameleft, texttop, availableWidth, fullWidth, color, ms)) { diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index c33ab8475..8b165454e 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -80,7 +80,7 @@ History::History(const PeerId &peerId) if (peer->isUser() && peer->asUser()->botInfo) { outboxReadBefore = INT_MAX; } - for (auto &countData : overviewCountData) { + for (auto &countData : _overviewCountData) { countData = -1; // not loaded yet } } @@ -1144,23 +1144,10 @@ HistoryItem *History::addNewGame(MsgId id, MTPDmessage::Flags flags, UserId viaB } bool History::addToOverview(MediaOverviewType type, MsgId msgId, AddToOverviewMethod method) { - bool adding = false; - switch (method) { - case AddToOverviewNew: - case AddToOverviewFront: adding = (overviewIds[type].constFind(msgId) == overviewIds[type].cend()); break; - case AddToOverviewBack: adding = (overviewCountData[type] != 0); break; - } - if (!adding) return false; - - overviewIds[type].insert(msgId); - switch (method) { - case AddToOverviewNew: - case AddToOverviewBack: overview[type].push_back(msgId); break; - case AddToOverviewFront: overview[type].push_front(msgId); break; - } + _overview[type].insert(msgId); if (method == AddToOverviewNew) { - if (overviewCountData[type] > 0) { - ++overviewCountData[type]; + if (_overviewCountData[type] > 0) { + ++_overviewCountData[type]; } Notify::mediaOverviewUpdated(peer, type); } @@ -1168,24 +1155,97 @@ bool History::addToOverview(MediaOverviewType type, MsgId msgId, AddToOverviewMe } void History::eraseFromOverview(MediaOverviewType type, MsgId msgId) { - if (overviewIds[type].isEmpty()) return; + auto i = _overview[type].find(msgId); + if (i == _overview[type].cend()) return; - auto i = overviewIds[type].find(msgId); - if (i == overviewIds[type].cend()) return; - - overviewIds[type].erase(i); - for (auto i = overview[type].begin(), e = overview[type].end(); i != e; ++i) { - if ((*i) == msgId) { - overview[type].erase(i); - if (overviewCountData[type] > 0) { - --overviewCountData[type]; - } - break; - } + _overview[type].erase(i); + if (_overviewCountData[type] > 0) { + --_overviewCountData[type]; } Notify::mediaOverviewUpdated(peer, type); } +void History::setUnreadMentionsCount(int count) { + if (_unreadMentions.size() > count) { + LOG(("API Warning: real mentions count is greater than received mentions count")); + count = _unreadMentions.size(); + } + _unreadMentionsCount = count; +} + +bool History::addToUnreadMentions(MsgId msgId, AddToOverviewMethod method) { + auto count = base::get_if(&_unreadMentionsCount); + auto allLoaded = count ? (_unreadMentions.size() >= *count) : false; + if (allLoaded) { + if (method == AddToOverviewNew) { + ++*count; + _unreadMentions.insert(msgId); + return true; + } + } else if (!_unreadMentions.empty() && method != AddToOverviewNew) { + _unreadMentions.insert(msgId); + return true; + } + return false; +} + +void History::eraseFromUnreadMentions(MsgId msgId) { + _unreadMentions.remove(msgId); + if (auto count = base::get_if(&_unreadMentionsCount)) { + if (*count > 0) { + --*count; + } + } + Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::UnreadMentionsChanged); +} + +void History::addUnreadMentionsSlice(const MTPmessages_Messages &result) { + auto count = 0; + auto messages = (const QVector*)nullptr; + auto getMessages = [](auto &list) { + App::feedUsers(list.vusers); + App::feedChats(list.vchats); + return &list.vmessages.v; + }; + switch (result.type()) { + case mtpc_messages_messages: { + auto &d = result.c_messages_messages(); + messages = getMessages(d); + count = messages->size(); + } break; + + case mtpc_messages_messagesSlice: { + auto &d = result.c_messages_messagesSlice(); + messages = getMessages(d); + count = d.vcount.v; + } break; + + case mtpc_messages_channelMessages: { + LOG(("API Error: unexpected messages.channelMessages in History::addUnreadMentionsSlice")); + auto &d = result.c_messages_channelMessages(); + messages = getMessages(d); + count = d.vcount.v; + } break; + + default: Unexpected("type in History::addUnreadMentionsSlice"); + } + + auto added = false; + for (auto &message : *messages) { + if (auto item = addToHistory(message)) { + if (item->mentionsMe() && item->isMediaUnread()) { + _unreadMentions.insert(item->id); + added = true; + } + } + } + if (!added) { + count = _unreadMentions.size(); + } + setUnreadMentionsCount(count); + Notify::peerUpdatedDelayed(peer, Notify::PeerUpdate::Flag::UnreadMentionsChanged); +} + HistoryItem *History::addNewItem(HistoryItem *adding, bool newMsg) { Expects(!isBuildingFrontBlock()); addItemToBlock(adding); @@ -1527,16 +1587,8 @@ void History::checkAddAllToOverview() { } int32 mask = 0; - for (int32 i = 0; i < OverviewCount; ++i) { - if (overviewCountData[i] == 0) continue; // all loaded - if (!overview[i].isEmpty() || !overviewIds[i].isEmpty()) { - overview[i].clear(); - overviewIds[i].clear(); - mask |= (1 << i); - } - } - for_const (HistoryBlock *block, blocks) { - for_const (HistoryItem *item, block->items) { + for_const (auto block, blocks) { + for_const (auto item, block->items) { mask |= item->addToOverview(AddToOverviewBack); } } @@ -2077,18 +2129,15 @@ void History::clear(bool leaveItems) { } } } - for (int32 i = 0; i < OverviewCount; ++i) { - if (!overview[i].isEmpty() || !overviewIds[i].isEmpty()) { - if (leaveItems) { - if (overviewCountData[i] == 0) { - overviewCountData[i] = overview[i].size(); + if (!leaveItems) { + for (auto i = 0; i != OverviewCount; ++i) { + if (!_overview[i].isEmpty()) { + _overviewCountData[i] = -1; // not loaded yet + _overview[i].clear(); + if (!App::quitting()) { + Notify::mediaOverviewUpdated(peer, MediaOverviewType(i)); } - } else { - overviewCountData[i] = -1; // not loaded yet } - overview[i].clear(); - overviewIds[i].clear(); - if (!App::quitting()) Notify::mediaOverviewUpdated(peer, MediaOverviewType(i)); } } clearBlocks(leaveItems); @@ -2222,14 +2271,14 @@ void History::overviewSliceDone(int32 overviewIndex, const MTPmessages_Messages App::feedUsers(d.vusers); App::feedChats(d.vchats); v = &d.vmessages.v; - overviewCountData[overviewIndex] = 0; + _overviewCountData[overviewIndex] = 0; } break; case mtpc_messages_messagesSlice: { auto &d(result.c_messages_messagesSlice()); App::feedUsers(d.vusers); App::feedChats(d.vchats); - overviewCountData[overviewIndex] = d.vcount.v; + _overviewCountData[overviewIndex] = d.vcount.v; v = &d.vmessages.v; } break; @@ -2242,7 +2291,7 @@ void History::overviewSliceDone(int32 overviewIndex, const MTPmessages_Messages } App::feedUsers(d.vusers); App::feedChats(d.vchats); - overviewCountData[overviewIndex] = d.vcount.v; + _overviewCountData[overviewIndex] = d.vcount.v; v = &d.vmessages.v; } break; @@ -2250,42 +2299,22 @@ void History::overviewSliceDone(int32 overviewIndex, const MTPmessages_Messages } if (!onlyCounts && v->isEmpty()) { - overviewCountData[overviewIndex] = 0; - } else if (overviewCountData[overviewIndex] > 0) { - for_const (auto msgId, overviewIds[overviewIndex]) { - if (msgId < 0) { - ++overviewCountData[overviewIndex]; - } else { - break; - } - } + _overviewCountData[overviewIndex] = 0; } - for (QVector::const_iterator i = v->cbegin(), e = v->cend(); i != e; ++i) { - HistoryItem *item = App::histories().addNewMessage(*i, NewMessageExisting); - if (item && overviewIds[overviewIndex].constFind(item->id) == overviewIds[overviewIndex].cend()) { - overviewIds[overviewIndex].insert(item->id); - overview[overviewIndex].push_front(item->id); + for (auto i = v->cbegin(), e = v->cend(); i != e; ++i) { + if (auto item = App::histories().addNewMessage(*i, NewMessageExisting)) { + _overview[overviewIndex].insert(item->id); } } } void History::changeMsgId(MsgId oldId, MsgId newId) { - for (auto i = 0; i < OverviewCount; ++i) { - auto j = overviewIds[i].find(oldId); - if (j != overviewIds[i].cend()) { - overviewIds[i].erase(j); - auto index = overview[i].indexOf(oldId); - if (overviewIds[i].constFind(newId) == overviewIds[i].cend()) { - overviewIds[i].insert(newId); - if (index >= 0) { - overview[i][index] = newId; - } else { - overview[i].push_back(newId); - } - } else if (index >= 0) { - overview[i].removeAt(index); - } + for (auto i = 0; i != OverviewCount; ++i) { + auto j = _overview[i].find(oldId); + if (j != _overview[i].cend()) { + _overview[i].erase(j); + _overview[i].insert(newId); } } } diff --git a/Telegram/SourceFiles/history/history.h b/Telegram/SourceFiles/history/history.h index e1004d8ea..1089f811e 100644 --- a/Telegram/SourceFiles/history/history.h +++ b/Telegram/SourceFiles/history/history.h @@ -25,6 +25,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/effects/send_action_animations.h" #include "base/observer.h" #include "base/timer.h" +#include "base/variant.h" +#include "base/flat_set.h" void HistoryInit(); @@ -349,7 +351,27 @@ public: // if this returns false there is no need to even try to handle them bool canHaveFromPhotos() const; - typedef QList Blocks; + int getUnreadMentionsLoadedCount() const { + return _unreadMentions.size(); + } + MsgId getMinLoadedUnreadMention() const { + return _unreadMentions.empty() ? 0 : _unreadMentions.front(); + } + MsgId getMaxLoadedUnreadMention() const { + return _unreadMentions.empty() ? 0 : _unreadMentions.back(); + } + int getUnreadMentionsCount(int notLoadedValue = -1) const { + return base::is_null_optional(_unreadMentionsCount) ? notLoadedValue : *base::get_if(&_unreadMentionsCount); + } + bool hasUnreadMentions() const { + return (getUnreadMentionsCount() > 0); + } + void setUnreadMentionsCount(int count); + bool addToUnreadMentions(MsgId msgId, AddToOverviewMethod method); + void eraseFromUnreadMentions(MsgId msgId); + void addUnreadMentionsSlice(const MTPmessages_Messages &result); + + using Blocks = QList; Blocks blocks; int width = 0; @@ -441,37 +463,33 @@ public: mutable const HistoryItem *textCachedFor = nullptr; // cache mutable Text lastItemTextCache; - typedef QList MediaOverview; - MediaOverview overview[OverviewCount]; - bool overviewCountLoaded(int32 overviewIndex) const { - return overviewCountData[overviewIndex] >= 0; + return _overviewCountData[overviewIndex] >= 0; } bool overviewLoaded(int32 overviewIndex) const { - return overviewCount(overviewIndex) == overview[overviewIndex].size(); + return overviewCount(overviewIndex) == _overview[overviewIndex].size(); } - int32 overviewCount(int32 overviewIndex, int32 defaultValue = -1) const { - int32 result = overviewCountData[overviewIndex], loaded = overview[overviewIndex].size(); + int overviewCount(int32 overviewIndex, int32 defaultValue = -1) const { + auto result = _overviewCountData[overviewIndex]; + auto loaded = _overview[overviewIndex].size(); if (result < 0) return defaultValue; if (result < loaded) { if (result > 0) { - const_cast(this)->overviewCountData[overviewIndex] = 0; + const_cast(this)->_overviewCountData[overviewIndex] = 0; } return loaded; } return result; } + const OrderedSet &overview(int32 overviewIndex) const { + return _overview[overviewIndex]; + } MsgId overviewMinId(int32 overviewIndex) const { - for_const (auto msgId, overviewIds[overviewIndex]) { - if (msgId > 0) { - return msgId; - } - } - return 0; + return _overview[overviewIndex].empty() ? 0 : *_overview[overviewIndex].begin(); } void overviewSliceDone(int32 overviewIndex, const MTPmessages_Messages &result, bool onlyCounts = false); bool overviewHasMsgId(int32 overviewIndex, MsgId msgId) const { - return overviewIds[overviewIndex].constFind(msgId) != overviewIds[overviewIndex].cend(); + return _overview[overviewIndex].contains(msgId); } void changeMsgId(MsgId oldId, MsgId newId); @@ -537,9 +555,12 @@ private: Q_DECL_CONSTEXPR friend inline QFlags operator~(Flags::enum_type f) noexcept { return ~QFlags(f); } - Flags _flags; - bool _mute; - int32 _unreadCount = 0; + Flags _flags = { 0 }; + bool _mute = false; + int _unreadCount = 0; + + base::optional _unreadMentionsCount = base::null_optional(); + base::flat_set _unreadMentions; Dialogs::RowsByLetter _chatListLinks[2]; Dialogs::RowsByLetter &chatListLinks(Dialogs::Mode list) { @@ -555,9 +576,8 @@ private: } uint64 _sortKeyInChatList = 0; // like ((unixtime) << 32) | (incremented counter) - using MediaOverviewIds = OrderedSet; - MediaOverviewIds overviewIds[OverviewCount]; - int32 overviewCountData[OverviewCount]; // -1 - not loaded, 0 - all loaded, > 0 - count, but not all loaded + OrderedSet _overview[OverviewCount]; + int32 _overviewCountData[OverviewCount]; // -1 - not loaded, 0 - all loaded, > 0 - count, but not all loaded // A pointer to the block that is currently being built. // We hold this pointer so we can destroy it while building diff --git a/Telegram/SourceFiles/history/history.style b/Telegram/SourceFiles/history/history.style index d4bb49d9f..db49b1f48 100644 --- a/Telegram/SourceFiles/history/history.style +++ b/Telegram/SourceFiles/history/history.style @@ -69,6 +69,12 @@ historyToDownBadgeSize: 22px; historyToDownShownAfter: 480px; historyToDownDuration: 150; +historyUnreadMentions: TwoIconButton(historyToDown) { + iconAbove: icon {{ "history_unread_mention", historyToDownFg, point(16px, 16px) }}; + iconAboveOver: icon {{ "history_unread_mention", historyToDownFgOver, point(16px, 16px) }}; +} +historyUnreadMentionsSkip: 4px; + membersInnerWidth: 310px; membersInnerHeightMax: 360px; membersInnerDropdown: InnerDropdown(defaultInnerDropdown) { diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index 7a439444e..f38402d76 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -384,6 +384,8 @@ void HistoryInner::paintEvent(QPaintEvent *e) { HistoryLayout::paintEmpty(p, width(), height()); } if (!noHistoryDisplayed) { + auto readMentions = HistoryItemsMap(); + adjustCurrent(clip.top()); auto selEnd = _selected.cend(); @@ -428,6 +430,9 @@ void HistoryInner::paintEvent(QPaintEvent *e) { if (item->hasViews()) { App::main()->scheduleViewIncrement(item); } + if (item->mentionsMe() && item->isMediaUnread()) { + readMentions.insert(item); + } int32 h = item->height(); p.translate(0, h); @@ -475,6 +480,9 @@ void HistoryInner::paintEvent(QPaintEvent *e) { if (item->hasViews()) { App::main()->scheduleViewIncrement(item); } + if (item->mentionsMe() && item->isMediaUnread()) { + readMentions.insert(item); + } } p.translate(0, h); y += h; @@ -493,6 +501,10 @@ void HistoryInner::paintEvent(QPaintEvent *e) { p.restore(); } + if (!readMentions.empty() && App::wnd()->doWeReadMentions()) { + App::main()->mediaMarkRead(readMentions); + } + if (mtop >= 0 || htop >= 0) { enumerateUserpics([&p, &clip](gsl::not_null message, int userpicTop) { // stop the enumeration if the userpic is below the painted rect diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index d660d21c1..bd887ccc4 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -575,7 +575,7 @@ public: return _flags & MTPDmessage::Flag::f_mentioned; } bool isMediaUnread() const { - return (_flags & MTPDmessage::Flag::f_media_unread) && (channelId() == NoChannel); + return _flags & MTPDmessage::Flag::f_media_unread; } void markMediaRead() { _flags &= ~MTPDmessage::Flag::f_media_unread; @@ -625,7 +625,7 @@ public: return _flags & MTPDmessage::Flag::f_post; } bool indexInOverview() const { - return (id > 0) && (!history()->isChannel() || history()->isMegagroup() || isPost()); + return (id > 0); } bool isSilent() const { return _flags & MTPDmessage::Flag::f_silent; diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 53038c98d..73732c17d 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -37,6 +37,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "styles/style_widgets.h" #include "styles/style_history.h" #include "window/notifications_manager.h" +#include "observer_peer.h" namespace { @@ -1232,6 +1233,13 @@ void HistoryMessage::applyEditionToEmpty() { finishEditionToEmpty(); } +void HistoryMessage::markMediaAsReadHook() { + if (mentionsMe()) { + history()->updateChatListEntry(); + history()->eraseFromUnreadMentions(id); + } +} + bool HistoryMessage::displayForwardedFrom() const { if (auto forwarded = Get()) { return Has() @@ -1276,6 +1284,11 @@ int32 HistoryMessage::addToOverview(AddToOverviewMethod method) { result |= (1 << OverviewLinks); } } + if (mentionsMe() && isMediaUnread()) { + if (history()->addToUnreadMentions(id, method)) { + Notify::peerUpdatedDelayed(history()->peer, Notify::PeerUpdate::Flag::UnreadMentionsChanged); + } + } return result; } @@ -1286,6 +1299,9 @@ void HistoryMessage::eraseFromOverview() { if (hasTextLinks()) { history()->eraseFromOverview(OverviewLinks, id); } + if (mentionsMe() && isMediaUnread()) { + history()->eraseFromUnreadMentions(id); + } } TextWithEntities HistoryMessage::selectedText(TextSelection selection) const { diff --git a/Telegram/SourceFiles/history/history_message.h b/Telegram/SourceFiles/history/history_message.h index c4c1c9128..9bbd2adac 100644 --- a/Telegram/SourceFiles/history/history_message.h +++ b/Telegram/SourceFiles/history/history_message.h @@ -166,6 +166,8 @@ private: int performResizeGetHeight(); void applyEditionToEmpty(); + void markMediaAsReadHook() override; + bool displayForwardedFrom() const; void paintFromName(Painter &p, QRect &trect, bool selected) const; void paintForwardedInfo(Painter &p, QRect &trect, bool selected) const; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 9fe1e553b..5f2120561 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -612,6 +612,7 @@ HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null , _topBar(this, controller) , _scroll(this, st::historyScroll, false) , _historyDown(_scroll, st::historyToDown) +, _unreadMentions(_scroll, st::historyUnreadMentions) , _fieldAutocomplete(this) , _send(this) , _unblock(this, lang(lng_unblock_button).toUpper(), st::historyUnblock) @@ -639,7 +640,8 @@ HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null subscribe(Auth().downloaderTaskFinished(), [this] { update(); }); connect(_topBar, &Window::TopBarWidget::clicked, this, [this] { topBarClick(); }); connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); - connect(_historyDown, SIGNAL(clicked()), this, SLOT(onHistoryToEnd())); + _historyDown->setClickedCallback([this] { historyDownClicked(); }); + _unreadMentions->setClickedCallback([this] { showNextUnreadMention(); }); connect(_fieldBarCancel, SIGNAL(clicked()), this, SLOT(onFieldBarCancel())); _send->setClickedCallback([this] { sendButtonClicked(); }); connect(_unblock, SIGNAL(clicked()), this, SLOT(onUnblock())); @@ -703,6 +705,7 @@ HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null updateScrollColors(); _historyDown->installEventFilter(this); + _unreadMentions->installEventFilter(this); _fieldAutocomplete->hide(); connect(_fieldAutocomplete, SIGNAL(mentionChosen(UserData*,FieldAutocomplete::ChooseMethod)), this, SLOT(onMentionInsert(UserData*))); @@ -769,9 +772,10 @@ HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null scrollToCurrentVoiceMessage(pair.from.contextId(), pair.to); } }); - subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::ChannelRightsChanged, [this](const Notify::PeerUpdate &update) { + subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::ChannelRightsChanged | Notify::PeerUpdate::Flag::UnreadMentionsChanged, [this](const Notify::PeerUpdate &update) { if (update.peer == _peer) { - onPreviewCheck(); + if (update.flags & Notify::PeerUpdate::Flag::ChannelRightsChanged) onPreviewCheck(); + if (update.flags & Notify::PeerUpdate::Flag::UnreadMentionsChanged) updateUnreadMentionsVisibility(); } })); subscribe(controller->window()->widgetGrabbed(), [this] { @@ -1966,6 +1970,7 @@ void HistoryWidget::updateControlsVisibility() { _topBar->setVisible(_peer != nullptr); } updateHistoryDownVisibility(); + updateUnreadMentionsVisibility(); if (!_history || _a_show.animating()) { if (_tabbedSection && !_tabbedSection->isHidden()) { _tabbedSection->beforeHiding(); @@ -2166,6 +2171,9 @@ void HistoryWidget::newUnreadMsg(History *history, HistoryItem *item) { destroyUnreadBar(); } if (App::wnd()->doWeReadServerHistory()) { + if (item->mentionsMe() && item->isMediaUnread()) { + App::main()->mediaMarkRead(item); + } historyWasRead(ReadServerHistoryChecks::ForceRequest); return; } @@ -2194,9 +2202,7 @@ void HistoryWidget::historyWasRead(ReadServerHistoryChecks checks) { void HistoryWidget::unreadCountChanged(History *history) { if (history == _history || history == _migrated) { updateHistoryDownVisibility(); - if (_historyDown) { - _historyDown->setUnreadCount(_history->unreadCount() + (_migrated ? _migrated->unreadCount() : 0)); - } + _historyDown->setUnreadCount(_history->unreadCount() + (_migrated ? _migrated->unreadCount() : 0)); } } @@ -2373,6 +2379,12 @@ bool HistoryWidget::doWeReadServerHistory() const { return false; } +bool HistoryWidget::doWeReadMentions() const { + if (!_history || !_list) return true; + if (_firstLoadRequest || _a_show.animating()) return false; + return true; +} + bool HistoryWidget::historyHasNotFreezedUnreadBar(History *history) const { if (history && history->showFrom && !history->showFrom->detached() && history->unreadBar) { if (auto unreadBar = history->unreadBar->Get()) { @@ -2614,7 +2626,7 @@ void HistoryWidget::onWindowVisibleChanged() { QTimer::singleShot(0, this, SLOT(preloadHistoryIfNeeded())); } -void HistoryWidget::onHistoryToEnd() { +void HistoryWidget::historyDownClicked() { if (_replyReturn && _replyReturn->history() == _history) { showHistory(_peer->id, _replyReturn->id); } else if (_replyReturn && _replyReturn->history() == _migrated) { @@ -2624,6 +2636,10 @@ void HistoryWidget::onHistoryToEnd() { } } +void HistoryWidget::showNextUnreadMention() { + showHistory(_peer->id, _history->getMinLoadedUnreadMention()); +} + void HistoryWidget::saveEditMsg() { if (_saveEditMsgRequestId) return; @@ -2921,6 +2937,7 @@ void HistoryWidget::showAnimated(Window::SlideDirection direction, const Window: show(); _topBar->updateControlsVisibility(); historyDownAnimationFinish(); + unreadMentionsAnimationFinish(); _topShadow->setVisible(params.withTopBarShadow ? false : true); _cacheOver = App::main()->grabForShowAnimation(params); @@ -2952,6 +2969,7 @@ void HistoryWidget::animationCallback() { update(); if (!_a_show.animating()) { historyDownAnimationFinish(); + unreadMentionsAnimationFinish(); _cacheUnder = _cacheOver = QPixmap(); doneShow(); } @@ -2981,6 +2999,7 @@ void HistoryWidget::finishAnimation() { _topShadow->setVisible(_peer != nullptr); _topBar->setVisible(_peer != nullptr); historyDownAnimationFinish(); + unreadMentionsAnimationFinish(); } void HistoryWidget::historyDownAnimationFinish() { @@ -2988,6 +3007,11 @@ void HistoryWidget::historyDownAnimationFinish() { updateHistoryDownPosition(); } +void HistoryWidget::unreadMentionsAnimationFinish() { + _unreadMentionsShown.finish(); + updateUnreadMentionsPosition(); +} + void HistoryWidget::step_recording(float64 ms, bool timer) { float64 dt = ms / AudioVoiceMsgUpdateView; if (dt >= 1) { @@ -3341,7 +3365,7 @@ bool HistoryWidget::insertBotCommand(const QString &cmd) { } bool HistoryWidget::eventFilter(QObject *obj, QEvent *e) { - if (obj == _historyDown && e->type() == QEvent::Wheel) { + if ((obj == _historyDown || obj == _unreadMentions) && e->type() == QEvent::Wheel) { return _scroll->viewportEvent(e); } return TWidget::eventFilter(obj, e); @@ -4816,6 +4840,11 @@ void HistoryWidget::updateHistoryGeometry(bool initial, bool loadedDown, const S if (!_historyDownShown.animating()) { // _historyDown is a child widget of _scroll, not me. _historyDown->moveToRight(st::historyToDownPosition.x(), _scroll->height() - _historyDown->height() - st::historyToDownPosition.y()); + if (!_unreadMentionsShown.animating()) { + // _unreadMentions is a child widget of _scroll, not me. + auto additionalSkip = _historyDownIsShown ? (_historyDown->height() + st::historyUnreadMentionsSkip) : 0; + _unreadMentions->moveToRight(st::historyToDownPosition.x(), _scroll->height() - additionalSkip - st::historyToDownPosition.y()); + } } controller()->floatPlayerAreaUpdated().notify(true); @@ -5011,6 +5040,7 @@ void HistoryWidget::updateHistoryDownPosition() { if (shouldBeHidden != _historyDown->isHidden()) { _historyDown->setVisible(!shouldBeHidden); } + updateUnreadMentionsPosition(); } void HistoryWidget::updateHistoryDownVisibility() { @@ -5025,7 +5055,7 @@ void HistoryWidget::updateHistoryDownVisibility() { } return (_list->itemTop(history->showFrom) >= _scroll->scrollTop() + _scroll->height()); }; - auto historyDownIsVisible = [this, &haveUnreadBelowBottom]() { + auto historyDownIsVisible = [this, &haveUnreadBelowBottom] { if (!_history || _firstLoadRequest) { return false; } @@ -5047,6 +5077,41 @@ void HistoryWidget::updateHistoryDownVisibility() { } } +void HistoryWidget::updateUnreadMentionsPosition() { + // _unreadMentions is a child widget of _scroll, not me. + auto right = anim::interpolate(-_unreadMentions->width(), st::historyToDownPosition.x(), _unreadMentionsShown.current(_unreadMentionsIsShown ? 1. : 0.)); + auto shift = anim::interpolate(0, _historyDown->height() + st::historyUnreadMentionsSkip, _historyDownShown.current(_historyDownIsShown ? 1. : 0.)); + auto top = _scroll->height() - _unreadMentions->height() - st::historyToDownPosition.y() - shift; + _unreadMentions->moveToRight(right, top); + auto shouldBeHidden = !_unreadMentionsIsShown && !_unreadMentionsShown.animating(); + if (shouldBeHidden != _unreadMentions->isHidden()) { + _unreadMentions->setVisible(!shouldBeHidden); + } +} + +void HistoryWidget::updateUnreadMentionsVisibility() { + if (_a_show.animating()) return; + + auto showUnreadMentions = _peer && (_peer->isChat() || _peer->isMegagroup()); + if (showUnreadMentions) { + Auth().api().preloadEnoughUnreadMentions(_history); + } + auto unreadMentionsIsVisible = [this, showUnreadMentions] { + if (!showUnreadMentions || _firstLoadRequest) { + return false; + } + return (_history->getUnreadMentionsLoadedCount() > 0); + }; + auto unreadMentionsIsShown = unreadMentionsIsVisible(); + if (unreadMentionsIsShown) { + _unreadMentions->setUnreadCount(_history->getUnreadMentionsCount()); + } + if (_unreadMentionsIsShown != unreadMentionsIsShown) { + _unreadMentionsIsShown = unreadMentionsIsShown; + _unreadMentionsShown.start([this] { updateUnreadMentionsPosition(); }, _unreadMentionsIsShown ? 0. : 1., _unreadMentionsIsShown ? 1. : 0., st::historyToDownDuration); + } +} + void HistoryWidget::mousePressEvent(QMouseEvent *e) { _replyForwardPressed = QRect(0, _field->y() - st::historySendPadding - st::historyReplyHeight, st::historyReplySkip, st::historyReplyHeight).contains(e->pos()); if (_replyForwardPressed && !_fieldBarCancel->isHidden()) { @@ -6448,6 +6513,7 @@ void HistoryWidget::paintEvent(QPaintEvent *e) { auto ms = getms(); _historyDownShown.step(ms); + _unreadMentionsShown.step(ms); auto progress = _a_show.current(ms, 1.); if (_a_show.animating()) { auto animationWidth = (!_tabbedSection || _tabbedSection->isHidden()) ? width() : _chatWidth; diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 6943ab7d8..2097051ca 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -179,6 +179,7 @@ public: void windowShown(); bool doWeReadServerHistory() const; + bool doWeReadMentions() const; void leaveToChildEvent(QEvent *e, QWidget *child) override; void dragEnterEvent(QDragEnterEvent *e) override; @@ -316,6 +317,8 @@ public: void updateHistoryDownPosition(); void updateHistoryDownVisibility(); + void updateUnreadMentionsPosition(); + void updateUnreadMentionsVisibility(); void updateFieldSubmitSettings(); @@ -412,7 +415,6 @@ public slots: void onReportSpamClear(); void onScroll(); - void onHistoryToEnd(); void onSend(bool ctrlShiftEnter = false, MsgId replyTo = -1); void onUnblock(); @@ -488,6 +490,8 @@ private: void updateTabbedSelectorSectionShown(); void recountChatWidth(); void setReportSpamStatus(DBIPeerReportSpamStatus status); + void historyDownClicked(); + void showNextUnreadMention(); void animationCallback(); void updateOverStates(QPoint pos); @@ -496,6 +500,7 @@ private: void recordUpdateCallback(QPoint globalPos); void chooseAttach(); void historyDownAnimationFinish(); + void unreadMentionsAnimationFinish(); void sendButtonClicked(); SendingFilesLists getSendingFilesLists(const QList &files); SendingFilesLists getSendingFilesLists(const QStringList &files); @@ -749,6 +754,10 @@ private: bool _historyDownIsShown = false; object_ptr _historyDown; + Animation _unreadMentionsShown; + bool _unreadMentionsIsShown = false; + object_ptr _unreadMentions; + object_ptr _fieldAutocomplete; UserData *_inlineBot = nullptr; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index bcf7bba53..66b96fb4a 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -63,6 +63,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "media/player/media_player_float.h" #include "base/qthelp_regex.h" #include "base/qthelp_url.h" +#include "base/flat_set.h" #include "window/themes/window_theme.h" #include "window/player_wrap_widget.h" #include "styles/style_boxes.h" @@ -1636,7 +1637,7 @@ void MainWidget::loadMediaBack(PeerData *peer, MediaOverviewType type, bool many } auto minId = history->overviewMinId(type); - auto limit = (many || history->overview[type].size() > MediaOverviewStartPerPage) ? SearchPerPage : MediaOverviewStartPerPage; + auto limit = (many || history->overview(type).size() > MediaOverviewStartPerPage) ? SearchPerPage : MediaOverviewStartPerPage; auto filter = TypeToMediaFilter(type); if (filter.type() == mtpc_inputMessagesFilterEmpty) { return; @@ -1974,9 +1975,9 @@ void MainWidget::inlineResultLoadFailed(FileLoader *loader, bool started) { //Ui::repaintInlineItem(); } -void MainWidget::mediaMarkRead(DocumentData *data) { - const DocumentItems &items(App::documentItems()); - DocumentItems::const_iterator i = items.constFind(data); +void MainWidget::mediaMarkRead(gsl::not_null data) { + auto &items = App::documentItems(); + auto i = items.constFind(data); if (i != items.cend()) { mediaMarkRead(i.value()); } @@ -1984,18 +1985,39 @@ void MainWidget::mediaMarkRead(DocumentData *data) { void MainWidget::mediaMarkRead(const HistoryItemsMap &items) { QVector markedIds; + QMap> channelMarkedIds; markedIds.reserve(items.size()); for_const (auto item, items) { - if (!item->out() && item->isMediaUnread()) { + if ((!item->out() || item->mentionsMe()) && item->isMediaUnread()) { item->markMediaRead(); if (item->id > 0) { - markedIds.push_back(MTP_int(item->id)); + if (auto channel = item->history()->peer->asChannel()) { + channelMarkedIds[channel].push_back(MTP_int(item->id)); + } else { + markedIds.push_back(MTP_int(item->id)); + } } } } if (!markedIds.isEmpty()) { MTP::send(MTPmessages_ReadMessageContents(MTP_vector(markedIds)), rpcDone(&MainWidget::messagesAffected, (PeerData*)0)); } + for (auto i = channelMarkedIds.cbegin(), e = channelMarkedIds.cend(); i != e; ++i) { + MTP::send(MTPchannels_ReadMessageContents(i.key()->inputChannel, MTP_vector(i.value()))); + } +} + +void MainWidget::mediaMarkRead(gsl::not_null item) { + if ((!item->out() || item->mentionsMe()) && item->isMediaUnread()) { + item->markMediaRead(); + if (item->id > 0) { + if (auto channel = item->history()->peer->asChannel()) { + MTP::send(MTPchannels_ReadMessageContents(channel->inputChannel, MTP_vector(1, MTP_int(item->id)))); + } else { + MTP::send(MTPmessages_ReadMessageContents(MTP_vector(1, MTP_int(item->id))), rpcDone(&MainWidget::messagesAffected, (PeerData*)0)); + } + } + } } void MainWidget::updateOnlineDisplay() { @@ -2341,7 +2363,7 @@ void MainWidget::onViewsIncrement() { for (ViewsIncrementMap::const_iterator j = i.value().cbegin(), end = i.value().cend(); j != end; ++j) { ids.push_back(MTP_int(j.key())); } - mtpRequestId req = MTP::send(MTPmessages_GetMessagesViews(i.key()->input, MTP_vector(ids), MTP_bool(true)), rpcDone(&MainWidget::viewsIncrementDone, ids), rpcFail(&MainWidget::viewsIncrementFail), 0, 5); + auto req = MTP::send(MTPmessages_GetMessagesViews(i.key()->input, MTP_vector(ids), MTP_bool(true)), rpcDone(&MainWidget::viewsIncrementDone, ids), rpcFail(&MainWidget::viewsIncrementFail), 0, 5); _viewsIncrementRequests.insert(i.key(), req); i = _viewsToIncrement.erase(i); } @@ -3532,6 +3554,7 @@ void MainWidget::gotChannelDifference(ChannelData *channel, const MTPupdates_Cha h->setUnreadCount(d.vunread_count.v); h->inboxReadBefore = d.vread_inbox_max_id.v + 1; } + h->setUnreadMentionsCount(d.vunread_mentions_count.v); if (_history->peer() == channel) { _history->updateHistoryDownVisibility(); _history->preloadHistoryIfNeeded(); @@ -3607,13 +3630,13 @@ void MainWidget::gotRangeDifference(ChannelData *channel, const MTPupdates_Chann bool isFinal = true; switch (diff.type()) { case mtpc_updates_channelDifferenceEmpty: { - const auto &d(diff.c_updates_channelDifferenceEmpty()); + auto &d = diff.c_updates_channelDifferenceEmpty(); nextRequestPts = d.vpts.v; isFinal = d.is_final(); } break; case mtpc_updates_channelDifferenceTooLong: { - const auto &d(diff.c_updates_channelDifferenceTooLong()); + auto &d = diff.c_updates_channelDifferenceTooLong(); App::feedUsers(d.vusers); App::feedChats(d.vchats); @@ -3623,7 +3646,7 @@ void MainWidget::gotRangeDifference(ChannelData *channel, const MTPupdates_Chann } break; case mtpc_updates_channelDifference: { - const auto &d(diff.c_updates_channelDifference()); + auto &d = diff.c_updates_channelDifference(); App::feedUsers(d.vusers); App::feedChats(d.vchats); @@ -4362,6 +4385,10 @@ bool MainWidget::doWeReadServerHistory() const { return isActive() && !_wideSection && !_overview && _history->doWeReadServerHistory(); } +bool MainWidget::doWeReadMentions() const { + return isActive() && !_wideSection && !_overview && _history->doWeReadMentions(); +} + bool MainWidget::lastWasOnline() const { return _lastWasOnline; } @@ -4731,6 +4758,8 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) { void MainWidget::feedUpdate(const MTPUpdate &update) { switch (update.type()) { + + // New messages. case mtpc_updateNewMessage: { auto &d = update.c_updateNewMessage(); @@ -4750,6 +4779,43 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { } } break; + case mtpc_updateNewChannelMessage: { + auto &d = update.c_updateNewChannelMessage(); + auto channel = App::channelLoaded(peerToChannel(peerFromMessage(d.vmessage))); + auto isDataLoaded = allDataLoadedForMessage(d.vmessage); + if (!requestingDifference() && (!channel || isDataLoaded != DataIsLoadedResult::Ok)) { + MTP_LOG(0, ("getDifference { good - after not all data loaded in updateNewChannelMessage }%1").arg(cTestMode() ? " TESTMODE" : "")); + + // Request last active supergroup participants if the 'from' user was not loaded yet. + // This will optimize similar getDifference() calls for almost all next messages. + if (isDataLoaded == DataIsLoadedResult::FromNotLoaded && channel && channel->isMegagroup()) { + if (channel->mgInfo->lastParticipants.size() < Global::ChatSizeMax() && (channel->mgInfo->lastParticipants.isEmpty() || channel->mgInfo->lastParticipants.size() < channel->membersCount())) { + Auth().api().requestLastParticipants(channel); + } + } + + if (!_byMinChannelTimer.isActive()) { // getDifference after timeout + _byMinChannelTimer.start(WaitForSkippedTimeout); + } + return; + } + if (channel && !_handlingChannelDifference) { + if (channel->ptsRequesting()) { // skip global updates while getting channel difference + return; + } else if (channel->ptsUpdateAndApply(d.vpts.v, d.vpts_count.v, update)) { + // We could've added an item. + // Better would be for history to be subscribed to new messages. + _history->peerMessagesUpdated(); + } + } else { + Auth().api().applyUpdateNoPtsCheck(update); + + // We could've added an item. + // Better would be for history to be subscribed to new messages. + _history->peerMessagesUpdated(); + } + } break; + case mtpc_updateMessageID: { auto &d = update.c_updateMessageID(); auto msg = App::histItemByRandom(d.vrandom_id.v); @@ -4779,11 +4845,58 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { App::historyUnregSentData(d.vrandom_id.v); } break; + // Message contents being read. case mtpc_updateReadMessagesContents: { auto &d = update.c_updateReadMessagesContents(); ptsUpdateAndApply(d.vpts.v, d.vpts_count.v, update); } break; + case mtpc_updateChannelReadMessagesContents: { + auto &d = update.c_updateChannelReadMessagesContents(); + auto channel = App::channelLoaded(d.vchannel_id.v); + if (!channel) { + if (!_byMinChannelTimer.isActive()) { // getDifference after timeout + _byMinChannelTimer.start(WaitForSkippedTimeout); + } + return; + } + auto possiblyReadMentions = base::flat_set(); + for_const (auto &msgId, d.vmessages.v) { + if (auto item = App::histItemById(channel, msgId.v)) { + if (item->isMediaUnread()) { + item->markMediaRead(); + Ui::repaintHistoryItem(item); + } + } else { + // Perhaps it was an unread mention! + possiblyReadMentions.insert(msgId.v); + } + } + Auth().api().checkForUnreadMentions(possiblyReadMentions, channel); + } break; + + // Edited messages. + case mtpc_updateEditMessage: { + auto &d = update.c_updateEditMessage(); + ptsUpdateAndApply(d.vpts.v, d.vpts_count.v, update); + } break; + + case mtpc_updateEditChannelMessage: { + auto &d = update.c_updateEditChannelMessage(); + auto channel = App::channelLoaded(peerToChannel(peerFromMessage(d.vmessage))); + + if (channel && !_handlingChannelDifference) { + if (channel->ptsRequesting()) { // skip global updates while getting channel difference + return; + } else { + channel->ptsUpdateAndApply(d.vpts.v, d.vpts_count.v, update); + } + } else { + Auth().api().applyUpdateNoPtsCheck(update); + } + } break; + + // Messages being read. case mtpc_updateReadHistoryInbox: { auto &d = update.c_updateReadHistoryInbox(); ptsUpdateAndApply(d.vpts.v, d.vpts_count.v, update); @@ -4798,6 +4911,53 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { } } break; + case mtpc_updateReadChannelInbox: { + auto &d = update.c_updateReadChannelInbox(); + App::feedInboxRead(peerFromChannel(d.vchannel_id.v), d.vmax_id.v); + } break; + + case mtpc_updateReadChannelOutbox: { + auto &d = update.c_updateReadChannelOutbox(); + auto peerId = peerFromChannel(d.vchannel_id.v); + auto when = requestingDifference() ? 0 : unixtime(); + App::feedOutboxRead(peerId, d.vmax_id.v, when); + if (_history->peer() && _history->peer()->id == peerId) { + _history->update(); + } + } break; + + // Deleted messages. + case mtpc_updateDeleteMessages: { + auto &d = update.c_updateDeleteMessages(); + + if (ptsUpdateAndApply(d.vpts.v, d.vpts_count.v, update)) { + // We could've removed some items. + // Better would be for history to be subscribed to removed messages. + _history->peerMessagesUpdated(); + } + } break; + + case mtpc_updateDeleteChannelMessages: { + auto &d = update.c_updateDeleteChannelMessages(); + auto channel = App::channelLoaded(d.vchannel_id.v); + + if (channel && !_handlingChannelDifference) { + if (channel->ptsRequesting()) { // skip global updates while getting channel difference + return; + } else if (channel->ptsUpdateAndApply(d.vpts.v, d.vpts_count.v, update)) { + // We could've removed some items. + // Better would be for history to be subscribed to removed messages. + _history->peerMessagesUpdated(); + } + } else { + // We could've removed some items. + // Better would be for history to be subscribed to removed messages. + _history->peerMessagesUpdated(); + + Auth().api().applyUpdateNoPtsCheck(update); + } + } break; + case mtpc_updateWebPage: { auto &d = update.c_updateWebPage(); @@ -4809,13 +4969,23 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { ptsUpdateAndApply(d.vpts.v, d.vpts_count.v, update); } break; - case mtpc_updateDeleteMessages: { - auto &d = update.c_updateDeleteMessages(); + case mtpc_updateChannelWebPage: { + auto &d = update.c_updateChannelWebPage(); - if (ptsUpdateAndApply(d.vpts.v, d.vpts_count.v, update)) { - // We could've removed some items. - // Better would be for history to be subscribed to removed messages. - _history->peerMessagesUpdated(); + // update web page anyway + App::feedWebPage(d.vwebpage); + _history->updatePreview(); + webPagesOrGamesUpdate(); + + auto channel = App::channelLoaded(d.vchannel_id.v); + if (channel && !_handlingChannelDifference) { + if (channel->ptsRequesting()) { // skip global updates while getting channel difference + return; + } else { + channel->ptsUpdateAndApply(d.vpts.v, d.vpts_count.v, update); + } + } else { + Auth().api().applyUpdateNoPtsCheck(update); } } break; @@ -5052,7 +5222,6 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { } } break; - /////// Channel updates case mtpc_updateChannel: { auto &d = update.c_updateChannel(); if (auto channel = App::channelLoaded(d.vchannel_id.v)) { @@ -5067,63 +5236,6 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { } } break; - case mtpc_updateNewChannelMessage: { - auto &d = update.c_updateNewChannelMessage(); - auto channel = App::channelLoaded(peerToChannel(peerFromMessage(d.vmessage))); - auto isDataLoaded = allDataLoadedForMessage(d.vmessage); - if (!requestingDifference() && (!channel || isDataLoaded != DataIsLoadedResult::Ok)) { - MTP_LOG(0, ("getDifference { good - after not all data loaded in updateNewChannelMessage }%1").arg(cTestMode() ? " TESTMODE" : "")); - - // Request last active supergroup participants if the 'from' user was not loaded yet. - // This will optimize similar getDifference() calls for almost all next messages. - if (isDataLoaded == DataIsLoadedResult::FromNotLoaded && channel && channel->isMegagroup()) { - if (channel->mgInfo->lastParticipants.size() < Global::ChatSizeMax() && (channel->mgInfo->lastParticipants.isEmpty() || channel->mgInfo->lastParticipants.size() < channel->membersCount())) { - Auth().api().requestLastParticipants(channel); - } - } - - if (!_byMinChannelTimer.isActive()) { // getDifference after timeout - _byMinChannelTimer.start(WaitForSkippedTimeout); - } - return; - } - if (channel && !_handlingChannelDifference) { - if (channel->ptsRequesting()) { // skip global updates while getting channel difference - return; - } else if (channel->ptsUpdateAndApply(d.vpts.v, d.vpts_count.v, update)) { - // We could've added an item. - // Better would be for history to be subscribed to new messages. - _history->peerMessagesUpdated(); - } - } else { - Auth().api().applyUpdateNoPtsCheck(update); - - // We could've added an item. - // Better would be for history to be subscribed to new messages. - _history->peerMessagesUpdated(); - } - } break; - - case mtpc_updateEditChannelMessage: { - auto &d = update.c_updateEditChannelMessage(); - auto channel = App::channelLoaded(peerToChannel(peerFromMessage(d.vmessage))); - - if (channel && !_handlingChannelDifference) { - if (channel->ptsRequesting()) { // skip global updates while getting channel difference - return; - } else { - channel->ptsUpdateAndApply(d.vpts.v, d.vpts_count.v, update); - } - } else { - Auth().api().applyUpdateNoPtsCheck(update); - } - } break; - - case mtpc_updateEditMessage: { - auto &d = update.c_updateEditMessage(); - ptsUpdateAndApply(d.vpts.v, d.vpts_count.v, update); - } break; - case mtpc_updateChannelPinnedMessage: { auto &d = update.c_updateChannelPinnedMessage(); @@ -5135,62 +5247,6 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { } } break; - case mtpc_updateReadChannelInbox: { - auto &d = update.c_updateReadChannelInbox(); - App::feedInboxRead(peerFromChannel(d.vchannel_id.v), d.vmax_id.v); - } break; - - case mtpc_updateReadChannelOutbox: { - auto &d = update.c_updateReadChannelOutbox(); - auto peerId = peerFromChannel(d.vchannel_id.v); - auto when = requestingDifference() ? 0 : unixtime(); - App::feedOutboxRead(peerId, d.vmax_id.v, when); - if (_history->peer() && _history->peer()->id == peerId) { - _history->update(); - } - } break; - - case mtpc_updateChannelWebPage: { - auto &d = update.c_updateChannelWebPage(); - - // update web page anyway - App::feedWebPage(d.vwebpage); - _history->updatePreview(); - webPagesOrGamesUpdate(); - - auto channel = App::channelLoaded(d.vchannel_id.v); - if (channel && !_handlingChannelDifference) { - if (channel->ptsRequesting()) { // skip global updates while getting channel difference - return; - } else { - channel->ptsUpdateAndApply(d.vpts.v, d.vpts_count.v, update); - } - } else { - Auth().api().applyUpdateNoPtsCheck(update); - } - } break; - - case mtpc_updateDeleteChannelMessages: { - auto &d = update.c_updateDeleteChannelMessages(); - auto channel = App::channelLoaded(d.vchannel_id.v); - - if (channel && !_handlingChannelDifference) { - if (channel->ptsRequesting()) { // skip global updates while getting channel difference - return; - } else if (channel->ptsUpdateAndApply(d.vpts.v, d.vpts_count.v, update)) { - // We could've removed some items. - // Better would be for history to be subscribed to removed messages. - _history->peerMessagesUpdated(); - } - } else { - // We could've removed some items. - // Better would be for history to be subscribed to removed messages. - _history->peerMessagesUpdated(); - - Auth().api().applyUpdateNoPtsCheck(update); - } - } break; - case mtpc_updateChannelTooLong: { auto &d = update.c_updateChannelTooLong(); if (auto channel = App::channelLoaded(d.vchannel_id.v)) { diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 8ad6e74ed..8b77e2511 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -227,6 +227,7 @@ public: bool isActive() const; bool doWeReadServerHistory() const; + bool doWeReadMentions() const; bool lastWasOnline() const; TimeMs lastSetOnline() const; @@ -347,8 +348,9 @@ public: void cancelForwarding(History *history); void finishForwarding(History *history, bool silent); // send them - void mediaMarkRead(DocumentData *data); + void mediaMarkRead(gsl::not_null data); void mediaMarkRead(const HistoryItemsMap &items); + void mediaMarkRead(gsl::not_null item); void webPageUpdated(WebPageData *page); void gameUpdated(GameData *game); diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 3ca6eaae8..aea485f7b 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -516,6 +516,11 @@ bool MainWindow::doWeReadServerHistory() { return isActive() && _main && !Ui::isLayerShown() && _main->doWeReadServerHistory(); } +bool MainWindow::doWeReadMentions() { + updateIsActive(0); + return isActive() && _main && !Ui::isLayerShown() && _main->doWeReadMentions(); +} + void MainWindow::checkHistoryActivation() { if (_main && doWeReadServerHistory()) { _main->markActiveHistoryAsRead(); diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index 1156b0d6b..b116b9c09 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -93,6 +93,7 @@ public: PasscodeWidget *passcodeWidget(); bool doWeReadServerHistory(); + bool doWeReadMentions(); void activate(); diff --git a/Telegram/SourceFiles/media/player/media_player_instance.cpp b/Telegram/SourceFiles/media/player/media_player_instance.cpp index 78ea5e6dc..53e9857ee 100644 --- a/Telegram/SourceFiles/media/player/media_player_instance.cpp +++ b/Telegram/SourceFiles/media/player/media_player_instance.cpp @@ -150,9 +150,9 @@ void Instance::rebuildPlaylist(Data *data) { data->playlist.clear(); if (data->history && data->history->loadedAtBottom()) { - auto &historyOverview = data->history->overview[data->overview]; + auto &historyOverview = data->history->overview(data->overview); if (data->migrated && data->migrated->loadedAtBottom() && data->history->loadedAtTop()) { - auto &migratedOverview = data->migrated->overview[data->overview]; + auto &migratedOverview = data->migrated->overview(data->overview); data->playlist.reserve(migratedOverview.size() + historyOverview.size()); for_const (auto msgId, migratedOverview) { data->playlist.push_back(FullMsgId(data->migrated->channelId(), msgId)); diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 0f321e240..967ce90bb 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -205,19 +205,22 @@ void MediaView::mediaOverviewUpdated(const Notify::PeerUpdate &update) { if (_history && (_history->peer == update.peer || (_migrated && _migrated->peer == update.peer)) && (update.mediaTypesMask & (1 << _overview)) && _msgid) { _index = -1; + auto i = 0; if (_msgmigrated) { - for (int i = 0, l = _migrated->overview[_overview].size(); i < l; ++i) { - if (_migrated->overview[_overview].at(i) == _msgid) { + for_const (auto msgId, _migrated->overview(_overview)) { + if (msgId == _msgid) { _index = i; break; } + ++i; } } else { - for (int i = 0, l = _history->overview[_overview].size(); i < l; ++i) { - if (_history->overview[_overview].at(i) == _msgid) { + for_const (auto msgId, _history->overview(_overview)) { + if (msgId == _msgid) { _index = i; break; } + ++i; } } updateControls(); @@ -227,7 +230,7 @@ void MediaView::mediaOverviewUpdated(const Notify::PeerUpdate &update) { _index = -1; for (int i = 0, l = _user->photos.size(); i < l; ++i) { - if (_user->photos.at(i) == _photo) { + if (_user->photos[i] == _photo) { _index = i; break; } @@ -392,19 +395,19 @@ void MediaView::updateControls() { updateHeader(); if (_photo || (_history && _overview != OverviewCount)) { _leftNavVisible = (_index > 0) || (_index == 0 && ( - (!_msgmigrated && _history && _history->overview[_overview].size() < _history->overviewCount(_overview)) || - (_msgmigrated && _migrated && _migrated->overview[_overview].size() < _migrated->overviewCount(_overview)) || - (!_msgmigrated && _history && _migrated && (!_migrated->overview[_overview].isEmpty() || _migrated->overviewCount(_overview) > 0)))) || + (!_msgmigrated && _history && _history->overview(_overview).size() < _history->overviewCount(_overview)) || + (_msgmigrated && _migrated && _migrated->overview(_overview).size() < _migrated->overviewCount(_overview)) || + (!_msgmigrated && _history && _migrated && (!_migrated->overview(_overview).isEmpty() || _migrated->overviewCount(_overview) > 0)))) || (_index < 0 && _photo == _additionalChatPhoto && ((_history && _history->overviewCount(_overview) > 0) || (_migrated && _history->overviewLoaded(_overview) && _migrated->overviewCount(_overview) > 0)) ); _rightNavVisible = (_index >= 0) && ( - (!_msgmigrated && _history && _index + 1 < _history->overview[_overview].size()) || - (_msgmigrated && _migrated && _index + 1 < _migrated->overview[_overview].size()) || - (_msgmigrated && _migrated && _history && (!_history->overview[_overview].isEmpty() || _history->overviewCount(_overview) > 0)) || - (!_msgmigrated && _history && _index + 1 == _history->overview[_overview].size() && _additionalChatPhoto) || - (_msgmigrated && _migrated && _index + 1 == _migrated->overview[_overview].size() && _history->overviewCount(_overview) == 0 && _additionalChatPhoto) || + (!_msgmigrated && _history && _index + 1 < _history->overview(_overview).size()) || + (_msgmigrated && _migrated && _index + 1 < _migrated->overview(_overview).size()) || + (_msgmigrated && _migrated && _history && (!_history->overview(_overview).isEmpty() || _history->overviewCount(_overview) > 0)) || + (!_msgmigrated && _history && _index + 1 == _history->overview(_overview).size() && _additionalChatPhoto) || + (_msgmigrated && _migrated && _index + 1 == _migrated->overview(_overview).size() && _history->overviewCount(_overview) == 0 && _additionalChatPhoto) || (!_history && _user && (_index + 1 < _user->photos.size() || _index + 1 < _user->photosCount))); if (_msgmigrated && !_history->overviewLoaded(_overview)) { _leftNavVisible = _rightNavVisible = false; @@ -2154,10 +2157,10 @@ bool MediaView::moveToNext(int32 delta) { auto lastChatPhoto = computeLastOverviewChatPhoto(); if (lastChatPhoto.item) { if (lastChatPhoto.item->history() == _history) { - _index = _history->overview[_overview].size() - 1; + _index = _history->overview(_overview).size() - 1; _msgmigrated = false; } else { - _index = _migrated->overview[_overview].size() - 1; + _index = _migrated->overview(_overview).size() - 1; _msgmigrated = true; } _msgid = lastChatPhoto.item->id; @@ -2186,14 +2189,14 @@ bool MediaView::moveToNext(int32 delta) { if (_history && _overview != OverviewCount) { bool newMigrated = _msgmigrated; if (!newMigrated && newIndex < 0 && _migrated) { - newIndex += _migrated->overview[_overview].size(); + newIndex += _migrated->overview(_overview).size(); newMigrated = true; - } else if (newMigrated && newIndex >= _migrated->overview[_overview].size()) { - newIndex -= _migrated->overview[_overview].size() + (_history->overviewCount(_overview) - _history->overview[_overview].size()); + } else if (newMigrated && newIndex >= _migrated->overview(_overview).size()) { + newIndex -= _migrated->overview(_overview).size() + (_history->overviewCount(_overview) - _history->overview(_overview).size()); newMigrated = false; } - if (newIndex >= 0 && newIndex < (newMigrated ? _migrated : _history)->overview[_overview].size()) { - if (auto item = App::histItemById(newMigrated ? 0 : _channel, (newMigrated ? _migrated : _history)->overview[_overview][newIndex])) { + if (newIndex >= 0 && newIndex < (newMigrated ? _migrated : _history)->overview(_overview).size()) { + if (auto item = App::histItemById(newMigrated ? 0 : _channel, getMsgIdFromOverview(newMigrated ? _migrated : _history, newIndex))) { _index = newIndex; _msgid = item->id; _msgmigrated = (item->history() == _migrated); @@ -2214,7 +2217,7 @@ bool MediaView::moveToNext(int32 delta) { preloadData(delta); } } - } else if (!newMigrated && newIndex == _history->overview[_overview].size() && _additionalChatPhoto) { + } else if (!newMigrated && newIndex == _history->overview(_overview).size() && _additionalChatPhoto) { _index = -1; _msgid = 0; _msgmigrated = false; @@ -2243,28 +2246,29 @@ void MediaView::preloadData(int32 delta) { bool indexOfMigratedItem = _msgmigrated; if (_index < 0) { if (_overview != OverviewChatPhotos || !_history) return; - indexInOverview = _history->overview[OverviewChatPhotos].size(); + indexInOverview = _history->overview(OverviewChatPhotos).size(); indexOfMigratedItem = false; } if (!_user && _overview == OverviewCount) return; - int32 from = indexInOverview + (delta ? delta : -1), to = indexInOverview + (delta ? delta * MediaOverviewPreloadCount : 1); + auto from = indexInOverview + (delta ? delta : -1); + auto to = indexInOverview + (delta ? delta * MediaOverviewPreloadCount : 1); if (from > to) qSwap(from, to); if (_history && _overview != OverviewCount) { - int32 forgetIndex = indexInOverview - delta * 2; - History *forgetHistory = indexOfMigratedItem ? _migrated : _history; + auto forgetIndex = indexInOverview - delta * 2; + auto forgetHistory = indexOfMigratedItem ? _migrated : _history; if (_migrated) { - if (indexOfMigratedItem && forgetIndex >= _migrated->overview[_overview].size()) { + if (indexOfMigratedItem && forgetIndex >= _migrated->overview(_overview).size()) { forgetHistory = _history; - forgetIndex -= _migrated->overview[_overview].size() + (_history->overviewCount(_overview) - _history->overview[_overview].size()); + forgetIndex -= _migrated->overview(_overview).size() + (_history->overviewCount(_overview) - _history->overview(_overview).size()); } else if (!indexOfMigratedItem && forgetIndex < 0) { forgetHistory = _migrated; - forgetIndex += _migrated->overview[_overview].size(); + forgetIndex += _migrated->overview(_overview).size(); } } - if (forgetIndex >= 0 && forgetIndex < forgetHistory->overview[_overview].size() && (forgetHistory != (indexOfMigratedItem ? _migrated : _history) || forgetIndex != indexInOverview)) { - if (HistoryItem *item = App::histItemById(forgetHistory->channelId(), forgetHistory->overview[_overview][forgetIndex])) { - if (HistoryMedia *media = item->getMedia()) { + if (forgetIndex >= 0 && forgetIndex < forgetHistory->overview(_overview).size() && (forgetHistory != (indexOfMigratedItem ? _migrated : _history) || forgetIndex != indexInOverview)) { + if (auto item = App::histItemById(forgetHistory->channelId(), getMsgIdFromOverview(forgetHistory, forgetIndex))) { + if (auto media = item->getMedia()) { switch (media->type()) { case MediaTypePhoto: static_cast(media)->photo()->forget(); break; case MediaTypeFile: @@ -2280,23 +2284,23 @@ void MediaView::preloadData(int32 delta) { History *previewHistory = indexOfMigratedItem ? _migrated : _history; int32 previewIndex = i; if (_migrated) { - if (indexOfMigratedItem && previewIndex >= _migrated->overview[_overview].size()) { + if (indexOfMigratedItem && previewIndex >= _migrated->overview(_overview).size()) { previewHistory = _history; - previewIndex -= _migrated->overview[_overview].size() + (_history->overviewCount(_overview) - _history->overview[_overview].size()); + previewIndex -= _migrated->overview(_overview).size() + (_history->overviewCount(_overview) - _history->overview(_overview).size()); } else if (!indexOfMigratedItem && previewIndex < 0) { previewHistory = _migrated; - previewIndex += _migrated->overview[_overview].size(); + previewIndex += _migrated->overview(_overview).size(); } } - if (previewIndex >= 0 && previewIndex < previewHistory->overview[_overview].size() && (previewHistory != (indexOfMigratedItem ? _migrated : _history) || previewIndex != indexInOverview)) { - if (HistoryItem *item = App::histItemById(previewHistory->channelId(), previewHistory->overview[_overview][previewIndex])) { - if (HistoryMedia *media = item->getMedia()) { + if (previewIndex >= 0 && previewIndex < previewHistory->overview(_overview).size() && (previewHistory != (indexOfMigratedItem ? _migrated : _history) || previewIndex != indexInOverview)) { + if (auto item = App::histItemById(previewHistory->channelId(), getMsgIdFromOverview(previewHistory, previewIndex))) { + if (auto media = item->getMedia()) { switch (media->type()) { case MediaTypePhoto: static_cast(media)->photo()->download(); break; case MediaTypeFile: case MediaTypeVideo: case MediaTypeGif: { - DocumentData *doc = media->getDocument(); + auto doc = media->getDocument(); doc->thumb->load(); doc->automaticLoad(item); } break; @@ -2751,12 +2755,14 @@ void MediaView::updateImage() { } void MediaView::findCurrent() { + auto i = 0; if (_msgmigrated) { - for (int i = 0, l = _migrated->overview[_overview].size(); i < l; ++i) { - if (_migrated->overview[_overview].at(i) == _msgid) { + for (auto msgId : _migrated->overview(_overview)) { + if (msgId == _msgid) { _index = i; break; } + ++i; } if (!_history->overviewCountLoaded(_overview)) { loadBack(); @@ -2766,11 +2772,12 @@ void MediaView::findCurrent() { } } } else { - for (int i = 0, l = _history->overview[_overview].size(); i < l; ++i) { - if (_history->overview[_overview].at(i) == _msgid) { + for (auto msgId : _history->overview(_overview)) { + if (msgId == _msgid) { _index = i; break; } + ++i; } if (!_history->overviewLoaded(_overview)) { if (!_history->overviewCountLoaded(_overview) || (_index < 2 && _history->overviewCount(_overview) > 0) || (_index < 1 && _migrated && !_migrated->overviewLoaded(_overview))) { @@ -2799,7 +2806,7 @@ void MediaView::loadBack() { App::main()->loadMediaBack(_migrated->peer, _overview); } else { App::main()->loadMediaBack(_history->peer, _overview); - if (_migrated && _index == 0 && (_migrated->overviewCount(_overview) < 0 || _migrated->overview[_overview].isEmpty()) && !_migrated->overviewLoaded(_overview)) { + if (_migrated && _index == 0 && (_migrated->overviewCount(_overview) < 0 || _migrated->overview(_overview).isEmpty()) && !_migrated->overviewLoaded(_overview)) { App::main()->loadMediaBack(_migrated->peer, _overview); } } @@ -2816,7 +2823,8 @@ void MediaView::loadBack() { MediaView::LastChatPhoto MediaView::computeLastOverviewChatPhoto() { LastChatPhoto emptyResult = { nullptr, nullptr }; auto lastPhotoInOverview = [&emptyResult](auto history, auto list) -> LastChatPhoto { - if (auto item = App::histItemById(history->channelId(), list.back())) { + auto end = list.end(); + if (auto item = App::histItemById(history->channelId(), *--end)) { if (auto media = item->getMedia()) { if (media->type() == MediaTypePhoto && !item->toHistoryMessage()) { return { item, static_cast(media)->photo() }; @@ -2827,13 +2835,13 @@ MediaView::LastChatPhoto MediaView::computeLastOverviewChatPhoto() { }; if (!_history) return emptyResult; - auto &list = _history->overview[OverviewChatPhotos]; + auto &list = _history->overview(OverviewChatPhotos); if (!list.isEmpty()) { return lastPhotoInOverview(_history, list); } if (!_migrated || !_history->overviewLoaded(OverviewChatPhotos)) return emptyResult; - auto &migratedList = _migrated->overview[OverviewChatPhotos]; + auto &migratedList = _migrated->overview(OverviewChatPhotos); if (!migratedList.isEmpty()) { return lastPhotoInOverview(_migrated, migratedList); } @@ -2890,17 +2898,17 @@ void MediaView::updateHeader() { int32 index = _index, count = 0, addcount = (_migrated && _overview != OverviewCount) ? _migrated->overviewCount(_overview) : 0; if (_history) { if (_overview != OverviewCount) { - bool lastOverviewPhotoLoaded = (!_history->overview[_overview].isEmpty() || ( - _migrated && _history->overviewCount(_overview) == 0 && !_migrated->overview[_overview].isEmpty())); + bool lastOverviewPhotoLoaded = (!_history->overview(_overview).isEmpty() || ( + _migrated && _history->overviewCount(_overview) == 0 && !_migrated->overview(_overview).isEmpty())); count = _history->overviewCount(_overview); if (addcount >= 0 && count >= 0) { count += addcount; } if (index >= 0 && (_msgmigrated ? (count >= 0 && addcount >= 0 && _history->overviewLoaded(_overview)) : (count >= 0))) { if (_msgmigrated) { - index += addcount - _migrated->overview[_overview].size(); + index += addcount - _migrated->overview(_overview).size(); } else { - index += count - _history->overview[_overview].size(); + index += count - _history->overview(_overview).size(); } if (_additionalChatPhoto && lastOverviewPhotoLoaded) { ++count; @@ -2948,3 +2956,15 @@ float64 MediaView::overLevel(OverState control) const { auto i = _animOpacities.constFind(control); return (i == _animOpacities.cend()) ? (_over == control ? 1 : 0) : i->current(); } + +MsgId MediaView::getMsgIdFromOverview(gsl::not_null history, int index) const { + auto &overview = history->overview(_overview); + if (index >= 0 && index < overview.size()) { + auto it = overview.begin(); + for (auto i = 0; i != index; ++i) { + ++it; + } + return *it; + } + return 0; +} diff --git a/Telegram/SourceFiles/mediaview.h b/Telegram/SourceFiles/mediaview.h index a455041b6..a200c88db 100644 --- a/Telegram/SourceFiles/mediaview.h +++ b/Telegram/SourceFiles/mediaview.h @@ -224,6 +224,8 @@ private: bool updateOverState(OverState newState); float64 overLevel(OverState control) const; + MsgId getMsgIdFromOverview(gsl::not_null history, int index) const; + QBrush _transparentBrush; PhotoData *_photo = nullptr; @@ -294,7 +296,7 @@ private: UserData *_user = nullptr; // if user profile photos overview // There can be additional first photo in chat photos overview, that is not - // in the _history->overview[OverviewChatPhotos] (if the item was deleted). + // in the _history->overview(OverviewChatPhotos) (if the item was deleted). PhotoData *_additionalChatPhoto = nullptr; // We save the information about the reason of the current mediaview show: diff --git a/Telegram/SourceFiles/observer_peer.h b/Telegram/SourceFiles/observer_peer.h index a2c127ab5..cb159fd87 100644 --- a/Telegram/SourceFiles/observer_peer.h +++ b/Telegram/SourceFiles/observer_peer.h @@ -51,6 +51,7 @@ struct PeerUpdate { MembersChanged = 0x00000200U, AdminsChanged = 0x00000400U, BannedUsersChanged = 0x00000800U, + UnreadMentionsChanged = 0x00001000U, // For users UserCanShareContact = 0x00010000U, diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index 570260a78..abfd97a3c 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -180,7 +180,7 @@ MsgId OverviewInner::itemMsgId(MsgId msgId) const { } int32 OverviewInner::migratedIndexSkip() const { - return (_migrated && _history->overviewLoaded(_type)) ? _migrated->overview[_type].size() : 0; + return (_migrated && _history->overviewLoaded(_type)) ? _migrated->overview(_type).size() : 0; } void OverviewInner::fixItemIndex(int32 ¤t, MsgId msgId) const { @@ -742,7 +742,7 @@ int32 OverviewInner::itemTop(const FullMsgId &msgId) const { void OverviewInner::preloadMore() { if (_inSearch) { if (!_searchRequest) { - MTPmessagesFilter filter = (_type == OverviewLinks) ? MTP_inputMessagesFilterUrl() : MTP_inputMessagesFilterDocument(); + auto filter = (_type == OverviewLinks) ? MTP_inputMessagesFilterUrl() : MTP_inputMessagesFilterDocument(); if (!_searchFull) { _searchRequest = MTP::send(MTPmessages_Search(MTP_flags(0), _history->peer->input, MTP_string(_searchQuery), MTP_inputUserEmpty(), filter, MTP_int(0), MTP_int(0), MTP_int(_lastSearchId), MTP_int(0), MTP_int(SearchPerPage), MTP_int(0), MTP_int(0)), rpcDone(&OverviewInner::searchReceived, _lastSearchId ? SearchFromOffset : SearchFromStart), rpcFail(&OverviewInner::searchFailed, _lastSearchId ? SearchFromOffset : SearchFromStart)); if (!_lastSearchId) { @@ -762,7 +762,7 @@ void OverviewInner::preloadMore() { } bool OverviewInner::preloadLocal() { - if (_itemsToBeLoaded >= migratedIndexSkip() + _history->overview[_type].size()) return false; + if (_itemsToBeLoaded >= migratedIndexSkip() + _history->overview(_type).size()) return false; _itemsToBeLoaded += LinksOverviewPerPage; mediaOverviewUpdated(); return true; @@ -800,7 +800,7 @@ void OverviewInner::paintEvent(QPaintEvent *e) { auto ms = getms(); Overview::Layout::PaintContext context(ms, _selMode); - if (_history->overview[_type].isEmpty() && (!_migrated || !_history->overviewLoaded(_type) || _migrated->overview[_type].isEmpty())) { + if (_history->overview(_type).isEmpty() && (!_migrated || !_history->overviewLoaded(_type) || _migrated->overview(_type).isEmpty())) { HistoryLayout::paintEmpty(p, _width, height()); return; } else if (_inSearch && _searchResults.isEmpty() && _searchFull && (!_migrated || _searchFullMigrated) && !_searchTimer.isActive()) { @@ -1625,17 +1625,28 @@ void OverviewInner::onTouchScrollTimer() { void OverviewInner::mediaOverviewUpdated() { if (_type == OverviewPhotos || _type == OverviewVideos) { - History::MediaOverview &o(_history->overview[_type]), *migratedOverview = _migrated ? &_migrated->overview[_type] : 0; - int32 migrateCount = migratedIndexSkip(); - int32 wasCount = _items.size(), fullCount = (migrateCount + o.size()); - int32 tocheck = qMin(fullCount, _itemsToBeLoaded); + auto &o = _history->overview(_type); + auto migratedOverview = _migrated ? &_migrated->overview(_type) : nullptr; + auto migrateCount = migratedIndexSkip(); + auto wasCount = _items.size(); + auto fullCount = (migrateCount + o.size()); + auto tocheck = qMin(fullCount, _itemsToBeLoaded); _items.reserve(tocheck); - int32 index = 0; - bool allGood = true; - for (int32 i = fullCount, l = fullCount - tocheck; i > l;) { + auto index = 0; + auto allGood = true; + auto migrateIt = migratedOverview ? migratedOverview->end() : o.end(); + auto it = o.end(); + for (auto i = fullCount, l = fullCount - tocheck; i > l;) { --i; - MsgId msgid = ((i < migrateCount) ? -migratedOverview->at(i) : o.at(i - migrateCount)); + auto msgid = MsgId(0); + if (i < migrateCount) { + --migrateIt; + msgid = -(*migrateIt); + } else { + --it; + msgid = *it; + } if (allGood) { if (_items.size() > index && complexMsgId(_items.at(index)->getItem()) == msgid) { ++index; @@ -1657,54 +1668,70 @@ void OverviewInner::mediaOverviewUpdated() { bool dateEveryMonth = (_type == OverviewFiles), dateEveryDay = (_type == OverviewLinks); bool withDates = (dateEveryMonth || dateEveryDay); - History::MediaOverview &o(_history->overview[_type]), *migratedOverview = _migrated ? &_migrated->overview[_type] : 0; - int32 migrateCount = migratedIndexSkip(); - int32 l = _inSearch ? _searchResults.size() : (migrateCount + o.size()), tocheck = qMin(l, _itemsToBeLoaded); + auto &o = _history->overview(_type); + auto migratedOverview = _migrated ? &_migrated->overview(_type) : nullptr; + auto migrateCount = migratedIndexSkip(); + auto l = _inSearch ? _searchResults.size() : (migrateCount + o.size()); + auto tocheck = qMin(l, _itemsToBeLoaded); _items.reserve((withDates ? 2 : 1) * tocheck); // day items - int32 top = 0, index = 0; + auto migrateIt = migratedOverview ? migratedOverview->end() : o.end(); + auto it = o.end(); + + auto top = 0; + auto count = 0; bool allGood = true; QDate prevDate; - for (int32 i = 0; i < tocheck; ++i) { - MsgId msgid = _inSearch ? _searchResults.at(l - i - 1) : ((l - i - 1 < migrateCount) ? -migratedOverview->at(l - i - 1) : o.at(l - i - 1 - migrateCount)); + for (auto i = 0; i < tocheck; ++i) { + auto msgid = MsgId(0); + auto index = l - i - 1; + if (_inSearch) { + msgid = _searchResults[index]; + } else if (index < migrateCount) { + --migrateIt; + msgid = -(*migrateIt); + } else { + --it; + msgid = *it; + } if (allGood) { - if (_items.size() > index && complexMsgId(_items.at(index)->getItem()) == msgid) { - if (withDates) prevDate = _items.at(index)->getItem()->date.date(); - top = _items.at(index)->Get()->top; + if (_items.size() > count && complexMsgId(_items.at(count)->getItem()) == msgid) { + if (withDates) prevDate = _items.at(count)->getItem()->date.date(); + top = _items.at(count)->Get()->top; if (!_reversed) { - top += _items.at(index)->height(); + top += _items.at(count)->height(); } - ++index; + ++count; continue; } - if (_items.size() > index + 1 && !_items.at(index)->toMediaItem() && complexMsgId(_items.at(index + 1)->getItem()) == msgid) { // day item - ++index; - if (withDates) prevDate = _items.at(index)->getItem()->date.date(); - top = _items.at(index)->Get()->top; + if (_items.size() > count + 1 && !_items.at(count)->toMediaItem() && complexMsgId(_items.at(count + 1)->getItem()) == msgid) { // day item + ++count; + if (withDates) prevDate = _items.at(count)->getItem()->date.date(); + top = _items.at(count)->Get()->top; if (!_reversed) { - top += _items.at(index)->height(); + top += _items.at(count)->height(); } - ++index; + ++count; continue; } allGood = false; } - HistoryItem *item = App::histItemById(itemChannel(msgid), itemMsgId(msgid)); + auto item = App::histItemById(itemChannel(msgid), itemMsgId(msgid)); auto layout = layoutPrepare(item); if (!layout) continue; if (withDates) { QDate date = item->date.date(); - if (!index || (index > 0 && (dateEveryMonth ? (date.month() != prevDate.month() || date.year() != prevDate.year()) : (date != prevDate)))) { - top += setLayoutItem(index, layoutPrepare(date, dateEveryMonth), top); - ++index; + if (!count || (count > 0 && (dateEveryMonth ? (date.month() != prevDate.month() || date.year() != prevDate.year()) : (date != prevDate)))) { + top += setLayoutItem(count, layoutPrepare(date, dateEveryMonth), top); + ++count; prevDate = date; } } - top += setLayoutItem(index, layout, top); - ++index; + top += setLayoutItem(count, layout, top); + ++count; } - if (_items.size() > index) _items.resize(index); + if (_items.size() > count) _items.resize(count); _height = top; } @@ -2214,9 +2241,9 @@ void OverviewWidget::mediaOverviewUpdated(const Notify::PeerUpdate &update) { History *m = (update.peer && update.peer->migrateFrom()) ? App::historyLoaded(update.peer->migrateFrom()->id) : 0; if (h) { for (int32 i = 0; i < OverviewCount; ++i) { - if (!h->overview[i].isEmpty() || h->overviewCount(i) > 0 || i == type()) { + if (!h->overview(i).isEmpty() || h->overviewCount(i) > 0 || i == type()) { mask |= (1 << i); - } else if (m && (!m->overview[i].isEmpty() || m->overviewCount(i) > 0)) { + } else if (m && (!m->overview(i).isEmpty() || m->overviewCount(i) > 0)) { mask |= (1 << i); } } diff --git a/Telegram/SourceFiles/overviewwidget.h b/Telegram/SourceFiles/overviewwidget.h index b616d772b..7334d7514 100644 --- a/Telegram/SourceFiles/overviewwidget.h +++ b/Telegram/SourceFiles/overviewwidget.h @@ -210,7 +210,7 @@ private: bool _searchFull = false; bool _searchFullMigrated = false; mtpRequestId _searchRequest = 0; - History::MediaOverview _searchResults; + QList _searchResults; MsgId _lastSearchId = 0; MsgId _lastSearchMigratedId = 0; int _searchedCount = 0; diff --git a/Telegram/SourceFiles/shortcuts.cpp b/Telegram/SourceFiles/shortcuts.cpp index c5eec18ee..eca5c734f 100644 --- a/Telegram/SourceFiles/shortcuts.cpp +++ b/Telegram/SourceFiles/shortcuts.cpp @@ -234,7 +234,7 @@ QKeySequence setShortcut(const QString &keys, const QString &command) { if (it == DataPtr->commands.cend()) { LOG(("Warning: could not find shortcut command handler '%1'").arg(command)); } else { - auto shortcut = std::make_unique(seq, Messenger::Instance().getGlobalShortcutParent(), nullptr, nullptr, Qt::ApplicationShortcut); + auto shortcut = std::make_unique(seq, Messenger::Instance().getActiveWindow(), nullptr, nullptr, Qt::ApplicationShortcut); if (!DataPtr->autoRepeatCommands.contains(command)) { shortcut->setAutoRepeat(false); } diff --git a/Telegram/SourceFiles/ui/special_buttons.cpp b/Telegram/SourceFiles/ui/special_buttons.cpp index e7177ca76..3799c807d 100644 --- a/Telegram/SourceFiles/ui/special_buttons.cpp +++ b/Telegram/SourceFiles/ui/special_buttons.cpp @@ -73,8 +73,10 @@ void HistoryDownButton::paintEvent(QPaintEvent *e) { } void HistoryDownButton::setUnreadCount(int unreadCount) { - _unreadCount = unreadCount; - update(); + if (_unreadCount != unreadCount) { + _unreadCount = unreadCount; + update(); + } } EmojiButton::EmojiButton(QWidget *parent, const style::IconButton &st) : RippleButton(parent, st.ripple) diff --git a/Telegram/gyp/telegram_sources.txt b/Telegram/gyp/telegram_sources.txt index a738e8702..66b53b254 100644 --- a/Telegram/gyp/telegram_sources.txt +++ b/Telegram/gyp/telegram_sources.txt @@ -1,4 +1,6 @@ <(src_loc)/base/build_config.h +<(src_loc)/base/flat_map.h +<(src_loc)/base/flat_set.h <(src_loc)/base/lambda.h <(src_loc)/base/observer.cpp <(src_loc)/base/observer.h