mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +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_replies_list.h
|
||||||
data/data_reply_preview.cpp
|
data/data_reply_preview.cpp
|
||||||
data/data_reply_preview.h
|
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.cpp
|
||||||
data/data_search_controller.h
|
data/data_search_controller.h
|
||||||
data/data_send_action.cpp
|
data/data_send_action.cpp
|
||||||
|
@ -897,6 +901,8 @@ PRIVATE
|
||||||
info/profile/info_profile_values.h
|
info/profile/info_profile_values.h
|
||||||
info/profile/info_profile_widget.cpp
|
info/profile/info_profile_widget.cpp
|
||||||
info/profile/info_profile_widget.h
|
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.cpp
|
||||||
info/settings/info_settings_widget.h
|
info/settings/info_settings_widget.h
|
||||||
info/similar_channels/info_similar_channels_widget.cpp
|
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_dlg_new_bot_name" = "Bot name";
|
||||||
"lng_no_chats" = "Your chats will be here";
|
"lng_no_chats" = "Your chats will be here";
|
||||||
"lng_no_chats_filter" = "No chats currently belong to this folder.";
|
"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_loading" = "Loading...";
|
||||||
"lng_contacts_not_found" = "No contacts found";
|
"lng_contacts_not_found" = "No contacts found";
|
||||||
"lng_topics_not_found" = "No topics found.";
|
"lng_topics_not_found" = "No topics found.";
|
||||||
|
|
|
@ -440,6 +440,26 @@ void ApiWrap::savePinnedOrder(not_null<Data::Forum*> forum) {
|
||||||
}).send();
|
}).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(
|
void ApiWrap::toggleHistoryArchived(
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
bool archived,
|
bool archived,
|
||||||
|
|
|
@ -34,6 +34,7 @@ class Forum;
|
||||||
class ForumTopic;
|
class ForumTopic;
|
||||||
class Thread;
|
class Thread;
|
||||||
class Story;
|
class Story;
|
||||||
|
class SavedMessages;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace InlineBots {
|
namespace InlineBots {
|
||||||
|
@ -152,6 +153,7 @@ public:
|
||||||
|
|
||||||
void savePinnedOrder(Data::Folder *folder);
|
void savePinnedOrder(Data::Folder *folder);
|
||||||
void savePinnedOrder(not_null<Data::Forum*> forum);
|
void savePinnedOrder(not_null<Data::Forum*> forum);
|
||||||
|
void savePinnedOrder(not_null<Data::SavedMessages*> saved);
|
||||||
void toggleHistoryArchived(
|
void toggleHistoryArchived(
|
||||||
not_null<History*> history,
|
not_null<History*> history,
|
||||||
bool archived,
|
bool archived,
|
||||||
|
|
|
@ -343,12 +343,6 @@ int Folder::storiesUnreadCount() const {
|
||||||
return _storiesUnreadCount;
|
return _storiesUnreadCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Folder::requestChatListMessage() {
|
|
||||||
if (!chatListMessageKnown()) {
|
|
||||||
owner().histories().requestDialogEntry(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
TimeId Folder::adjustedChatListTimeId() const {
|
TimeId Folder::adjustedChatListTimeId() const {
|
||||||
return chatListTimeId();
|
return chatListTimeId();
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,9 +49,9 @@ public:
|
||||||
Dialogs::BadgesState chatListBadgesState() const override;
|
Dialogs::BadgesState chatListBadgesState() const override;
|
||||||
HistoryItem *chatListMessage() const override;
|
HistoryItem *chatListMessage() const override;
|
||||||
bool chatListMessageKnown() const override;
|
bool chatListMessageKnown() const override;
|
||||||
void requestChatListMessage() override;
|
|
||||||
const QString &chatListName() const override;
|
const QString &chatListName() const override;
|
||||||
const QString &chatListNameSortKey() const override;
|
const QString &chatListNameSortKey() const override;
|
||||||
|
int chatListNameVersion() const override;
|
||||||
const base::flat_set<QString> &chatListNameWords() const override;
|
const base::flat_set<QString> &chatListNameWords() const override;
|
||||||
const base::flat_set<QChar> &chatListFirstLetters() const override;
|
const base::flat_set<QChar> &chatListFirstLetters() const override;
|
||||||
|
|
||||||
|
@ -82,8 +82,6 @@ public:
|
||||||
private:
|
private:
|
||||||
void indexNameParts();
|
void indexNameParts();
|
||||||
|
|
||||||
int chatListNameVersion() const override;
|
|
||||||
|
|
||||||
void reorderLastHistories();
|
void reorderLastHistories();
|
||||||
|
|
||||||
void paintUserpic(
|
void paintUserpic(
|
||||||
|
|
|
@ -98,6 +98,7 @@ public:
|
||||||
|
|
||||||
void setRealRootId(MsgId realId);
|
void setRealRootId(MsgId realId);
|
||||||
void readTillEnd();
|
void readTillEnd();
|
||||||
|
void requestChatListMessage();
|
||||||
|
|
||||||
void applyTopic(const MTPDforumTopic &data);
|
void applyTopic(const MTPDforumTopic &data);
|
||||||
|
|
||||||
|
@ -109,9 +110,9 @@ public:
|
||||||
Dialogs::BadgesState chatListBadgesState() const override;
|
Dialogs::BadgesState chatListBadgesState() const override;
|
||||||
HistoryItem *chatListMessage() const override;
|
HistoryItem *chatListMessage() const override;
|
||||||
bool chatListMessageKnown() const override;
|
bool chatListMessageKnown() const override;
|
||||||
void requestChatListMessage() override;
|
|
||||||
const QString &chatListName() const override;
|
const QString &chatListName() const override;
|
||||||
const QString &chatListNameSortKey() const override;
|
const QString &chatListNameSortKey() const override;
|
||||||
|
int chatListNameVersion() const override;
|
||||||
const base::flat_set<QString> &chatListNameWords() const override;
|
const base::flat_set<QString> &chatListNameWords() const override;
|
||||||
const base::flat_set<QChar> &chatListFirstLetters() const override;
|
const base::flat_set<QChar> &chatListFirstLetters() const override;
|
||||||
|
|
||||||
|
@ -187,8 +188,6 @@ private:
|
||||||
void allowChatListMessageResolve();
|
void allowChatListMessageResolve();
|
||||||
void resolveChatListMessageGroup();
|
void resolveChatListMessageGroup();
|
||||||
|
|
||||||
int chatListNameVersion() const override;
|
|
||||||
|
|
||||||
void subscribeToUnreadChanges();
|
void subscribeToUnreadChanges();
|
||||||
[[nodiscard]] Dialogs::UnreadState unreadStateFor(
|
[[nodiscard]] Dialogs::UnreadState unreadStateFor(
|
||||||
int count,
|
int count,
|
||||||
|
|
|
@ -18,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_folder.h"
|
#include "data/data_folder.h"
|
||||||
#include "data/data_forum.h"
|
#include "data/data_forum.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
|
#include "data/data_saved_messages.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_file_origin.h"
|
#include "data/data_file_origin.h"
|
||||||
#include "data/data_histories.h"
|
#include "data/data_histories.h"
|
||||||
|
@ -1029,6 +1030,10 @@ bool PeerData::sharedMediaInfo() const {
|
||||||
return isSelf() || isRepliesChat();
|
return isSelf() || isRepliesChat();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PeerData::savedSublistsInfo() const {
|
||||||
|
return isSelf() && owner().savedMessages().supported();
|
||||||
|
}
|
||||||
|
|
||||||
bool PeerData::hasStoriesHidden() const {
|
bool PeerData::hasStoriesHidden() const {
|
||||||
if (const auto user = asUser()) {
|
if (const auto user = asUser()) {
|
||||||
return user->hasStoriesHidden();
|
return user->hasStoriesHidden();
|
||||||
|
|
|
@ -203,6 +203,7 @@ public:
|
||||||
[[nodiscard]] bool isGigagroup() const;
|
[[nodiscard]] bool isGigagroup() const;
|
||||||
[[nodiscard]] bool isRepliesChat() const;
|
[[nodiscard]] bool isRepliesChat() const;
|
||||||
[[nodiscard]] bool sharedMediaInfo() const;
|
[[nodiscard]] bool sharedMediaInfo() const;
|
||||||
|
[[nodiscard]] bool savedSublistsInfo() const;
|
||||||
[[nodiscard]] bool hasStoriesHidden() const;
|
[[nodiscard]] bool hasStoriesHidden() const;
|
||||||
void setStoriesHidden(bool hidden);
|
void setStoriesHidden(bool hidden);
|
||||||
|
|
||||||
|
|
|
@ -141,6 +141,18 @@ int PremiumLimits::topicsPinnedCurrent() const {
|
||||||
return appConfigLimit("topics_pinned_limit", 5);
|
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 {
|
int PremiumLimits::channelsPublicDefault() const {
|
||||||
return appConfigLimit("channels_public_limit_default", 10);
|
return appConfigLimit("channels_public_limit_default", 10);
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,6 +59,10 @@ public:
|
||||||
|
|
||||||
[[nodiscard]] int topicsPinnedCurrent() const;
|
[[nodiscard]] int topicsPinnedCurrent() const;
|
||||||
|
|
||||||
|
[[nodiscard]] int savedSublistsPinnedDefault() const;
|
||||||
|
[[nodiscard]] int savedSublistsPinnedPremium() const;
|
||||||
|
[[nodiscard]] int savedSublistsPinnedCurrent() const;
|
||||||
|
|
||||||
[[nodiscard]] int channelsPublicDefault() const;
|
[[nodiscard]] int channelsPublicDefault() const;
|
||||||
[[nodiscard]] int channelsPublicPremium() const;
|
[[nodiscard]] int channelsPublicPremium() const;
|
||||||
[[nodiscard]] int channelsPublicCurrent() 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_emoji_statuses.h"
|
||||||
#include "data/data_forum_icons.h"
|
#include "data/data_forum_icons.h"
|
||||||
#include "data/data_cloud_themes.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_stories.h"
|
||||||
#include "data/data_streaming.h"
|
#include "data/data_streaming.h"
|
||||||
#include "data/data_media_rotation.h"
|
#include "data/data_media_rotation.h"
|
||||||
|
@ -261,7 +263,8 @@ Session::Session(not_null<Main::Session*> session)
|
||||||
, _forumIcons(std::make_unique<ForumIcons>(this))
|
, _forumIcons(std::make_unique<ForumIcons>(this))
|
||||||
, _notifySettings(std::make_unique<NotifySettings>(this))
|
, _notifySettings(std::make_unique<NotifySettings>(this))
|
||||||
, _customEmojiManager(std::make_unique<CustomEmojiManager>(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());
|
_cache->open(_session->local().cacheKey());
|
||||||
_bigFileCache->open(_session->local().cacheBigFileKey());
|
_bigFileCache->open(_session->local().cacheBigFileKey());
|
||||||
|
|
||||||
|
@ -1712,6 +1715,11 @@ void Session::requestItemRepaint(not_null<const HistoryItem*> item) {
|
||||||
topic->updateChatListEntry();
|
topic->updateChatListEntry();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (const auto sublist = item->savedSublist()) {
|
||||||
|
if (sublist->lastItemDialogsView().dependsOn(item)) {
|
||||||
|
sublist->updateChatListEntry();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<not_null<const HistoryItem*>> Session::itemRepaintRequest() const {
|
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();
|
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(
|
rpl::producer<int> Session::maxPinnedChatsLimitValue(
|
||||||
Data::Folder *folder) const {
|
Data::Folder *folder) const {
|
||||||
// Premium limit from appconfig.
|
// 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(
|
const std::vector<Dialogs::Key> &Session::pinnedChatsOrder(
|
||||||
Data::Folder *folder) const {
|
Data::Folder *folder) const {
|
||||||
return chatsList(folder)->pinned()->order();
|
return chatsList(folder)->pinned()->order();
|
||||||
|
@ -2192,6 +2219,11 @@ const std::vector<Dialogs::Key> &Session::pinnedChatsOrder(
|
||||||
return forum->topicsList()->pinned()->order();
|
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) {
|
void Session::clearPinnedChats(Data::Folder *folder) {
|
||||||
chatsList(folder)->pinned()->clear();
|
chatsList(folder)->pinned()->clear();
|
||||||
}
|
}
|
||||||
|
@ -4198,6 +4230,8 @@ not_null<Dialogs::MainList*> Session::chatsListFor(
|
||||||
const auto topic = entry->asTopic();
|
const auto topic = entry->asTopic();
|
||||||
return topic
|
return topic
|
||||||
? topic->forum()->topicsList()
|
? topic->forum()->topicsList()
|
||||||
|
: entry->asSublist()
|
||||||
|
? _savedMessages->chatsList()
|
||||||
: chatsList(entry->folder());
|
: chatsList(entry->folder());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,7 @@ class GroupCall;
|
||||||
class NotifySettings;
|
class NotifySettings;
|
||||||
class CustomEmojiManager;
|
class CustomEmojiManager;
|
||||||
class Stories;
|
class Stories;
|
||||||
|
class SavedMessages;
|
||||||
|
|
||||||
struct RepliesReadTillUpdate {
|
struct RepliesReadTillUpdate {
|
||||||
FullMsgId id;
|
FullMsgId id;
|
||||||
|
@ -137,6 +138,9 @@ public:
|
||||||
[[nodiscard]] Stories &stories() const {
|
[[nodiscard]] Stories &stories() const {
|
||||||
return *_stories;
|
return *_stories;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] SavedMessages &savedMessages() const {
|
||||||
|
return *_savedMessages;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] MsgId nextNonHistoryEntryId() {
|
[[nodiscard]] MsgId nextNonHistoryEntryId() {
|
||||||
return ++_nonHistoryEntryId;
|
return ++_nonHistoryEntryId;
|
||||||
|
@ -352,18 +356,24 @@ public:
|
||||||
[[nodiscard]] int pinnedChatsLimit(Folder *folder) const;
|
[[nodiscard]] int pinnedChatsLimit(Folder *folder) const;
|
||||||
[[nodiscard]] int pinnedChatsLimit(FilterId filterId) const;
|
[[nodiscard]] int pinnedChatsLimit(FilterId filterId) const;
|
||||||
[[nodiscard]] int pinnedChatsLimit(not_null<Forum*> forum) const;
|
[[nodiscard]] int pinnedChatsLimit(not_null<Forum*> forum) const;
|
||||||
|
[[nodiscard]] int pinnedChatsLimit(
|
||||||
|
not_null<SavedMessages*> saved) const;
|
||||||
[[nodiscard]] rpl::producer<int> maxPinnedChatsLimitValue(
|
[[nodiscard]] rpl::producer<int> maxPinnedChatsLimitValue(
|
||||||
Folder *folder) const;
|
Folder *folder) const;
|
||||||
[[nodiscard]] rpl::producer<int> maxPinnedChatsLimitValue(
|
[[nodiscard]] rpl::producer<int> maxPinnedChatsLimitValue(
|
||||||
FilterId filterId) const;
|
FilterId filterId) const;
|
||||||
[[nodiscard]] rpl::producer<int> maxPinnedChatsLimitValue(
|
[[nodiscard]] rpl::producer<int> maxPinnedChatsLimitValue(
|
||||||
not_null<Forum*> forum) const;
|
not_null<Forum*> forum) const;
|
||||||
|
[[nodiscard]] rpl::producer<int> maxPinnedChatsLimitValue(
|
||||||
|
not_null<SavedMessages*> saved) const;
|
||||||
[[nodiscard]] const std::vector<Dialogs::Key> &pinnedChatsOrder(
|
[[nodiscard]] const std::vector<Dialogs::Key> &pinnedChatsOrder(
|
||||||
Folder *folder) const;
|
Folder *folder) const;
|
||||||
[[nodiscard]] const std::vector<Dialogs::Key> &pinnedChatsOrder(
|
[[nodiscard]] const std::vector<Dialogs::Key> &pinnedChatsOrder(
|
||||||
not_null<Forum*> forum) const;
|
not_null<Forum*> forum) const;
|
||||||
[[nodiscard]] const std::vector<Dialogs::Key> &pinnedChatsOrder(
|
[[nodiscard]] const std::vector<Dialogs::Key> &pinnedChatsOrder(
|
||||||
FilterId filterId) const;
|
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 setChatPinned(Dialogs::Key key, FilterId filterId, bool pinned);
|
||||||
void setPinnedFromEntryList(Dialogs::Key key, bool pinned);
|
void setPinnedFromEntryList(Dialogs::Key key, bool pinned);
|
||||||
void clearPinnedChats(Folder *folder);
|
void clearPinnedChats(Folder *folder);
|
||||||
|
@ -1041,6 +1051,7 @@ private:
|
||||||
const std::unique_ptr<NotifySettings> _notifySettings;
|
const std::unique_ptr<NotifySettings> _notifySettings;
|
||||||
const std::unique_ptr<CustomEmojiManager> _customEmojiManager;
|
const std::unique_ptr<CustomEmojiManager> _customEmojiManager;
|
||||||
const std::unique_ptr<Stories> _stories;
|
const std::unique_ptr<Stories> _stories;
|
||||||
|
const std::unique_ptr<SavedMessages> _savedMessages;
|
||||||
|
|
||||||
MsgId _nonHistoryEntryId = ServerMaxMsgId.bare + ScheduledMsgIdsRange;
|
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_folder.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
#include "data/data_chat_filters.h"
|
#include "data/data_chat_filters.h"
|
||||||
|
#include "data/data_saved_sublist.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "core/core_settings.h"
|
#include "core/core_settings.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
|
@ -83,6 +84,8 @@ Entry::Entry(not_null<Data::Session*> owner, Type type)
|
||||||
? (Flag::IsThread | Flag::IsHistory)
|
? (Flag::IsThread | Flag::IsHistory)
|
||||||
: (type == Type::ForumTopic)
|
: (type == Type::ForumTopic)
|
||||||
? Flag::IsThread
|
? Flag::IsThread
|
||||||
|
: (type == Type::SavedSublist)
|
||||||
|
? Flag::IsSavedSublist
|
||||||
: Flag(0)) {
|
: Flag(0)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +112,7 @@ Data::Forum *Entry::asForum() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Data::Folder *Entry::asFolder() {
|
Data::Folder *Entry::asFolder() {
|
||||||
return (_flags & Flag::IsThread)
|
return (_flags & (Flag::IsThread | Flag::IsSavedSublist))
|
||||||
? nullptr
|
? nullptr
|
||||||
: static_cast<Data::Folder*>(this);
|
: static_cast<Data::Folder*>(this);
|
||||||
}
|
}
|
||||||
|
@ -126,6 +129,12 @@ Data::ForumTopic *Entry::asTopic() {
|
||||||
: nullptr;
|
: nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Data::SavedSublist *Entry::asSublist() {
|
||||||
|
return (_flags & Flag::IsSavedSublist)
|
||||||
|
? static_cast<Data::SavedSublist*>(this)
|
||||||
|
: nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
const History *Entry::asHistory() const {
|
const History *Entry::asHistory() const {
|
||||||
return const_cast<Entry*>(this)->asHistory();
|
return const_cast<Entry*>(this)->asHistory();
|
||||||
}
|
}
|
||||||
|
@ -146,6 +155,10 @@ const Data::ForumTopic *Entry::asTopic() const {
|
||||||
return const_cast<Entry*>(this)->asTopic();
|
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) {
|
void Entry::pinnedIndexChanged(FilterId filterId, int was, int now) {
|
||||||
if (!filterId && session().supportMode()) {
|
if (!filterId && session().supportMode()) {
|
||||||
// Force reorder in support mode.
|
// Force reorder in support mode.
|
||||||
|
|
|
@ -25,6 +25,7 @@ class Session;
|
||||||
class Forum;
|
class Forum;
|
||||||
class Folder;
|
class Folder;
|
||||||
class ForumTopic;
|
class ForumTopic;
|
||||||
|
class SavedSublist;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
@ -151,6 +152,7 @@ public:
|
||||||
History,
|
History,
|
||||||
Folder,
|
Folder,
|
||||||
ForumTopic,
|
ForumTopic,
|
||||||
|
SavedSublist,
|
||||||
};
|
};
|
||||||
Entry(not_null<Data::Session*> owner, Type type);
|
Entry(not_null<Data::Session*> owner, Type type);
|
||||||
virtual ~Entry();
|
virtual ~Entry();
|
||||||
|
@ -163,12 +165,14 @@ public:
|
||||||
Data::Folder *asFolder();
|
Data::Folder *asFolder();
|
||||||
Data::Thread *asThread();
|
Data::Thread *asThread();
|
||||||
Data::ForumTopic *asTopic();
|
Data::ForumTopic *asTopic();
|
||||||
|
Data::SavedSublist *asSublist();
|
||||||
|
|
||||||
const History *asHistory() const;
|
const History *asHistory() const;
|
||||||
const Data::Forum *asForum() const;
|
const Data::Forum *asForum() const;
|
||||||
const Data::Folder *asFolder() const;
|
const Data::Folder *asFolder() const;
|
||||||
const Data::Thread *asThread() const;
|
const Data::Thread *asThread() const;
|
||||||
const Data::ForumTopic *asTopic() const;
|
const Data::ForumTopic *asTopic() const;
|
||||||
|
const Data::SavedSublist *asSublist() const;
|
||||||
|
|
||||||
PositionChange adjustByPosInChatList(
|
PositionChange adjustByPosInChatList(
|
||||||
FilterId filterId,
|
FilterId filterId,
|
||||||
|
@ -206,27 +210,29 @@ public:
|
||||||
void setChatListTimeId(TimeId date);
|
void setChatListTimeId(TimeId date);
|
||||||
virtual void updateChatListExistence();
|
virtual void updateChatListExistence();
|
||||||
bool needUpdateInChatList() const;
|
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 kArchiveFixOnTopIndex = 1;
|
||||||
static constexpr auto kTopPromotionFixOnTopIndex = 2;
|
static constexpr auto kTopPromotionFixOnTopIndex = 2;
|
||||||
|
|
||||||
virtual bool shouldBeInChatList() const = 0;
|
[[nodiscard]] virtual bool shouldBeInChatList() const = 0;
|
||||||
virtual UnreadState chatListUnreadState() const = 0;
|
[[nodiscard]] virtual UnreadState chatListUnreadState() const = 0;
|
||||||
virtual BadgesState chatListBadgesState() const = 0;
|
[[nodiscard]] virtual BadgesState chatListBadgesState() const = 0;
|
||||||
virtual HistoryItem *chatListMessage() const = 0;
|
[[nodiscard]] virtual HistoryItem *chatListMessage() const = 0;
|
||||||
virtual bool chatListMessageKnown() const = 0;
|
[[nodiscard]] virtual bool chatListMessageKnown() const = 0;
|
||||||
virtual void requestChatListMessage() = 0;
|
[[nodiscard]] virtual const QString &chatListName() const = 0;
|
||||||
virtual const QString &chatListName() const = 0;
|
[[nodiscard]] virtual const QString &chatListNameSortKey() const = 0;
|
||||||
virtual const QString &chatListNameSortKey() const = 0;
|
[[nodiscard]] virtual int chatListNameVersion() const = 0;
|
||||||
virtual const base::flat_set<QString> &chatListNameWords() const = 0;
|
[[nodiscard]] virtual auto chatListNameWords() const
|
||||||
virtual const base::flat_set<QChar> &chatListFirstLetters() const = 0;
|
-> 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;
|
return true;
|
||||||
}
|
}
|
||||||
virtual Data::Folder *folder() const {
|
[[nodiscard]] virtual Data::Folder *folder() const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,8 +261,9 @@ private:
|
||||||
enum class Flag : uchar {
|
enum class Flag : uchar {
|
||||||
IsThread = (1 << 0),
|
IsThread = (1 << 0),
|
||||||
IsHistory = (1 << 1),
|
IsHistory = (1 << 1),
|
||||||
UpdatePostponed = (1 << 2),
|
IsSavedSublist = (1 << 2),
|
||||||
InUnreadChangeBlock = (1 << 3),
|
UpdatePostponed = (1 << 3),
|
||||||
|
InUnreadChangeBlock = (1 << 4),
|
||||||
};
|
};
|
||||||
friend inline constexpr bool is_flag_type(Flag) { return true; }
|
friend inline constexpr bool is_flag_type(Flag) { return true; }
|
||||||
using Flags = base::flags<Flag>;
|
using Flags = base::flags<Flag>;
|
||||||
|
@ -265,8 +272,6 @@ private:
|
||||||
void pinnedIndexChanged(FilterId filterId, int was, int now);
|
void pinnedIndexChanged(FilterId filterId, int was, int now);
|
||||||
[[nodiscard]] uint64 computeSortPosition(FilterId filterId) const;
|
[[nodiscard]] uint64 computeSortPosition(FilterId filterId) const;
|
||||||
|
|
||||||
[[nodiscard]] virtual int chatListNameVersion() const = 0;
|
|
||||||
|
|
||||||
void setChatListExistence(bool exists);
|
void setChatListExistence(bool exists);
|
||||||
not_null<Row*> mainChatListLink(FilterId filterId) const;
|
not_null<Row*> mainChatListLink(FilterId filterId) const;
|
||||||
Row *maybeMainChatListLink(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_chat_filters.h"
|
||||||
#include "data/data_cloud_file.h"
|
#include "data/data_cloud_file.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
|
#include "data/data_saved_messages.h"
|
||||||
#include "data/data_stories.h"
|
#include "data/data_stories.h"
|
||||||
#include "data/stickers/data_stickers.h"
|
#include "data/stickers/data_stickers.h"
|
||||||
#include "data/data_send_action.h"
|
#include "data/data_send_action.h"
|
||||||
|
@ -219,7 +220,9 @@ InnerWidget::InnerWidget(
|
||||||
session().data().chatsListChanges(),
|
session().data().chatsListChanges(),
|
||||||
session().data().chatsListLoadedEvents()
|
session().data().chatsListLoadedEvents()
|
||||||
) | rpl::filter([=](Data::Folder *folder) {
|
) | rpl::filter([=](Data::Folder *folder) {
|
||||||
return !_openedForum && (folder == _openedFolder);
|
return !_savedSublists
|
||||||
|
&& !_openedForum
|
||||||
|
&& (folder == _openedFolder);
|
||||||
}) | rpl::start_with_next([=] {
|
}) | rpl::start_with_next([=] {
|
||||||
refresh();
|
refresh();
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
@ -499,6 +502,8 @@ int InnerWidget::searchInChatSkip() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void InnerWidget::changeOpenedFolder(Data::Folder *folder) {
|
void InnerWidget::changeOpenedFolder(Data::Folder *folder) {
|
||||||
|
Expects(!folder || !_savedSublists);
|
||||||
|
|
||||||
if (_openedFolder == folder) {
|
if (_openedFolder == folder) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -513,6 +518,8 @@ void InnerWidget::changeOpenedFolder(Data::Folder *folder) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void InnerWidget::changeOpenedForum(Data::Forum *forum) {
|
void InnerWidget::changeOpenedForum(Data::Forum *forum) {
|
||||||
|
Expects(!forum || !_savedSublists);
|
||||||
|
|
||||||
if (_openedForum == forum) {
|
if (_openedForum == forum) {
|
||||||
return;
|
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) {
|
void InnerWidget::paintEvent(QPaintEvent *e) {
|
||||||
Painter p(this);
|
Painter p(this);
|
||||||
|
|
||||||
p.setInactive(
|
p.setInactive(
|
||||||
_controller->isGifPausedAtLeastFor(Window::GifPauseReason::Any));
|
_controller->isGifPausedAtLeastFor(Window::GifPauseReason::Any));
|
||||||
if (_controller->contentOverlapped(this, e)) {
|
if (!_savedSublists && _controller->contentOverlapped(this, e)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto activeEntry = _controller->activeChatEntryCurrent();
|
const auto activeEntry = _controller->activeChatEntryCurrent();
|
||||||
|
@ -1416,11 +1450,14 @@ void InnerWidget::mousePressEvent(QMouseEvent *e) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const std::vector<Key> &InnerWidget::pinnedChatsOrder() const {
|
const std::vector<Key> &InnerWidget::pinnedChatsOrder() const {
|
||||||
return _openedForum
|
const auto owner = &session().data();
|
||||||
? session().data().pinnedChatsOrder(_openedForum)
|
return _savedSublists
|
||||||
|
? owner->pinnedChatsOrder(&owner->savedMessages())
|
||||||
|
: _openedForum
|
||||||
|
? owner->pinnedChatsOrder(_openedForum)
|
||||||
: _filterId
|
: _filterId
|
||||||
? session().data().pinnedChatsOrder(_filterId)
|
? owner->pinnedChatsOrder(_filterId)
|
||||||
: session().data().pinnedChatsOrder(_openedFolder);
|
: owner->pinnedChatsOrder(_openedFolder);
|
||||||
}
|
}
|
||||||
|
|
||||||
void InnerWidget::checkReorderPinnedStart(QPoint localPosition) {
|
void InnerWidget::checkReorderPinnedStart(QPoint localPosition) {
|
||||||
|
@ -1473,7 +1510,9 @@ void InnerWidget::savePinnedOrder() {
|
||||||
return; // Something has changed in the set of pinned chats.
|
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);
|
session().api().savePinnedOrder(_openedForum);
|
||||||
} else if (_filterId) {
|
} else if (_filterId) {
|
||||||
Api::SaveNewFilterPinned(&session(), _filterId);
|
Api::SaveNewFilterPinned(&session(), _filterId);
|
||||||
|
@ -1577,7 +1616,7 @@ bool InnerWidget::updateReorderPinned(QPoint localPosition) {
|
||||||
const auto delta = [&] {
|
const auto delta = [&] {
|
||||||
if (localPosition.y() < _visibleTop) {
|
if (localPosition.y() < _visibleTop) {
|
||||||
return localPosition.y() - _visibleTop;
|
return localPosition.y() - _visibleTop;
|
||||||
} else if ((_openedFolder || _openedForum || _filterId)
|
} else if ((_savedSublists || _openedFolder || _openedForum || _filterId)
|
||||||
&& localPosition.y() > _visibleBottom) {
|
&& localPosition.y() > _visibleBottom) {
|
||||||
return localPosition.y() - _visibleBottom;
|
return localPosition.y() - _visibleBottom;
|
||||||
}
|
}
|
||||||
|
@ -1832,6 +1871,8 @@ void InnerWidget::handleChatListEntryRefreshes() {
|
||||||
return false;
|
return false;
|
||||||
} else if (const auto topic = event.key.topic()) {
|
} else if (const auto topic = event.key.topic()) {
|
||||||
return (topic->forum() == _openedForum);
|
return (topic->forum() == _openedForum);
|
||||||
|
} else if (event.key.sublist()) {
|
||||||
|
return _savedSublists;
|
||||||
} else {
|
} else {
|
||||||
return !_openedForum;
|
return !_openedForum;
|
||||||
}
|
}
|
||||||
|
@ -1848,6 +1889,8 @@ void InnerWidget::handleChatListEntryRefreshes() {
|
||||||
&& (_state == WidgetState::Default)
|
&& (_state == WidgetState::Default)
|
||||||
&& (key.topic()
|
&& (key.topic()
|
||||||
? (key.topic()->forum() == _openedForum)
|
? (key.topic()->forum() == _openedForum)
|
||||||
|
: key.sublist()
|
||||||
|
? _savedSublists
|
||||||
: (entry->folder() == _openedFolder))) {
|
: (entry->folder() == _openedFolder))) {
|
||||||
_dialogMoved.fire({ from, to });
|
_dialogMoved.fire({ from, to });
|
||||||
}
|
}
|
||||||
|
@ -2051,7 +2094,11 @@ void InnerWidget::enterEventHook(QEnterEvent *e) {
|
||||||
|
|
||||||
Row *InnerWidget::shownRowByKey(Key key) {
|
Row *InnerWidget::shownRowByKey(Key key) {
|
||||||
const auto entry = key.entry();
|
const auto entry = key.entry();
|
||||||
if (_openedForum) {
|
if (_savedSublists) {
|
||||||
|
if (!entry->asSublist()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
} else if (_openedForum) {
|
||||||
const auto topic = entry->asTopic();
|
const auto topic = entry->asTopic();
|
||||||
if (!topic || topic->forum() != _openedForum) {
|
if (!topic || topic->forum() != _openedForum) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -2114,7 +2161,9 @@ void InnerWidget::updateSelectedRow(Key key) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void InnerWidget::refreshShownList() {
|
void InnerWidget::refreshShownList() {
|
||||||
const auto list = _openedForum
|
const auto list = _savedSublists
|
||||||
|
? session().data().savedMessages().chatsList()->indexed()
|
||||||
|
: _openedForum
|
||||||
? _openedForum->topicsList()->indexed()
|
? _openedForum->topicsList()->indexed()
|
||||||
: _filterId
|
: _filterId
|
||||||
? session().data().chatsFilters().chatsList(_filterId)->indexed()
|
? session().data().chatsFilters().chatsList(_filterId)->indexed()
|
||||||
|
@ -2294,15 +2343,19 @@ void InnerWidget::applyFilterUpdate(QString newFilter, bool force) {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (!_searchInChat && !_searchFromPeer && !words.isEmpty()) {
|
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());
|
append(_openedForum->topicsList()->indexed());
|
||||||
} else {
|
} else {
|
||||||
append(session().data().chatsList()->indexed());
|
const auto owner = &session().data();
|
||||||
|
append(owner->chatsList()->indexed());
|
||||||
const auto id = Data::Folder::kId;
|
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(add->chatsList()->indexed());
|
||||||
}
|
}
|
||||||
append(session().data().contactsNoChatsList());
|
append(owner->contactsNoChatsList());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
refresh(true);
|
refresh(true);
|
||||||
|
@ -2759,6 +2812,10 @@ void InnerWidget::refreshEmptyLabel() {
|
||||||
const auto data = &session().data();
|
const auto data = &session().data();
|
||||||
const auto state = !_shownList->empty()
|
const auto state = !_shownList->empty()
|
||||||
? EmptyState::None
|
? EmptyState::None
|
||||||
|
: _savedSublists
|
||||||
|
? (data->savedMessages().chatsList()->loaded()
|
||||||
|
? EmptyState::EmptySavedSublists
|
||||||
|
: EmptyState::Loading)
|
||||||
: _openedForum
|
: _openedForum
|
||||||
? (_openedForum->topicsList()->loaded()
|
? (_openedForum->topicsList()->loaded()
|
||||||
? EmptyState::EmptyForum
|
? EmptyState::EmptyForum
|
||||||
|
@ -2783,6 +2840,8 @@ void InnerWidget::refreshEmptyLabel() {
|
||||||
? tr::lng_no_chats_filter()
|
? tr::lng_no_chats_filter()
|
||||||
: (state == EmptyState::EmptyForum)
|
: (state == EmptyState::EmptyForum)
|
||||||
? tr::lng_forum_no_topics()
|
? tr::lng_forum_no_topics()
|
||||||
|
: (state == EmptyState::EmptySavedSublists)
|
||||||
|
? tr::lng_no_saved_sublists()
|
||||||
: tr::lng_contacts_loading();
|
: tr::lng_contacts_loading();
|
||||||
auto link = (state == EmptyState::NoContacts)
|
auto link = (state == EmptyState::NoContacts)
|
||||||
? tr::lng_add_contact_button()
|
? tr::lng_add_contact_button()
|
||||||
|
|
|
@ -107,6 +107,7 @@ public:
|
||||||
|
|
||||||
void changeOpenedFolder(Data::Folder *folder);
|
void changeOpenedFolder(Data::Folder *folder);
|
||||||
void changeOpenedForum(Data::Forum *forum);
|
void changeOpenedForum(Data::Forum *forum);
|
||||||
|
void showSavedSublists();
|
||||||
void selectSkip(int32 direction);
|
void selectSkip(int32 direction);
|
||||||
void selectSkipPage(int32 pixels, int32 direction);
|
void selectSkipPage(int32 pixels, int32 direction);
|
||||||
|
|
||||||
|
@ -198,6 +199,7 @@ private:
|
||||||
NoContacts,
|
NoContacts,
|
||||||
EmptyFolder,
|
EmptyFolder,
|
||||||
EmptyForum,
|
EmptyForum,
|
||||||
|
EmptySavedSublists,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PinnedRow {
|
struct PinnedRow {
|
||||||
|
@ -503,6 +505,8 @@ private:
|
||||||
float64 _narrowRatio = 0.;
|
float64 _narrowRatio = 0.;
|
||||||
bool _geometryInited = false;
|
bool _geometryInited = false;
|
||||||
|
|
||||||
|
bool _savedSublists = false;
|
||||||
|
|
||||||
base::unique_qptr<Ui::PopupMenu> _menu;
|
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_folder.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
|
#include "data/data_saved_sublist.h"
|
||||||
#include "history/history.h"
|
#include "history/history.h"
|
||||||
|
|
||||||
namespace Dialogs {
|
namespace Dialogs {
|
||||||
|
@ -25,6 +26,9 @@ Key::Key(Data::Thread *thread) : _value(thread) {
|
||||||
Key::Key(Data::ForumTopic *topic) : _value(topic) {
|
Key::Key(Data::ForumTopic *topic) : _value(topic) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Key::Key(Data::SavedSublist *sublist) : _value(sublist) {
|
||||||
|
}
|
||||||
|
|
||||||
Key::Key(not_null<History*> history) : _value(history) {
|
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::ForumTopic*> topic) : _value(topic) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Key::Key(not_null<Data::SavedSublist*> sublist) : _value(sublist) {
|
||||||
|
}
|
||||||
|
|
||||||
not_null<Entry*> Key::entry() const {
|
not_null<Entry*> Key::entry() const {
|
||||||
Expects(_value != nullptr);
|
Expects(_value != nullptr);
|
||||||
|
|
||||||
|
@ -59,6 +66,10 @@ Data::Thread *Key::thread() const {
|
||||||
return _value ? _value->asThread() : nullptr;
|
return _value ? _value->asThread() : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Data::SavedSublist *Key::sublist() const {
|
||||||
|
return _value ? _value->asSublist() : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
History *Key::owningHistory() const {
|
History *Key::owningHistory() const {
|
||||||
if (const auto thread = this->thread()) {
|
if (const auto thread = this->thread()) {
|
||||||
return thread->owningHistory();
|
return thread->owningHistory();
|
||||||
|
|
|
@ -14,6 +14,7 @@ namespace Data {
|
||||||
class Thread;
|
class Thread;
|
||||||
class Folder;
|
class Folder;
|
||||||
class ForumTopic;
|
class ForumTopic;
|
||||||
|
class SavedSublist;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace Dialogs {
|
namespace Dialogs {
|
||||||
|
@ -29,12 +30,14 @@ public:
|
||||||
Key(Data::Folder *folder);
|
Key(Data::Folder *folder);
|
||||||
Key(Data::Thread *thread);
|
Key(Data::Thread *thread);
|
||||||
Key(Data::ForumTopic *topic);
|
Key(Data::ForumTopic *topic);
|
||||||
|
Key(Data::SavedSublist *sublist);
|
||||||
Key(not_null<Entry*> entry) : _value(entry) {
|
Key(not_null<Entry*> entry) : _value(entry) {
|
||||||
}
|
}
|
||||||
Key(not_null<History*> history);
|
Key(not_null<History*> history);
|
||||||
Key(not_null<Data::Thread*> thread);
|
Key(not_null<Data::Thread*> thread);
|
||||||
Key(not_null<Data::Folder*> folder);
|
Key(not_null<Data::Folder*> folder);
|
||||||
Key(not_null<Data::ForumTopic*> topic);
|
Key(not_null<Data::ForumTopic*> topic);
|
||||||
|
Key(not_null<Data::SavedSublist*> sublist);
|
||||||
|
|
||||||
explicit operator bool() const {
|
explicit operator bool() const {
|
||||||
return (_value != nullptr);
|
return (_value != nullptr);
|
||||||
|
@ -46,6 +49,7 @@ public:
|
||||||
[[nodiscard]] Data::Thread *thread() const;
|
[[nodiscard]] Data::Thread *thread() const;
|
||||||
[[nodiscard]] History *owningHistory() const;
|
[[nodiscard]] History *owningHistory() const;
|
||||||
[[nodiscard]] PeerData *peer() const;
|
[[nodiscard]] PeerData *peer() const;
|
||||||
|
[[nodiscard]] Data::SavedSublist *sublist() const;
|
||||||
|
|
||||||
friend inline constexpr auto operator<=>(Key, Key) noexcept = default;
|
friend inline constexpr auto operator<=>(Key, Key) noexcept = default;
|
||||||
|
|
||||||
|
|
|
@ -261,8 +261,10 @@ void Row::recountHeight(float64 narrowRatio) {
|
||||||
: st::defaultDialogRow.height;
|
: st::defaultDialogRow.height;
|
||||||
} else if (_id.folder()) {
|
} else if (_id.folder()) {
|
||||||
_height = st::defaultDialogRow.height;
|
_height = st::defaultDialogRow.height;
|
||||||
} else {
|
} else if (_id.topic()) {
|
||||||
_height = st::forumTopicRow.height;
|
_height = st::forumTopicRow.height;
|
||||||
|
} else {
|
||||||
|
_height = st::defaultDialogRow.height;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -131,6 +131,9 @@ public:
|
||||||
[[nodiscard]] Data::Thread *thread() const {
|
[[nodiscard]] Data::Thread *thread() const {
|
||||||
return _id.thread();
|
return _id.thread();
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] Data::SavedSublist *sublist() const {
|
||||||
|
return _id.sublist();
|
||||||
|
}
|
||||||
[[nodiscard]] not_null<Entry*> entry() const {
|
[[nodiscard]] not_null<Entry*> entry() const {
|
||||||
return _id.entry();
|
return _id.entry();
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "data/data_drafts.h"
|
#include "data/data_drafts.h"
|
||||||
#include "data/data_forum_topic.h"
|
#include "data/data_forum_topic.h"
|
||||||
|
#include "data/data_saved_sublist.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "dialogs/dialogs_list.h"
|
#include "dialogs/dialogs_list.h"
|
||||||
#include "dialogs/dialogs_three_state_icon.h"
|
#include "dialogs/dialogs_three_state_icon.h"
|
||||||
|
@ -732,6 +733,7 @@ void RowPainter::Paint(
|
||||||
const auto entry = row->entry();
|
const auto entry = row->entry();
|
||||||
const auto history = row->history();
|
const auto history = row->history();
|
||||||
const auto thread = row->thread();
|
const auto thread = row->thread();
|
||||||
|
const auto sublist = row->sublist();
|
||||||
const auto peer = history ? history->peer.get() : nullptr;
|
const auto peer = history ? history->peer.get() : nullptr;
|
||||||
const auto badgesState = entry->chatListBadgesState();
|
const auto badgesState = entry->chatListBadgesState();
|
||||||
entry->chatListPreloadData(); // Allow chat list message resolve.
|
entry->chatListPreloadData(); // Allow chat list message resolve.
|
||||||
|
@ -810,6 +812,8 @@ void RowPainter::Paint(
|
||||||
? nullptr
|
? nullptr
|
||||||
: thread
|
: thread
|
||||||
? &thread->lastItemDialogsView()
|
? &thread->lastItemDialogsView()
|
||||||
|
: sublist
|
||||||
|
? &sublist->lastItemDialogsView()
|
||||||
: nullptr;
|
: nullptr;
|
||||||
if (view) {
|
if (view) {
|
||||||
const auto forum = context.st->topicsHeight
|
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/notify/data_notify_settings.h"
|
||||||
#include "data/stickers/data_stickers.h"
|
#include "data/stickers/data_stickers.h"
|
||||||
#include "data/data_drafts.h"
|
#include "data/data_drafts.h"
|
||||||
|
#include "data/data_saved_sublist.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
#include "data/data_media_types.h"
|
#include "data/data_media_types.h"
|
||||||
#include "data/data_channel_admins.h"
|
#include "data/data_channel_admins.h"
|
||||||
|
@ -609,6 +610,11 @@ not_null<HistoryItem*> History::addNewItem(
|
||||||
addNewToBack(item, unread);
|
addNewToBack(item, unread);
|
||||||
checkForLoadedAtTop(item);
|
checkForLoadedAtTop(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const auto sublist = item->savedSublist()) {
|
||||||
|
sublist->applyMaybeLast(item);
|
||||||
|
}
|
||||||
|
|
||||||
return item;
|
return item;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1427,6 +1433,12 @@ void History::addCreatedOlderSlice(
|
||||||
if (loadedAtBottom()) {
|
if (loadedAtBottom()) {
|
||||||
// Add photos to overview and authors to lastAuthors.
|
// Add photos to overview and authors to lastAuthors.
|
||||||
addItemsToLists(items);
|
addItemsToLists(items);
|
||||||
|
|
||||||
|
for (const auto &item : items) {
|
||||||
|
if (const auto sublist = item->savedSublist()) {
|
||||||
|
sublist->applyMaybeLast(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
addToSharedMedia(items);
|
addToSharedMedia(items);
|
||||||
}
|
}
|
||||||
|
|
|
@ -365,6 +365,7 @@ public:
|
||||||
void takeLocalDraft(not_null<History*> from);
|
void takeLocalDraft(not_null<History*> from);
|
||||||
void applyCloudDraft(MsgId topicRootId);
|
void applyCloudDraft(MsgId topicRootId);
|
||||||
void draftSavedToCloud(MsgId topicRootId);
|
void draftSavedToCloud(MsgId topicRootId);
|
||||||
|
void requestChatListMessage();
|
||||||
|
|
||||||
[[nodiscard]] const Data::ForwardDraft &forwardDraft(
|
[[nodiscard]] const Data::ForwardDraft &forwardDraft(
|
||||||
MsgId topicRootId) const;
|
MsgId topicRootId) const;
|
||||||
|
@ -383,9 +384,9 @@ public:
|
||||||
Dialogs::BadgesState chatListBadgesState() const override;
|
Dialogs::BadgesState chatListBadgesState() const override;
|
||||||
HistoryItem *chatListMessage() const override;
|
HistoryItem *chatListMessage() const override;
|
||||||
bool chatListMessageKnown() const override;
|
bool chatListMessageKnown() const override;
|
||||||
void requestChatListMessage() override;
|
|
||||||
const QString &chatListName() const override;
|
const QString &chatListName() const override;
|
||||||
const QString &chatListNameSortKey() const override;
|
const QString &chatListNameSortKey() const override;
|
||||||
|
int chatListNameVersion() const override;
|
||||||
const base::flat_set<QString> &chatListNameWords() const override;
|
const base::flat_set<QString> &chatListNameWords() const override;
|
||||||
const base::flat_set<QChar> &chatListFirstLetters() const override;
|
const base::flat_set<QChar> &chatListFirstLetters() const override;
|
||||||
void chatListPreloadData() override;
|
void chatListPreloadData() override;
|
||||||
|
@ -589,8 +590,6 @@ private:
|
||||||
[[nodiscard]] Dialogs::UnreadState computeUnreadState() const;
|
[[nodiscard]] Dialogs::UnreadState computeUnreadState() const;
|
||||||
void setFolderPointer(Data::Folder *folder);
|
void setFolderPointer(Data::Folder *folder);
|
||||||
|
|
||||||
int chatListNameVersion() const override;
|
|
||||||
|
|
||||||
void hasUnreadMentionChanged(bool has) override;
|
void hasUnreadMentionChanged(bool has) override;
|
||||||
void hasUnreadReactionChanged(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 "api/api_updates.h"
|
||||||
#include "data/notify/data_notify_settings.h"
|
#include "data/notify/data_notify_settings.h"
|
||||||
#include "data/data_bot_app.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_scheduled_messages.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "data/data_session.h"
|
#include "data/data_session.h"
|
||||||
|
@ -145,6 +147,8 @@ struct HistoryItem::CreateConfig {
|
||||||
QString originalSenderName;
|
QString originalSenderName;
|
||||||
QString originalPostAuthor;
|
QString originalPostAuthor;
|
||||||
|
|
||||||
|
PeerId savedSublistPeer = 0;
|
||||||
|
|
||||||
QString forwardPsaType;
|
QString forwardPsaType;
|
||||||
PeerId savedFromPeer = 0;
|
PeerId savedFromPeer = 0;
|
||||||
MsgId savedFromMsgId = 0;
|
MsgId savedFromMsgId = 0;
|
||||||
|
@ -769,6 +773,9 @@ HistoryItem::~HistoryItem() {
|
||||||
if (const auto reply = Get<HistoryMessageReply>()) {
|
if (const auto reply = Get<HistoryMessageReply>()) {
|
||||||
reply->clearData(this);
|
reply->clearData(this);
|
||||||
}
|
}
|
||||||
|
if (const auto saved = Get<HistoryMessageSaved>()) {
|
||||||
|
saved->sublist->removeOne(this);
|
||||||
|
}
|
||||||
clearDependencyMessage();
|
clearDependencyMessage();
|
||||||
applyTTL(0);
|
applyTTL(0);
|
||||||
}
|
}
|
||||||
|
@ -1229,6 +1236,9 @@ void HistoryItem::invalidateChatListEntry() {
|
||||||
if (const auto topic = this->topic()) {
|
if (const auto topic = this->topic()) {
|
||||||
topic->lastItemDialogsView().itemInvalidated(this);
|
topic->lastItemDialogsView().itemInvalidated(this);
|
||||||
}
|
}
|
||||||
|
if (const auto sublist = savedSublist()) {
|
||||||
|
sublist->lastItemDialogsView().itemInvalidated(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistoryItem::customEmojiRepaint() {
|
void HistoryItem::customEmojiRepaint() {
|
||||||
|
@ -3027,6 +3037,20 @@ bool HistoryItem::isEmpty() const {
|
||||||
&& !Has<HistoryMessageLogEntryOriginal>();
|
&& !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(
|
TextWithEntities HistoryItem::notificationText(
|
||||||
NotificationTextOptions options) const {
|
NotificationTextOptions options) const {
|
||||||
auto result = [&] {
|
auto result = [&] {
|
||||||
|
@ -3178,9 +3202,28 @@ void HistoryItem::createComponents(CreateConfig &&config) {
|
||||||
} else if (config.inlineMarkup) {
|
} else if (config.inlineMarkup) {
|
||||||
mask |= HistoryMessageReplyMarkup::Bit();
|
mask |= HistoryMessageReplyMarkup::Bit();
|
||||||
}
|
}
|
||||||
|
if (_history->peer->isSelf()) {
|
||||||
|
mask |= HistoryMessageSaved::Bit();
|
||||||
|
}
|
||||||
|
|
||||||
UpdateComponents(mask);
|
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>()) {
|
if (const auto reply = Get<HistoryMessageReply>()) {
|
||||||
reply->set(std::move(config.reply));
|
reply->set(std::move(config.reply));
|
||||||
if (!reply->updateData(this)) {
|
if (!reply->updateData(this)) {
|
||||||
|
@ -3474,6 +3517,9 @@ void HistoryItem::applyTTL(const MTPDmessageService &data) {
|
||||||
|
|
||||||
void HistoryItem::createComponents(const MTPDmessage &data) {
|
void HistoryItem::createComponents(const MTPDmessage &data) {
|
||||||
auto config = CreateConfig();
|
auto config = CreateConfig();
|
||||||
|
config.savedSublistPeer = data.vsaved_peer_id()
|
||||||
|
? peerFromMTP(*data.vsaved_peer_id())
|
||||||
|
: PeerId();
|
||||||
if (const auto forwarded = data.vfwd_from()) {
|
if (const auto forwarded = data.vfwd_from()) {
|
||||||
forwarded->match([&](const MTPDmessageFwdHeader &data) {
|
forwarded->match([&](const MTPDmessageFwdHeader &data) {
|
||||||
FillForwardedInfo(config, data);
|
FillForwardedInfo(config, data);
|
||||||
|
|
|
@ -56,6 +56,7 @@ class ForumTopic;
|
||||||
class Thread;
|
class Thread;
|
||||||
struct SponsoredFrom;
|
struct SponsoredFrom;
|
||||||
class Story;
|
class Story;
|
||||||
|
class SavedSublist;
|
||||||
} // namespace Data
|
} // namespace Data
|
||||||
|
|
||||||
namespace Main {
|
namespace Main {
|
||||||
|
@ -485,6 +486,9 @@ public:
|
||||||
[[nodiscard]] QString originalPostAuthor() const;
|
[[nodiscard]] QString originalPostAuthor() const;
|
||||||
[[nodiscard]] MsgId originalId() const;
|
[[nodiscard]] MsgId originalId() const;
|
||||||
|
|
||||||
|
[[nodiscard]] Data::SavedSublist *savedSublist() const;
|
||||||
|
[[nodiscard]] PeerData *savedSublistPeer() const;
|
||||||
|
|
||||||
[[nodiscard]] bool isEmpty() const;
|
[[nodiscard]] bool isEmpty() const;
|
||||||
|
|
||||||
[[nodiscard]] MessageGroupId groupId() const;
|
[[nodiscard]] MessageGroupId groupId() const;
|
||||||
|
|
|
@ -141,7 +141,7 @@ struct HistoryMessageForwarded : public RuntimeComponent<HistoryMessageForwarded
|
||||||
};
|
};
|
||||||
|
|
||||||
struct HistoryMessageSaved : public RuntimeComponent<HistoryMessageSaved, HistoryItem> {
|
struct HistoryMessageSaved : public RuntimeComponent<HistoryMessageSaved, HistoryItem> {
|
||||||
PeerData *peer = nullptr;
|
Data::SavedSublist *sublist = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
class ReplyToMessagePointer final {
|
class ReplyToMessagePointer final {
|
||||||
|
|
|
@ -689,11 +689,11 @@ void TopBarWidget::infoClicked() {
|
||||||
return;
|
return;
|
||||||
} else if (const auto topic = key.topic()) {
|
} else if (const auto topic = key.topic()) {
|
||||||
_controller->showSection(std::make_shared<Info::Memento>(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>(
|
_controller->showSection(std::make_shared<Info::Memento>(
|
||||||
key.peer(),
|
key.peer(),
|
||||||
Info::Section(Storage::SharedMediaType::Photo)));
|
Info::Section::Type::SavedSublists));
|
||||||
} else if (key.peer()->isRepliesChat()) {
|
} else if (key.peer()->sharedMediaInfo()) {
|
||||||
_controller->showSection(std::make_shared<Info::Memento>(
|
_controller->showSection(std::make_shared<Info::Memento>(
|
||||||
key.peer(),
|
key.peer(),
|
||||||
Info::Section(Storage::SharedMediaType::Photo)));
|
Info::Section(Storage::SharedMediaType::Photo)));
|
||||||
|
|
|
@ -126,6 +126,7 @@ public:
|
||||||
Media,
|
Media,
|
||||||
CommonGroups,
|
CommonGroups,
|
||||||
SimilarChannels,
|
SimilarChannels,
|
||||||
|
SavedSublists,
|
||||||
Members,
|
Members,
|
||||||
Settings,
|
Settings,
|
||||||
Downloads,
|
Downloads,
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "info/media/info_media_widget.h"
|
#include "info/media/info_media_widget.h"
|
||||||
#include "info/members/info_members_widget.h"
|
#include "info/members/info_members_widget.h"
|
||||||
#include "info/common_groups/info_common_groups_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/settings/info_settings_widget.h"
|
||||||
#include "info/similar_channels/info_similar_channels_widget.h"
|
#include "info/similar_channels/info_similar_channels_widget.h"
|
||||||
#include "info/polls/info_polls_results_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) {
|
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::MediaType::Photo);
|
||||||
}
|
}
|
||||||
return Section(Section::Type::Profile);
|
return Section(Section::Type::Profile);
|
||||||
|
@ -145,6 +148,8 @@ std::shared_ptr<ContentMemento> Memento::DefaultContent(
|
||||||
case Section::Type::SimilarChannels:
|
case Section::Type::SimilarChannels:
|
||||||
return std::make_shared<SimilarChannels::Memento>(
|
return std::make_shared<SimilarChannels::Memento>(
|
||||||
peer->asChannel());
|
peer->asChannel());
|
||||||
|
case Section::Type::SavedSublists:
|
||||||
|
return std::make_shared<Saved::SublistsMemento>(&peer->session());
|
||||||
case Section::Type::Members:
|
case Section::Type::Members:
|
||||||
return std::make_shared<Members::Memento>(
|
return std::make_shared<Members::Memento>(
|
||||||
peer,
|
peer,
|
||||||
|
|
|
@ -188,23 +188,32 @@ void WrapWidget::injectActivePeerProfile(not_null<PeerData*> peer) {
|
||||||
? _historyStack.front().section->section().type()
|
? _historyStack.front().section->section().type()
|
||||||
: _controller->section().type();
|
: _controller->section().type();
|
||||||
const auto firstSectionMediaType = [&] {
|
const auto firstSectionMediaType = [&] {
|
||||||
if (firstSectionType == Section::Type::Profile) {
|
if (firstSectionType == Section::Type::Profile
|
||||||
|
|| firstSectionType == Section::Type::SavedSublists) {
|
||||||
return Section::MediaType::kCount;
|
return Section::MediaType::kCount;
|
||||||
}
|
}
|
||||||
return hasStackHistory()
|
return hasStackHistory()
|
||||||
? _historyStack.front().section->section().mediaType()
|
? _historyStack.front().section->section().mediaType()
|
||||||
: _controller->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::Media
|
||||||
: Section::Type::Profile;
|
: Section::Type::Profile;
|
||||||
const auto expectedMediaType = peer->sharedMediaInfo()
|
const auto expectedMediaType = savedSublistsInfo
|
||||||
|
? Section::MediaType::kCount
|
||||||
|
: sharedMediaInfo
|
||||||
? Section::MediaType::Photo
|
? Section::MediaType::Photo
|
||||||
: Section::MediaType::kCount;
|
: Section::MediaType::kCount;
|
||||||
if (firstSectionType != expectedType
|
if (firstSectionType != expectedType
|
||||||
|| firstSectionMediaType != expectedMediaType
|
|| firstSectionMediaType != expectedMediaType
|
||||||
|| firstPeer != peer) {
|
|| firstPeer != peer) {
|
||||||
auto section = peer->sharedMediaInfo()
|
auto section = savedSublistsInfo
|
||||||
|
? Section(Section::Type::SavedSublists)
|
||||||
|
: sharedMediaInfo
|
||||||
? Section(Section::MediaType::Photo)
|
? Section(Section::MediaType::Photo)
|
||||||
: Section(Section::Type::Profile);
|
: Section(Section::Type::Profile);
|
||||||
injectActiveProfileMemento(std::move(
|
injectActiveProfileMemento(std::move(
|
||||||
|
@ -545,6 +554,8 @@ void WrapWidget::removeFromStack(const std::vector<Section> §ions) {
|
||||||
const auto &s = item.section->section();
|
const auto &s = item.section->section();
|
||||||
if (s.type() != section.type()) {
|
if (s.type() != section.type()) {
|
||||||
return false;
|
return false;
|
||||||
|
} else if (s.type() == Section::Type::SavedSublists) {
|
||||||
|
return true;
|
||||||
} else if (s.type() == Section::Type::Media) {
|
} else if (s.type() == Section::Type::Media) {
|
||||||
return (s.mediaType() == section.mediaType());
|
return (s.mediaType() == section.mediaType());
|
||||||
} else if (s.type() == Section::Type::Settings) {
|
} else if (s.type() == Section::Type::Settings) {
|
||||||
|
|
|
@ -43,7 +43,7 @@ InnerWidget::InnerWidget(
|
||||||
// Allows showing additional shared media links and tabs.
|
// Allows showing additional shared media links and tabs.
|
||||||
// Used for shared media in Saved Messages.
|
// Used for shared media in Saved Messages.
|
||||||
void InnerWidget::setupOtherTypes() {
|
void InnerWidget::setupOtherTypes() {
|
||||||
if (_controller->key().peer()->isSelf() && _isStackBottom) {
|
if (_controller->key().peer()->sharedMediaInfo() && _isStackBottom) {
|
||||||
createOtherTypes();
|
createOtherTypes();
|
||||||
} else {
|
} else {
|
||||||
_otherTypes.destroy();
|
_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
|
} // namespace Info::SimilarChannels
|
||||||
|
|
||||||
|
|
|
@ -68,4 +68,3 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Info::SimilarChannels
|
} // namespace Info::SimilarChannels
|
||||||
|
|
||||||
|
|
|
@ -500,7 +500,7 @@ void Filler::addTogglePin() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Filler::addToggleMuteSubmenu(bool addSeparator) {
|
void Filler::addToggleMuteSubmenu(bool addSeparator) {
|
||||||
if (_thread->peer()->isSelf()) {
|
if (!_thread || _thread->peer()->isSelf()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
PeerMenuAddMuteSubmenuAction(_controller, _thread, _addAction);
|
PeerMenuAddMuteSubmenuAction(_controller, _thread, _addAction);
|
||||||
|
@ -526,6 +526,8 @@ void Filler::addSupportInfo() {
|
||||||
void Filler::addInfo() {
|
void Filler::addInfo() {
|
||||||
if (_peer && (_peer->isSelf() || _peer->isRepliesChat())) {
|
if (_peer && (_peer->isSelf() || _peer->isRepliesChat())) {
|
||||||
return;
|
return;
|
||||||
|
} else if (!_thread) {
|
||||||
|
return;
|
||||||
} else if (_controller->adaptive().isThreeColumn()) {
|
} else if (_controller->adaptive().isThreeColumn()) {
|
||||||
const auto thread = _controller->activeChatCurrent().thread();
|
const auto thread = _controller->activeChatCurrent().thread();
|
||||||
if (thread && thread == _thread) {
|
if (thread && thread == _thread) {
|
||||||
|
@ -534,8 +536,6 @@ void Filler::addInfo() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (!_thread) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
const auto controller = _controller;
|
const auto controller = _controller;
|
||||||
const auto weak = base::make_weak(_thread);
|
const auto weak = base::make_weak(_thread);
|
||||||
|
|
Loading…
Add table
Reference in a new issue