tdesktop/Telegram/SourceFiles/data/data_folder.cpp

558 lines
14 KiB
C++
Raw Normal View History

2018-01-04 09:40:58 +00:00
/*
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
*/
2019-04-15 11:54:03 +00:00
#include "data/data_folder.h"
2018-01-04 09:40:58 +00:00
#include "data/data_session.h"
#include "data/data_channel.h"
#include "dialogs/dialogs_key.h"
#include "history/history.h"
#include "history/history_item.h"
#include "lang/lang_keys.h"
#include "storage/storage_facade.h"
2019-04-15 11:54:03 +00:00
//#include "storage/storage_feed_messages.h" // #feed
#include "auth_session.h"
2018-01-29 17:13:24 +00:00
#include "apiwrap.h"
#include "mainwidget.h"
#include "styles/style_dialogs.h" // st::dialogsArchiveUserpic
2018-01-04 09:40:58 +00:00
namespace Data {
2019-04-17 12:08:02 +00:00
namespace {
constexpr auto kLoadedChatsMinCount = 20;
} // namespace
2018-01-04 09:40:58 +00:00
2018-03-06 17:07:42 +00:00
// #feed
//MessagePosition FeedPositionFromMTP(const MTPFeedPosition &position) {
// Expects(position.type() == mtpc_feedPosition);
//
// const auto &data = position.c_feedPosition();
// return MessagePosition(data.vdate.v, FullMsgId(
// peerToChannel(peerFromMTP(data.vpeer)),
// data.vid.v));
//}
2019-04-15 11:54:03 +00:00
Folder::Folder(not_null<Data::Session*> owner, FolderId id)
: Entry(owner, this)
, _id(id)
, _chatsList(Dialogs::SortMode::Date)
, _name(lang(lng_archived_chats)) {
indexNameParts();
}
2019-04-15 11:54:03 +00:00
FolderId Folder::id() const {
return _id;
}
2019-04-15 11:54:03 +00:00
void Folder::indexNameParts() {
_nameWords.clear();
_nameFirstLetters.clear();
auto toIndexList = QStringList();
auto appendToIndex = [&](const QString &value) {
if (!value.isEmpty()) {
toIndexList.push_back(TextUtilities::RemoveAccents(value));
}
};
appendToIndex(_name);
const auto appendTranslit = !toIndexList.isEmpty()
&& cRussianLetters().match(toIndexList.front()).hasMatch();
if (appendTranslit) {
appendToIndex(translitRusEng(toIndexList.front()));
}
auto toIndex = toIndexList.join(' ');
toIndex += ' ' + rusKeyboardLayoutSwitch(toIndex);
const auto namesList = TextUtilities::PrepareSearchWords(toIndex);
for (const auto &name : namesList) {
_nameWords.insert(name);
_nameFirstLetters.insert(name[0]);
}
2018-01-04 09:40:58 +00:00
}
void Folder::registerOne(not_null<History*> history) {
if (_chatsList.contains(history)) {
return;
}
const auto invisible = _chatsList.empty();
_chatsList.addToEnd(history);
//session().storage().invalidate( // #feed
// Storage::FeedMessagesInvalidate(_id));
if (history->chatListMessageKnown()) {
if (const auto last = history->chatListMessage()) {
if (justUpdateChatListMessage(last)) {
updateChatListEntry();
}
}
} else if (chatListMessageKnown()) {
history->requestChatListMessage();
}
if (unreadCountKnown()) {
if (history->unreadCountKnown()) {
// If history unreadCount is known that means that we've
// already had the channel information and if it was in the
// feed already (not yet known) it wouldn't get here.
// That means here we get if we add a new channel to feed.
if (const auto count = history->unreadCount()) {
unreadCountChanged(count, history->mute() ? count : 0);
}
} else if (!_settingHistories) {
session().api().requestDialogEntry(this);
}
}
if (invisible && !_chatsList.empty()) {
updateChatListExistence();
//for (const auto history : _histories) { // #TODO archived
// history->updateChatListExistence();
//}
} else {
history->updateChatListExistence();
}
owner().notifyFolderUpdated(this, FolderUpdateFlag::List);
2018-01-04 09:40:58 +00:00
}
void Folder::unregisterOne(not_null<History*> history) {
if (!_chatsList.contains(history)) {
return;
}
_chatsList.del(history);
//session().storage().remove( // #feed
// Storage::FeedMessagesRemoveAll(_id, channel->bareId()));
if (chatListMessageKnown()) {
if (const auto last = chatListMessage()) {
if (last->history() == history) {
recountChatListMessage();
}
}
}
if (unreadCountKnown()) {
if (history->unreadCountKnown()) {
if (const auto delta = -history->unreadCount()) {
unreadCountChanged(delta, history->mute() ? delta : 0);
}
2018-01-29 17:13:24 +00:00
} else {
session().api().requestDialogEntry(this);
}
}
if (_chatsList.empty()) {
updateChatListExistence();
//for (const auto history : _histories) { // #TODO archive
// history->updateChatListExistence();
//}
} else {
history->updateChatListExistence();
}
owner().notifyFolderUpdated(this, FolderUpdateFlag::List);
}
not_null<Dialogs::IndexedList*> Folder::chatsList() {
return &_chatsList;
}
2019-04-15 11:54:03 +00:00
void Folder::updateChatListMessage(not_null<HistoryItem*> item) {
2019-01-15 11:57:45 +00:00
if (justUpdateChatListMessage(item)) {
if (_chatListMessage && *_chatListMessage) {
setChatListTimeId((*_chatListMessage)->date());
}
}
}
2019-04-15 11:54:03 +00:00
void Folder::loadUserpic() {
//constexpr auto kPaintUserpicsCount = 4; // #feed
//auto load = kPaintUserpicsCount;
//for (const auto history : _histories) {
2019-04-15 11:54:03 +00:00
// history->peer->loadUserpic();
// if (!--load) {
// break;
// }
//}
}
2019-04-15 11:54:03 +00:00
void Folder::paintUserpic(
Painter &p,
int x,
int y,
int size) const {
p.setPen(Qt::NoPen);
p.setBrush(st::historyPeerArchiveUserpicBg);
{
PainterHighQualityEnabler hq(p);
p.drawRoundedRect(x, y, size, size, size / 3., size / 3.);
}
st::dialogsArchiveUserpic.paintInCenter(p, { x, y, size, size });
2019-04-15 11:54:03 +00:00
//const auto small = (size - st::lineWidth) / 2; // #feed
//const auto delta = size - small;
//auto index = 0;
//for (const auto history : _histories) {
2019-04-15 11:54:03 +00:00
// history->peer->paintUserpic(p, x, y, small);
// switch (++index) {
// case 1:
// case 3: x += delta; break;
// case 2: x -= delta; y += delta; break;
// case 4: return;
// }
//}
}
bool Folder::historiesLoaded() const {
return _historiesLoaded;
2018-01-29 17:13:24 +00:00
}
void Folder::setHistoriesLoaded(bool loaded) {
if (_historiesLoaded != loaded) {
_historiesLoaded = loaded;
owner().notifyFolderUpdated(this, FolderUpdateFlag::List);
2018-02-07 16:37:05 +00:00
}
}
// // #feed
//int32 Folder::chatsHash() const {
// const auto ordered = ranges::view::all(
// _histories
// ) | ranges::view::transform([](not_null<History*> history) {
// return history->peer->bareId();
// }) | ranges::to_vector | ranges::action::sort;
// return Api::CountHash(ordered);
//}
//
//void Folder::setChats(std::vector<not_null<PeerData*>> chats) {
// const auto remove = ranges::view::all(
// _histories
// ) | ranges::view::transform([](not_null<History*> history) {
// return history->peer;
// }) | ranges::view::filter([&](not_null<PeerData*> peer) {
// return !base::contains(chats, peer);
// }) | ranges::to_vector;
//
// const auto add = ranges::view::all(
// chats
// ) | ranges::view::filter([&](not_null<PeerData*> peer) {
// return ranges::find(
// _histories,
// peer,
// [](auto history) { return history->peer; }
// ) == end(_histories);
// }) | ranges::view::transform([](PeerData *peer) {
// return not_null<PeerData*>(peer);
// }) | ranges::to_vector;
//
// changeChatsList(add, remove);
//
// setChatsLoaded(true);
//}
//
//void Folder::changeChatsList(
// const std::vector<not_null<PeerData*>> &add,
// const std::vector<not_null<PeerData*>> &remove) {
// _settingChats = true;
// const auto restore = gsl::finally([&] { _settingChats = false; });
//
// for (const auto channel : remove) {
// channel->clearFeed();
// }
//
// //// We assume the last message was correct before requesting the list.
// //// So we save it and don't allow channels from the list to change it.
// //// After that we restore it.
// const auto oldChatListMessage = base::take(_chatListMessage);
// for (const auto channel : add) {
// _chatListMessage = std::nullopt;
// channel->setFeed(this);
// }
// _chatListMessage = oldChatListMessage;
//}
2019-04-15 11:54:03 +00:00
bool Folder::justUpdateChatListMessage(not_null<HistoryItem*> item) {
2019-01-15 11:57:45 +00:00
if (!_chatListMessage) {
return false;
2019-01-15 11:57:45 +00:00
} else if (*_chatListMessage
&& item->position() <= (*_chatListMessage)->position()) {
return false;
}
2019-01-15 11:57:45 +00:00
_chatListMessage = item;
return true;
}
2019-04-15 11:54:03 +00:00
void Folder::messageRemoved(not_null<HistoryItem*> item) {
2019-01-15 11:57:45 +00:00
if (chatListMessage() == item) {
recountChatListMessage();
}
}
2019-04-15 11:54:03 +00:00
void Folder::historyCleared(not_null<History*> history) {
2019-01-15 11:57:45 +00:00
if (const auto last = chatListMessage()) {
if (last->history() == history) {
messageRemoved(last);
}
}
}
2019-04-15 11:54:03 +00:00
void Folder::requestChatListMessage() {
2019-01-15 11:57:45 +00:00
if (!chatListMessageKnown()) {
session().api().requestDialogEntry(this);
}
}
2019-04-15 11:54:03 +00:00
void Folder::recountChatListMessage() {
2019-01-15 11:57:45 +00:00
_chatListMessage = std::nullopt;
for (const auto entry : _chatsList) {
if (entry->history() && !entry->history()->chatListMessageKnown()) {
2019-01-15 11:57:45 +00:00
requestChatListMessage();
return;
}
}
2019-01-15 11:57:45 +00:00
setChatListMessageFromChannels();
}
2019-04-15 11:54:03 +00:00
void Folder::setChatListMessageFromChannels() {
2019-01-15 11:57:45 +00:00
_chatListMessage = nullptr;
for (const auto entry : _chatsList) {
if (entry->history()) {
if (const auto last = entry->history()->chatListMessage()) {
justUpdateChatListMessage(last);
}
}
}
2019-01-15 11:57:45 +00:00
updateChatListDate();
}
2019-04-15 11:54:03 +00:00
void Folder::updateChatListDate() {
2019-01-15 11:57:45 +00:00
if (_chatListMessage && *_chatListMessage) {
setChatListTimeId((*_chatListMessage)->date());
}
2018-01-04 09:40:58 +00:00
}
2019-04-15 11:54:03 +00:00
int Folder::unreadCount() const {
return _unreadCount ? *_unreadCount : 0;
}
2019-04-15 11:54:03 +00:00
rpl::producer<int> Folder::unreadCountValue() const {
2018-02-04 19:57:03 +00:00
return rpl::single(
unreadCount()
) | rpl::then(_unreadCountChanges.events());
}
2019-04-15 11:54:03 +00:00
bool Folder::unreadCountKnown() const {
return !!_unreadCount;
}
void Folder::applyDialog(const MTPDdialogFolder &data) {
//const auto addChannel = [&](ChannelId channelId) { // #feed
// if (const auto channel = owner().channelLoaded(channelId)) {
// channel->setFeed(this);
// }
//};
//for (const auto &channelId : data.vfeed_other_channels.v) {
// addChannel(channelId.v);
//}
_chatListMessage = nullptr;
if (const auto peerId = peerFromMTP(data.vpeer)) {
owner().history(peerId)->setFolder(this);
const auto fullId = FullMsgId(
peerToChannel(peerId),
data.vtop_message.v);
if (const auto item = App::histItemById(fullId)) {
justUpdateChatListMessage(item);
}
}
updateChatListDate();
setUnreadCounts(
data.vunread_unmuted_messages_count.v,
data.vunread_muted_messages_count.v);
//setUnreadMark(data.is_unread_mark());
//setUnreadMentionsCount(data.vunread_mentions_count.v);
//if (data.has_read_max_position()) { // #feed
// setUnreadPosition(FeedPositionFromMTP(data.vread_max_position));
//}
2019-04-17 12:08:02 +00:00
if (_chatsList.size() < kLoadedChatsMinCount) {
session().api().requestFolderDialogs(_id);
}
}
2019-04-15 11:54:03 +00:00
void Folder::changedInChatListHook(Dialogs::Mode list, bool added) {
2018-12-04 10:32:06 +00:00
if (list != Dialogs::Mode::All) {
return;
}
if (const auto count = unreadCount()) {
const auto mutedCount = _unreadMutedCount;
2018-12-04 10:32:06 +00:00
const auto nonMutedCount = count - mutedCount;
const auto mutedDelta = added ? mutedCount : -mutedCount;
const auto nonMutedDelta = added ? nonMutedCount : -nonMutedCount;
owner().unreadIncrement(nonMutedDelta, false);
owner().unreadIncrement(mutedDelta, true);
2018-12-04 10:32:06 +00:00
const auto fullMuted = (nonMutedCount == 0);
const auto entriesWithUnreadDelta = added ? 1 : -1;
const auto mutedEntriesWithUnreadDelta = fullMuted
? entriesWithUnreadDelta
: 0;
owner().unreadEntriesChanged(
2018-12-04 10:32:06 +00:00
entriesWithUnreadDelta,
mutedEntriesWithUnreadDelta);
}
}
2018-12-04 10:32:06 +00:00
template <typename PerformUpdate>
2019-04-15 11:54:03 +00:00
void Folder::updateUnreadCounts(PerformUpdate &&performUpdate) {
2018-12-04 10:32:06 +00:00
const auto wasUnreadCount = _unreadCount ? *_unreadCount : 0;
const auto wasUnreadMutedCount = _unreadMutedCount;
const auto wasFullMuted = (wasUnreadMutedCount > 0)
&& (wasUnreadCount == wasUnreadMutedCount);
performUpdate();
Assert(_unreadCount.has_value());
2018-02-04 19:57:03 +00:00
_unreadCountChanges.fire(unreadCount());
updateChatListEntry();
if (inChatList(Dialogs::Mode::All)) {
2018-12-04 10:32:06 +00:00
const auto nowUnreadCount = *_unreadCount;
const auto nowUnreadMutedCount = _unreadMutedCount;
const auto nowFullMuted = (nowUnreadMutedCount > 0)
&& (nowUnreadCount == nowUnreadMutedCount);
owner().unreadIncrement(
2018-12-04 10:32:06 +00:00
(nowUnreadCount - nowUnreadMutedCount)
- (wasUnreadCount - wasUnreadMutedCount),
false);
owner().unreadIncrement(
2018-12-04 10:32:06 +00:00
nowUnreadMutedCount - wasUnreadMutedCount,
true);
2018-12-04 10:32:06 +00:00
const auto entriesDelta = (nowUnreadCount && !wasUnreadCount)
? 1
: (wasUnreadCount && !nowUnreadCount)
? -1
: 0;
const auto mutedEntriesDelta = (!wasFullMuted && nowFullMuted)
? 1
: (wasFullMuted && !nowFullMuted)
? -1
: 0;
owner().unreadEntriesChanged(
2018-12-04 10:32:06 +00:00
entriesDelta,
mutedEntriesDelta);
}
2018-01-04 09:40:58 +00:00
}
2019-04-15 11:54:03 +00:00
void Folder::setUnreadCounts(int unreadNonMutedCount, int unreadMutedCount) {
2018-12-04 10:32:06 +00:00
if (unreadCountKnown()
&& (*_unreadCount == unreadNonMutedCount + unreadMutedCount)
&& (_unreadMutedCount == unreadMutedCount)) {
return;
}
updateUnreadCounts([&] {
_unreadCount = unreadNonMutedCount + unreadMutedCount;
_unreadMutedCount = unreadMutedCount;
});
}
// #feed
//void Folder::setUnreadPosition(const MessagePosition &position) {
// if (_unreadPosition.current() < position) {
// _unreadPosition = position;
// }
//}
2019-04-15 11:54:03 +00:00
void Folder::unreadCountChanged(int unreadCountDelta, int mutedCountDelta) {
if (!unreadCountKnown()) {
return;
}
2018-12-04 10:32:06 +00:00
updateUnreadCounts([&] {
accumulate_max(unreadCountDelta, -*_unreadCount);
*_unreadCount += unreadCountDelta;
mutedCountDelta = snap(
mutedCountDelta,
-_unreadMutedCount,
*_unreadCount - _unreadMutedCount);
_unreadMutedCount += mutedCountDelta;
});
}
//
//void Folder::setUnreadMark(bool unread) {
// if (_unreadMark != unread) {
// _unreadMark = unread;
// if (!_unreadCount || !*_unreadCount) {
// if (inChatList(Dialogs::Mode::All)) {
// const auto delta = _unreadMark ? 1 : -1;
// owner().unreadIncrement(delta, mute());
// owner().unreadEntriesChanged(
// delta,
// mute() ? delta : 0);
//
// updateChatListEntry();
// }
// }
// Notify::peerUpdatedDelayed(
// peer,
// Notify::PeerUpdate::Flag::UnreadViewChanged);
// }
//}
//
//bool Folder::unreadMark() const {
// return _unreadMark;
//}
// #feed
//MessagePosition Folder::unreadPosition() const {
// return _unreadPosition.current();
//}
//
//rpl::producer<MessagePosition> Folder::unreadPositionChanges() const {
// return _unreadPosition.changes();
//}
2019-04-15 11:54:03 +00:00
bool Folder::toImportant() const {
return false; // TODO feeds workmode
}
2019-04-15 11:54:03 +00:00
bool Folder::useProxyPromotion() const {
return false;
}
2019-04-15 11:54:03 +00:00
bool Folder::shouldBeInChatList() const {
return !_chatsList.empty();
}
2019-04-15 11:54:03 +00:00
int Folder::chatListUnreadCount() const {
return unreadCount();
}
2019-04-15 11:54:03 +00:00
bool Folder::chatListUnreadMark() const {
2018-06-26 18:03:45 +00:00
return false; // #feed unread mark
}
2019-04-15 11:54:03 +00:00
bool Folder::chatListMutedBadge() const {
return _unreadCount ? (*_unreadCount <= _unreadMutedCount) : false;
}
2019-04-15 11:54:03 +00:00
HistoryItem *Folder::chatListMessage() const {
2019-01-15 11:57:45 +00:00
return _chatListMessage ? *_chatListMessage : nullptr;
}
2019-04-15 11:54:03 +00:00
bool Folder::chatListMessageKnown() const {
2019-01-15 11:57:45 +00:00
return _chatListMessage.has_value();
}
2019-04-15 11:54:03 +00:00
const QString &Folder::chatListName() const {
return _name;
}
2019-04-15 11:54:03 +00:00
const base::flat_set<QString> &Folder::chatListNameWords() const {
return _nameWords;
}
2019-04-15 11:54:03 +00:00
const base::flat_set<QChar> &Folder::chatListFirstLetters() const {
return _nameFirstLetters;
}
2018-01-04 09:40:58 +00:00
} // namespace Data