mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Track unread mentions and unread reactions the same way.
This commit is contained in:
parent
6207770120
commit
e9c79886d2
31 changed files with 885 additions and 347 deletions
|
@ -147,6 +147,8 @@ PRIVATE
|
|||
api/api_text_entities.h
|
||||
api/api_toggling_media.cpp
|
||||
api/api_toggling_media.h
|
||||
api/api_unread_things.cpp
|
||||
api/api_unread_things.h
|
||||
api/api_updates.cpp
|
||||
api/api_updates.h
|
||||
api/api_user_privacy.cpp
|
||||
|
@ -686,6 +688,8 @@ PRIVATE
|
|||
history/history_message.h
|
||||
history/history_service.cpp
|
||||
history/history_service.h
|
||||
history/history_unread_things.cpp
|
||||
history/history_unread_things.h
|
||||
history/history_widget.cpp
|
||||
history/history_widget.h
|
||||
info/info_content_widget.cpp
|
||||
|
|
BIN
Telegram/Resources/icons/dialogs/dialogs_reaction.png
Normal file
BIN
Telegram/Resources/icons/dialogs/dialogs_reaction.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 513 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_reaction@2x.png
Normal file
BIN
Telegram/Resources/icons/dialogs/dialogs_reaction@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 905 B |
BIN
Telegram/Resources/icons/dialogs/dialogs_reaction@3x.png
Normal file
BIN
Telegram/Resources/icons/dialogs/dialogs_reaction@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
Telegram/Resources/icons/history_unread_reaction.png
Normal file
BIN
Telegram/Resources/icons/history_unread_reaction.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 977 B |
BIN
Telegram/Resources/icons/history_unread_reaction@2x.png
Normal file
BIN
Telegram/Resources/icons/history_unread_reaction@2x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.9 KiB |
BIN
Telegram/Resources/icons/history_unread_reaction@3x.png
Normal file
BIN
Telegram/Resources/icons/history_unread_reaction@3x.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
138
Telegram/SourceFiles/api/api_unread_things.cpp
Normal file
138
Telegram/SourceFiles/api/api_unread_things.cpp
Normal file
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "api/api_unread_things.h"
|
||||
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_session.h"
|
||||
#include "main/main_session.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history_unread_things.h"
|
||||
#include "apiwrap.h"
|
||||
|
||||
namespace Api {
|
||||
namespace {
|
||||
|
||||
constexpr auto kPreloadIfLess = 5;
|
||||
constexpr auto kFirstRequestLimit = 10;
|
||||
constexpr auto kNextRequestLimit = 100;
|
||||
|
||||
} // namespace
|
||||
|
||||
UnreadThings::UnreadThings(not_null<ApiWrap*> api) : _api(api) {
|
||||
}
|
||||
|
||||
bool UnreadThings::trackMentions(PeerData *peer) const {
|
||||
return peer && (peer->isChat() || peer->isMegagroup());
|
||||
}
|
||||
|
||||
bool UnreadThings::trackReactions(PeerData *peer) const {
|
||||
return trackMentions(peer) || (peer && peer->isUser());
|
||||
}
|
||||
|
||||
void UnreadThings::preloadEnough(History *history) {
|
||||
if (!history) {
|
||||
return;
|
||||
}
|
||||
if (trackMentions(history->peer)) {
|
||||
preloadEnoughMentions(history);
|
||||
}
|
||||
if (trackReactions(history->peer)) {
|
||||
preloadEnoughReactions(history);
|
||||
}
|
||||
}
|
||||
|
||||
void UnreadThings::mediaAndMentionsRead(
|
||||
const base::flat_set<MsgId> &readIds,
|
||||
ChannelData *channel) {
|
||||
for (const auto &msgId : readIds) {
|
||||
_api->requestMessageData(channel, msgId, [=] {
|
||||
const auto item = channel
|
||||
? _api->session().data().message(channel->id, msgId)
|
||||
: _api->session().data().nonChannelMessage(msgId);
|
||||
if (item && item->mentionsMe()) {
|
||||
item->markMediaAndMentionRead();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void UnreadThings::preloadEnoughMentions(not_null<History*> history) {
|
||||
const auto fullCount = history->unreadMentions().count();
|
||||
const auto loadedCount = history->unreadMentions().loadedCount();
|
||||
const auto allLoaded = (fullCount >= 0) && (loadedCount >= fullCount);
|
||||
if (fullCount >= 0 && loadedCount < kPreloadIfLess && !allLoaded) {
|
||||
requestMentions(history, loadedCount);
|
||||
}
|
||||
}
|
||||
|
||||
void UnreadThings::preloadEnoughReactions(not_null<History*> history) {
|
||||
const auto fullCount = history->unreadReactions().count();
|
||||
const auto loadedCount = history->unreadReactions().loadedCount();
|
||||
const auto allLoaded = (fullCount >= 0) && (loadedCount >= fullCount);
|
||||
if (fullCount >= 0 && loadedCount < kPreloadIfLess && !allLoaded) {
|
||||
requestReactions(history, loadedCount);
|
||||
}
|
||||
}
|
||||
|
||||
void UnreadThings::requestMentions(not_null<History*> history, int loaded) {
|
||||
if (_mentionsRequests.contains(history)) {
|
||||
return;
|
||||
}
|
||||
const auto offsetId = std::max(
|
||||
history->unreadMentions().maxLoaded(),
|
||||
MsgId(1));
|
||||
const auto limit = loaded ? kNextRequestLimit : kFirstRequestLimit;
|
||||
const auto addOffset = loaded ? -(limit + 1) : -limit;
|
||||
const auto maxId = 0;
|
||||
const auto minId = 0;
|
||||
const auto requestId = _api->request(MTPmessages_GetUnreadMentions(
|
||||
history->peer->input,
|
||||
MTP_int(offsetId),
|
||||
MTP_int(addOffset),
|
||||
MTP_int(limit),
|
||||
MTP_int(maxId),
|
||||
MTP_int(minId)
|
||||
)).done([=](const MTPmessages_Messages &result) {
|
||||
_mentionsRequests.remove(history);
|
||||
history->unreadMentions().addSlice(result);
|
||||
}).fail([=] {
|
||||
_mentionsRequests.remove(history);
|
||||
}).send();
|
||||
_mentionsRequests.emplace(history, requestId);
|
||||
}
|
||||
|
||||
void UnreadThings::requestReactions(not_null<History*> history, int loaded) {
|
||||
if (_reactionsRequests.contains(history)) {
|
||||
return;
|
||||
}
|
||||
const auto offsetId = std::max(
|
||||
history->unreadMentions().maxLoaded(),
|
||||
MsgId(1));
|
||||
const auto limit = loaded ? kNextRequestLimit : kFirstRequestLimit;
|
||||
const auto addOffset = loaded ? -(limit + 1) : -limit;
|
||||
const auto maxId = 0;
|
||||
const auto minId = 0;
|
||||
const auto requestId = _api->request(MTPmessages_GetUnreadReactions(
|
||||
history->peer->input,
|
||||
MTP_int(offsetId),
|
||||
MTP_int(addOffset),
|
||||
MTP_int(limit),
|
||||
MTP_int(maxId),
|
||||
MTP_int(minId)
|
||||
)).done([=](const MTPmessages_Messages &result) {
|
||||
_reactionsRequests.remove(history);
|
||||
history->unreadReactions().addSlice(result);
|
||||
}).fail([this, history] {
|
||||
_reactionsRequests.remove(history);
|
||||
}).send();
|
||||
_reactionsRequests.emplace(history, requestId);
|
||||
}
|
||||
|
||||
} // namespace UnreadThings
|
44
Telegram/SourceFiles/api/api_unread_things.h
Normal file
44
Telegram/SourceFiles/api/api_unread_things.h
Normal file
|
@ -0,0 +1,44 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class History;
|
||||
class ApiWrap;
|
||||
class PeerData;
|
||||
class ChannelData;
|
||||
|
||||
namespace Api {
|
||||
|
||||
class UnreadThings final {
|
||||
public:
|
||||
explicit UnreadThings(not_null<ApiWrap*> api);
|
||||
|
||||
[[nodiscard]] bool trackMentions(PeerData *peer) const;
|
||||
[[nodiscard]] bool trackReactions(PeerData *peer) const;
|
||||
|
||||
void preloadEnough(History *history);
|
||||
|
||||
void mediaAndMentionsRead(
|
||||
const base::flat_set<MsgId> &readIds,
|
||||
ChannelData *channel = nullptr);
|
||||
|
||||
private:
|
||||
void preloadEnoughMentions(not_null<History*> history);
|
||||
void preloadEnoughReactions(not_null<History*> history);
|
||||
|
||||
void requestMentions(not_null<History*> history, int loaded);
|
||||
void requestReactions(not_null<History*> history, int loaded);
|
||||
|
||||
const not_null<ApiWrap*> _api;
|
||||
|
||||
base::flat_map<not_null<History*>, mtpRequestId> _mentionsRequests;
|
||||
base::flat_map<not_null<History*>, mtpRequestId> _reactionsRequests;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Api
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "api/api_chat_participants.h"
|
||||
#include "api/api_text_entities.h"
|
||||
#include "api/api_user_privacy.h"
|
||||
#include "api/api_unread_things.h"
|
||||
#include "main/main_session.h"
|
||||
#include "main/main_account.h"
|
||||
#include "mtproto/mtp_instance.h"
|
||||
|
@ -1178,25 +1179,29 @@ void Updates::applyUpdateNoPtsCheck(const MTPUpdate &update) {
|
|||
|
||||
case mtpc_updateReadMessagesContents: {
|
||||
const auto &d = update.c_updateReadMessagesContents();
|
||||
auto possiblyReadMentions = base::flat_set<MsgId>();
|
||||
auto unknownReadIds = base::flat_set<MsgId>();
|
||||
for (const auto &msgId : d.vmessages().v) {
|
||||
if (const auto item = _session->data().nonChannelMessage(msgId.v)) {
|
||||
const auto unreadForPeer = item->isUnreadMedia()
|
||||
|| item->isUnreadMention();
|
||||
const auto unreadForMe = item->hasUnreadReaction();
|
||||
if (item->isUnreadMedia() || item->isUnreadMention()) {
|
||||
item->markMediaRead();
|
||||
item->markMediaAndMentionRead();
|
||||
_session->data().requestItemRepaint(item);
|
||||
|
||||
if (item->out()
|
||||
&& item->history()->peer->isUser()
|
||||
&& !requestingDifference()) {
|
||||
item->history()->peer->asUser()->madeAction(base::unixtime::now());
|
||||
item->history()->peer->asUser()->madeAction(
|
||||
base::unixtime::now());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Perhaps it was an unread mention!
|
||||
possiblyReadMentions.insert(msgId.v);
|
||||
unknownReadIds.insert(msgId.v);
|
||||
}
|
||||
}
|
||||
session().api().checkForUnreadMentions(possiblyReadMentions);
|
||||
session().api().unreadThings().mediaAndMentionsRead(unknownReadIds);
|
||||
} break;
|
||||
|
||||
case mtpc_updateReadHistoryInbox: {
|
||||
|
@ -1565,19 +1570,21 @@ void Updates::feedUpdate(const MTPUpdate &update) {
|
|||
}
|
||||
return;
|
||||
}
|
||||
auto possiblyReadMentions = base::flat_set<MsgId>();
|
||||
auto unknownReadIds = base::flat_set<MsgId>();
|
||||
for (const auto &msgId : d.vmessages().v) {
|
||||
if (auto item = session().data().message(channel->id, msgId.v)) {
|
||||
if (item->isUnreadMedia() || item->isUnreadMention()) {
|
||||
item->markMediaRead();
|
||||
item->markMediaAndMentionRead();
|
||||
session().data().requestItemRepaint(item);
|
||||
}
|
||||
} else {
|
||||
// Perhaps it was an unread mention!
|
||||
possiblyReadMentions.insert(msgId.v);
|
||||
unknownReadIds.insert(msgId.v);
|
||||
}
|
||||
}
|
||||
session().api().checkForUnreadMentions(possiblyReadMentions, channel);
|
||||
session().api().unreadThings().mediaAndMentionsRead(
|
||||
unknownReadIds,
|
||||
channel);
|
||||
} break;
|
||||
|
||||
// Edited messages.
|
||||
|
|
|
@ -26,6 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "api/api_user_privacy.h"
|
||||
#include "api/api_views.h"
|
||||
#include "api/api_confirm_phone.h"
|
||||
#include "api/api_unread_things.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/data_drafts.h"
|
||||
#include "data/data_changes.h"
|
||||
|
@ -96,9 +97,6 @@ constexpr auto kSaveCloudDraftTimeout = 1000;
|
|||
constexpr auto kTopPromotionInterval = TimeId(60 * 60);
|
||||
constexpr auto kTopPromotionMinDelay = TimeId(10);
|
||||
constexpr auto kSmallDelayMs = 5;
|
||||
constexpr auto kUnreadMentionsPreloadIfLess = 5;
|
||||
constexpr auto kUnreadMentionsFirstRequestLimit = 10;
|
||||
constexpr auto kUnreadMentionsNextRequestLimit = 100;
|
||||
constexpr auto kSharedMediaLimit = 100;
|
||||
constexpr auto kReadFeaturedSetsTimeout = crl::time(1000);
|
||||
constexpr auto kFileLoaderQueueStopTimeout = crl::time(5000);
|
||||
|
@ -142,7 +140,8 @@ ApiWrap::ApiWrap(not_null<Main::Session*> session)
|
|||
, _confirmPhone(std::make_unique<Api::ConfirmPhone>(this))
|
||||
, _peerPhoto(std::make_unique<Api::PeerPhoto>(this))
|
||||
, _polls(std::make_unique<Api::Polls>(this))
|
||||
, _chatParticipants(std::make_unique<Api::ChatParticipants>(this)) {
|
||||
, _chatParticipants(std::make_unique<Api::ChatParticipants>(this))
|
||||
, _unreadThings(std::make_unique<Api::UnreadThings>(this)) {
|
||||
crl::on_main(session, [=] {
|
||||
// You can't use _session->lifetime() in the constructor,
|
||||
// only queued, because it is not constructed yet.
|
||||
|
@ -1287,7 +1286,7 @@ void ApiWrap::migrateFail(not_null<PeerData*> peer, const QString &error) {
|
|||
}
|
||||
}
|
||||
|
||||
void ApiWrap::markMediaRead(
|
||||
void ApiWrap::markContentsRead(
|
||||
const base::flat_set<not_null<HistoryItem*>> &items) {
|
||||
auto markedIds = QVector<MTPint>();
|
||||
auto channelMarkedIds = base::flat_map<
|
||||
|
@ -1295,12 +1294,7 @@ void ApiWrap::markMediaRead(
|
|||
QVector<MTPint>>();
|
||||
markedIds.reserve(items.size());
|
||||
for (const auto &item : items) {
|
||||
if ((!item->isUnreadMedia() || item->out())
|
||||
&& !item->isUnreadMention()) {
|
||||
continue;
|
||||
}
|
||||
item->markMediaRead();
|
||||
if (!item->isRegular()) {
|
||||
if (!item->markContentsRead() || !item->isRegular()) {
|
||||
continue;
|
||||
}
|
||||
if (const auto channel = item->history()->peer->asChannel()) {
|
||||
|
@ -1324,13 +1318,8 @@ void ApiWrap::markMediaRead(
|
|||
}
|
||||
}
|
||||
|
||||
void ApiWrap::markMediaRead(not_null<HistoryItem*> item) {
|
||||
if ((!item->isUnreadMedia() || item->out())
|
||||
&& !item->isUnreadMention()) {
|
||||
return;
|
||||
}
|
||||
item->markMediaRead();
|
||||
if (!item->isRegular()) {
|
||||
void ApiWrap::markContentsRead(not_null<HistoryItem*> item) {
|
||||
if (!item->markContentsRead() || !item->isRegular()) {
|
||||
return;
|
||||
}
|
||||
const auto ids = MTP_vector<MTPint>(1, MTP_int(item->id));
|
||||
|
@ -2910,45 +2899,6 @@ void ApiWrap::jumpToHistoryDate(not_null<PeerData*> peer, const QDate &date) {
|
|||
}
|
||||
}
|
||||
|
||||
void ApiWrap::preloadEnoughUnreadMentions(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] {
|
||||
_unreadMentionsRequests.remove(history);
|
||||
}).send();
|
||||
_unreadMentionsRequests.emplace(history, requestId);
|
||||
}
|
||||
|
||||
void ApiWrap::checkForUnreadMentions(
|
||||
const base::flat_set<MsgId> &possiblyReadMentions,
|
||||
ChannelData *channel) {
|
||||
for (const auto &msgId : possiblyReadMentions) {
|
||||
requestMessageData(channel, msgId, [=] {
|
||||
const auto item = channel
|
||||
? _session->data().message(channel->id, msgId)
|
||||
: _session->data().nonChannelMessage(msgId);
|
||||
if (item && item->mentionsMe()) {
|
||||
item->markMediaRead();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void ApiWrap::requestSharedMediaCount(
|
||||
not_null<PeerData*> peer,
|
||||
Storage::SharedMediaType type) {
|
||||
|
@ -4146,3 +4096,7 @@ Api::Polls &ApiWrap::polls() {
|
|||
Api::ChatParticipants &ApiWrap::chatParticipants() {
|
||||
return *_chatParticipants;
|
||||
}
|
||||
|
||||
Api::UnreadThings &ApiWrap::unreadThings() {
|
||||
return *_unreadThings;
|
||||
}
|
||||
|
|
|
@ -67,6 +67,7 @@ class ConfirmPhone;
|
|||
class PeerPhoto;
|
||||
class Polls;
|
||||
class ChatParticipants;
|
||||
class UnreadThings;
|
||||
|
||||
namespace details {
|
||||
|
||||
|
@ -206,8 +207,9 @@ public:
|
|||
FnMut<void(not_null<ChannelData*>)> done,
|
||||
Fn<void(const QString &)> fail = nullptr);
|
||||
|
||||
void markMediaRead(const base::flat_set<not_null<HistoryItem*>> &items);
|
||||
void markMediaRead(not_null<HistoryItem*> item);
|
||||
void markContentsRead(
|
||||
const base::flat_set<not_null<HistoryItem*>> &items);
|
||||
void markContentsRead(not_null<HistoryItem*> item);
|
||||
|
||||
void deleteAllFromParticipant(
|
||||
not_null<ChannelData*> channel,
|
||||
|
@ -250,11 +252,6 @@ public:
|
|||
|
||||
void jumpToDate(Dialogs::Key chat, const QDate &date);
|
||||
|
||||
void preloadEnoughUnreadMentions(not_null<History*> history);
|
||||
void checkForUnreadMentions(
|
||||
const base::flat_set<MsgId> &possiblyReadMentions,
|
||||
ChannelData *channel = nullptr);
|
||||
|
||||
using SliceType = Data::LoadDirection;
|
||||
void requestSharedMedia(
|
||||
not_null<PeerData*> peer,
|
||||
|
@ -356,6 +353,7 @@ public:
|
|||
[[nodiscard]] Api::PeerPhoto &peerPhoto();
|
||||
[[nodiscard]] Api::Polls &polls();
|
||||
[[nodiscard]] Api::ChatParticipants &chatParticipants();
|
||||
[[nodiscard]] Api::UnreadThings &unreadThings();
|
||||
|
||||
void updatePrivacyLastSeens();
|
||||
|
||||
|
@ -562,8 +560,6 @@ private:
|
|||
mtpRequestId _contactsRequestId = 0;
|
||||
mtpRequestId _contactsStatusesRequestId = 0;
|
||||
|
||||
base::flat_map<not_null<History*>, mtpRequestId> _unreadMentionsRequests;
|
||||
|
||||
base::flat_set<std::tuple<
|
||||
not_null<PeerData*>,
|
||||
SharedMediaType,
|
||||
|
@ -636,6 +632,7 @@ private:
|
|||
const std::unique_ptr<Api::PeerPhoto> _peerPhoto;
|
||||
const std::unique_ptr<Api::Polls> _polls;
|
||||
const std::unique_ptr<Api::ChatParticipants> _chatParticipants;
|
||||
const std::unique_ptr<Api::UnreadThings> _unreadThings;
|
||||
|
||||
mtpRequestId _wallPaperRequestId = 0;
|
||||
QString _wallPaperSlug;
|
||||
|
|
|
@ -183,7 +183,11 @@ void SetupUnreadMentionsMenu(
|
|||
}
|
||||
return base::EventFilterResult::Continue;
|
||||
});
|
||||
}
|
||||
|
||||
void SetupUnreadReactionsMenu(
|
||||
not_null<Ui::RpWidget*> button,
|
||||
Fn<PeerData*()> currentPeer) {
|
||||
}
|
||||
|
||||
} // namespace SendMenu
|
||||
|
|
|
@ -54,4 +54,8 @@ void SetupUnreadMentionsMenu(
|
|||
not_null<Ui::RpWidget*> button,
|
||||
Fn<PeerData*()> currentPeer);
|
||||
|
||||
void SetupUnreadReactionsMenu(
|
||||
not_null<Ui::RpWidget*> button,
|
||||
Fn<PeerData*()> currentPeer);
|
||||
|
||||
} // namespace SendMenu
|
||||
|
|
|
@ -116,18 +116,19 @@ struct HistoryUpdate {
|
|||
TopPromoted = (1U << 2),
|
||||
Folder = (1U << 3),
|
||||
UnreadMentions = (1U << 4),
|
||||
ClientSideMessages = (1U << 5),
|
||||
ChatOccupied = (1U << 6),
|
||||
MessageSent = (1U << 7),
|
||||
ScheduledSent = (1U << 8),
|
||||
ForwardDraft = (1U << 9),
|
||||
OutboxRead = (1U << 10),
|
||||
BotKeyboard = (1U << 11),
|
||||
CloudDraft = (1U << 12),
|
||||
LocalDraftSet = (1U << 13),
|
||||
PinnedMessages = (1U << 14),
|
||||
UnreadReactions = (1U << 5),
|
||||
ClientSideMessages = (1U << 6),
|
||||
ChatOccupied = (1U << 7),
|
||||
MessageSent = (1U << 8),
|
||||
ScheduledSent = (1U << 9),
|
||||
ForwardDraft = (1U << 10),
|
||||
OutboxRead = (1U << 11),
|
||||
BotKeyboard = (1U << 12),
|
||||
CloudDraft = (1U << 13),
|
||||
LocalDraftSet = (1U << 14),
|
||||
PinnedMessages = (1U << 15),
|
||||
|
||||
LastUsedBit = (1U << 14),
|
||||
LastUsedBit = (1U << 15),
|
||||
};
|
||||
using Flags = base::flags<Flag>;
|
||||
friend inline constexpr auto is_flag_type(Flag) { return true; }
|
||||
|
|
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_folder.h"
|
||||
#include "data/data_histories.h"
|
||||
#include "dialogs/dialogs_main_list.h"
|
||||
#include "history/history_unread_things.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "main/main_session.h"
|
||||
#include "apiwrap.h"
|
||||
|
@ -202,13 +203,13 @@ bool ChatFilter::contains(not_null<History*> history) const {
|
|||
|| ((_flags & flag)
|
||||
&& (!(_flags & Flag::NoMuted)
|
||||
|| !history->mute()
|
||||
|| (history->hasUnreadMentions()
|
||||
|| (history->unreadMentions().has()
|
||||
&& history->folderKnown()
|
||||
&& !history->folder()))
|
||||
&& (!(_flags & Flag::NoRead)
|
||||
|| history->unreadCount()
|
||||
|| history->unreadMark()
|
||||
|| history->hasUnreadMentions()
|
||||
|| history->unreadMentions().has()
|
||||
|| history->fakeUnreadWhileOpened())
|
||||
&& (!(_flags & Flag::NoArchived)
|
||||
|| (history->folderKnown() && !history->folder())))
|
||||
|
|
|
@ -1303,7 +1303,14 @@ void Session::photoLoadFail(
|
|||
void Session::markMediaRead(not_null<const DocumentData*> document) {
|
||||
const auto i = _documentItems.find(document);
|
||||
if (i != end(_documentItems)) {
|
||||
_session->api().markMediaRead({ begin(i->second), end(i->second) });
|
||||
auto items = base::flat_set<not_null<HistoryItem*>>();
|
||||
items.reserve(i->second.size());
|
||||
for (const auto &item : i->second) {
|
||||
if (item->isUnreadMention() || item->isIncomingUnreadMedia()) {
|
||||
items.emplace(item);
|
||||
}
|
||||
}
|
||||
_session->api().markContentsRead(items);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "main/main_session.h"
|
||||
#include "history/view/history_view_send_action.h"
|
||||
#include "history/view/history_view_item_preview.h"
|
||||
#include "history/history_unread_things.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_item.h"
|
||||
#include "history/history.h"
|
||||
|
@ -778,7 +779,7 @@ void RowPainter::paint(
|
|||
: QDateTime();
|
||||
}();
|
||||
const auto displayMentionBadge = history
|
||||
? history->hasUnreadMentions()
|
||||
? history->unreadMentions().has()
|
||||
: false;
|
||||
const auto displayUnreadCounter = [&] {
|
||||
if (displayMentionBadge
|
||||
|
@ -941,7 +942,7 @@ void RowPainter::paint(
|
|||
const auto unreadMuted = history->chatListMutedBadge();
|
||||
const auto mentionMuted = (history->folder() != nullptr);
|
||||
const auto displayMentionBadge = displayUnreadInfo
|
||||
&& history->hasUnreadMentions();
|
||||
&& history->unreadMentions().has();
|
||||
const auto displayUnreadCounter = (unreadCount > 0);
|
||||
const auto displayUnreadMark = !displayUnreadCounter
|
||||
&& !displayMentionBadge
|
||||
|
|
|
@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history_service.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_inner_widget.h"
|
||||
#include "history/history_unread_things.h"
|
||||
#include "dialogs/dialogs_indexed_list.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/data_drafts.h"
|
||||
|
@ -128,11 +129,11 @@ void History::popNotification(ItemNotification notification) {
|
|||
}
|
||||
|
||||
bool History::hasPendingResizedItems() const {
|
||||
return _flags & Flag::f_has_pending_resized_items;
|
||||
return _flags & Flag::HasPendingResizedItems;
|
||||
}
|
||||
|
||||
void History::setHasPendingResizedItems() {
|
||||
_flags |= Flag::f_has_pending_resized_items;
|
||||
_flags |= Flag::HasPendingResizedItems;
|
||||
}
|
||||
|
||||
void History::itemRemoved(not_null<HistoryItem*> item) {
|
||||
|
@ -691,106 +692,40 @@ not_null<HistoryItem*> History::addNewLocalMessage(
|
|||
true);
|
||||
}
|
||||
|
||||
void History::setUnreadMentionsCount(int count) {
|
||||
const auto had = _unreadMentionsCount && (*_unreadMentionsCount > 0);
|
||||
if (_unreadMentions.size() > count) {
|
||||
LOG(("API Warning: real mentions count is greater than received mentions count"));
|
||||
count = _unreadMentions.size();
|
||||
}
|
||||
_unreadMentionsCount = count;
|
||||
const auto has = (count > 0);
|
||||
if (has != had) {
|
||||
owner().chatsFilters().refreshHistory(this);
|
||||
updateChatListEntry();
|
||||
}
|
||||
void History::setUnreadThingsKnown() {
|
||||
_flags &= ~Flag::UnreadThingsKnown;
|
||||
}
|
||||
|
||||
bool History::addToUnreadMentions(
|
||||
MsgId msgId,
|
||||
UnreadMentionType type) {
|
||||
if (peer->isChannel() && !peer->isMegagroup()) {
|
||||
return false;
|
||||
}
|
||||
auto allLoaded = _unreadMentionsCount
|
||||
? (_unreadMentions.size() >= *_unreadMentionsCount)
|
||||
: false;
|
||||
if (allLoaded) {
|
||||
if (type == UnreadMentionType::New) {
|
||||
_unreadMentions.insert(msgId);
|
||||
setUnreadMentionsCount(*_unreadMentionsCount + 1);
|
||||
return true;
|
||||
}
|
||||
} else if (!_unreadMentions.empty() && type != UnreadMentionType::New) {
|
||||
_unreadMentions.insert(msgId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void History::eraseFromUnreadMentions(MsgId msgId) {
|
||||
_unreadMentions.remove(msgId);
|
||||
if (_unreadMentionsCount && *_unreadMentionsCount > 0) {
|
||||
setUnreadMentionsCount(*_unreadMentionsCount - 1);
|
||||
}
|
||||
session().changes().historyUpdated(this, UpdateFlag::UnreadMentions);
|
||||
}
|
||||
|
||||
void History::addUnreadMentionsSlice(const MTPmessages_Messages &result) {
|
||||
auto count = 0;
|
||||
auto messages = (const QVector<MTPMessage>*)nullptr;
|
||||
auto getMessages = [&](auto &list) {
|
||||
owner().processUsers(list.vusers());
|
||||
owner().processChats(list.vchats());
|
||||
return &list.vmessages().v;
|
||||
HistoryUnreadThings::Proxy History::unreadMentions() {
|
||||
return {
|
||||
this,
|
||||
_unreadThings,
|
||||
HistoryUnreadThings::Type::Mentions,
|
||||
!!(_flags & Flag::UnreadThingsKnown),
|
||||
};
|
||||
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;
|
||||
HistoryUnreadThings::ConstProxy History::unreadMentions() const {
|
||||
return {
|
||||
_unreadThings ? &_unreadThings->mentions : nullptr,
|
||||
!!(_flags & Flag::UnreadThingsKnown),
|
||||
};
|
||||
}
|
||||
|
||||
case mtpc_messages_channelMessages: {
|
||||
LOG(("API Error: unexpected messages.channelMessages! (History::addUnreadMentionsSlice)"));
|
||||
auto &d = result.c_messages_channelMessages();
|
||||
messages = getMessages(d);
|
||||
count = d.vcount().v;
|
||||
} break;
|
||||
HistoryUnreadThings::Proxy History::unreadReactions() {
|
||||
return {
|
||||
this,
|
||||
_unreadThings,
|
||||
HistoryUnreadThings::Type::Reactions,
|
||||
!!(_flags & Flag::UnreadThingsKnown),
|
||||
};
|
||||
}
|
||||
|
||||
case mtpc_messages_messagesNotModified: {
|
||||
LOG(("API Error: received messages.messagesNotModified! (History::addUnreadMentionsSlice)"));
|
||||
} break;
|
||||
|
||||
default: Unexpected("type in History::addUnreadMentionsSlice");
|
||||
}
|
||||
|
||||
auto added = false;
|
||||
if (messages) {
|
||||
const auto localFlags = MessageFlags();
|
||||
const auto type = NewMessageType::Existing;
|
||||
for (const auto &message : *messages) {
|
||||
const auto item = addNewMessage(
|
||||
IdFromMessage(message),
|
||||
message,
|
||||
localFlags,
|
||||
type);
|
||||
if (item && item->isUnreadMention()) {
|
||||
_unreadMentions.insert(item->id);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
count = _unreadMentions.size();
|
||||
}
|
||||
setUnreadMentionsCount(count);
|
||||
session().changes().historyUpdated(this, UpdateFlag::UnreadMentions);
|
||||
HistoryUnreadThings::ConstProxy History::unreadReactions() const {
|
||||
return {
|
||||
_unreadThings ? &_unreadThings->reactions : nullptr,
|
||||
!!(_flags & Flag::UnreadThingsKnown),
|
||||
};
|
||||
}
|
||||
|
||||
not_null<HistoryItem*> History::addNewToBack(
|
||||
|
@ -1368,7 +1303,7 @@ void History::addItemsToLists(
|
|||
markupSenders = &peer->asChannel()->mgInfo->markupSenders;
|
||||
}
|
||||
for (const auto &item : ranges::views::reverse(items)) {
|
||||
item->addToUnreadMentions(UnreadMentionType::Existing);
|
||||
item->addToUnreadThings(HistoryUnreadThings::AddType::Existing);
|
||||
if (item->from()->id) {
|
||||
if (lastAuthors) { // chats
|
||||
if (auto user = item->from()->asUser()) {
|
||||
|
@ -1433,7 +1368,7 @@ void History::checkAddAllToUnreadMentions() {
|
|||
for (const auto &block : blocks) {
|
||||
for (const auto &message : block->messages) {
|
||||
const auto item = message->data();
|
||||
item->addToUnreadMentions(UnreadMentionType::Existing);
|
||||
item->addToUnreadThings(HistoryUnreadThings::AddType::Existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1754,7 +1689,7 @@ void History::setFakeUnreadWhileOpened(bool enabled) {
|
|||
&& (!inChatList()
|
||||
|| (!unreadCount()
|
||||
&& !unreadMark()
|
||||
&& !hasUnreadMentions())))) {
|
||||
&& !unreadMentions().has())))) {
|
||||
return;
|
||||
}
|
||||
_fakeUnreadWhileOpened = enabled;
|
||||
|
@ -2601,7 +2536,8 @@ void History::applyDialog(
|
|||
data.vread_outbox_max_id().v);
|
||||
applyDialogTopMessage(data.vtop_message().v);
|
||||
setUnreadMark(data.is_unread_mark());
|
||||
setUnreadMentionsCount(data.vunread_mentions_count().v);
|
||||
unreadMentions().setCount(data.vunread_mentions_count().v);
|
||||
unreadReactions().setCount(data.vunread_reactions_count().v);
|
||||
if (const auto channel = peer->asChannel()) {
|
||||
if (const auto pts = data.vpts()) {
|
||||
channel->ptsReceived(pts->v);
|
||||
|
@ -2832,7 +2768,7 @@ void History::resizeToWidth(int newWidth) {
|
|||
if (!resizeAllItems && !hasPendingResizedItems()) {
|
||||
return;
|
||||
}
|
||||
_flags &= ~(Flag::f_has_pending_resized_items);
|
||||
_flags &= ~(Flag::HasPendingResizedItems);
|
||||
|
||||
_width = newWidth;
|
||||
int y = 0;
|
||||
|
@ -2845,7 +2781,7 @@ void History::resizeToWidth(int newWidth) {
|
|||
|
||||
void History::forceFullResize() {
|
||||
_width = 0;
|
||||
_flags |= Flag::f_has_pending_resized_items;
|
||||
_flags |= Flag::HasPendingResizedItems;
|
||||
}
|
||||
|
||||
not_null<History*> History::migrateToOrMe() const {
|
||||
|
|
|
@ -27,6 +27,13 @@ class HistoryService;
|
|||
struct HistoryMessageMarkupData;
|
||||
class HistoryMainElementDelegateMixin;
|
||||
|
||||
namespace HistoryUnreadThings {
|
||||
enum class AddType;
|
||||
struct All;
|
||||
class Proxy;
|
||||
class ConstProxy;
|
||||
} // namespace HistoryUnreadThings
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
@ -71,11 +78,6 @@ enum class NewMessageType {
|
|||
Existing,
|
||||
};
|
||||
|
||||
enum class UnreadMentionType {
|
||||
New, // when new message is added to history
|
||||
Existing, // when some messages slice was received
|
||||
};
|
||||
|
||||
enum class ItemNotificationType {
|
||||
Message,
|
||||
Reaction,
|
||||
|
@ -333,25 +335,11 @@ public:
|
|||
|
||||
void clearLastKeyboard();
|
||||
|
||||
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 _unreadMentionsCount ? *_unreadMentionsCount : notLoadedValue;
|
||||
}
|
||||
bool hasUnreadMentions() const {
|
||||
return (getUnreadMentionsCount() > 0);
|
||||
}
|
||||
void setUnreadMentionsCount(int count);
|
||||
bool addToUnreadMentions(MsgId msgId, UnreadMentionType type);
|
||||
void eraseFromUnreadMentions(MsgId msgId);
|
||||
void addUnreadMentionsSlice(const MTPmessages_Messages &result);
|
||||
void setUnreadThingsKnown();
|
||||
[[nodiscard]] HistoryUnreadThings::Proxy unreadMentions();
|
||||
[[nodiscard]] HistoryUnreadThings::ConstProxy unreadMentions() const;
|
||||
[[nodiscard]] HistoryUnreadThings::Proxy unreadReactions();
|
||||
[[nodiscard]] HistoryUnreadThings::ConstProxy unreadReactions() const;
|
||||
|
||||
Data::Draft *draft(Data::DraftKey key) const;
|
||||
void setDraft(Data::DraftKey key, std::unique_ptr<Data::Draft> &&draft);
|
||||
|
@ -493,7 +481,8 @@ private:
|
|||
friend class HistoryBlock;
|
||||
|
||||
enum class Flag {
|
||||
f_has_pending_resized_items = (1 << 0),
|
||||
HasPendingResizedItems = (1 << 0),
|
||||
UnreadThingsKnown = (1 << 1),
|
||||
};
|
||||
using Flags = base::flags<Flag>;
|
||||
friend inline constexpr auto is_flag_type(Flag) {
|
||||
|
@ -622,12 +611,11 @@ private:
|
|||
std::optional<MsgId> _inboxReadBefore;
|
||||
std::optional<MsgId> _outboxReadBefore;
|
||||
std::optional<int> _unreadCount;
|
||||
std::optional<int> _unreadMentionsCount;
|
||||
base::flat_set<MsgId> _unreadMentions;
|
||||
std::optional<HistoryItem*> _lastMessage;
|
||||
std::optional<HistoryItem*> _lastServerMessage;
|
||||
base::flat_set<not_null<HistoryItem*>> _clientSideMessages;
|
||||
std::unordered_set<std::unique_ptr<HistoryItem>> _messages;
|
||||
std::unique_ptr<HistoryUnreadThings::All> _unreadThings;
|
||||
|
||||
// This almost always is equal to _lastMessage. The only difference is
|
||||
// for a group that migrated to a supergroup. Then _lastMessage can
|
||||
|
|
|
@ -1019,7 +1019,8 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
if (item->hasViews()) {
|
||||
session().api().views().scheduleIncrement(item);
|
||||
}
|
||||
if (item->isUnreadMention() && !item->isUnreadMedia()) {
|
||||
if (item->isUnreadMention()
|
||||
&& !item->isUnreadMedia()) {
|
||||
readMentions.insert(item);
|
||||
_widget->enqueueMessageHighlight(view);
|
||||
}
|
||||
|
@ -1051,7 +1052,7 @@ void HistoryInner::paintEvent(QPaintEvent *e) {
|
|||
}
|
||||
|
||||
if (!readMentions.empty() && _widget->doWeReadMentions()) {
|
||||
session().api().markMediaRead(readMentions);
|
||||
session().api().markContentsRead(readMentions);
|
||||
}
|
||||
|
||||
if (mtop >= 0 || htop >= 0) {
|
||||
|
|
|
@ -12,10 +12,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/view/history_view_element.h"
|
||||
#include "history/view/history_view_item_preview.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/view/media/history_view_media_grouped.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_service.h"
|
||||
#include "history/history_message.h"
|
||||
#include "history/history_unread_things.h"
|
||||
#include "history/history.h"
|
||||
#include "mtproto/mtproto_config.h"
|
||||
#include "media/clip/media_clip_reader.h"
|
||||
|
@ -332,7 +333,11 @@ bool HistoryItem::hasUnreadMediaFlag() const {
|
|||
}
|
||||
|
||||
bool HistoryItem::isUnreadMention() const {
|
||||
return mentionsMe() && (_flags & MessageFlag::MediaIsUnread);
|
||||
return !out() && mentionsMe() && (_flags & MessageFlag::MediaIsUnread);
|
||||
}
|
||||
|
||||
bool HistoryItem::hasUnreadReaction() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool HistoryItem::mentionsMe() const {
|
||||
|
@ -356,15 +361,34 @@ bool HistoryItem::isUnreadMedia() const {
|
|||
return false;
|
||||
}
|
||||
|
||||
void HistoryItem::markMediaRead() {
|
||||
bool HistoryItem::isIncomingUnreadMedia() const {
|
||||
return !out() && isUnreadMedia();
|
||||
}
|
||||
|
||||
void HistoryItem::markMediaAndMentionRead() {
|
||||
_flags &= ~MessageFlag::MediaIsUnread;
|
||||
|
||||
if (mentionsMe()) {
|
||||
history()->updateChatListEntry();
|
||||
history()->eraseFromUnreadMentions(id);
|
||||
history()->unreadMentions().erase(id);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryItem::markReactionsRead() {
|
||||
|
||||
}
|
||||
|
||||
bool HistoryItem::markContentsRead() {
|
||||
if (hasUnreadReaction()) {
|
||||
markReactionsRead();
|
||||
return true;
|
||||
} else if (isUnreadMention() || isIncomingUnreadMedia()) {
|
||||
markMediaAndMentionRead();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void HistoryItem::setIsPinned(bool pinned) {
|
||||
const auto changed = (isPinned() != pinned);
|
||||
if (pinned) {
|
||||
|
@ -526,7 +550,7 @@ void HistoryItem::clearMainView() {
|
|||
_mainView = nullptr;
|
||||
}
|
||||
|
||||
void HistoryItem::addToUnreadMentions(UnreadMentionType type) {
|
||||
void HistoryItem::addToUnreadThings(HistoryUnreadThings::AddType type) {
|
||||
}
|
||||
|
||||
void HistoryItem::applyEditionToHistoryCleared() {
|
||||
|
@ -592,7 +616,7 @@ void HistoryItem::applySentMessage(
|
|||
|
||||
void HistoryItem::indexAsNewItem() {
|
||||
if (isRegular()) {
|
||||
addToUnreadMentions(UnreadMentionType::New);
|
||||
addToUnreadThings(HistoryUnreadThings::AddType::New);
|
||||
if (const auto types = sharedMediaTypes()) {
|
||||
_history->session().storage().add(Storage::SharedMediaAddNew(
|
||||
_history->peer->id,
|
||||
|
|
|
@ -16,7 +16,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include <any>
|
||||
|
||||
enum class UnreadMentionType;
|
||||
struct HistoryMessageReplyMarkup;
|
||||
class ReplyKeyboard;
|
||||
class HistoryMessage;
|
||||
|
@ -50,6 +49,10 @@ namespace Window {
|
|||
class SessionController;
|
||||
} // namespace Window
|
||||
|
||||
namespace HistoryUnreadThings {
|
||||
enum class AddType;
|
||||
} // namespace HistoryUnreadThings
|
||||
|
||||
namespace HistoryView {
|
||||
struct TextState;
|
||||
struct StateRequest;
|
||||
|
@ -140,9 +143,13 @@ public:
|
|||
void markClientSideAsRead();
|
||||
[[nodiscard]] bool mentionsMe() const;
|
||||
[[nodiscard]] bool isUnreadMention() const;
|
||||
[[nodiscard]] bool hasUnreadReaction() const;
|
||||
[[nodiscard]] bool isUnreadMedia() const;
|
||||
[[nodiscard]] bool isIncomingUnreadMedia() const;
|
||||
[[nodiscard]] bool hasUnreadMediaFlag() const;
|
||||
void markMediaRead();
|
||||
void markReactionsRead();
|
||||
void markMediaAndMentionRead();
|
||||
bool markContentsRead();
|
||||
void setIsPinned(bool isPinned);
|
||||
|
||||
// For edit media in history_message.
|
||||
|
@ -274,7 +281,7 @@ public:
|
|||
virtual void contributeToSlowmode(TimeId realDate = 0) {
|
||||
}
|
||||
|
||||
virtual void addToUnreadMentions(UnreadMentionType type);
|
||||
virtual void addToUnreadThings(HistoryUnreadThings::AddType type);
|
||||
virtual void destroyHistoryEntry() {
|
||||
}
|
||||
[[nodiscard]] virtual Storage::SharedMediaTypesMask sharedMediaTypes() const = 0;
|
||||
|
|
|
@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history_item_components.h"
|
||||
#include "history/history_location_manager.h"
|
||||
#include "history/history_service.h"
|
||||
#include "history/history_unread_things.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
#include "history/view/history_view_context_menu.h" // CopyPostLink.
|
||||
#include "history/view/history_view_spoiler_click_handler.h"
|
||||
|
@ -1529,19 +1530,32 @@ void HistoryMessage::contributeToSlowmode(TimeId realDate) {
|
|||
}
|
||||
}
|
||||
|
||||
void HistoryMessage::addToUnreadMentions(UnreadMentionType type) {
|
||||
if (isRegular() && isUnreadMention()) {
|
||||
if (history()->addToUnreadMentions(id, type)) {
|
||||
void HistoryMessage::addToUnreadThings(HistoryUnreadThings::AddType type) {
|
||||
if (!isRegular()) {
|
||||
return;
|
||||
}
|
||||
if (isUnreadMention()) {
|
||||
if (history()->unreadMentions().add(id, type)) {
|
||||
history()->session().changes().historyUpdated(
|
||||
history(),
|
||||
Data::HistoryUpdate::Flag::UnreadMentions);
|
||||
}
|
||||
}
|
||||
if (hasUnreadReaction()) {
|
||||
if (history()->unreadReactions().add(id, type)) {
|
||||
history()->session().changes().historyUpdated(
|
||||
history(),
|
||||
Data::HistoryUpdate::Flag::UnreadReactions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryMessage::destroyHistoryEntry() {
|
||||
if (isUnreadMention()) {
|
||||
history()->eraseFromUnreadMentions(id);
|
||||
history()->unreadMentions().erase(id);
|
||||
}
|
||||
if (hasUnreadReaction()) {
|
||||
history()->unreadReactions().erase(id);
|
||||
}
|
||||
if (const auto reply = Get<HistoryMessageReply>()) {
|
||||
changeReplyToTopCounter(reply, -1);
|
||||
|
|
|
@ -175,7 +175,7 @@ public:
|
|||
void updateForwardedInfo(const MTPMessageFwdHeader *fwd) override;
|
||||
void contributeToSlowmode(TimeId realDate = 0) override;
|
||||
|
||||
void addToUnreadMentions(UnreadMentionType type) override;
|
||||
void addToUnreadThings(HistoryUnreadThings::AddType type) override;
|
||||
void destroyHistoryEntry() override;
|
||||
[[nodiscard]] Storage::SharedMediaTypesMask sharedMediaTypes() const override;
|
||||
|
||||
|
|
190
Telegram/SourceFiles/history/history_unread_things.cpp
Normal file
190
Telegram/SourceFiles/history/history_unread_things.cpp
Normal file
|
@ -0,0 +1,190 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#include "history/history_unread_things.h"
|
||||
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_channel.h"
|
||||
#include "data/data_chat_filters.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace HistoryUnreadThings {
|
||||
|
||||
void Proxy::setCount(int count) {
|
||||
if (!_known) {
|
||||
_history->setUnreadThingsKnown();
|
||||
}
|
||||
if (!_data) {
|
||||
if (!count) {
|
||||
return;
|
||||
}
|
||||
createData();
|
||||
}
|
||||
auto &list = resolveList();
|
||||
const auto loaded = list.loadedCount();
|
||||
if (loaded > count) {
|
||||
LOG(("API Warning: "
|
||||
"real count is greater than received unread count"));
|
||||
count = loaded;
|
||||
}
|
||||
if (!count) {
|
||||
const auto &other = (_type == Type::Mentions)
|
||||
? _data->reactions
|
||||
: _data->mentions;
|
||||
if (other.count(-1) == 0) {
|
||||
_data = nullptr;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const auto had = (list.count() > 0);
|
||||
list.setCount(count);
|
||||
const auto has = (count > 0);
|
||||
if (has != had) {
|
||||
_history->owner().chatsFilters().refreshHistory(_history);
|
||||
_history->updateChatListEntry();
|
||||
}
|
||||
}
|
||||
|
||||
bool Proxy::add(MsgId msgId, AddType type) {
|
||||
const auto peer = _history->peer;
|
||||
if (peer->isChannel() && !peer->isMegagroup()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_data) {
|
||||
createData();
|
||||
}
|
||||
auto &list = resolveList();
|
||||
const auto count = list.count();
|
||||
const auto loaded = list.loadedCount();
|
||||
const auto allLoaded = (count >= 0) && (loaded >= count);
|
||||
if (allLoaded) {
|
||||
if (type == AddType::New) {
|
||||
list.insert(msgId);
|
||||
setCount(count + 1);
|
||||
return true;
|
||||
}
|
||||
} else if (loaded > 0 && type != AddType::New) {
|
||||
list.insert(msgId);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
void Proxy::erase(MsgId msgId) {
|
||||
if (!_data) {
|
||||
return;
|
||||
}
|
||||
auto &list = resolveList();
|
||||
list.erase(msgId);
|
||||
if (const auto count = list.count(); count > 0) {
|
||||
setCount(count - 1);
|
||||
}
|
||||
_history->session().changes().historyUpdated(
|
||||
_history,
|
||||
Data::HistoryUpdate::Flag::UnreadMentions);
|
||||
}
|
||||
|
||||
void Proxy::addSlice(const MTPmessages_Messages &slice) {
|
||||
auto fullCount = slice.match([&](
|
||||
const MTPDmessages_messagesNotModified &) {
|
||||
LOG(("API Error: received messages.messagesNotModified! "
|
||||
"(Proxy::addSlice)"));
|
||||
return 0;
|
||||
}, [&](const MTPDmessages_messages &data) {
|
||||
return int(data.vmessages().v.size());
|
||||
}, [&](const MTPDmessages_messagesSlice &data) {
|
||||
return data.vcount().v;
|
||||
}, [&](const MTPDmessages_channelMessages &data) {
|
||||
if (_history->peer->isChannel()) {
|
||||
_history->peer->asChannel()->ptsReceived(data.vpts().v);
|
||||
} else {
|
||||
LOG(("API Error: received messages.channelMessages when "
|
||||
"no channel was passed! (Proxy::addSlice)"));
|
||||
}
|
||||
return data.vcount().v;
|
||||
});
|
||||
|
||||
auto &owner = _history->owner();
|
||||
const auto messages = slice.match([&](
|
||||
const MTPDmessages_messagesNotModified &) {
|
||||
LOG(("API Error: received messages.messagesNotModified! "
|
||||
"(Proxy::addSlice)"));
|
||||
return QVector<MTPMessage>();
|
||||
}, [&](const auto &data) {
|
||||
owner.processUsers(data.vusers());
|
||||
owner.processChats(data.vchats());
|
||||
return data.vmessages().v;
|
||||
});
|
||||
if (messages.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_data) {
|
||||
createData();
|
||||
}
|
||||
auto added = false;
|
||||
auto &list = resolveList();
|
||||
const auto localFlags = MessageFlags();
|
||||
const auto type = NewMessageType::Existing;
|
||||
for (const auto &message : messages) {
|
||||
const auto item = _history->addNewMessage(
|
||||
IdFromMessage(message),
|
||||
message,
|
||||
localFlags,
|
||||
type);
|
||||
const auto is = [&] {
|
||||
switch (_type) {
|
||||
case Type::Mentions: return item->isUnreadMention();
|
||||
case Type::Reactions: return item->hasUnreadReaction();
|
||||
}
|
||||
Unexpected("Type in Proxy::addSlice.");
|
||||
}();
|
||||
if (is) {
|
||||
list.insert(item->id);
|
||||
added = true;
|
||||
}
|
||||
}
|
||||
if (!added) {
|
||||
fullCount = list.loadedCount();
|
||||
}
|
||||
setCount(fullCount);
|
||||
const auto flag = [&] {
|
||||
using Flag = Data::HistoryUpdate::Flag;
|
||||
switch (_type) {
|
||||
case Type::Mentions: return Flag::UnreadMentions;
|
||||
case Type::Reactions: return Flag::UnreadReactions;
|
||||
}
|
||||
Unexpected("Type in Proxy::addSlice.");
|
||||
}();
|
||||
_history->session().changes().historyUpdated(_history, flag);
|
||||
}
|
||||
|
||||
void Proxy::createData() {
|
||||
_data = std::make_unique<All>();
|
||||
if (_known) {
|
||||
_data->mentions.setCount(0);
|
||||
_data->reactions.setCount(0);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] List &Proxy::resolveList() {
|
||||
Expects(_data != nullptr);
|
||||
|
||||
switch (_type) {
|
||||
case Type::Mentions: return _data->mentions;
|
||||
case Type::Reactions: return _data->reactions;
|
||||
}
|
||||
Unexpected("Unread things type in Proxy::resolveList.");
|
||||
}
|
||||
|
||||
} // namespace HistoryUnreadThings
|
131
Telegram/SourceFiles/history/history_unread_things.h
Normal file
131
Telegram/SourceFiles/history/history_unread_things.h
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
This file is part of Telegram Desktop,
|
||||
the official desktop application for the Telegram messaging service.
|
||||
|
||||
For license and copyright information please follow this link:
|
||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
class History;
|
||||
|
||||
namespace HistoryUnreadThings {
|
||||
|
||||
enum class AddType {
|
||||
New,
|
||||
Existing,
|
||||
};
|
||||
|
||||
enum class Type {
|
||||
Mentions,
|
||||
Reactions,
|
||||
};
|
||||
|
||||
class List final {
|
||||
public:
|
||||
[[nodiscard]] int loadedCount() const {
|
||||
return _messages.size();
|
||||
}
|
||||
[[nodiscard]] MsgId minLoaded() const {
|
||||
return _messages.empty() ? 0 : _messages.front();
|
||||
}
|
||||
[[nodiscard]] MsgId maxLoaded() const {
|
||||
return _messages.empty() ? 0 : _messages.back();
|
||||
}
|
||||
[[nodiscard]] int count(int notKnownValue = -1) const {
|
||||
return _count.value_or(notKnownValue);
|
||||
}
|
||||
[[nodiscard]] bool has() const {
|
||||
return (count() > 0);
|
||||
}
|
||||
void setCount(int count) {
|
||||
_count = count;
|
||||
}
|
||||
void insert(MsgId msgId) {
|
||||
_messages.insert(msgId);
|
||||
}
|
||||
void erase(MsgId msgId) {
|
||||
_messages.remove(msgId);
|
||||
}
|
||||
|
||||
private:
|
||||
std::optional<int> _count;
|
||||
base::flat_set<MsgId> _messages;
|
||||
|
||||
};
|
||||
|
||||
struct All {
|
||||
List mentions;
|
||||
List reactions;
|
||||
};
|
||||
|
||||
class ConstProxy {
|
||||
public:
|
||||
ConstProxy(const List *list, bool known) : _list(list), _known(known) {
|
||||
}
|
||||
ConstProxy(const ConstProxy &) = delete;
|
||||
ConstProxy &operator=(const ConstProxy &) = delete;
|
||||
|
||||
[[nodiscard]] int loadedCount() const {
|
||||
return _list ? _list->loadedCount() : 0;
|
||||
}
|
||||
[[nodiscard]] MsgId minLoaded() const {
|
||||
return _list ? _list->minLoaded() : 0;
|
||||
}
|
||||
[[nodiscard]] MsgId maxLoaded() const {
|
||||
return _list ? _list->maxLoaded() : 0;
|
||||
}
|
||||
[[nodiscard]] int count(int notKnownValue = -1) const {
|
||||
return _list
|
||||
? _list->count(notKnownValue)
|
||||
: _known
|
||||
? 0
|
||||
: notKnownValue;
|
||||
}
|
||||
[[nodiscard]] bool has() const {
|
||||
return _list && _list->has();
|
||||
}
|
||||
|
||||
private:
|
||||
const List *_list = nullptr;
|
||||
const bool _known = false;
|
||||
|
||||
};
|
||||
|
||||
class Proxy final : public ConstProxy {
|
||||
public:
|
||||
Proxy(
|
||||
not_null<History*> history,
|
||||
std::unique_ptr<All> &data,
|
||||
Type type,
|
||||
bool known)
|
||||
: ConstProxy(
|
||||
(!data
|
||||
? nullptr
|
||||
: (type == Type::Mentions)
|
||||
? &data->mentions
|
||||
: &data->reactions),
|
||||
known)
|
||||
, _history(history)
|
||||
, _data(data)
|
||||
, _type(type) {
|
||||
}
|
||||
|
||||
void setCount(int count);
|
||||
bool add(MsgId msgId, AddType type);
|
||||
void erase(MsgId msgId);
|
||||
|
||||
void addSlice(const MTPmessages_Messages &slice);
|
||||
|
||||
private:
|
||||
void createData();
|
||||
[[nodiscard]] List &resolveList();
|
||||
|
||||
const not_null<History*> _history;
|
||||
std::unique_ptr<All> &_data;
|
||||
Type _type = Type::Mentions;
|
||||
bool _known = false;
|
||||
|
||||
};
|
||||
|
||||
} // namespace HistoryUnreadThings
|
|
@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "api/api_sending.h"
|
||||
#include "api/api_text_entities.h"
|
||||
#include "api/api_send_progress.h"
|
||||
#include "api/api_unread_things.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "boxes/delete_messages_box.h"
|
||||
#include "boxes/send_files_box.h"
|
||||
|
@ -73,6 +74,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "history/history_drag_area.h"
|
||||
#include "history/history_inner_widget.h"
|
||||
#include "history/history_item_components.h"
|
||||
#include "history/history_unread_things.h"
|
||||
#include "history/view/controls/history_view_voice_record_bar.h"
|
||||
#include "history/view/controls/history_view_ttl_button.h"
|
||||
#include "history/view/history_view_service_message.h"
|
||||
|
@ -215,6 +217,9 @@ HistoryWidget::HistoryWidget(
|
|||
, _unreadMentions(
|
||||
_scroll,
|
||||
controller->chatStyle()->value(lifetime(), st::historyUnreadMentions))
|
||||
, _unreadReactions(
|
||||
_scroll,
|
||||
controller->chatStyle()->value(lifetime(), st::historyUnreadReactions))
|
||||
, _fieldAutocomplete(this, controller)
|
||||
, _supportAutocomplete(session().supportMode()
|
||||
? object_ptr<Support::Autocomplete>(this, &session())
|
||||
|
@ -278,8 +283,13 @@ HistoryWidget::HistoryWidget(
|
|||
}
|
||||
}, lifetime());
|
||||
|
||||
_historyDown->addClickHandler([=] { historyDownClicked(); });
|
||||
_unreadMentions->addClickHandler([=] { showNextUnreadMention(); });
|
||||
_historyDown.widget->addClickHandler([=] { historyDownClicked(); });
|
||||
_unreadMentions.widget->addClickHandler([=] {
|
||||
showNextUnreadMention();
|
||||
});
|
||||
_unreadReactions.widget->addClickHandler([=] {
|
||||
showNextUnreadReaction();
|
||||
});
|
||||
_fieldBarCancel->addClickHandler([=] { cancelFieldAreaState(); });
|
||||
_send->addClickHandler([=] { sendButtonClicked(); });
|
||||
|
||||
|
@ -353,9 +363,13 @@ HistoryWidget::HistoryWidget(
|
|||
_scroll->updateBars();
|
||||
}, lifetime());
|
||||
|
||||
_historyDown->installEventFilter(this);
|
||||
_unreadMentions->installEventFilter(this);
|
||||
SendMenu::SetupUnreadMentionsMenu(_unreadMentions.data(), [=] {
|
||||
_historyDown.widget->installEventFilter(this);
|
||||
_unreadMentions.widget->installEventFilter(this);
|
||||
_unreadReactions.widget->installEventFilter(this);
|
||||
SendMenu::SetupUnreadMentionsMenu(_unreadMentions.widget.data(), [=] {
|
||||
return _history ? _history->peer.get() : nullptr;
|
||||
});
|
||||
SendMenu::SetupUnreadReactionsMenu(_unreadReactions.widget.data(), [=] {
|
||||
return _history ? _history->peer.get() : nullptr;
|
||||
});
|
||||
|
||||
|
@ -562,6 +576,7 @@ HistoryWidget::HistoryWidget(
|
|||
| HistoryUpdateFlag::BotKeyboard
|
||||
| HistoryUpdateFlag::CloudDraft
|
||||
| HistoryUpdateFlag::UnreadMentions
|
||||
| HistoryUpdateFlag::UnreadReactions
|
||||
| HistoryUpdateFlag::UnreadView
|
||||
| HistoryUpdateFlag::TopPromoted
|
||||
| HistoryUpdateFlag::ClientSideMessages
|
||||
|
@ -591,8 +606,9 @@ HistoryWidget::HistoryWidget(
|
|||
if (flags & HistoryUpdateFlag::ClientSideMessages) {
|
||||
updateSendButtonType();
|
||||
}
|
||||
if (flags & HistoryUpdateFlag::UnreadMentions) {
|
||||
updateUnreadMentionsVisibility();
|
||||
if ((flags & HistoryUpdateFlag::UnreadMentions)
|
||||
|| (flags & HistoryUpdateFlag::UnreadReactions)) {
|
||||
updateUnreadThingsVisibility();
|
||||
}
|
||||
if (flags & HistoryUpdateFlag::UnreadView) {
|
||||
unreadCountUpdated();
|
||||
|
@ -907,7 +923,7 @@ void HistoryWidget::initVoiceRecordBar() {
|
|||
_voiceRecordBar->lockShowStarts(
|
||||
) | rpl::start_with_next([=] {
|
||||
updateHistoryDownVisibility();
|
||||
updateUnreadMentionsVisibility();
|
||||
updateUnreadThingsVisibility();
|
||||
}, lifetime());
|
||||
|
||||
_voiceRecordBar->updateSendButtonTypeRequests(
|
||||
|
@ -2459,7 +2475,7 @@ void HistoryWidget::updateControlsVisibility() {
|
|||
_topBar->setVisible(_peer != nullptr);
|
||||
}
|
||||
updateHistoryDownVisibility();
|
||||
updateUnreadMentionsVisibility();
|
||||
updateUnreadThingsVisibility();
|
||||
if (!_history || _a_show.animating()) {
|
||||
hideChildWidgets();
|
||||
return;
|
||||
|
@ -2744,7 +2760,7 @@ void HistoryWidget::newItemAdded(not_null<HistoryItem*> item) {
|
|||
destroyUnreadBar();
|
||||
if (doWeReadServerHistory()) {
|
||||
if (item->isUnreadMention() && !item->isUnreadMedia()) {
|
||||
session().api().markMediaRead(item);
|
||||
session().api().markContentsRead(item);
|
||||
}
|
||||
session().data().histories().readInboxOnNewMessage(item);
|
||||
|
||||
|
@ -2769,7 +2785,7 @@ void HistoryWidget::unreadCountUpdated() {
|
|||
});
|
||||
} else {
|
||||
updateHistoryDownVisibility();
|
||||
_historyDown->setUnreadCount(_history->chatListUnreadCount());
|
||||
_historyDown.widget->setUnreadCount(_history->chatListUnreadCount());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3243,7 +3259,7 @@ void HistoryWidget::preloadHistoryIfNeeded() {
|
|||
}
|
||||
|
||||
updateHistoryDownVisibility();
|
||||
updateUnreadMentionsVisibility();
|
||||
updateUnreadThingsVisibility();
|
||||
if (!_scrollToAnimation.animating()) {
|
||||
preloadHistoryByScroll();
|
||||
checkReplyReturns();
|
||||
|
@ -3332,7 +3348,7 @@ void HistoryWidget::historyDownClicked() {
|
|||
}
|
||||
|
||||
void HistoryWidget::showNextUnreadMention() {
|
||||
const auto msgId = _history->getMinLoadedUnreadMention();
|
||||
const auto msgId = _history->unreadMentions().minLoaded();
|
||||
const auto already = (_showAtMsgId == msgId);
|
||||
|
||||
// Mark mention voice/video message as read.
|
||||
|
@ -3354,6 +3370,12 @@ void HistoryWidget::showNextUnreadMention() {
|
|||
showHistory(_peer->id, msgId);
|
||||
}
|
||||
|
||||
void HistoryWidget::showNextUnreadReaction() {
|
||||
const auto msgId = _history->unreadReactions().minLoaded();
|
||||
const auto already = (_showAtMsgId == msgId);
|
||||
showHistory(_peer->id, msgId);
|
||||
}
|
||||
|
||||
void HistoryWidget::saveEditMsg() {
|
||||
Expects(_history != nullptr);
|
||||
|
||||
|
@ -3698,8 +3720,7 @@ void HistoryWidget::showAnimated(
|
|||
_preserveScrollTop = true;
|
||||
show();
|
||||
_topBar->finishAnimating();
|
||||
historyDownAnimationFinish();
|
||||
unreadMentionsAnimationFinish();
|
||||
cornerButtonsAnimationFinish();
|
||||
if (_pinnedBar) {
|
||||
_pinnedBar->finishAnimating();
|
||||
}
|
||||
|
@ -3732,8 +3753,7 @@ void HistoryWidget::showAnimated(
|
|||
void HistoryWidget::animationCallback() {
|
||||
update();
|
||||
if (!_a_show.animating()) {
|
||||
historyDownAnimationFinish();
|
||||
unreadMentionsAnimationFinish();
|
||||
cornerButtonsAnimationFinish();
|
||||
if (_pinnedBar) {
|
||||
_pinnedBar->finishAnimating();
|
||||
}
|
||||
|
@ -3808,22 +3828,20 @@ void HistoryWidget::checkSuggestToGigagroup() {
|
|||
}
|
||||
|
||||
void HistoryWidget::finishAnimating() {
|
||||
if (!_a_show.animating()) return;
|
||||
if (!_a_show.animating()) {
|
||||
return;
|
||||
}
|
||||
_a_show.stop();
|
||||
_topShadow->setVisible(_peer != nullptr);
|
||||
_topBar->setVisible(_peer != nullptr);
|
||||
historyDownAnimationFinish();
|
||||
unreadMentionsAnimationFinish();
|
||||
cornerButtonsAnimationFinish();
|
||||
}
|
||||
|
||||
void HistoryWidget::historyDownAnimationFinish() {
|
||||
_historyDownShown.stop();
|
||||
updateHistoryDownPosition();
|
||||
}
|
||||
|
||||
void HistoryWidget::unreadMentionsAnimationFinish() {
|
||||
_unreadMentionsShown.stop();
|
||||
updateUnreadMentionsPosition();
|
||||
void HistoryWidget::cornerButtonsAnimationFinish() {
|
||||
_historyDown.animation.stop();
|
||||
_unreadMentions.animation.stop();
|
||||
_unreadReactions.animation.stop();
|
||||
updateCornerButtonsPositions();
|
||||
}
|
||||
|
||||
void HistoryWidget::chooseAttach() {
|
||||
|
@ -4047,7 +4065,10 @@ bool HistoryWidget::eventFilter(QObject *obj, QEvent *e) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if ((obj == _historyDown || obj == _unreadMentions) && e->type() == QEvent::Wheel) {
|
||||
if (e->type() == QEvent::Wheel
|
||||
&& (obj == _historyDown.widget
|
||||
|| obj == _unreadMentions.widget
|
||||
|| obj == _unreadReactions.widget)) {
|
||||
return _scroll->viewportEvent(e);
|
||||
}
|
||||
return TWidget::eventFilter(obj, e);
|
||||
|
@ -4946,7 +4967,7 @@ void HistoryWidget::updateControlsGeometry() {
|
|||
|
||||
updateFieldSize();
|
||||
|
||||
updateHistoryDownPosition();
|
||||
updateCornerButtonsPositions();
|
||||
|
||||
if (_membersDropdown) {
|
||||
_membersDropdown->setMaxHeight(countMembersDropdownHeightMax());
|
||||
|
@ -5156,16 +5177,7 @@ void HistoryWidget::updateHistoryGeometry(
|
|||
if (_supportAutocomplete) {
|
||||
_supportAutocomplete->setBoundings(_scroll->geometry());
|
||||
}
|
||||
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() - _unreadMentions->height() - additionalSkip - st::historyToDownPosition.y());
|
||||
}
|
||||
}
|
||||
|
||||
updateCornerButtonsPositions();
|
||||
controller()->floatPlayerAreaUpdated();
|
||||
}
|
||||
|
||||
|
@ -5470,15 +5482,71 @@ int HistoryWidget::computeMaxFieldHeight() const {
|
|||
return std::min(st::historyComposeFieldMaxHeight, available);
|
||||
}
|
||||
|
||||
void HistoryWidget::updateHistoryDownPosition() {
|
||||
// _historyDown is a child widget of _scroll, not me.
|
||||
auto top = anim::interpolate(0, _historyDown->height() + st::historyToDownPosition.y(), _historyDownShown.value(_historyDownIsShown ? 1. : 0.));
|
||||
_historyDown->moveToRight(st::historyToDownPosition.x(), _scroll->height() - top);
|
||||
auto shouldBeHidden = !_historyDownIsShown && !_historyDownShown.animating();
|
||||
if (shouldBeHidden != _historyDown->isHidden()) {
|
||||
_historyDown->setVisible(!shouldBeHidden);
|
||||
void HistoryWidget::updateCornerButtonsPositions() {
|
||||
const auto checkVisibility = [](CornerButton &button) {
|
||||
const auto shouldBeHidden = !button.shown
|
||||
&& !button.animation.animating();
|
||||
if (shouldBeHidden != button.widget->isHidden()) {
|
||||
button.widget->setVisible(!shouldBeHidden);
|
||||
}
|
||||
};
|
||||
const auto shown = [](CornerButton &button) {
|
||||
return button.animation.value(button.shown ? 1. : 0.);
|
||||
};
|
||||
|
||||
// All corner buttons is a child widgets of _scroll, not me.
|
||||
|
||||
const auto historyDownShown = shown(_historyDown);
|
||||
const auto unreadMentionsShown = shown(_unreadMentions);
|
||||
const auto unreadReactionsShown = shown(_unreadReactions);
|
||||
const auto skip = st::historyUnreadThingsSkip;
|
||||
{
|
||||
const auto top = anim::interpolate(
|
||||
0,
|
||||
_historyDown.widget->height() + st::historyToDownPosition.y(),
|
||||
historyDownShown);
|
||||
_historyDown.widget->moveToRight(
|
||||
st::historyToDownPosition.x(),
|
||||
_scroll->height() - top);
|
||||
}
|
||||
updateUnreadMentionsPosition();
|
||||
{
|
||||
const auto right = anim::interpolate(
|
||||
-_unreadMentions.widget->width(),
|
||||
st::historyToDownPosition.x(),
|
||||
unreadMentionsShown);
|
||||
const auto shift = anim::interpolate(
|
||||
0,
|
||||
_historyDown.widget->height() + skip,
|
||||
historyDownShown);
|
||||
const auto top = _scroll->height()
|
||||
- _unreadMentions.widget->height()
|
||||
- st::historyToDownPosition.y()
|
||||
- shift;
|
||||
_unreadMentions.widget->moveToRight(right, top);
|
||||
}
|
||||
{
|
||||
const auto right = anim::interpolate(
|
||||
-_unreadReactions.widget->width(),
|
||||
st::historyToDownPosition.x(),
|
||||
unreadReactionsShown);
|
||||
const auto shift = anim::interpolate(
|
||||
0,
|
||||
_historyDown.widget->height() + skip,
|
||||
historyDownShown
|
||||
) + anim::interpolate(
|
||||
0,
|
||||
_unreadMentions.widget->height() + skip,
|
||||
unreadMentionsShown);
|
||||
const auto top = _scroll->height()
|
||||
- _unreadReactions.widget->height()
|
||||
- st::historyToDownPosition.y()
|
||||
- shift;
|
||||
_unreadReactions.widget->moveToRight(right, top);
|
||||
}
|
||||
|
||||
checkVisibility(_historyDown);
|
||||
checkVisibility(_unreadMentions);
|
||||
checkVisibility(_unreadReactions);
|
||||
}
|
||||
|
||||
void HistoryWidget::updateHistoryDownVisibility() {
|
||||
|
@ -5495,7 +5563,7 @@ void HistoryWidget::updateHistoryDownVisibility() {
|
|||
const auto top = _list->itemTop(unread);
|
||||
return (top >= _scroll->scrollTop() + _scroll->height());
|
||||
};
|
||||
const auto historyDownIsVisible = [&] {
|
||||
updateCornerButtonVisibility(_historyDown, [&] {
|
||||
if (!_list || _firstLoadRequest) {
|
||||
return false;
|
||||
}
|
||||
|
@ -5514,60 +5582,69 @@ void HistoryWidget::updateHistoryDownVisibility() {
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
auto historyDownIsShown = historyDownIsVisible();
|
||||
if (_historyDownIsShown != historyDownIsShown) {
|
||||
_historyDownIsShown = historyDownIsShown;
|
||||
_historyDownShown.start([=] { updateHistoryDownPosition(); }, _historyDownIsShown ? 0. : 1., _historyDownIsShown ? 1. : 0., st::historyToDownDuration);
|
||||
}());
|
||||
}
|
||||
|
||||
void HistoryWidget::updateCornerButtonVisibility(
|
||||
CornerButton &button,
|
||||
bool shown) {
|
||||
if (button.shown != shown) {
|
||||
button.shown = shown;
|
||||
button.animation.start(
|
||||
[=] { updateCornerButtonsPositions(); },
|
||||
shown ? 0. : 1.,
|
||||
shown ? 1. : 0.,
|
||||
st::historyToDownDuration);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::updateUnreadMentionsPosition() {
|
||||
// _unreadMentions is a child widget of _scroll, not me.
|
||||
auto right = anim::interpolate(-_unreadMentions->width(), st::historyToDownPosition.x(), _unreadMentionsShown.value(_unreadMentionsIsShown ? 1. : 0.));
|
||||
auto shift = anim::interpolate(0, _historyDown->height() + st::historyUnreadMentionsSkip, _historyDownShown.value(_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::updateUnreadThingsVisibility() {
|
||||
if (_a_show.animating()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryWidget::updateUnreadMentionsVisibility() {
|
||||
if (_a_show.animating()) return;
|
||||
auto &unreadThings = session().api().unreadThings();
|
||||
unreadThings.preloadEnough(_history);
|
||||
|
||||
auto showUnreadMentions = _peer && (_peer->isChat() || _peer->isMegagroup());
|
||||
if (showUnreadMentions) {
|
||||
session().api().preloadEnoughUnreadMentions(_history);
|
||||
}
|
||||
const auto unreadMentionsIsShown = [&] {
|
||||
if (!showUnreadMentions || _firstLoadRequest) {
|
||||
return false;
|
||||
}
|
||||
if (_voiceRecordBar->isLockPresent()) {
|
||||
return false;
|
||||
}
|
||||
if (!_history->getUnreadMentionsLoadedCount()) {
|
||||
return false;
|
||||
}
|
||||
// If we have an unheard voice message with the mention
|
||||
// and our message is the last one, we can't see the status
|
||||
// (delivered/read) of this message.
|
||||
// (Except for MacBooks with the TouchPad.)
|
||||
if (_scroll->scrollTop() == _scroll->scrollTopMax()) {
|
||||
if (const auto lastMessage = _history->lastMessage()) {
|
||||
return !lastMessage->from()->isSelf();
|
||||
const auto updateWithLoadedCount = [&](CornerButton &button, int count) {
|
||||
updateCornerButtonVisibility(button, [&] {
|
||||
if (!count
|
||||
|| _firstLoadRequest
|
||||
|| _voiceRecordBar->isLockPresent()) {
|
||||
return false;
|
||||
}
|
||||
// If we have an unheard voice message with the mention
|
||||
// and our message is the last one, we can't see the status
|
||||
// (delivered/read) of this message.
|
||||
// (Except for MacBooks with the TouchPad.)
|
||||
if (_scroll->scrollTop() == _scroll->scrollTopMax()) {
|
||||
if (const auto lastMessage = _history->lastMessage()) {
|
||||
return !lastMessage->from()->isSelf();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}());
|
||||
};
|
||||
if (unreadThings.trackMentions(_peer)) {
|
||||
if (const auto count = _history->unreadMentions().count(0)) {
|
||||
_unreadMentions.widget->setUnreadCount(count);
|
||||
}
|
||||
return true;
|
||||
}();
|
||||
if (unreadMentionsIsShown) {
|
||||
_unreadMentions->setUnreadCount(_history->getUnreadMentionsCount());
|
||||
updateWithLoadedCount(
|
||||
_unreadMentions,
|
||||
_history->unreadMentions().loadedCount());
|
||||
} else {
|
||||
updateCornerButtonVisibility(_unreadMentions, false);
|
||||
}
|
||||
if (_unreadMentionsIsShown != unreadMentionsIsShown) {
|
||||
_unreadMentionsIsShown = unreadMentionsIsShown;
|
||||
_unreadMentionsShown.start([=] { updateUnreadMentionsPosition(); }, _unreadMentionsIsShown ? 0. : 1., _unreadMentionsIsShown ? 1. : 0., st::historyToDownDuration);
|
||||
|
||||
if (unreadThings.trackReactions(_peer)) {
|
||||
if (const auto count = _history->unreadReactions().count(0)) {
|
||||
_unreadReactions.widget->setUnreadCount(count);
|
||||
}
|
||||
updateWithLoadedCount(
|
||||
_unreadReactions,
|
||||
_history->unreadReactions().loadedCount());
|
||||
} else {
|
||||
updateCornerButtonVisibility(_unreadReactions, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -242,11 +242,6 @@ public:
|
|||
|
||||
void applyCloudDraft(History *history);
|
||||
|
||||
void updateHistoryDownPosition();
|
||||
void updateHistoryDownVisibility();
|
||||
void updateUnreadMentionsPosition();
|
||||
void updateUnreadMentionsVisibility();
|
||||
|
||||
void updateFieldSubmitSettings();
|
||||
|
||||
void activate();
|
||||
|
@ -332,7 +327,15 @@ private:
|
|||
};
|
||||
using TextUpdateEvents = base::flags<TextUpdateEvent>;
|
||||
friend inline constexpr bool is_flag_type(TextUpdateEvent) { return true; };
|
||||
struct CornerButton {
|
||||
template <typename ...Args>
|
||||
CornerButton(Args &&...args) : widget(std::forward<Args>(args)...) {
|
||||
}
|
||||
|
||||
Ui::Animations::Simple animation;
|
||||
bool shown = false;
|
||||
object_ptr<Ui::HistoryDownButton> widget;
|
||||
};
|
||||
void checkSuggestToGigagroup();
|
||||
|
||||
void initTabbedSelector();
|
||||
|
@ -384,6 +387,7 @@ private:
|
|||
void recountChatWidth();
|
||||
void historyDownClicked();
|
||||
void showNextUnreadMention();
|
||||
void showNextUnreadReaction();
|
||||
void handlePeerUpdate();
|
||||
void setMembersShowAreaActive(bool active);
|
||||
void handleHistoryChange(not_null<const History*> history);
|
||||
|
@ -416,11 +420,15 @@ private:
|
|||
void animationCallback();
|
||||
void updateOverStates(QPoint pos);
|
||||
void chooseAttach();
|
||||
void historyDownAnimationFinish();
|
||||
void unreadMentionsAnimationFinish();
|
||||
void cornerButtonsAnimationFinish();
|
||||
void sendButtonClicked();
|
||||
void newItemAdded(not_null<HistoryItem*> item);
|
||||
|
||||
void updateCornerButtonsPositions();
|
||||
void updateHistoryDownVisibility();
|
||||
void updateUnreadThingsVisibility();
|
||||
void updateCornerButtonVisibility(CornerButton &button, bool shown);
|
||||
|
||||
bool canSendFiles(not_null<const QMimeData*> data) const;
|
||||
bool confirmSendingFiles(
|
||||
const QStringList &files,
|
||||
|
@ -694,13 +702,9 @@ private:
|
|||
bool _synteticScrollEvent = false;
|
||||
Ui::Animations::Simple _scrollToAnimation;
|
||||
|
||||
Ui::Animations::Simple _historyDownShown;
|
||||
bool _historyDownIsShown = false;
|
||||
object_ptr<Ui::HistoryDownButton> _historyDown;
|
||||
|
||||
Ui::Animations::Simple _unreadMentionsShown;
|
||||
bool _unreadMentionsIsShown = false;
|
||||
object_ptr<Ui::HistoryDownButton> _unreadMentions;
|
||||
CornerButton _historyDown;
|
||||
CornerButton _unreadMentions;
|
||||
CornerButton _unreadReactions;
|
||||
|
||||
const object_ptr<FieldAutocomplete> _fieldAutocomplete;
|
||||
object_ptr<Support::Autocomplete> _supportAutocomplete;
|
||||
|
|
|
@ -121,7 +121,11 @@ historyUnreadMentions: TwoIconButton(historyToDown) {
|
|||
iconAbove: icon {{ "history_unread_mention", historyToDownFg, point(16px, 16px) }};
|
||||
iconAboveOver: icon {{ "history_unread_mention", historyToDownFgOver, point(16px, 16px) }};
|
||||
}
|
||||
historyUnreadMentionsSkip: 4px;
|
||||
historyUnreadReactions: TwoIconButton(historyToDown) {
|
||||
iconAbove: icon {{ "history_unread_reaction", historyToDownFg, point(16px, 16px) }};
|
||||
iconAboveOver: icon {{ "history_unread_reaction", historyToDownFgOver, point(16px, 16px) }};
|
||||
}
|
||||
historyUnreadThingsSkip: 4px;
|
||||
|
||||
membersInnerWidth: 310px;
|
||||
membersInnerHeightMax: 360px;
|
||||
|
|
|
@ -918,8 +918,8 @@ void Manager::notificationReplied(
|
|||
history->session().api().sendMessage(std::move(message));
|
||||
|
||||
const auto item = history->owner().message(history->peer, id.msgId);
|
||||
if (item && item->isUnreadMention() && !item->isUnreadMedia()) {
|
||||
history->session().api().markMediaRead(item);
|
||||
if (item && item->isUnreadMention() && !item->isIncomingUnreadMedia()) {
|
||||
history->session().api().markContentsRead(item);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue