mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +02:00
Show saved messages sublists in profile.
This commit is contained in:
parent
ead40c759e
commit
18c4d210e5
41 changed files with 1030 additions and 65 deletions
|
@ -550,6 +550,10 @@ PRIVATE
|
|||
data/data_replies_list.h
|
||||
data/data_reply_preview.cpp
|
||||
data/data_reply_preview.h
|
||||
data/data_saved_messages.cpp
|
||||
data/data_saved_messages.h
|
||||
data/data_saved_sublist.cpp
|
||||
data/data_saved_sublist.h
|
||||
data/data_search_controller.cpp
|
||||
data/data_search_controller.h
|
||||
data/data_send_action.cpp
|
||||
|
@ -897,6 +901,8 @@ PRIVATE
|
|||
info/profile/info_profile_values.h
|
||||
info/profile/info_profile_widget.cpp
|
||||
info/profile/info_profile_widget.h
|
||||
info/saved/info_saved_sublists_widget.cpp
|
||||
info/saved/info_saved_sublists_widget.h
|
||||
info/settings/info_settings_widget.cpp
|
||||
info/settings/info_settings_widget.h
|
||||
info/similar_channels/info_similar_channels_widget.cpp
|
||||
|
|
|
@ -395,6 +395,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_dlg_new_bot_name" = "Bot name";
|
||||
"lng_no_chats" = "Your chats will be here";
|
||||
"lng_no_chats_filter" = "No chats currently belong to this folder.";
|
||||
"lng_no_saved_sublists" = "You can save messages from other chats here.";
|
||||
"lng_contacts_loading" = "Loading...";
|
||||
"lng_contacts_not_found" = "No contacts found";
|
||||
"lng_topics_not_found" = "No topics found.";
|
||||
|
|
|
@ -440,6 +440,26 @@ void ApiWrap::savePinnedOrder(not_null<Data::Forum*> forum) {
|
|||
}).send();
|
||||
}
|
||||
|
||||
void ApiWrap::savePinnedOrder(not_null<Data::SavedMessages*> saved) {
|
||||
const auto &order = _session->data().pinnedChatsOrder(saved);
|
||||
const auto input = [](Dialogs::Key key) {
|
||||
if (const auto history = key.history()) {
|
||||
return MTP_inputDialogPeer(history->peer->input);
|
||||
}
|
||||
Unexpected("Key type in pinnedDialogsOrder().");
|
||||
};
|
||||
auto peers = QVector<MTPInputDialogPeer>();
|
||||
peers.reserve(order.size());
|
||||
ranges::transform(
|
||||
order,
|
||||
ranges::back_inserter(peers),
|
||||
input);
|
||||
request(MTPmessages_ReorderPinnedSavedDialogs(
|
||||
MTP_flags(MTPmessages_ReorderPinnedSavedDialogs::Flag::f_force),
|
||||
MTP_vector(peers)
|
||||
)).send();
|
||||
}
|
||||
|
||||
void ApiWrap::toggleHistoryArchived(
|
||||
not_null<History*> history,
|
||||
bool archived,
|
||||
|
|
|
@ -34,6 +34,7 @@ class Forum;
|
|||
class ForumTopic;
|
||||
class Thread;
|
||||
class Story;
|
||||
class SavedMessages;
|
||||
} // namespace Data
|
||||
|
||||
namespace InlineBots {
|
||||
|
@ -152,6 +153,7 @@ public:
|
|||
|
||||
void savePinnedOrder(Data::Folder *folder);
|
||||
void savePinnedOrder(not_null<Data::Forum*> forum);
|
||||
void savePinnedOrder(not_null<Data::SavedMessages*> saved);
|
||||
void toggleHistoryArchived(
|
||||
not_null<History*> history,
|
||||
bool archived,
|
||||
|
|
|
@ -343,12 +343,6 @@ int Folder::storiesUnreadCount() const {
|
|||
return _storiesUnreadCount;
|
||||
}
|
||||
|
||||
void Folder::requestChatListMessage() {
|
||||
if (!chatListMessageKnown()) {
|
||||
owner().histories().requestDialogEntry(this);
|
||||
}
|
||||
}
|
||||
|
||||
TimeId Folder::adjustedChatListTimeId() const {
|
||||
return chatListTimeId();
|
||||
}
|
||||
|
|
|
@ -49,9 +49,9 @@ public:
|
|||
Dialogs::BadgesState chatListBadgesState() const override;
|
||||
HistoryItem *chatListMessage() const override;
|
||||
bool chatListMessageKnown() const override;
|
||||
void requestChatListMessage() override;
|
||||
const QString &chatListName() const override;
|
||||
const QString &chatListNameSortKey() const override;
|
||||
int chatListNameVersion() const override;
|
||||
const base::flat_set<QString> &chatListNameWords() const override;
|
||||
const base::flat_set<QChar> &chatListFirstLetters() const override;
|
||||
|
||||
|
@ -82,8 +82,6 @@ public:
|
|||
private:
|
||||
void indexNameParts();
|
||||
|
||||
int chatListNameVersion() const override;
|
||||
|
||||
void reorderLastHistories();
|
||||
|
||||
void paintUserpic(
|
||||
|
|
|
@ -98,6 +98,7 @@ public:
|
|||
|
||||
void setRealRootId(MsgId realId);
|
||||
void readTillEnd();
|
||||
void requestChatListMessage();
|
||||
|
||||
void applyTopic(const MTPDforumTopic &data);
|
||||
|
||||
|
@ -109,9 +110,9 @@ public:
|
|||
Dialogs::BadgesState chatListBadgesState() const override;
|
||||
HistoryItem *chatListMessage() const override;
|
||||
bool chatListMessageKnown() const override;
|
||||
void requestChatListMessage() override;
|
||||
const QString &chatListName() const override;
|
||||
const QString &chatListNameSortKey() const override;
|
||||
int chatListNameVersion() const override;
|
||||
const base::flat_set<QString> &chatListNameWords() const override;
|
||||
const base::flat_set<QChar> &chatListFirstLetters() const override;
|
||||
|
||||
|
@ -187,8 +188,6 @@ private:
|
|||
void allowChatListMessageResolve();
|
||||
void resolveChatListMessageGroup();
|
||||
|
||||
int chatListNameVersion() const override;
|
||||
|
||||
void subscribeToUnreadChanges();
|
||||
[[nodiscard]] Dialogs::UnreadState unreadStateFor(
|
||||
int count,
|
||||
|
|
|
@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_folder.h"
|
||||
#include "data/data_forum.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_saved_messages.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_file_origin.h"
|
||||
#include "data/data_histories.h"
|
||||
|
@ -1029,6 +1030,10 @@ bool PeerData::sharedMediaInfo() const {
|
|||
return isSelf() || isRepliesChat();
|
||||
}
|
||||
|
||||
bool PeerData::savedSublistsInfo() const {
|
||||
return isSelf() && owner().savedMessages().supported();
|
||||
}
|
||||
|
||||
bool PeerData::hasStoriesHidden() const {
|
||||
if (const auto user = asUser()) {
|
||||
return user->hasStoriesHidden();
|
||||
|
|
|
@ -203,6 +203,7 @@ public:
|
|||
[[nodiscard]] bool isGigagroup() const;
|
||||
[[nodiscard]] bool isRepliesChat() const;
|
||||
[[nodiscard]] bool sharedMediaInfo() const;
|
||||
[[nodiscard]] bool savedSublistsInfo() const;
|
||||
[[nodiscard]] bool hasStoriesHidden() const;
|
||||
void setStoriesHidden(bool hidden);
|
||||
|
||||
|
|
|
@ -141,6 +141,18 @@ int PremiumLimits::topicsPinnedCurrent() const {
|
|||
return appConfigLimit("topics_pinned_limit", 5);
|
||||
}
|
||||
|
||||
int PremiumLimits::savedSublistsPinnedDefault() const {
|
||||
return appConfigLimit("saved_dialogs_pinned_limit_default", 5);
|
||||
}
|
||||
int PremiumLimits::savedSublistsPinnedPremium() const {
|
||||
return appConfigLimit("saved_dialogs_pinned_limit_premium", 100);
|
||||
}
|
||||
int PremiumLimits::savedSublistsPinnedCurrent() const {
|
||||
return isPremium()
|
||||
? savedSublistsPinnedPremium()
|
||||
: savedSublistsPinnedDefault();
|
||||
}
|
||||
|
||||
int PremiumLimits::channelsPublicDefault() const {
|
||||
return appConfigLimit("channels_public_limit_default", 10);
|
||||
}
|
||||
|
|
|
@ -59,6 +59,10 @@ public:
|
|||
|
||||
[[nodiscard]] int topicsPinnedCurrent() const;
|
||||
|
||||
[[nodiscard]] int savedSublistsPinnedDefault() const;
|
||||
[[nodiscard]] int savedSublistsPinnedPremium() const;
|
||||
[[nodiscard]] int savedSublistsPinnedCurrent() const;
|
||||
|
||||
[[nodiscard]] int channelsPublicDefault() const;
|
||||
[[nodiscard]] int channelsPublicPremium() const;
|
||||
[[nodiscard]] int channelsPublicCurrent() const;
|
||||
|
|
181
Telegram/SourceFiles/data/data_saved_messages.cpp
Normal file
181
Telegram/SourceFiles/data/data_saved_messages.cpp
Normal file
|
@ -0,0 +1,181 @@
|
|||
/*
|
||||
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_messages.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "data/data_peer.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/data_session.h"
|
||||
#include "history/history.h"
|
||||
#include "history/history_item.h"
|
||||
#include "main/main_session.h"
|
||||
|
||||
namespace Data {
|
||||
namespace {
|
||||
|
||||
constexpr auto kPerPage = 50;
|
||||
constexpr auto kFirstPerPage = 10;
|
||||
|
||||
} // namespace
|
||||
|
||||
SavedMessages::SavedMessages(not_null<Session*> owner)
|
||||
: _owner(owner)
|
||||
, _chatsList(
|
||||
&owner->session(),
|
||||
FilterId(),
|
||||
owner->maxPinnedChatsLimitValue(this)) {
|
||||
}
|
||||
|
||||
SavedMessages::~SavedMessages() = default;
|
||||
|
||||
bool SavedMessages::supported() const {
|
||||
return !_unsupported;
|
||||
}
|
||||
|
||||
Session &SavedMessages::owner() const {
|
||||
return *_owner;
|
||||
}
|
||||
|
||||
Main::Session &SavedMessages::session() const {
|
||||
return _owner->session();
|
||||
}
|
||||
|
||||
not_null<Dialogs::MainList*> SavedMessages::chatsList() {
|
||||
return &_chatsList;
|
||||
}
|
||||
|
||||
not_null<SavedSublist*> SavedMessages::sublist(not_null<PeerData*> peer) {
|
||||
const auto i = _sublists.find(peer);
|
||||
if (i != end(_sublists)) {
|
||||
return i->second.get();
|
||||
}
|
||||
return _sublists.emplace(
|
||||
peer,
|
||||
std::make_unique<SavedSublist>(peer)).first->second.get();
|
||||
}
|
||||
|
||||
void SavedMessages::loadMore() {
|
||||
if (_loadMoreRequestId || _chatsList.loaded()) {
|
||||
return;
|
||||
}
|
||||
_loadMoreRequestId = _owner->session().api().request(
|
||||
MTPmessages_GetSavedDialogs(
|
||||
MTP_flags(0),
|
||||
MTP_int(_offsetDate),
|
||||
MTP_int(_offsetId),
|
||||
_offsetPeer ? _offsetPeer->input : MTP_inputPeerEmpty(),
|
||||
MTP_int(kPerPage),
|
||||
MTP_long(0)) // hash
|
||||
).done([=](const MTPmessages_SavedDialogs &result) {
|
||||
auto list = (const QVector<MTPSavedDialog>*)nullptr;
|
||||
result.match([](const MTPDmessages_savedDialogsNotModified &) {
|
||||
LOG(("API Error: messages.savedDialogsNotModified."));
|
||||
}, [&](const auto &data) {
|
||||
_owner->processUsers(data.vusers());
|
||||
_owner->processChats(data.vchats());
|
||||
_owner->processMessages(
|
||||
data.vmessages(),
|
||||
NewMessageType::Existing);
|
||||
list = &data.vdialogs().v;
|
||||
});
|
||||
_loadMoreRequestId = 0;
|
||||
if (!list) {
|
||||
_chatsList.setLoaded();
|
||||
return;
|
||||
}
|
||||
auto lastValid = false;
|
||||
const auto selfId = _owner->session().userPeerId();
|
||||
for (const auto &dialog : *list) {
|
||||
const auto &data = dialog.data();
|
||||
const auto peer = _owner->peer(peerFromMTP(data.vpeer()));
|
||||
const auto topId = MsgId(data.vtop_message().v);
|
||||
if (const auto item = _owner->message(selfId, topId)) {
|
||||
_offsetPeer = peer;
|
||||
_offsetDate = item->date();
|
||||
_offsetId = topId;
|
||||
lastValid = true;
|
||||
sublist(peer)->applyMaybeLast(item);
|
||||
} else {
|
||||
lastValid = false;
|
||||
}
|
||||
}
|
||||
if (!lastValid) {
|
||||
LOG(("API Error: Unknown message in the end of a slice."));
|
||||
_chatsList.setLoaded();
|
||||
} else if (result.type() == mtpc_messages_savedDialogs) {
|
||||
_chatsList.setLoaded();
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (error.type() == u"SAVED_DIALOGS_UNSUPPORTED"_q) {
|
||||
_unsupported = true;
|
||||
}
|
||||
_chatsList.setLoaded();
|
||||
_loadMoreRequestId = 0;
|
||||
}).send();
|
||||
}
|
||||
|
||||
void SavedMessages::loadMore(not_null<SavedSublist*> sublist) {
|
||||
if (_loadMoreRequests.contains(sublist) || sublist->isFullLoaded()) {
|
||||
return;
|
||||
}
|
||||
const auto &list = sublist->messages();
|
||||
const auto offsetId = list.empty() ? MsgId(0) : list.back()->id;
|
||||
const auto offsetDate = list.empty() ? MsgId(0) : list.back()->date();
|
||||
const auto limit = offsetId ? kPerPage : kFirstPerPage;
|
||||
const auto requestId = _owner->session().api().request(
|
||||
MTPmessages_GetSavedHistory(
|
||||
sublist->peer()->input,
|
||||
MTP_int(offsetId),
|
||||
MTP_int(offsetDate),
|
||||
MTP_int(0), // add_offset
|
||||
MTP_int(limit),
|
||||
MTP_int(0), // max_id
|
||||
MTP_int(0), // min_id
|
||||
MTP_long(0)) // hash
|
||||
).done([=](const MTPmessages_Messages &result) {
|
||||
auto list = (const QVector<MTPMessage>*)nullptr;
|
||||
result.match([](const MTPDmessages_channelMessages &) {
|
||||
LOG(("API Error: messages.channelMessages in sublist."));
|
||||
}, [](const MTPDmessages_messagesNotModified &) {
|
||||
LOG(("API Error: messages.messagesNotModified in sublist."));
|
||||
}, [&](const auto &data) {
|
||||
owner().processUsers(data.vusers());
|
||||
owner().processChats(data.vchats());
|
||||
list = &data.vmessages().v;
|
||||
});
|
||||
|
||||
_loadMoreRequests.remove(sublist);
|
||||
if (!list) {
|
||||
sublist->setFullLoaded();
|
||||
return;
|
||||
}
|
||||
auto items = std::vector<not_null<HistoryItem*>>();
|
||||
items.reserve(list->size());
|
||||
for (const auto &message : *list) {
|
||||
const auto item = owner().addNewMessage(
|
||||
message,
|
||||
{},
|
||||
NewMessageType::Existing);
|
||||
if (item) {
|
||||
items.push_back(item);
|
||||
}
|
||||
}
|
||||
sublist->append(std::move(items));
|
||||
if (result.type() == mtpc_messages_messages) {
|
||||
sublist->setFullLoaded();
|
||||
}
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
if (error.type() == u"SAVED_DIALOGS_UNSUPPORTED"_q) {
|
||||
_unsupported = true;
|
||||
}
|
||||
sublist->setFullLoaded();
|
||||
_loadMoreRequests.remove(sublist);
|
||||
}).send();
|
||||
}
|
||||
|
||||
} // namespace Data
|
56
Telegram/SourceFiles/data/data_saved_messages.h
Normal file
56
Telegram/SourceFiles/data/data_saved_messages.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "dialogs/dialogs_main_list.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Data {
|
||||
|
||||
class Session;
|
||||
class SavedSublist;
|
||||
|
||||
class SavedMessages final {
|
||||
public:
|
||||
explicit SavedMessages(not_null<Session*> owner);
|
||||
~SavedMessages();
|
||||
|
||||
[[nodiscard]] bool supported() const;
|
||||
|
||||
[[nodiscard]] Session &owner() const;
|
||||
[[nodiscard]] Main::Session &session() const;
|
||||
|
||||
[[nodiscard]] not_null<Dialogs::MainList*> chatsList();
|
||||
[[nodiscard]] not_null<SavedSublist*> sublist(not_null<PeerData*> peer);
|
||||
|
||||
void loadMore();
|
||||
void loadMore(not_null<SavedSublist*> sublist);
|
||||
|
||||
private:
|
||||
const not_null<Session*> _owner;
|
||||
|
||||
Dialogs::MainList _chatsList;
|
||||
base::flat_map<
|
||||
not_null<PeerData*>,
|
||||
std::unique_ptr<SavedSublist>> _sublists;
|
||||
|
||||
base::flat_map<not_null<SavedSublist*>, mtpRequestId> _loadMoreRequests;
|
||||
mtpRequestId _loadMoreRequestId = 0;
|
||||
|
||||
TimeId _offsetDate = 0;
|
||||
MsgId _offsetId = 0;
|
||||
PeerData *_offsetPeer = nullptr;
|
||||
|
||||
bool _unsupported = false;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Data
|
212
Telegram/SourceFiles/data/data_saved_sublist.cpp
Normal file
212
Telegram/SourceFiles/data/data_saved_sublist.cpp
Normal file
|
@ -0,0 +1,212 @@
|
|||
/*
|
||||
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;
|
||||
}
|
||||
|
||||
void SavedSublist::applyMaybeLast(not_null<HistoryItem*> item) {
|
||||
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;
|
||||
} else if (_items.size() == 1 && before(_items.front(), item)) {
|
||||
_items[0] = item;
|
||||
} else if (before(_items.back(), item)) {
|
||||
for (auto i = begin(_items); i != end(_items); ++i) {
|
||||
if (item == *i) {
|
||||
break;
|
||||
} else if (before(*i, item)) {
|
||||
_items.insert(i, item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (_items.front() == item) {
|
||||
setChatListTimeId(item->date());
|
||||
resolveChatListMessageGroup();
|
||||
}
|
||||
}
|
||||
|
||||
void SavedSublist::removeOne(not_null<HistoryItem*> item) {
|
||||
if (_items.empty()) {
|
||||
return;
|
||||
}
|
||||
const auto last = (_items.front() == item);
|
||||
_items.erase(ranges::remove(_items, item), end(_items));
|
||||
if (last) {
|
||||
if (_items.empty()) {
|
||||
if (isFullLoaded()) {
|
||||
updateChatListExistence();
|
||||
} else {
|
||||
updateChatListEntry();
|
||||
crl::on_main(this, [=] {
|
||||
owner().savedMessages().loadMore(this);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setChatListTimeId(_items.front()->date());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SavedSublist::append(std::vector<not_null<HistoryItem*>> &&items) {
|
||||
if (items.empty()) {
|
||||
setFullLoaded();
|
||||
} else if (!_items.empty()) {
|
||||
_items.insert(end(_items), begin(items), end(items));
|
||||
} else {
|
||||
_items = std::move(items);
|
||||
setChatListTimeId(_items.front()->date());
|
||||
}
|
||||
}
|
||||
|
||||
void SavedSublist::setFullLoaded(bool loaded) {
|
||||
if (loaded != isFullLoaded()) {
|
||||
if (loaded) {
|
||||
_flags |= Flag::FullLoaded;
|
||||
if (_items.empty()) {
|
||||
updateChatListExistence();
|
||||
}
|
||||
} else {
|
||||
_flags &= ~Flag::FullLoaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
79
Telegram/SourceFiles/data/data_saved_sublist.h
Normal file
79
Telegram/SourceFiles/data/data_saved_sublist.h
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "dialogs/ui/dialogs_message_view.h"
|
||||
#include "dialogs/dialogs_entry.h"
|
||||
|
||||
class PeerData;
|
||||
class History;
|
||||
|
||||
namespace Data {
|
||||
|
||||
class Session;
|
||||
|
||||
class SavedSublist final : public Dialogs::Entry {
|
||||
public:
|
||||
explicit SavedSublist(not_null<PeerData*> peer);
|
||||
~SavedSublist();
|
||||
|
||||
[[nodiscard]] not_null<History*> history() const;
|
||||
[[nodiscard]] not_null<PeerData*> peer() const;
|
||||
[[nodiscard]] bool isHiddenAuthor() const;
|
||||
[[nodiscard]] bool isFullLoaded() const;
|
||||
|
||||
[[nodiscard]] auto messages() const
|
||||
-> const std::vector<not_null<HistoryItem*>> &;
|
||||
void applyMaybeLast(not_null<HistoryItem*> item);
|
||||
void removeOne(not_null<HistoryItem*> item);
|
||||
void append(std::vector<not_null<HistoryItem*>> &&items);
|
||||
void setFullLoaded(bool loaded = true);
|
||||
|
||||
[[nodiscard]] Dialogs::Ui::MessageView &lastItemDialogsView() {
|
||||
return _lastItemDialogsView;
|
||||
}
|
||||
|
||||
int fixedOnTopIndex() const override;
|
||||
bool shouldBeInChatList() const override;
|
||||
Dialogs::UnreadState chatListUnreadState() const override;
|
||||
Dialogs::BadgesState chatListBadgesState() const override;
|
||||
HistoryItem *chatListMessage() const override;
|
||||
bool chatListMessageKnown() const override;
|
||||
const QString &chatListName() const override;
|
||||
const QString &chatListNameSortKey() const override;
|
||||
int chatListNameVersion() const override;
|
||||
const base::flat_set<QString> &chatListNameWords() const override;
|
||||
const base::flat_set<QChar> &chatListFirstLetters() const override;
|
||||
|
||||
void chatListPreloadData() override;
|
||||
void paintUserpic(
|
||||
Painter &p,
|
||||
Ui::PeerUserpicView &view,
|
||||
const Dialogs::Ui::PaintContext &context) const override;
|
||||
|
||||
private:
|
||||
enum class Flag : uchar {
|
||||
ResolveChatListMessage = (1 << 0),
|
||||
FullLoaded = (1 << 1),
|
||||
};
|
||||
friend inline constexpr bool is_flag_type(Flag) { return true; }
|
||||
using Flags = base::flags<Flag>;
|
||||
|
||||
bool hasOrphanMediaGroupPart() const;
|
||||
void allowChatListMessageResolve();
|
||||
void resolveChatListMessageGroup();
|
||||
|
||||
const not_null<History*> _history;
|
||||
|
||||
std::vector<not_null<HistoryItem*>> _items;
|
||||
Dialogs::Ui::MessageView _lastItemDialogsView;
|
||||
Flags _flags;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Data
|
|
@ -60,6 +60,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_emoji_statuses.h"
|
||||
#include "data/data_forum_icons.h"
|
||||
#include "data/data_cloud_themes.h"
|
||||
#include "data/data_saved_messages.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/data_stories.h"
|
||||
#include "data/data_streaming.h"
|
||||
#include "data/data_media_rotation.h"
|
||||
|
@ -261,7 +263,8 @@ Session::Session(not_null<Main::Session*> session)
|
|||
, _forumIcons(std::make_unique<ForumIcons>(this))
|
||||
, _notifySettings(std::make_unique<NotifySettings>(this))
|
||||
, _customEmojiManager(std::make_unique<CustomEmojiManager>(this))
|
||||
, _stories(std::make_unique<Stories>(this)) {
|
||||
, _stories(std::make_unique<Stories>(this))
|
||||
, _savedMessages(std::make_unique<SavedMessages>(this)) {
|
||||
_cache->open(_session->local().cacheKey());
|
||||
_bigFileCache->open(_session->local().cacheBigFileKey());
|
||||
|
||||
|
@ -1712,6 +1715,11 @@ void Session::requestItemRepaint(not_null<const HistoryItem*> item) {
|
|||
topic->updateChatListEntry();
|
||||
}
|
||||
}
|
||||
if (const auto sublist = item->savedSublist()) {
|
||||
if (sublist->lastItemDialogsView().dependsOn(item)) {
|
||||
sublist->updateChatListEntry();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<not_null<const HistoryItem*>> Session::itemRepaintRequest() const {
|
||||
|
@ -2137,6 +2145,11 @@ int Session::pinnedChatsLimit(not_null<Data::Forum*> forum) const {
|
|||
return limits.topicsPinnedCurrent();
|
||||
}
|
||||
|
||||
int Session::pinnedChatsLimit(not_null<Data::SavedMessages*> saved) const {
|
||||
const auto limits = Data::PremiumLimits(_session);
|
||||
return limits.savedSublistsPinnedCurrent();
|
||||
}
|
||||
|
||||
rpl::producer<int> Session::maxPinnedChatsLimitValue(
|
||||
Data::Folder *folder) const {
|
||||
// Premium limit from appconfig.
|
||||
|
@ -2177,6 +2190,20 @@ rpl::producer<int> Session::maxPinnedChatsLimitValue(
|
|||
});
|
||||
}
|
||||
|
||||
rpl::producer<int> Session::maxPinnedChatsLimitValue(
|
||||
not_null<SavedMessages*> saved) const {
|
||||
// Premium limit from appconfig.
|
||||
// We always use premium limit in the MainList limit producer,
|
||||
// because it slices the list to that limit. We don't want to slice
|
||||
// premium-ly added chats from the pinned list because of sync issues.
|
||||
return rpl::single(rpl::empty_value()) | rpl::then(
|
||||
_session->account().appConfig().refreshed()
|
||||
) | rpl::map([=] {
|
||||
const auto limits = Data::PremiumLimits(_session);
|
||||
return limits.savedSublistsPinnedPremium();
|
||||
});
|
||||
}
|
||||
|
||||
const std::vector<Dialogs::Key> &Session::pinnedChatsOrder(
|
||||
Data::Folder *folder) const {
|
||||
return chatsList(folder)->pinned()->order();
|
||||
|
@ -2192,6 +2219,11 @@ const std::vector<Dialogs::Key> &Session::pinnedChatsOrder(
|
|||
return forum->topicsList()->pinned()->order();
|
||||
}
|
||||
|
||||
const std::vector<Dialogs::Key> &Session::pinnedChatsOrder(
|
||||
not_null<Data::SavedMessages*> saved) const {
|
||||
return saved->chatsList()->pinned()->order();
|
||||
}
|
||||
|
||||
void Session::clearPinnedChats(Data::Folder *folder) {
|
||||
chatsList(folder)->pinned()->clear();
|
||||
}
|
||||
|
@ -4198,6 +4230,8 @@ not_null<Dialogs::MainList*> Session::chatsListFor(
|
|||
const auto topic = entry->asTopic();
|
||||
return topic
|
||||
? topic->forum()->topicsList()
|
||||
: entry->asSublist()
|
||||
? _savedMessages->chatsList()
|
||||
: chatsList(entry->folder());
|
||||
}
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ class GroupCall;
|
|||
class NotifySettings;
|
||||
class CustomEmojiManager;
|
||||
class Stories;
|
||||
class SavedMessages;
|
||||
|
||||
struct RepliesReadTillUpdate {
|
||||
FullMsgId id;
|
||||
|
@ -137,6 +138,9 @@ public:
|
|||
[[nodiscard]] Stories &stories() const {
|
||||
return *_stories;
|
||||
}
|
||||
[[nodiscard]] SavedMessages &savedMessages() const {
|
||||
return *_savedMessages;
|
||||
}
|
||||
|
||||
[[nodiscard]] MsgId nextNonHistoryEntryId() {
|
||||
return ++_nonHistoryEntryId;
|
||||
|
@ -352,18 +356,24 @@ public:
|
|||
[[nodiscard]] int pinnedChatsLimit(Folder *folder) const;
|
||||
[[nodiscard]] int pinnedChatsLimit(FilterId filterId) const;
|
||||
[[nodiscard]] int pinnedChatsLimit(not_null<Forum*> forum) const;
|
||||
[[nodiscard]] int pinnedChatsLimit(
|
||||
not_null<SavedMessages*> saved) const;
|
||||
[[nodiscard]] rpl::producer<int> maxPinnedChatsLimitValue(
|
||||
Folder *folder) const;
|
||||
[[nodiscard]] rpl::producer<int> maxPinnedChatsLimitValue(
|
||||
FilterId filterId) const;
|
||||
[[nodiscard]] rpl::producer<int> maxPinnedChatsLimitValue(
|
||||
not_null<Forum*> forum) const;
|
||||
[[nodiscard]] rpl::producer<int> maxPinnedChatsLimitValue(
|
||||
not_null<SavedMessages*> saved) const;
|
||||
[[nodiscard]] const std::vector<Dialogs::Key> &pinnedChatsOrder(
|
||||
Folder *folder) const;
|
||||
[[nodiscard]] const std::vector<Dialogs::Key> &pinnedChatsOrder(
|
||||
not_null<Forum*> forum) const;
|
||||
[[nodiscard]] const std::vector<Dialogs::Key> &pinnedChatsOrder(
|
||||
FilterId filterId) const;
|
||||
[[nodiscard]] const std::vector<Dialogs::Key> &pinnedChatsOrder(
|
||||
not_null<Data::SavedMessages*> saved) const;
|
||||
void setChatPinned(Dialogs::Key key, FilterId filterId, bool pinned);
|
||||
void setPinnedFromEntryList(Dialogs::Key key, bool pinned);
|
||||
void clearPinnedChats(Folder *folder);
|
||||
|
@ -1041,6 +1051,7 @@ private:
|
|||
const std::unique_ptr<NotifySettings> _notifySettings;
|
||||
const std::unique_ptr<CustomEmojiManager> _customEmojiManager;
|
||||
const std::unique_ptr<Stories> _stories;
|
||||
const std::unique_ptr<SavedMessages> _savedMessages;
|
||||
|
||||
MsgId _nonHistoryEntryId = ServerMaxMsgId.bare + ScheduledMsgIdsRange;
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_folder.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_chat_filters.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "core/application.h"
|
||||
#include "core/core_settings.h"
|
||||
#include "mainwidget.h"
|
||||
|
@ -83,6 +84,8 @@ Entry::Entry(not_null<Data::Session*> owner, Type type)
|
|||
? (Flag::IsThread | Flag::IsHistory)
|
||||
: (type == Type::ForumTopic)
|
||||
? Flag::IsThread
|
||||
: (type == Type::SavedSublist)
|
||||
? Flag::IsSavedSublist
|
||||
: Flag(0)) {
|
||||
}
|
||||
|
||||
|
@ -109,7 +112,7 @@ Data::Forum *Entry::asForum() {
|
|||
}
|
||||
|
||||
Data::Folder *Entry::asFolder() {
|
||||
return (_flags & Flag::IsThread)
|
||||
return (_flags & (Flag::IsThread | Flag::IsSavedSublist))
|
||||
? nullptr
|
||||
: static_cast<Data::Folder*>(this);
|
||||
}
|
||||
|
@ -126,6 +129,12 @@ Data::ForumTopic *Entry::asTopic() {
|
|||
: nullptr;
|
||||
}
|
||||
|
||||
Data::SavedSublist *Entry::asSublist() {
|
||||
return (_flags & Flag::IsSavedSublist)
|
||||
? static_cast<Data::SavedSublist*>(this)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
const History *Entry::asHistory() const {
|
||||
return const_cast<Entry*>(this)->asHistory();
|
||||
}
|
||||
|
@ -146,6 +155,10 @@ const Data::ForumTopic *Entry::asTopic() const {
|
|||
return const_cast<Entry*>(this)->asTopic();
|
||||
}
|
||||
|
||||
const Data::SavedSublist *Entry::asSublist() const {
|
||||
return const_cast<Entry*>(this)->asSublist();
|
||||
}
|
||||
|
||||
void Entry::pinnedIndexChanged(FilterId filterId, int was, int now) {
|
||||
if (!filterId && session().supportMode()) {
|
||||
// Force reorder in support mode.
|
||||
|
|
|
@ -25,6 +25,7 @@ class Session;
|
|||
class Forum;
|
||||
class Folder;
|
||||
class ForumTopic;
|
||||
class SavedSublist;
|
||||
} // namespace Data
|
||||
|
||||
namespace Ui {
|
||||
|
@ -151,6 +152,7 @@ public:
|
|||
History,
|
||||
Folder,
|
||||
ForumTopic,
|
||||
SavedSublist,
|
||||
};
|
||||
Entry(not_null<Data::Session*> owner, Type type);
|
||||
virtual ~Entry();
|
||||
|
@ -163,12 +165,14 @@ public:
|
|||
Data::Folder *asFolder();
|
||||
Data::Thread *asThread();
|
||||
Data::ForumTopic *asTopic();
|
||||
Data::SavedSublist *asSublist();
|
||||
|
||||
const History *asHistory() const;
|
||||
const Data::Forum *asForum() const;
|
||||
const Data::Folder *asFolder() const;
|
||||
const Data::Thread *asThread() const;
|
||||
const Data::ForumTopic *asTopic() const;
|
||||
const Data::SavedSublist *asSublist() const;
|
||||
|
||||
PositionChange adjustByPosInChatList(
|
||||
FilterId filterId,
|
||||
|
@ -206,27 +210,29 @@ public:
|
|||
void setChatListTimeId(TimeId date);
|
||||
virtual void updateChatListExistence();
|
||||
bool needUpdateInChatList() const;
|
||||
virtual TimeId adjustedChatListTimeId() const;
|
||||
[[nodiscard]] virtual TimeId adjustedChatListTimeId() const;
|
||||
|
||||
virtual int fixedOnTopIndex() const = 0;
|
||||
[[nodiscard]] virtual int fixedOnTopIndex() const = 0;
|
||||
static constexpr auto kArchiveFixOnTopIndex = 1;
|
||||
static constexpr auto kTopPromotionFixOnTopIndex = 2;
|
||||
|
||||
virtual bool shouldBeInChatList() const = 0;
|
||||
virtual UnreadState chatListUnreadState() const = 0;
|
||||
virtual BadgesState chatListBadgesState() const = 0;
|
||||
virtual HistoryItem *chatListMessage() const = 0;
|
||||
virtual bool chatListMessageKnown() const = 0;
|
||||
virtual void requestChatListMessage() = 0;
|
||||
virtual const QString &chatListName() const = 0;
|
||||
virtual const QString &chatListNameSortKey() const = 0;
|
||||
virtual const base::flat_set<QString> &chatListNameWords() const = 0;
|
||||
virtual const base::flat_set<QChar> &chatListFirstLetters() const = 0;
|
||||
[[nodiscard]] virtual bool shouldBeInChatList() const = 0;
|
||||
[[nodiscard]] virtual UnreadState chatListUnreadState() const = 0;
|
||||
[[nodiscard]] virtual BadgesState chatListBadgesState() const = 0;
|
||||
[[nodiscard]] virtual HistoryItem *chatListMessage() const = 0;
|
||||
[[nodiscard]] virtual bool chatListMessageKnown() const = 0;
|
||||
[[nodiscard]] virtual const QString &chatListName() const = 0;
|
||||
[[nodiscard]] virtual const QString &chatListNameSortKey() const = 0;
|
||||
[[nodiscard]] virtual int chatListNameVersion() const = 0;
|
||||
[[nodiscard]] virtual auto chatListNameWords() const
|
||||
-> const base::flat_set<QString> & = 0;
|
||||
[[nodiscard]] virtual auto chatListFirstLetters() const
|
||||
-> const base::flat_set<QChar> & = 0;
|
||||
|
||||
virtual bool folderKnown() const {
|
||||
[[nodiscard]] virtual bool folderKnown() const {
|
||||
return true;
|
||||
}
|
||||
virtual Data::Folder *folder() const {
|
||||
[[nodiscard]] virtual Data::Folder *folder() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -255,8 +261,9 @@ private:
|
|||
enum class Flag : uchar {
|
||||
IsThread = (1 << 0),
|
||||
IsHistory = (1 << 1),
|
||||
UpdatePostponed = (1 << 2),
|
||||
InUnreadChangeBlock = (1 << 3),
|
||||
IsSavedSublist = (1 << 2),
|
||||
UpdatePostponed = (1 << 3),
|
||||
InUnreadChangeBlock = (1 << 4),
|
||||
};
|
||||
friend inline constexpr bool is_flag_type(Flag) { return true; }
|
||||
using Flags = base::flags<Flag>;
|
||||
|
@ -265,8 +272,6 @@ private:
|
|||
void pinnedIndexChanged(FilterId filterId, int was, int now);
|
||||
[[nodiscard]] uint64 computeSortPosition(FilterId filterId) const;
|
||||
|
||||
[[nodiscard]] virtual int chatListNameVersion() const = 0;
|
||||
|
||||
void setChatListExistence(bool exists);
|
||||
not_null<Row*> mainChatListLink(FilterId filterId) const;
|
||||
Row *maybeMainChatListLink(FilterId filterId) const;
|
||||
|
|
|
@ -40,6 +40,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_chat_filters.h"
|
||||
#include "data/data_cloud_file.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_saved_messages.h"
|
||||
#include "data/data_stories.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/data_send_action.h"
|
||||
|
@ -219,7 +220,9 @@ InnerWidget::InnerWidget(
|
|||
session().data().chatsListChanges(),
|
||||
session().data().chatsListLoadedEvents()
|
||||
) | rpl::filter([=](Data::Folder *folder) {
|
||||
return !_openedForum && (folder == _openedFolder);
|
||||
return !_savedSublists
|
||||
&& !_openedForum
|
||||
&& (folder == _openedFolder);
|
||||
}) | rpl::start_with_next([=] {
|
||||
refresh();
|
||||
}, lifetime());
|
||||
|
@ -499,6 +502,8 @@ int InnerWidget::searchInChatSkip() const {
|
|||
}
|
||||
|
||||
void InnerWidget::changeOpenedFolder(Data::Folder *folder) {
|
||||
Expects(!folder || !_savedSublists);
|
||||
|
||||
if (_openedFolder == folder) {
|
||||
return;
|
||||
}
|
||||
|
@ -513,6 +518,8 @@ void InnerWidget::changeOpenedFolder(Data::Folder *folder) {
|
|||
}
|
||||
|
||||
void InnerWidget::changeOpenedForum(Data::Forum *forum) {
|
||||
Expects(!forum || !_savedSublists);
|
||||
|
||||
if (_openedForum == forum) {
|
||||
return;
|
||||
}
|
||||
|
@ -553,12 +560,39 @@ void InnerWidget::changeOpenedForum(Data::Forum *forum) {
|
|||
}
|
||||
}
|
||||
|
||||
void InnerWidget::showSavedSublists() {
|
||||
Expects(!_geometryInited);
|
||||
Expects(!_savedSublists);
|
||||
|
||||
_savedSublists = true;
|
||||
|
||||
stopReorderPinned();
|
||||
clearSelection();
|
||||
|
||||
_filterId = 0;
|
||||
_openedForum = nullptr;
|
||||
_st = &st::defaultDialogRow;
|
||||
refreshShownList();
|
||||
|
||||
_openedForumLifetime.destroy();
|
||||
|
||||
//session().data().savedMessages().chatsListChanges(
|
||||
//) | rpl::start_with_next([=] {
|
||||
// refresh();
|
||||
//}, lifetime());
|
||||
|
||||
refreshWithCollapsedRows(true);
|
||||
if (_loadMoreCallback) {
|
||||
_loadMoreCallback();
|
||||
}
|
||||
}
|
||||
|
||||
void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||
Painter p(this);
|
||||
|
||||
p.setInactive(
|
||||
_controller->isGifPausedAtLeastFor(Window::GifPauseReason::Any));
|
||||
if (_controller->contentOverlapped(this, e)) {
|
||||
if (!_savedSublists && _controller->contentOverlapped(this, e)) {
|
||||
return;
|
||||
}
|
||||
const auto activeEntry = _controller->activeChatEntryCurrent();
|
||||
|
@ -1416,11 +1450,14 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) {
|
|||
}
|
||||
}
|
||||
const std::vector<Key> &InnerWidget::pinnedChatsOrder() const {
|
||||
return _openedForum
|
||||
? session().data().pinnedChatsOrder(_openedForum)
|
||||
const auto owner = &session().data();
|
||||
return _savedSublists
|
||||
? owner->pinnedChatsOrder(&owner->savedMessages())
|
||||
: _openedForum
|
||||
? owner->pinnedChatsOrder(_openedForum)
|
||||
: _filterId
|
||||
? session().data().pinnedChatsOrder(_filterId)
|
||||
: session().data().pinnedChatsOrder(_openedFolder);
|
||||
? owner->pinnedChatsOrder(_filterId)
|
||||
: owner->pinnedChatsOrder(_openedFolder);
|
||||
}
|
||||
|
||||
void InnerWidget::checkReorderPinnedStart(QPoint localPosition) {
|
||||
|
@ -1473,7 +1510,9 @@ void InnerWidget::savePinnedOrder() {
|
|||
return; // Something has changed in the set of pinned chats.
|
||||
}
|
||||
}
|
||||
if (_openedForum) {
|
||||
if (_savedSublists) {
|
||||
session().api().savePinnedOrder(&session().data().savedMessages());
|
||||
} else if (_openedForum) {
|
||||
session().api().savePinnedOrder(_openedForum);
|
||||
} else if (_filterId) {
|
||||
Api::SaveNewFilterPinned(&session(), _filterId);
|
||||
|
@ -1577,7 +1616,7 @@ bool InnerWidget::updateReorderPinned(QPoint localPosition) {
|
|||
const auto delta = [&] {
|
||||
if (localPosition.y() < _visibleTop) {
|
||||
return localPosition.y() - _visibleTop;
|
||||
} else if ((_openedFolder || _openedForum || _filterId)
|
||||
} else if ((_savedSublists || _openedFolder || _openedForum || _filterId)
|
||||
&& localPosition.y() > _visibleBottom) {
|
||||
return localPosition.y() - _visibleBottom;
|
||||
}
|
||||
|
@ -1832,6 +1871,8 @@ void InnerWidget::handleChatListEntryRefreshes() {
|
|||
return false;
|
||||
} else if (const auto topic = event.key.topic()) {
|
||||
return (topic->forum() == _openedForum);
|
||||
} else if (event.key.sublist()) {
|
||||
return _savedSublists;
|
||||
} else {
|
||||
return !_openedForum;
|
||||
}
|
||||
|
@ -1848,6 +1889,8 @@ void InnerWidget::handleChatListEntryRefreshes() {
|
|||
&& (_state == WidgetState::Default)
|
||||
&& (key.topic()
|
||||
? (key.topic()->forum() == _openedForum)
|
||||
: key.sublist()
|
||||
? _savedSublists
|
||||
: (entry->folder() == _openedFolder))) {
|
||||
_dialogMoved.fire({ from, to });
|
||||
}
|
||||
|
@ -2051,7 +2094,11 @@ void InnerWidget::enterEventHook(QEnterEvent *e) {
|
|||
|
||||
Row *InnerWidget::shownRowByKey(Key key) {
|
||||
const auto entry = key.entry();
|
||||
if (_openedForum) {
|
||||
if (_savedSublists) {
|
||||
if (!entry->asSublist()) {
|
||||
return nullptr;
|
||||
}
|
||||
} else if (_openedForum) {
|
||||
const auto topic = entry->asTopic();
|
||||
if (!topic || topic->forum() != _openedForum) {
|
||||
return nullptr;
|
||||
|
@ -2114,7 +2161,9 @@ void InnerWidget::updateSelectedRow(Key key) {
|
|||
}
|
||||
|
||||
void InnerWidget::refreshShownList() {
|
||||
const auto list = _openedForum
|
||||
const auto list = _savedSublists
|
||||
? session().data().savedMessages().chatsList()->indexed()
|
||||
: _openedForum
|
||||
? _openedForum->topicsList()->indexed()
|
||||
: _filterId
|
||||
? session().data().chatsFilters().chatsList(_filterId)->indexed()
|
||||
|
@ -2294,15 +2343,19 @@ void InnerWidget::applyFilterUpdate(QString newFilter, bool force) {
|
|||
}
|
||||
};
|
||||
if (!_searchInChat && !_searchFromPeer && !words.isEmpty()) {
|
||||
if (_openedForum) {
|
||||
if (_savedSublists) {
|
||||
const auto owner = &session().data();
|
||||
append(owner->savedMessages().chatsList()->indexed());
|
||||
} else if (_openedForum) {
|
||||
append(_openedForum->topicsList()->indexed());
|
||||
} else {
|
||||
append(session().data().chatsList()->indexed());
|
||||
const auto owner = &session().data();
|
||||
append(owner->chatsList()->indexed());
|
||||
const auto id = Data::Folder::kId;
|
||||
if (const auto add = session().data().folderLoaded(id)) {
|
||||
if (const auto add = owner->folderLoaded(id)) {
|
||||
append(add->chatsList()->indexed());
|
||||
}
|
||||
append(session().data().contactsNoChatsList());
|
||||
append(owner->contactsNoChatsList());
|
||||
}
|
||||
}
|
||||
refresh(true);
|
||||
|
@ -2759,6 +2812,10 @@ void InnerWidget::refreshEmptyLabel() {
|
|||
const auto data = &session().data();
|
||||
const auto state = !_shownList->empty()
|
||||
? EmptyState::None
|
||||
: _savedSublists
|
||||
? (data->savedMessages().chatsList()->loaded()
|
||||
? EmptyState::EmptySavedSublists
|
||||
: EmptyState::Loading)
|
||||
: _openedForum
|
||||
? (_openedForum->topicsList()->loaded()
|
||||
? EmptyState::EmptyForum
|
||||
|
@ -2783,6 +2840,8 @@ void InnerWidget::refreshEmptyLabel() {
|
|||
? tr::lng_no_chats_filter()
|
||||
: (state == EmptyState::EmptyForum)
|
||||
? tr::lng_forum_no_topics()
|
||||
: (state == EmptyState::EmptySavedSublists)
|
||||
? tr::lng_no_saved_sublists()
|
||||
: tr::lng_contacts_loading();
|
||||
auto link = (state == EmptyState::NoContacts)
|
||||
? tr::lng_add_contact_button()
|
||||
|
|
|
@ -107,6 +107,7 @@ public:
|
|||
|
||||
void changeOpenedFolder(Data::Folder *folder);
|
||||
void changeOpenedForum(Data::Forum *forum);
|
||||
void showSavedSublists();
|
||||
void selectSkip(int32 direction);
|
||||
void selectSkipPage(int32 pixels, int32 direction);
|
||||
|
||||
|
@ -198,6 +199,7 @@ private:
|
|||
NoContacts,
|
||||
EmptyFolder,
|
||||
EmptyForum,
|
||||
EmptySavedSublists,
|
||||
};
|
||||
|
||||
struct PinnedRow {
|
||||
|
@ -503,6 +505,8 @@ private:
|
|||
float64 _narrowRatio = 0.;
|
||||
bool _geometryInited = false;
|
||||
|
||||
bool _savedSublists = false;
|
||||
|
||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
||||
|
||||
};
|
||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "data/data_folder.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "history/history.h"
|
||||
|
||||
namespace Dialogs {
|
||||
|
@ -25,6 +26,9 @@ Key::Key(Data::Thread *thread) : _value(thread) {
|
|||
Key::Key(Data::ForumTopic *topic) : _value(topic) {
|
||||
}
|
||||
|
||||
Key::Key(Data::SavedSublist *sublist) : _value(sublist) {
|
||||
}
|
||||
|
||||
Key::Key(not_null<History*> history) : _value(history) {
|
||||
}
|
||||
|
||||
|
@ -37,6 +41,9 @@ Key::Key(not_null<Data::Folder*> folder) : _value(folder) {
|
|||
Key::Key(not_null<Data::ForumTopic*> topic) : _value(topic) {
|
||||
}
|
||||
|
||||
Key::Key(not_null<Data::SavedSublist*> sublist) : _value(sublist) {
|
||||
}
|
||||
|
||||
not_null<Entry*> Key::entry() const {
|
||||
Expects(_value != nullptr);
|
||||
|
||||
|
@ -59,6 +66,10 @@ Data::Thread *Key::thread() const {
|
|||
return _value ? _value->asThread() : nullptr;
|
||||
}
|
||||
|
||||
Data::SavedSublist *Key::sublist() const {
|
||||
return _value ? _value->asSublist() : nullptr;
|
||||
}
|
||||
|
||||
History *Key::owningHistory() const {
|
||||
if (const auto thread = this->thread()) {
|
||||
return thread->owningHistory();
|
||||
|
|
|
@ -14,6 +14,7 @@ namespace Data {
|
|||
class Thread;
|
||||
class Folder;
|
||||
class ForumTopic;
|
||||
class SavedSublist;
|
||||
} // namespace Data
|
||||
|
||||
namespace Dialogs {
|
||||
|
@ -29,12 +30,14 @@ public:
|
|||
Key(Data::Folder *folder);
|
||||
Key(Data::Thread *thread);
|
||||
Key(Data::ForumTopic *topic);
|
||||
Key(Data::SavedSublist *sublist);
|
||||
Key(not_null<Entry*> entry) : _value(entry) {
|
||||
}
|
||||
Key(not_null<History*> history);
|
||||
Key(not_null<Data::Thread*> thread);
|
||||
Key(not_null<Data::Folder*> folder);
|
||||
Key(not_null<Data::ForumTopic*> topic);
|
||||
Key(not_null<Data::SavedSublist*> sublist);
|
||||
|
||||
explicit operator bool() const {
|
||||
return (_value != nullptr);
|
||||
|
@ -46,6 +49,7 @@ public:
|
|||
[[nodiscard]] Data::Thread *thread() const;
|
||||
[[nodiscard]] History *owningHistory() const;
|
||||
[[nodiscard]] PeerData *peer() const;
|
||||
[[nodiscard]] Data::SavedSublist *sublist() const;
|
||||
|
||||
friend inline constexpr auto operator<=>(Key, Key) noexcept = default;
|
||||
|
||||
|
|
|
@ -261,8 +261,10 @@ void Row::recountHeight(float64 narrowRatio) {
|
|||
: st::defaultDialogRow.height;
|
||||
} else if (_id.folder()) {
|
||||
_height = st::defaultDialogRow.height;
|
||||
} else {
|
||||
} else if (_id.topic()) {
|
||||
_height = st::forumTopicRow.height;
|
||||
} else {
|
||||
_height = st::defaultDialogRow.height;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -131,6 +131,9 @@ public:
|
|||
[[nodiscard]] Data::Thread *thread() const {
|
||||
return _id.thread();
|
||||
}
|
||||
[[nodiscard]] Data::SavedSublist *sublist() const {
|
||||
return _id.sublist();
|
||||
}
|
||||
[[nodiscard]] not_null<Entry*> entry() const {
|
||||
return _id.entry();
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "data/data_drafts.h"
|
||||
#include "data/data_forum_topic.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/data_session.h"
|
||||
#include "dialogs/dialogs_list.h"
|
||||
#include "dialogs/dialogs_three_state_icon.h"
|
||||
|
@ -732,6 +733,7 @@ void RowPainter::Paint(
|
|||
const auto entry = row->entry();
|
||||
const auto history = row->history();
|
||||
const auto thread = row->thread();
|
||||
const auto sublist = row->sublist();
|
||||
const auto peer = history ? history->peer.get() : nullptr;
|
||||
const auto badgesState = entry->chatListBadgesState();
|
||||
entry->chatListPreloadData(); // Allow chat list message resolve.
|
||||
|
@ -810,6 +812,8 @@ void RowPainter::Paint(
|
|||
? nullptr
|
||||
: thread
|
||||
? &thread->lastItemDialogsView()
|
||||
: sublist
|
||||
? &sublist->lastItemDialogsView()
|
||||
: nullptr;
|
||||
if (view) {
|
||||
const auto forum = context.st->topicsHeight
|
||||
|
|
|
@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/notify/data_notify_settings.h"
|
||||
#include "data/stickers/data_stickers.h"
|
||||
#include "data/data_drafts.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_media_types.h"
|
||||
#include "data/data_channel_admins.h"
|
||||
|
@ -609,6 +610,11 @@ not_null<HistoryItem*> History::addNewItem(
|
|||
addNewToBack(item, unread);
|
||||
checkForLoadedAtTop(item);
|
||||
}
|
||||
|
||||
if (const auto sublist = item->savedSublist()) {
|
||||
sublist->applyMaybeLast(item);
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
|
@ -1427,6 +1433,12 @@ void History::addCreatedOlderSlice(
|
|||
if (loadedAtBottom()) {
|
||||
// Add photos to overview and authors to lastAuthors.
|
||||
addItemsToLists(items);
|
||||
|
||||
for (const auto &item : items) {
|
||||
if (const auto sublist = item->savedSublist()) {
|
||||
sublist->applyMaybeLast(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
addToSharedMedia(items);
|
||||
}
|
||||
|
|
|
@ -365,6 +365,7 @@ public:
|
|||
void takeLocalDraft(not_null<History*> from);
|
||||
void applyCloudDraft(MsgId topicRootId);
|
||||
void draftSavedToCloud(MsgId topicRootId);
|
||||
void requestChatListMessage();
|
||||
|
||||
[[nodiscard]] const Data::ForwardDraft &forwardDraft(
|
||||
MsgId topicRootId) const;
|
||||
|
@ -383,9 +384,9 @@ public:
|
|||
Dialogs::BadgesState chatListBadgesState() const override;
|
||||
HistoryItem *chatListMessage() const override;
|
||||
bool chatListMessageKnown() const override;
|
||||
void requestChatListMessage() override;
|
||||
const QString &chatListName() const override;
|
||||
const QString &chatListNameSortKey() const override;
|
||||
int chatListNameVersion() const override;
|
||||
const base::flat_set<QString> &chatListNameWords() const override;
|
||||
const base::flat_set<QChar> &chatListFirstLetters() const override;
|
||||
void chatListPreloadData() override;
|
||||
|
@ -589,8 +590,6 @@ private:
|
|||
[[nodiscard]] Dialogs::UnreadState computeUnreadState() const;
|
||||
void setFolderPointer(Data::Folder *folder);
|
||||
|
||||
int chatListNameVersion() const override;
|
||||
|
||||
void hasUnreadMentionChanged(bool has) override;
|
||||
void hasUnreadReactionChanged(bool has) override;
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "api/api_updates.h"
|
||||
#include "data/notify/data_notify_settings.h"
|
||||
#include "data/data_bot_app.h"
|
||||
#include "data/data_saved_messages.h"
|
||||
#include "data/data_saved_sublist.h"
|
||||
#include "data/data_scheduled_messages.h"
|
||||
#include "data/data_changes.h"
|
||||
#include "data/data_session.h"
|
||||
|
@ -145,6 +147,8 @@ struct HistoryItem::CreateConfig {
|
|||
QString originalSenderName;
|
||||
QString originalPostAuthor;
|
||||
|
||||
PeerId savedSublistPeer = 0;
|
||||
|
||||
QString forwardPsaType;
|
||||
PeerId savedFromPeer = 0;
|
||||
MsgId savedFromMsgId = 0;
|
||||
|
@ -769,6 +773,9 @@ HistoryItem::~HistoryItem() {
|
|||
if (const auto reply = Get<HistoryMessageReply>()) {
|
||||
reply->clearData(this);
|
||||
}
|
||||
if (const auto saved = Get<HistoryMessageSaved>()) {
|
||||
saved->sublist->removeOne(this);
|
||||
}
|
||||
clearDependencyMessage();
|
||||
applyTTL(0);
|
||||
}
|
||||
|
@ -1229,6 +1236,9 @@ void HistoryItem::invalidateChatListEntry() {
|
|||
if (const auto topic = this->topic()) {
|
||||
topic->lastItemDialogsView().itemInvalidated(this);
|
||||
}
|
||||
if (const auto sublist = savedSublist()) {
|
||||
sublist->lastItemDialogsView().itemInvalidated(this);
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryItem::customEmojiRepaint() {
|
||||
|
@ -3027,6 +3037,20 @@ bool HistoryItem::isEmpty() const {
|
|||
&& !Has<HistoryMessageLogEntryOriginal>();
|
||||
}
|
||||
|
||||
Data::SavedSublist *HistoryItem::savedSublist() const {
|
||||
if (const auto saved = Get<HistoryMessageSaved>()) {
|
||||
return saved->sublist;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PeerData *HistoryItem::savedSublistPeer() const {
|
||||
if (const auto sublist = savedSublist()) {
|
||||
return sublist->peer();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
TextWithEntities HistoryItem::notificationText(
|
||||
NotificationTextOptions options) const {
|
||||
auto result = [&] {
|
||||
|
@ -3178,9 +3202,28 @@ void HistoryItem::createComponents(CreateConfig &&config) {
|
|||
} else if (config.inlineMarkup) {
|
||||
mask |= HistoryMessageReplyMarkup::Bit();
|
||||
}
|
||||
if (_history->peer->isSelf()) {
|
||||
mask |= HistoryMessageSaved::Bit();
|
||||
}
|
||||
|
||||
UpdateComponents(mask);
|
||||
|
||||
if (const auto saved = Get<HistoryMessageSaved>()) {
|
||||
if (!config.savedSublistPeer) {
|
||||
if (config.savedFromPeer) {
|
||||
config.savedSublistPeer = config.savedFromPeer;
|
||||
} else if (config.originalSenderId) {
|
||||
config.savedSublistPeer = config.originalSenderId;
|
||||
} else if (!config.originalSenderName.isEmpty()) {
|
||||
config.savedSublistPeer = PeerData::kSavedHiddenAuthorId;
|
||||
} else {
|
||||
config.savedSublistPeer = _history->session().userPeerId();
|
||||
}
|
||||
}
|
||||
const auto peer = _history->owner().peer(config.savedSublistPeer);
|
||||
saved->sublist = _history->owner().savedMessages().sublist(peer);
|
||||
}
|
||||
|
||||
if (const auto reply = Get<HistoryMessageReply>()) {
|
||||
reply->set(std::move(config.reply));
|
||||
if (!reply->updateData(this)) {
|
||||
|
@ -3474,6 +3517,9 @@ void HistoryItem::applyTTL(const MTPDmessageService &data) {
|
|||
|
||||
void HistoryItem::createComponents(const MTPDmessage &data) {
|
||||
auto config = CreateConfig();
|
||||
config.savedSublistPeer = data.vsaved_peer_id()
|
||||
? peerFromMTP(*data.vsaved_peer_id())
|
||||
: PeerId();
|
||||
if (const auto forwarded = data.vfwd_from()) {
|
||||
forwarded->match([&](const MTPDmessageFwdHeader &data) {
|
||||
FillForwardedInfo(config, data);
|
||||
|
|
|
@ -56,6 +56,7 @@ class ForumTopic;
|
|||
class Thread;
|
||||
struct SponsoredFrom;
|
||||
class Story;
|
||||
class SavedSublist;
|
||||
} // namespace Data
|
||||
|
||||
namespace Main {
|
||||
|
@ -485,6 +486,9 @@ public:
|
|||
[[nodiscard]] QString originalPostAuthor() const;
|
||||
[[nodiscard]] MsgId originalId() const;
|
||||
|
||||
[[nodiscard]] Data::SavedSublist *savedSublist() const;
|
||||
[[nodiscard]] PeerData *savedSublistPeer() const;
|
||||
|
||||
[[nodiscard]] bool isEmpty() const;
|
||||
|
||||
[[nodiscard]] MessageGroupId groupId() const;
|
||||
|
|
|
@ -141,7 +141,7 @@ struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded
|
|||
};
|
||||
|
||||
struct HistoryMessageSaved : public RuntimeComponent<HistoryMessageSaved, HistoryItem> {
|
||||
PeerData *peer = nullptr;
|
||||
Data::SavedSublist *sublist = nullptr;
|
||||
};
|
||||
|
||||
class ReplyToMessagePointer final {
|
||||
|
|
|
@ -689,11 +689,11 @@ void TopBarWidget::infoClicked() {
|
|||
return;
|
||||
} else if (const auto topic = key.topic()) {
|
||||
_controller->showSection(std::make_shared<Info::Memento>(topic));
|
||||
} else if (key.peer()->isSelf()) {
|
||||
} else if (key.peer()->savedSublistsInfo()) {
|
||||
_controller->showSection(std::make_shared<Info::Memento>(
|
||||
key.peer(),
|
||||
Info::Section(Storage::SharedMediaType::Photo)));
|
||||
} else if (key.peer()->isRepliesChat()) {
|
||||
Info::Section::Type::SavedSublists));
|
||||
} else if (key.peer()->sharedMediaInfo()) {
|
||||
_controller->showSection(std::make_shared<Info::Memento>(
|
||||
key.peer(),
|
||||
Info::Section(Storage::SharedMediaType::Photo)));
|
||||
|
|
|
@ -126,6 +126,7 @@ public:
|
|||
Media,
|
||||
CommonGroups,
|
||||
SimilarChannels,
|
||||
SavedSublists,
|
||||
Members,
|
||||
Settings,
|
||||
Downloads,
|
||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "info/media/info_media_widget.h"
|
||||
#include "info/members/info_members_widget.h"
|
||||
#include "info/common_groups/info_common_groups_widget.h"
|
||||
#include "info/saved/info_saved_sublists_widget.h"
|
||||
#include "info/settings/info_settings_widget.h"
|
||||
#include "info/similar_channels/info_similar_channels_widget.h"
|
||||
#include "info/polls/info_polls_results_widget.h"
|
||||
|
@ -111,7 +112,9 @@ std::vector<std::shared_ptr<ContentMemento>> Memento::DefaultStack(
|
|||
}
|
||||
|
||||
Section Memento::DefaultSection(not_null<PeerData*> peer) {
|
||||
if (peer->sharedMediaInfo()) {
|
||||
if (peer->savedSublistsInfo()) {
|
||||
return Section(Section::Type::SavedSublists);
|
||||
} else if (peer->sharedMediaInfo()) {
|
||||
return Section(Section::MediaType::Photo);
|
||||
}
|
||||
return Section(Section::Type::Profile);
|
||||
|
@ -145,6 +148,8 @@ std::shared_ptr<ContentMemento> Memento::DefaultContent(
|
|||
case Section::Type::SimilarChannels:
|
||||
return std::make_shared<SimilarChannels::Memento>(
|
||||
peer->asChannel());
|
||||
case Section::Type::SavedSublists:
|
||||
return std::make_shared<Saved::SublistsMemento>(&peer->session());
|
||||
case Section::Type::Members:
|
||||
return std::make_shared<Members::Memento>(
|
||||
peer,
|
||||
|
|
|
@ -188,23 +188,32 @@ void WrapWidget::injectActivePeerProfile(not_null<PeerData*> peer) {
|
|||
? _historyStack.front().section->section().type()
|
||||
: _controller->section().type();
|
||||
const auto firstSectionMediaType = [&] {
|
||||
if (firstSectionType == Section::Type::Profile) {
|
||||
if (firstSectionType == Section::Type::Profile
|
||||
|| firstSectionType == Section::Type::SavedSublists) {
|
||||
return Section::MediaType::kCount;
|
||||
}
|
||||
return hasStackHistory()
|
||||
? _historyStack.front().section->section().mediaType()
|
||||
: _controller->section().mediaType();
|
||||
}();
|
||||
const auto expectedType = peer->sharedMediaInfo()
|
||||
const auto savedSublistsInfo = peer->savedSublistsInfo();
|
||||
const auto sharedMediaInfo = peer->sharedMediaInfo();
|
||||
const auto expectedType = savedSublistsInfo
|
||||
? Section::Type::SavedSublists
|
||||
: sharedMediaInfo
|
||||
? Section::Type::Media
|
||||
: Section::Type::Profile;
|
||||
const auto expectedMediaType = peer->sharedMediaInfo()
|
||||
const auto expectedMediaType = savedSublistsInfo
|
||||
? Section::MediaType::kCount
|
||||
: sharedMediaInfo
|
||||
? Section::MediaType::Photo
|
||||
: Section::MediaType::kCount;
|
||||
if (firstSectionType != expectedType
|
||||
|| firstSectionMediaType != expectedMediaType
|
||||
|| firstPeer != peer) {
|
||||
auto section = peer->sharedMediaInfo()
|
||||
auto section = savedSublistsInfo
|
||||
? Section(Section::Type::SavedSublists)
|
||||
: sharedMediaInfo
|
||||
? Section(Section::MediaType::Photo)
|
||||
: Section(Section::Type::Profile);
|
||||
injectActiveProfileMemento(std::move(
|
||||
|
@ -545,6 +554,8 @@ void WrapWidget::removeFromStack(const std::vector<Section> §ions) {
|
|||
const auto &s = item.section->section();
|
||||
if (s.type() != section.type()) {
|
||||
return false;
|
||||
} else if (s.type() == Section::Type::SavedSublists) {
|
||||
return true;
|
||||
} else if (s.type() == Section::Type::Media) {
|
||||
return (s.mediaType() == section.mediaType());
|
||||
} else if (s.type() == Section::Type::Settings) {
|
||||
|
|
|
@ -43,7 +43,7 @@ InnerWidget::InnerWidget(
|
|||
// Allows showing additional shared media links and tabs.
|
||||
// Used for shared media in Saved Messages.
|
||||
void InnerWidget::setupOtherTypes() {
|
||||
if (_controller->key().peer()->isSelf() && _isStackBottom) {
|
||||
if (_controller->key().peer()->sharedMediaInfo() && _isStackBottom) {
|
||||
createOtherTypes();
|
||||
} else {
|
||||
_otherTypes.destroy();
|
||||
|
|
105
Telegram/SourceFiles/info/saved/info_saved_sublists_widget.cpp
Normal file
105
Telegram/SourceFiles/info/saved/info_saved_sublists_widget.cpp
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
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 "info/saved/info_saved_sublists_widget.h"
|
||||
//
|
||||
#include "data/data_saved_messages.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "dialogs/dialogs_inner_widget.h"
|
||||
#include "info/info_controller.h"
|
||||
#include "info/info_memento.h"
|
||||
#include "main/main_session.h"
|
||||
#include "lang/lang_keys.h"
|
||||
|
||||
namespace Info::Saved {
|
||||
|
||||
SublistsMemento::SublistsMemento(not_null<Main::Session*> session)
|
||||
: ContentMemento(session->user(), nullptr, PeerId()) {
|
||||
}
|
||||
|
||||
Section SublistsMemento::section() const {
|
||||
return Section(Section::Type::SavedSublists);
|
||||
}
|
||||
|
||||
object_ptr<ContentWidget> SublistsMemento::createWidget(
|
||||
QWidget *parent,
|
||||
not_null<Controller*> controller,
|
||||
const QRect &geometry) {
|
||||
auto result = object_ptr<SublistsWidget>(parent, controller);
|
||||
result->setInternalState(geometry, this);
|
||||
return result;
|
||||
}
|
||||
|
||||
SublistsMemento::~SublistsMemento() = default;
|
||||
|
||||
SublistsWidget::SublistsWidget(
|
||||
QWidget *parent,
|
||||
not_null<Controller*> controller)
|
||||
: ContentWidget(parent, controller) {
|
||||
_inner = setInnerWidget(object_ptr<Dialogs::InnerWidget>(
|
||||
this,
|
||||
controller->parentController(),
|
||||
rpl::single(Dialogs::InnerWidget::ChildListShown())));
|
||||
_inner->showSavedSublists();
|
||||
_inner->setNarrowRatio(0.);
|
||||
|
||||
const auto saved = &controller->session().data().savedMessages();
|
||||
_inner->heightValue() | rpl::start_with_next([=] {
|
||||
if (!saved->supported()) {
|
||||
crl::on_main(controller, [=] {
|
||||
controller->showSection(
|
||||
Memento::Default(controller->session().user()),
|
||||
Window::SectionShow::Way::Backward);
|
||||
});
|
||||
}
|
||||
}, lifetime());
|
||||
|
||||
_inner->setLoadMoreCallback([=] {
|
||||
saved->loadMore();
|
||||
});
|
||||
}
|
||||
|
||||
rpl::producer<QString> SublistsWidget::title() {
|
||||
return tr::lng_saved_messages();
|
||||
}
|
||||
|
||||
bool SublistsWidget::showInternal(not_null<ContentMemento*> memento) {
|
||||
if (!controller()->validateMementoPeer(memento)) {
|
||||
return false;
|
||||
}
|
||||
if (auto my = dynamic_cast<SublistsMemento*>(memento.get())) {
|
||||
restoreState(my);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SublistsWidget::setInternalState(
|
||||
const QRect &geometry,
|
||||
not_null<SublistsMemento*> memento) {
|
||||
setGeometry(geometry);
|
||||
Ui::SendPendingMoveResizeEvents(this);
|
||||
restoreState(memento);
|
||||
}
|
||||
|
||||
std::shared_ptr<ContentMemento> SublistsWidget::doCreateMemento() {
|
||||
auto result = std::make_shared<SublistsMemento>(
|
||||
&controller()->session());
|
||||
saveState(result.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
void SublistsWidget::saveState(not_null<SublistsMemento*> memento) {
|
||||
memento->setScrollTop(scrollTopSave());
|
||||
}
|
||||
|
||||
void SublistsWidget::restoreState(not_null<SublistsMemento*> memento) {
|
||||
scrollTopRestore(memento->scrollTop());
|
||||
}
|
||||
|
||||
} // namespace Info::Saved
|
64
Telegram/SourceFiles/info/saved/info_saved_sublists_widget.h
Normal file
64
Telegram/SourceFiles/info/saved/info_saved_sublists_widget.h
Normal file
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
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
|
||||
|
||||
#include "info/info_content_widget.h"
|
||||
|
||||
namespace Dialogs {
|
||||
class InnerWidget;
|
||||
} // namespace Dialogs
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
} // namespace Main
|
||||
|
||||
namespace Info::Saved {
|
||||
|
||||
class SublistsMemento final : public ContentMemento {
|
||||
public:
|
||||
explicit SublistsMemento(not_null<Main::Session*> session);
|
||||
|
||||
object_ptr<ContentWidget> createWidget(
|
||||
QWidget *parent,
|
||||
not_null<Controller*> controller,
|
||||
const QRect &geometry) override;
|
||||
|
||||
Section section() const override;
|
||||
|
||||
~SublistsMemento();
|
||||
|
||||
private:
|
||||
|
||||
};
|
||||
|
||||
class SublistsWidget final : public ContentWidget {
|
||||
public:
|
||||
SublistsWidget(
|
||||
QWidget *parent,
|
||||
not_null<Controller*> controller);
|
||||
|
||||
bool showInternal(
|
||||
not_null<ContentMemento*> memento) override;
|
||||
|
||||
void setInternalState(
|
||||
const QRect &geometry,
|
||||
not_null<SublistsMemento*> memento);
|
||||
|
||||
rpl::producer<QString> title() override;
|
||||
|
||||
private:
|
||||
void saveState(not_null<SublistsMemento*> memento);
|
||||
void restoreState(not_null<SublistsMemento*> memento);
|
||||
|
||||
std::shared_ptr<ContentMemento> doCreateMemento() override;
|
||||
|
||||
Dialogs::InnerWidget *_inner = nullptr;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Info::Saved
|
|
@ -513,4 +513,3 @@ void Widget::restoreState(not_null<Memento*> memento) {
|
|||
}
|
||||
|
||||
} // namespace Info::SimilarChannels
|
||||
|
||||
|
|
|
@ -68,4 +68,3 @@ private:
|
|||
};
|
||||
|
||||
} // namespace Info::SimilarChannels
|
||||
|
||||
|
|
|
@ -500,7 +500,7 @@ void Filler::addTogglePin() {
|
|||
}
|
||||
|
||||
void Filler::addToggleMuteSubmenu(bool addSeparator) {
|
||||
if (_thread->peer()->isSelf()) {
|
||||
if (!_thread || _thread->peer()->isSelf()) {
|
||||
return;
|
||||
}
|
||||
PeerMenuAddMuteSubmenuAction(_controller, _thread, _addAction);
|
||||
|
@ -526,6 +526,8 @@ void Filler::addSupportInfo() {
|
|||
void Filler::addInfo() {
|
||||
if (_peer && (_peer->isSelf() || _peer->isRepliesChat())) {
|
||||
return;
|
||||
} else if (!_thread) {
|
||||
return;
|
||||
} else if (_controller->adaptive().isThreeColumn()) {
|
||||
const auto thread = _controller->activeChatCurrent().thread();
|
||||
if (thread && thread == _thread) {
|
||||
|
@ -534,8 +536,6 @@ void Filler::addInfo() {
|
|||
return;
|
||||
}
|
||||
}
|
||||
} else if (!_thread) {
|
||||
return;
|
||||
}
|
||||
const auto controller = _controller;
|
||||
const auto weak = base::make_weak(_thread);
|
||||
|
|
Loading…
Add table
Reference in a new issue