mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-03-25 04:38:23 +00:00
Display and follow unread mentions in history.
This commit is contained in:
parent
7ad21ff713
commit
e209737b1a
BIN
Telegram/Resources/icons/history_unread_mention.png
Normal file
BIN
Telegram/Resources/icons/history_unread_mention.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 583 B |
BIN
Telegram/Resources/icons/history_unread_mention@2x.png
Normal file
BIN
Telegram/Resources/icons/history_unread_mention@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 995 B |
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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;
|
||||
|
@ -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()) {
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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)) {
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -93,6 +93,7 @@ public:
|
||||
PasscodeWidget *passcodeWidget();
|
||||
|
||||
bool doWeReadServerHistory();
|
||||
bool doWeReadMentions();
|
||||
|
||||
void activate();
|
||||
|
||||
|
@ -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));
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -51,6 +51,7 @@ struct PeerUpdate {
|
||||
MembersChanged = 0x00000200U,
|
||||
AdminsChanged = 0x00000400U,
|
||||
BannedUsersChanged = 0x00000800U,
|
||||
UnreadMentionsChanged = 0x00001000U,
|
||||
|
||||
// For users
|
||||
UserCanShareContact = 0x00010000U,
|
||||
|
@ -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<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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user