Display and follow unread mentions in history.

This commit is contained in:
John Preston 2017-08-11 09:16:07 +02:00
parent 7ad21ff713
commit e209737b1a
31 changed files with 747 additions and 357 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 995 B

View File

@ -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<MsgId>();
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<PeerData*> peer, const QDate &date) {
}).send();
}
void ApiWrap::preloadEnoughUnreadMentions(gsl::not_null<History*> 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<MsgId> &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;

View File

@ -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<PeerData*> peer, const QDate &date);
void preloadEnoughUnreadMentions(gsl::not_null<History*> history);
void checkForUnreadMentions(const base::flat_set<MsgId> &possiblyReadMentions, ChannelData *channel = nullptr);
~ApiWrap();
private:
@ -189,6 +194,8 @@ private:
mtpRequestId _contactsStatusesRequestId = 0;
base::flat_map<gsl::not_null<History*>, mtpRequestId> _unreadMentionsRequests;
base::Observable<PeerData*> _fullPeerUpdated;
};

View File

@ -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 <typename... Types>
using variant = mapbox::util::variant<Types...>;
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 <typename... Types>
using optional_variant = variant<std::nullptr_t, Types...>;
using variant = mapbox::util::variant<Types...>;
template <typename T, typename... Types>
inline T *get_if(variant<Types...> *v) {
@ -41,9 +53,36 @@ inline const T *get_if(const variant<Types...> *v) {
return (v && v->template is<T>()) ? &v->template get_unchecked<T>() : nullptr;
}
template <typename... Types>
using optional_variant = variant<null_variant_type, Types...>;
template <typename... Types>
inline bool is_null_variant(const optional_variant<Types...> &variant) {
return get_if<std::nullptr_t>(&variant) != nullptr;
return get_if<null_variant_type>(&variant) != nullptr;
}
template <typename Type>
using optional = optional_variant<Type>;
using null_optional_type = null_variant_type;
template <typename Type>
inline Type *get_if(optional<Type> *v) {
return (v && v->template is<Type>()) ? &v->template get_unchecked<Type>() : nullptr;
}
template <typename Type>
inline const Type *get_if(const optional<Type> *v) {
return (v && v->template is<Type>()) ? &v->template get_unchecked<Type>() : nullptr;
}
template <typename Type>
inline bool is_null_optional(const optional<Type> &optional) {
return is_null_variant(optional);
}
inline constexpr null_optional_type null_optional() {
return null_optional_type {};
}
} // namespace base

View File

@ -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()

View File

@ -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;

View File

@ -1458,6 +1458,7 @@ void DialogsInner::dialogsReceived(const QVector<MTPDialog> &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()) {

View File

@ -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)) {

View File

@ -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<MTPMessage>*)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<MTPMessage>::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);
}
}
}

View File

@ -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<HistoryBlock*> 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<HistoryBlock*>;
Blocks blocks;
int width = 0;
@ -441,37 +463,33 @@ public:
mutable const HistoryItem *textCachedFor = nullptr; // cache
mutable Text lastItemTextCache;
typedef QList<MsgId> 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<History*>(this)->overviewCountData[overviewIndex] = 0;
const_cast<History*>(this)->_overviewCountData[overviewIndex] = 0;
}
return loaded;
}
return result;
}
const OrderedSet<MsgId> &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<Flags::enum_type> operator~(Flags::enum_type f) noexcept {
return ~QFlags<Flags::enum_type>(f);
}
Flags _flags;
bool _mute;
int32 _unreadCount = 0;
Flags _flags = { 0 };
bool _mute = false;
int _unreadCount = 0;
base::optional<int> _unreadMentionsCount = base::null_optional();
base::flat_set<MsgId> _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<MsgId>;
MediaOverviewIds overviewIds[OverviewCount];
int32 overviewCountData[OverviewCount]; // -1 - not loaded, 0 - all loaded, > 0 - count, but not all loaded
OrderedSet<MsgId> _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

View File

