2023-12-27 01:09:20 +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
|
|
|
|
*/
|
|
|
|
#include "data/data_saved_sublist.h"
|
|
|
|
|
|
|
|
#include "data/data_histories.h"
|
|
|
|
#include "data/data_peer.h"
|
|
|
|
#include "data/data_saved_messages.h"
|
|
|
|
#include "data/data_session.h"
|
|
|
|
#include "history/view/history_view_item_preview.h"
|
|
|
|
#include "history/history.h"
|
|
|
|
#include "history/history_item.h"
|
|
|
|
|
|
|
|
namespace Data {
|
|
|
|
|
|
|
|
SavedSublist::SavedSublist(not_null<PeerData*> peer)
|
|
|
|
: Entry(&peer->owner(), Dialogs::Entry::Type::SavedSublist)
|
|
|
|
, _history(peer->owner().history(peer)) {
|
|
|
|
}
|
|
|
|
|
|
|
|
SavedSublist::~SavedSublist() = default;
|
|
|
|
|
|
|
|
not_null<History*> SavedSublist::history() const {
|
|
|
|
return _history;
|
|
|
|
}
|
|
|
|
|
|
|
|
not_null<PeerData*> SavedSublist::peer() const {
|
|
|
|
return _history->peer;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SavedSublist::isHiddenAuthor() const {
|
|
|
|
return peer()->isSavedHiddenAuthor();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SavedSublist::isFullLoaded() const {
|
|
|
|
return (_flags & Flag::FullLoaded) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto SavedSublist::messages() const
|
|
|
|
-> const std::vector<not_null<HistoryItem*>> & {
|
|
|
|
return _items;
|
|
|
|
}
|
|
|
|
|
2023-12-28 13:43:00 +00:00
|
|
|
void SavedSublist::applyMaybeLast(not_null<HistoryItem*> item, bool added) {
|
2023-12-27 01:09:20 +00:00
|
|
|
const auto before = [](
|
|
|
|
not_null<HistoryItem*> a,
|
|
|
|
not_null<HistoryItem*> b) {
|
|
|
|
return IsServerMsgId(a->id)
|
|
|
|
? (IsServerMsgId(b->id) ? (a->id < b->id) : true)
|
|
|
|
: (IsServerMsgId(b->id) ? false : (a->id < b->id));
|
|
|
|
};
|
|
|
|
|
|
|
|
if (_items.empty()) {
|
|
|
|
_items.push_back(item);
|
|
|
|
} else if (_items.front() == item) {
|
|
|
|
return;
|
2023-12-28 13:43:00 +00:00
|
|
|
} else if (!isFullLoaded()
|
|
|
|
&& _items.size() == 1
|
|
|
|
&& before(_items.front(), item)) {
|
2023-12-27 01:09:20 +00:00
|
|
|
_items[0] = item;
|
|
|
|
} else if (before(_items.back(), item)) {
|
|
|
|
for (auto i = begin(_items); i != end(_items); ++i) {
|
|
|
|
if (item == *i) {
|
2023-12-28 13:43:00 +00:00
|
|
|
return;
|
2023-12-27 01:09:20 +00:00
|
|
|
} else if (before(*i, item)) {
|
|
|
|
_items.insert(i, item);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-12-28 13:43:00 +00:00
|
|
|
if (added && _fullCount) {
|
|
|
|
++*_fullCount;
|
|
|
|
}
|
2023-12-27 01:09:20 +00:00
|
|
|
if (_items.front() == item) {
|
|
|
|
setChatListTimeId(item->date());
|
|
|
|
resolveChatListMessageGroup();
|
|
|
|
}
|
2023-12-28 13:43:00 +00:00
|
|
|
_changed.fire({});
|
2023-12-27 01:09:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void SavedSublist::removeOne(not_null<HistoryItem*> item) {
|
|
|
|
if (_items.empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
const auto last = (_items.front() == item);
|
2023-12-28 13:43:00 +00:00
|
|
|
const auto from = ranges::remove(_items, item);
|
|
|
|
const auto removed = end(_items) - from;
|
|
|
|
if (removed) {
|
|
|
|
_items.erase(from, end(_items));
|
|
|
|
}
|
|
|
|
if (_fullCount) {
|
|
|
|
--*_fullCount;
|
|
|
|
}
|
2023-12-27 01:09:20 +00:00
|
|
|
if (last) {
|
|
|
|
if (_items.empty()) {
|
|
|
|
if (isFullLoaded()) {
|
|
|
|
updateChatListExistence();
|
|
|
|
} else {
|
|
|
|
updateChatListEntry();
|
|
|
|
crl::on_main(this, [=] {
|
|
|
|
owner().savedMessages().loadMore(this);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
setChatListTimeId(_items.front()->date());
|
|
|
|
}
|
|
|
|
}
|
2023-12-28 13:43:00 +00:00
|
|
|
if (removed || _fullCount) {
|
|
|
|
_changed.fire({});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rpl::producer<> SavedSublist::changes() const {
|
|
|
|
return _changed.events();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::optional<int> SavedSublist::fullCount() const {
|
|
|
|
return isFullLoaded() ? int(_items.size()) : _fullCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
rpl::producer<int> SavedSublist::fullCountValue() const {
|
|
|
|
return _changed.events_starting_with({}) | rpl::map([=] {
|
|
|
|
return fullCount();
|
|
|
|
}) | rpl::filter_optional();
|
2023-12-27 01:09:20 +00:00
|
|
|
}
|
|
|
|
|
2023-12-28 13:43:00 +00:00
|
|
|
void SavedSublist::append(
|
|
|
|
std::vector<not_null<HistoryItem*>> &&items,
|
|
|
|
int fullCount) {
|
|
|
|
_fullCount = fullCount;
|
2023-12-27 01:09:20 +00:00
|
|
|
if (items.empty()) {
|
|
|
|
setFullLoaded();
|
|
|
|
} else if (!_items.empty()) {
|
|
|
|
_items.insert(end(_items), begin(items), end(items));
|
2023-12-28 13:43:00 +00:00
|
|
|
_changed.fire({});
|
2023-12-27 01:09:20 +00:00
|
|
|
} else {
|
|
|
|
_items = std::move(items);
|
|
|
|
setChatListTimeId(_items.front()->date());
|
2023-12-28 13:43:00 +00:00
|
|
|
_changed.fire({});
|
2023-12-27 01:09:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void SavedSublist::setFullLoaded(bool loaded) {
|
|
|
|
if (loaded != isFullLoaded()) {
|
|
|
|
if (loaded) {
|
|
|
|
_flags |= Flag::FullLoaded;
|
|
|
|
if (_items.empty()) {
|
|
|
|
updateChatListExistence();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
_flags &= ~Flag::FullLoaded;
|
|
|
|
}
|
2023-12-28 13:43:00 +00:00
|
|
|
_changed.fire({});
|
2023-12-27 01:09:20 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int SavedSublist::fixedOnTopIndex() const {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SavedSublist::shouldBeInChatList() const {
|
|
|
|
return isPinnedDialog(FilterId()) || !_items.empty();
|
|
|
|
}
|
|
|
|
|
|
|
|
Dialogs::UnreadState SavedSublist::chatListUnreadState() const {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Dialogs::BadgesState SavedSublist::chatListBadgesState() const {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
HistoryItem *SavedSublist::chatListMessage() const {
|
|
|
|
return _items.empty() ? nullptr : _items.front().get();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SavedSublist::chatListMessageKnown() const {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const QString &SavedSublist::chatListName() const {
|
|
|
|
return _history->chatListName();
|
|
|
|
}
|
|
|
|
|
|
|
|
const base::flat_set<QString> &SavedSublist::chatListNameWords() const {
|
|
|
|
return _history->chatListNameWords();
|
|
|
|
}
|
|
|
|
|
|
|
|
const base::flat_set<QChar> &SavedSublist::chatListFirstLetters() const {
|
|
|
|
return _history->chatListFirstLetters();
|
|
|
|
}
|
|
|
|
|
|
|
|
const QString &SavedSublist::chatListNameSortKey() const {
|
|
|
|
return _history->chatListNameSortKey();
|
|
|
|
}
|
|
|
|
|
|
|
|
int SavedSublist::chatListNameVersion() const {
|
|
|
|
return _history->chatListNameVersion();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SavedSublist::paintUserpic(
|
|
|
|
Painter &p,
|
|
|
|
Ui::PeerUserpicView &view,
|
|
|
|
const Dialogs::Ui::PaintContext &context) const {
|
|
|
|
_history->paintUserpic(p, view, context);
|
|
|
|
}
|
|
|
|
|
|
|
|
void SavedSublist::chatListPreloadData() {
|
|
|
|
peer()->loadUserpic();
|
|
|
|
allowChatListMessageResolve();
|
|
|
|
}
|
|
|
|
|
|
|
|
void SavedSublist::allowChatListMessageResolve() {
|
|
|
|
if (_flags & Flag::ResolveChatListMessage) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
_flags |= Flag::ResolveChatListMessage;
|
|
|
|
resolveChatListMessageGroup();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool SavedSublist::hasOrphanMediaGroupPart() const {
|
|
|
|
if (isFullLoaded() || _items.size() != 1) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return (_items.front()->groupId() != MessageGroupId());
|
|
|
|
}
|
|
|
|
|
|
|
|
void SavedSublist::resolveChatListMessageGroup() {
|
|
|
|
const auto item = chatListMessage();
|
|
|
|
if (!(_flags & Flag::ResolveChatListMessage)
|
|
|
|
|| !item
|
|
|
|
|| !hasOrphanMediaGroupPart()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// If we set a single album part, request the full album.
|
|
|
|
const auto withImages = !item->toPreview({
|
|
|
|
.hideSender = true,
|
|
|
|
.hideCaption = true }).images.empty();
|
|
|
|
if (withImages) {
|
|
|
|
owner().histories().requestGroupAround(item);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace Data
|