@ -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) {

View File

@ -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<HistoryMessage*> message, int userpicTop) {
// stop the enumeration if the userpic is below the painted rect

View File

@ -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;

View File

@ -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<HistoryMessageForwarded>()) {
return Has<HistoryMessageVia>()
@ -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 {

View File

@ -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;

View File

@ -612,6 +612,7 @@ HistoryWidget::HistoryWidget(QWidget *parent, gsl::not_null<Window::Controller*>
, _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<Window::Controller*>
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<Window::Controller*>
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<Window::Controller*>
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<HistoryMessageUnreadBar>()) {
@ -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;

View File

@ -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<QUrl> &files);
SendingFilesLists getSendingFilesLists(const QStringList &files);
@ -749,6 +754,10 @@ private:
bool _historyDownIsShown = false;
object_ptr<Ui::HistoryDownButton> _historyDown;
Animation _unreadMentionsShown;
bool _unreadMentionsIsShown = false;
object_ptr<Ui::HistoryDownButton> _unreadMentions;
object_ptr<FieldAutocomplete> _fieldAutocomplete;
UserData *_inlineBot = nullptr;

View File

@ -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<DocumentData*> 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<MTPint> markedIds;
QMap<ChannelData*, QVector<MTPint>> 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<MTPint>(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<MTPint>(i.value())));
}
}
void MainWidget::mediaMarkRead(gsl::not_null<HistoryItem*> 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<MTPint>(1, MTP_int(item->id))));
} else {
MTP::send(MTPmessages_ReadMessageContents(MTP_vector<MTPint>(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<MTPint>(ids), MTP_bool(true)), rpcDone(&MainWidget::viewsIncrementDone, ids), rpcFail(&MainWidget::viewsIncrementFail), 0, 5);
auto req = MTP::send(MTPmessages_GetMessagesViews(i.key()->input, MTP_vector<MTPint>(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<MsgId>();
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)) {

View File

@ -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<DocumentData*> data);
void mediaMarkRead(const HistoryItemsMap &items);
void mediaMarkRead(gsl::not_null<HistoryItem*> item);
void webPageUpdated(WebPageData *page);
void gameUpdated(GameData *game);

View File

@ -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();

View File

@ -93,6 +93,7 @@ public:
PasscodeWidget *passcodeWidget();
bool doWeReadServerHistory();
bool doWeReadMentions();
void activate();

View File

@ -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));

View File

@ -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<HistoryPhoto*>(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<HistoryPhoto*>(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<HistoryPhoto*>(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*> 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;
}

View File

@ -224,6 +224,8 @@ private:
bool updateOverState(OverState newState);
float64 overLevel(OverState control) const;
MsgId getMsgIdFromOverview(gsl::not_null<History*> 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:

View File

@ -51,6 +51,7 @@ struct PeerUpdate {
MembersChanged = 0x00000200U,
AdminsChanged = 0x00000400U,
BannedUsersChanged = 0x00000800U,
UnreadMentionsChanged = 0x00001000U,
// For users
UserCanShareContact = 0x00010000U,

View File

@ -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 &current, 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<Overview::Layout::Info>()->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<Overview::Layout::Info>()->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<Overview::Layout::Info>()->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<Overview::Layout::Info>()->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);
}
}

View File

@ -210,7 +210,7 @@ private:
bool _searchFull = false;
bool _searchFullMigrated = false;
mtpRequestId _searchRequest = 0;
History::MediaOverview _searchResults;
QList<MsgId> _searchResults;
MsgId _lastSearchId = 0;
MsgId _lastSearchMigratedId = 0;
int _searchedCount = 0;

View File

@ -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<QShortcut>(seq, Messenger::Instance().getGlobalShortcutParent(), nullptr, nullptr, Qt::ApplicationShortcut);
auto shortcut = std::make_unique<QShortcut>(seq, Messenger::Instance().getActiveWindow(), nullptr, nullptr, Qt::ApplicationShortcut);
if (!DataPtr->autoRepeatCommands.contains(command)) {
shortcut->setAutoRepeat(false);
}

View File

@ -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)

View File

@ -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