Support forwarding to monoforums.

This commit is contained in:
John Preston 2025-05-13 18:18:24 +04:00
parent e17bf18350
commit 76db55ff19
36 changed files with 516 additions and 78 deletions

View file

@ -388,7 +388,7 @@ void ApiWrap::savePinnedOrder(not_null<Data::SavedMessages*> saved) {
const auto &order = _session->data().pinnedChatsOrder(saved); const auto &order = _session->data().pinnedChatsOrder(saved);
const auto input = [](Dialogs::Key key) { const auto input = [](Dialogs::Key key) {
if (const auto sublist = key.sublist()) { if (const auto sublist = key.sublist()) {
return MTP_inputDialogPeer(sublist->peer()->input); return MTP_inputDialogPeer(sublist->sublistPeer()->input);
} }
Unexpected("Key type in pinnedDialogsOrder()."); Unexpected("Key type in pinnedDialogsOrder().");
}; };
@ -3303,6 +3303,13 @@ void ApiWrap::forwardMessages(
if (topMsgId) { if (topMsgId) {
sendFlags |= SendFlag::f_top_msg_id; sendFlags |= SendFlag::f_top_msg_id;
} }
const auto monoforumPeerId = action.replyTo.monoforumPeerId;
const auto monoforumPeer = monoforumPeerId
? session().data().peer(monoforumPeerId).get()
: nullptr;
if (monoforumPeer) {
sendFlags |= SendFlag::f_reply_to;
}
auto forwardFrom = draft.items.front()->history()->peer; auto forwardFrom = draft.items.front()->history()->peer;
auto ids = QVector<MTPint>(); auto ids = QVector<MTPint>();
@ -3332,7 +3339,9 @@ void ApiWrap::forwardMessages(
MTP_vector<MTPlong>(randomIds), MTP_vector<MTPlong>(randomIds),
peer->input, peer->input,
MTP_int(topMsgId), MTP_int(topMsgId),
MTPInputReplyTo(), (monoforumPeer
? MTP_inputReplyToMonoForum(monoforumPeer->input)
: MTPInputReplyTo()),
MTP_int(action.options.scheduled), MTP_int(action.options.scheduled),
(sendAs ? sendAs->input : MTP_inputPeerEmpty()), (sendAs ? sendAs->input : MTP_inputPeerEmpty()),
Data::ShortcutIdToMTP(_session, action.options.shortcutId), Data::ShortcutIdToMTP(_session, action.options.shortcutId),
@ -3379,7 +3388,10 @@ void ApiWrap::forwardMessages(
.id = newId.msg, .id = newId.msg,
.flags = flags, .flags = flags,
.from = NewMessageFromId(action), .from = NewMessageFromId(action),
.replyTo = { .topicRootId = topMsgId }, .replyTo = {
.topicRootId = topMsgId,
.monoforumPeerId = monoforumPeerId,
},
.date = NewMessageDate(action.options), .date = NewMessageDate(action.options),
.shortcutId = action.options.shortcutId, .shortcutId = action.options.shortcutId,
.starsPaid = action.options.starsApproved, .starsPaid = action.options.starsApproved,

View file

@ -23,6 +23,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "data/data_peer_values.h" #include "data/data_peer_values.h"
#include "data/data_saved_messages.h"
#include "data/data_saved_sublist.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_stories.h" #include "data/data_stories.h"
#include "data/data_channel.h" #include "data/data_channel.h"
@ -867,6 +869,45 @@ void ChooseRecipientBoxController::rowClicked(not_null<PeerListRow*> row) {
*weak = owned.data(); *weak = owned.data();
delegate()->peerListUiShow()->showBox(std::move(owned)); delegate()->peerListUiShow()->showBox(std::move(owned));
return; return;
} else if (const auto monoforum = peer->monoforum()) {
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
auto callback = [=](not_null<Data::SavedSublist*> sublist) {
const auto exists = guard.get();
if (!exists) {
if (*weak) {
(*weak)->closeBox();
}
return;
}
auto onstack = std::move(_callback);
onstack(sublist);
if (guard) {
_callback = std::move(onstack);
} else if (*weak) {
(*weak)->closeBox();
}
};
const auto filter = [=](not_null<Data::SavedSublist*> sublist) {
return guard && (!_filter || _filter(sublist));
};
auto owned = Box<PeerListBox>(
std::make_unique<ChooseSublistBoxController>(
monoforum,
std::move(callback),
filter),
[=](not_null<PeerListBox*> box) {
box->addButton(tr::lng_cancel(), [=] {
box->closeBox();
});
monoforum->destroyed(
) | rpl::start_with_next([=] {
box->closeBox();
}, box->lifetime());
});
*weak = owned.data();
delegate()->peerListUiShow()->showBox(std::move(owned));
return;
} }
const auto history = peer->owner().history(peer); const auto history = peer->owner().history(peer);
auto callback = std::move(_callback); auto callback = std::move(_callback);
@ -1137,6 +1178,111 @@ auto ChooseTopicBoxController::createRow(not_null<Data::ForumTopic*> topic)
return skip ? nullptr : std::make_unique<Row>(topic); return skip ? nullptr : std::make_unique<Row>(topic);
}; };
ChooseSublistBoxController::ChooseSublistBoxController(
not_null<Data::SavedMessages*> monoforum,
FnMut<void(not_null<Data::SavedSublist*>)> callback,
Fn<bool(not_null<Data::SavedSublist*>)> filter)
: _monoforum(monoforum)
, _callback(std::move(callback))
, _filter(std::move(filter)) {
setStyleOverrides(&st::chooseTopicList);
_monoforum->chatsListChanges(
) | rpl::start_with_next([=] {
refreshRows();
}, lifetime());
_monoforum->sublistDestroyed(
) | rpl::start_with_next([=](not_null<Data::SavedSublist*> sublist) {
const auto id = sublist->sublistPeer()->id.value;
if (const auto row = delegate()->peerListFindRow(id)) {
delegate()->peerListRemoveRow(row);
delegate()->peerListRefreshRows();
}
}, lifetime());
}
Main::Session &ChooseSublistBoxController::session() const {
return _monoforum->session();
}
void ChooseSublistBoxController::rowClicked(not_null<PeerListRow*> row) {
const auto weak = base::make_weak(this);
auto onstack = base::take(_callback);
onstack(_monoforum->sublist(row->peer()));
if (weak) {
_callback = std::move(onstack);
}
}
void ChooseSublistBoxController::prepare() {
delegate()->peerListSetTitle(tr::lng_forward_choose());
setSearchNoResultsText(tr::lng_topics_not_found(tr::now));
delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled);
refreshRows(true);
session().changes().entryUpdates(
Data::EntryUpdate::Flag::Repaint
) | rpl::start_with_next([=](const Data::EntryUpdate &update) {
if (const auto sublist = update.entry->asSublist()) {
if (sublist->parent() == _monoforum) {
const auto id = sublist->sublistPeer()->id.value;
if (const auto row = delegate()->peerListFindRow(id)) {
delegate()->peerListUpdateRow(row);
}
}
}
}, lifetime());
}
void ChooseSublistBoxController::refreshRows(bool initial) {
auto added = false;
for (const auto &row : _monoforum->chatsList()->indexed()->all()) {
if (const auto sublist = row->sublist()) {
const auto id = sublist->sublistPeer()->id.value;
auto already = delegate()->peerListFindRow(id);
if (initial || !already) {
if (auto created = createRow(sublist)) {
delegate()->peerListAppendRow(std::move(created));
added = true;
}
} else if (already->isSearchResult()) {
delegate()->peerListAppendFoundRow(already);
added = true;
}
}
}
if (added) {
delegate()->peerListRefreshRows();
}
}
void ChooseSublistBoxController::loadMoreRows() {
_monoforum->loadMore();
}
std::unique_ptr<PeerListRow> ChooseSublistBoxController::createSearchRow(
PeerListRowId id) {
const auto peer = session().data().peer(PeerId(id));
if (const auto sublist = _monoforum->sublistLoaded(peer)) {
auto result = std::make_unique<PeerListRow>(sublist->sublistPeer());
result->setCustomStatus(QString());
return result;
}
return nullptr;
}
auto ChooseSublistBoxController::createRow(
not_null<Data::SavedSublist*> sublist)
-> std::unique_ptr<PeerListRow> {
if (const auto skip = _filter && !_filter(sublist)) {
return nullptr;
}
auto result = std::make_unique<PeerListRow>(sublist->sublistPeer());
result->setCustomStatus(QString());
return result;
};
void PaintRestrictionBadge( void PaintRestrictionBadge(
Painter &p, Painter &p,
not_null<const style::PeerListItem*> st, not_null<const style::PeerListItem*> st,

View file

@ -27,6 +27,8 @@ namespace Data {
class Thread; class Thread;
class Forum; class Forum;
class ForumTopic; class ForumTopic;
class SavedSublist;
class SavedMessages;
} // namespace Data } // namespace Data
namespace Ui { namespace Ui {
@ -393,3 +395,30 @@ private:
Fn<bool(not_null<Data::ForumTopic*>)> _filter; Fn<bool(not_null<Data::ForumTopic*>)> _filter;
}; };
class ChooseSublistBoxController final
: public PeerListController
, public base::has_weak_ptr {
public:
ChooseSublistBoxController(
not_null<Data::SavedMessages*> monoforum,
FnMut<void(not_null<Data::SavedSublist*>)> callback,
Fn<bool(not_null<Data::SavedSublist*>)> filter = nullptr);
Main::Session &session() const override;
void rowClicked(not_null<PeerListRow*> row) override;
void prepare() override;
void loadMoreRows() override;
std::unique_ptr<PeerListRow> createSearchRow(PeerListRowId id) override;
private:
void refreshRows(bool initial = false);
[[nodiscard]] std::unique_ptr<PeerListRow> createRow(
not_null<Data::SavedSublist*> sublist);
const not_null<Data::SavedMessages*> _monoforum;
FnMut<void(not_null<Data::SavedSublist*>)> _callback;
Fn<bool(not_null<Data::SavedSublist*>)> _filter;
};

View file

@ -22,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_forum_topic.h" #include "data/data_forum_topic.h"
#include "data/data_histories.h" #include "data/data_histories.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_saved_sublist.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/stickers/data_custom_emoji.h" #include "data/stickers/data_custom_emoji.h"
@ -1163,8 +1164,11 @@ void SingleRowController::prepare() {
return; return;
} }
const auto topic = strong->asTopic(); const auto topic = strong->asTopic();
const auto sublist = strong->asSublist();
auto row = topic auto row = topic
? ChooseTopicBoxController::MakeRow(topic) ? ChooseTopicBoxController::MakeRow(topic)
: sublist
? std::make_unique<PeerListRow>(sublist->sublistPeer())
: std::make_unique<PeerListRow>(strong->peer()); : std::make_unique<PeerListRow>(strong->peer());
const auto raw = row.get(); const auto raw = row.get();
if (_status) { if (_status) {

View file

@ -45,6 +45,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_histories.h" #include "data/data_histories.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_peer_values.h" #include "data/data_peer_values.h"
#include "data/data_saved_messages.h"
#include "data/data_saved_sublist.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_folder.h" #include "data/data_folder.h"
#include "data/data_forum.h" #include "data/data_forum.h"
@ -114,7 +116,9 @@ private:
not_null<History*> history; not_null<History*> history;
not_null<PeerData*> peer; not_null<PeerData*> peer;
Data::ForumTopic *topic = nullptr; Data::ForumTopic *topic = nullptr;
Data::SavedSublist *sublist = nullptr;
rpl::lifetime topicLifetime; rpl::lifetime topicLifetime;
rpl::lifetime sublistLifetime;
Ui::RoundImageCheckbox checkbox; Ui::RoundImageCheckbox checkbox;
Ui::Text::String name; Ui::Text::String name;
Ui::Animations::Simple nameActive; Ui::Animations::Simple nameActive;
@ -143,6 +147,7 @@ private:
void preloadUserpic(not_null<Dialogs::Entry*> entry); void preloadUserpic(not_null<Dialogs::Entry*> entry);
void changeCheckState(Chat *chat); void changeCheckState(Chat *chat);
void chooseForumTopic(not_null<Data::Forum*> forum); void chooseForumTopic(not_null<Data::Forum*> forum);
void chooseMonoforumSublist(not_null<Data::SavedMessages*> monoforum);
enum class ChangeStateWay { enum class ChangeStateWay {
Default, Default,
SkipCallback, SkipCallback,
@ -638,15 +643,18 @@ void ShareBox::addPeerToMultiSelect(not_null<Data::Thread*> thread) {
auto addItemWay = Ui::MultiSelect::AddItemWay::Default; auto addItemWay = Ui::MultiSelect::AddItemWay::Default;
const auto peer = thread->peer(); const auto peer = thread->peer();
const auto topic = thread->asTopic(); const auto topic = thread->asTopic();
const auto sublist = thread->asSublist();
_select->addItem( _select->addItem(
peer->id.value, peer->id.value,
(topic (topic
? topic->title() ? topic->title()
: sublist
? sublist->sublistPeer()->shortName()
: peer->isSelf() : peer->isSelf()
? tr::lng_saved_short(tr::now) ? tr::lng_saved_short(tr::now)
: peer->shortName()), : peer->shortName()),
st::activeButtonBg, st::activeButtonBg,
(topic ((topic || sublist)
? ForceRoundUserpicCallback(peer) ? ForceRoundUserpicCallback(peer)
: PaintUserpicCallback(peer, true)), : PaintUserpicCallback(peer, true)),
addItemWay); addItemWay);
@ -970,6 +978,8 @@ void ShareBox::Inner::updateChatName(not_null<Chat*> chat) {
const auto peer = chat->peer; const auto peer = chat->peer;
const auto text = chat->topic const auto text = chat->topic
? chat->topic->title() ? chat->topic->title()
: chat->sublist
? chat->sublist->sublistPeer()->name()
: peer->isSelf() : peer->isSelf()
? tr::lng_saved_messages(tr::now) ? tr::lng_saved_messages(tr::now)
: peer->isRepliesChat() : peer->isRepliesChat()
@ -1209,7 +1219,7 @@ ShareBox::Inner::Chat::Chat(
st.checkbox, st.checkbox,
updateCallback, updateCallback,
PaintUserpicCallback(peer, true), PaintUserpicCallback(peer, true),
[=](int size) { return peer->isForum() [=](int size) { return (peer->isForum() || peer->isMonoforum())
? int(size * Ui::ForumUserpicRadiusMultiplier()) ? int(size * Ui::ForumUserpicRadiusMultiplier())
: std::optional<int>(); }) : std::optional<int>(); })
, name(st.checkbox.imageRadius * 2) { , name(st.checkbox.imageRadius * 2) {
@ -1350,10 +1360,13 @@ void ShareBox::Inner::changeCheckState(Chat *chat) {
const auto checked = chat->checkbox.checked(); const auto checked = chat->checkbox.checked();
const auto forum = chat->peer->forum(); const auto forum = chat->peer->forum();
if (checked || !forum) { const auto monoforum = chat->peer->monoforum();
if (checked || (!forum && !monoforum)) {
changePeerCheckState(chat, !checked); changePeerCheckState(chat, !checked);
} else { } else if (forum) {
chooseForumTopic(chat->peer->forum()); chooseForumTopic(forum);
} else if (monoforum) {
chooseMonoforumSublist(monoforum);
} }
} }
@ -1404,6 +1417,54 @@ void ShareBox::Inner::chooseForumTopic(not_null<Data::Forum*> forum) {
_show->showBox(std::move(box)); _show->showBox(std::move(box));
} }
void ShareBox::Inner::chooseMonoforumSublist(
not_null<Data::SavedMessages*> monoforum) {
const auto guard = Ui::MakeWeak(this);
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
auto chosen = [=](not_null<Data::SavedSublist*> sublist) {
if (const auto strong = *weak) {
strong->closeBox();
}
if (!guard) {
return;
}
const auto row = _chatsIndexed->getRow(sublist->owningHistory());
if (!row) {
return;
}
const auto chat = getChat(row);
Assert(!chat->sublist);
chat->sublist = sublist;
chat->sublist->destroyed(
) | rpl::start_with_next([=] {
changePeerCheckState(chat, false);
}, chat->sublistLifetime);
updateChatName(chat);
changePeerCheckState(chat, true);
};
auto initBox = [=](not_null<PeerListBox*> box) {
box->addButton(tr::lng_cancel(), [=] {
box->closeBox();
});
monoforum->destroyed(
) | rpl::start_with_next([=] {
box->closeBox();
}, box->lifetime());
};
auto filter = [=](not_null<Data::SavedSublist*> sublist) {
return guard && _descriptor.filterCallback(sublist);
};
auto box = Box<PeerListBox>(
std::make_unique<ChooseSublistBoxController>(
monoforum,
std::move(chosen),
std::move(filter)),
std::move(initBox));
*weak = box.data();
_show->showBox(std::move(box));
}
void ShareBox::Inner::peerUnselected(not_null<PeerData*> peer) { void ShareBox::Inner::peerUnselected(not_null<PeerData*> peer) {
if (const auto i = _dataMap.find(peer); i != end(_dataMap)) { if (const auto i = _dataMap.find(peer); i != end(_dataMap)) {
changePeerCheckState( changePeerCheckState(
@ -1434,6 +1495,11 @@ void ShareBox::Inner::changePeerCheckState(
chat->topic = nullptr; chat->topic = nullptr;
updateChatName(chat); updateChatName(chat);
} }
if (chat->sublist) {
chat->sublistLifetime.destroy();
chat->sublist = nullptr;
updateChatName(chat);
}
} }
if (useCallback != ChangeStateWay::SkipCallback if (useCallback != ChangeStateWay::SkipCallback
&& _peerSelectedChangedCallback) { && _peerSelectedChangedCallback) {
@ -1565,6 +1631,8 @@ not_null<Data::Thread*> ShareBox::Inner::chatThread(
not_null<Chat*> chat) const { not_null<Chat*> chat) const {
return chat->topic return chat->topic
? (Data::Thread*)chat->topic ? (Data::Thread*)chat->topic
: chat->sublist
? (Data::Thread*)chat->sublist
: chat->peer->owner().history(chat->peer).get(); : chat->peer->owner().history(chat->peer).get();
} }
@ -1675,6 +1743,7 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
api.sendMessage(std::move(message)); api.sendMessage(std::move(message));
} }
const auto topicRootId = thread->topicRootId(); const auto topicRootId = thread->topicRootId();
const auto sublistPeer = thread->maybeSublistPeer();
const auto kGeneralId = Data::ForumTopic::kGeneralId; const auto kGeneralId = Data::ForumTopic::kGeneralId;
const auto topMsgId = (topicRootId == kGeneralId) const auto topMsgId = (topicRootId == kGeneralId)
? MsgId(0) ? MsgId(0)
@ -1699,7 +1768,8 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
| (options.shortcutId | (options.shortcutId
? Flag::f_quick_reply_shortcut ? Flag::f_quick_reply_shortcut
: Flag(0)) : Flag(0))
| (starsPaid ? Flag::f_allow_paid_stars : Flag()); | (starsPaid ? Flag::f_allow_paid_stars : Flag())
| (sublistPeer ? Flag::f_reply_to : Flag());
threadHistory->sendRequestId = api.request( threadHistory->sendRequestId = api.request(
MTPmessages_ForwardMessages( MTPmessages_ForwardMessages(
MTP_flags(sendFlags), MTP_flags(sendFlags),
@ -1708,7 +1778,9 @@ ShareBox::SubmitCallback ShareBox::DefaultForwardCallback(
MTP_vector<MTPlong>(generateRandom()), MTP_vector<MTPlong>(generateRandom()),
peer->input, peer->input,
MTP_int(topMsgId), MTP_int(topMsgId),
MTPInputReplyTo(), (sublistPeer
? MTP_inputReplyToMonoForum(sublistPeer->input)
: MTPInputReplyTo()),
MTP_int(options.scheduled), MTP_int(options.scheduled),
MTP_inputPeerEmpty(), // send_as MTP_inputPeerEmpty(), // send_as
Data::ShortcutIdToMTP(session, options.shortcutId), Data::ShortcutIdToMTP(session, options.shortcutId),

View file

@ -228,6 +228,7 @@ void ChannelData::setFlags(ChannelDataFlags which) {
} }
} }
if (diff & (Flag::Forum if (diff & (Flag::Forum
| Flag::Monoforum
| Flag::CallNotEmpty | Flag::CallNotEmpty
| Flag::SimilarExpanded | Flag::SimilarExpanded
| Flag::Signatures | Flag::Signatures
@ -236,12 +237,14 @@ void ChannelData::setFlags(ChannelDataFlags which) {
if (diff & Flag::CallNotEmpty) { if (diff & Flag::CallNotEmpty) {
history->updateChatListEntry(); history->updateChatListEntry();
} }
if (diff & Flag::Forum) { if (diff & (Flag::Forum | Flag::Monoforum)) {
Core::App().notifications().clearFromHistory(history); Core::App().notifications().clearFromHistory(history);
history->updateChatListEntryHeight(); history->updateChatListEntryHeight();
if (history->inChatList()) { if (history->inChatList()) {
if (const auto forum = this->forum()) { if (const auto forum = this->forum()) {
forum->preloadTopics(); forum->preloadTopics();
} else if (const auto monoforum = this->monoforum()) {
monoforum->loadMore();
} }
} }
} }

View file

@ -48,7 +48,6 @@ Forum::Forum(not_null<History*> history)
, _topicsList(&session(), {}, owner().maxPinnedChatsLimitValue(this)) { , _topicsList(&session(), {}, owner().maxPinnedChatsLimitValue(this)) {
Expects(_history->peer->isChannel()); Expects(_history->peer->isChannel());
if (_history->inChatList()) { if (_history->inChatList()) {
preloadTopics(); preloadTopics();
} }

View file

@ -867,7 +867,7 @@ void ForumTopic::setMuted(bool muted) {
session().changes().topicUpdated(this, UpdateFlag::Notifications); session().changes().topicUpdated(this, UpdateFlag::Notifications);
} }
not_null<HistoryView::SendActionPainter*> ForumTopic::sendActionPainter() { HistoryView::SendActionPainter *ForumTopic::sendActionPainter() {
return _sendActionPainter.get(); return _sendActionPainter.get();
} }

View file

@ -181,7 +181,7 @@ public:
void setMuted(bool muted) override; void setMuted(bool muted) override;
[[nodiscard]] auto sendActionPainter() [[nodiscard]] auto sendActionPainter()
->not_null<HistoryView::SendActionPainter*> override; -> HistoryView::SendActionPainter* override;
private: private:
enum class Flag : uchar { enum class Flag : uchar {

View file

@ -1028,7 +1028,7 @@ void Reactions::requestMyTags(SavedSublist *sublist) {
using Flag = MTPmessages_GetSavedReactionTags::Flag; using Flag = MTPmessages_GetSavedReactionTags::Flag;
my.requestId = api.request(MTPmessages_GetSavedReactionTags( my.requestId = api.request(MTPmessages_GetSavedReactionTags(
MTP_flags(sublist ? Flag::f_peer : Flag()), MTP_flags(sublist ? Flag::f_peer : Flag()),
(sublist ? sublist->peer()->input : MTP_inputPeerEmpty()), (sublist ? sublist->sublistPeer()->input : MTP_inputPeerEmpty()),
MTP_long(my.hash) MTP_long(my.hash)
)).done([=](const MTPmessages_SavedReactionTags &result) { )).done([=](const MTPmessages_SavedReactionTags &result) {
auto &my = _myTags[sublist]; auto &my = _myTags[sublist];

View file

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_unread_things.h"
#include "main/main_session.h" #include "main/main_session.h"
namespace Data { namespace Data {
@ -23,6 +24,7 @@ constexpr auto kPerPage = 50;
constexpr auto kFirstPerPage = 10; constexpr auto kFirstPerPage = 10;
constexpr auto kListPerPage = 100; constexpr auto kListPerPage = 100;
constexpr auto kListFirstPerPage = 20; constexpr auto kListFirstPerPage = 20;
constexpr auto kLoadedSublistsMinCount = 20;
} // namespace } // namespace
@ -36,6 +38,10 @@ SavedMessages::SavedMessages(
FilterId(), FilterId(),
_owner->maxPinnedChatsLimitValue(this)) _owner->maxPinnedChatsLimitValue(this))
, _loadMore([=] { sendLoadMoreRequests(); }) { , _loadMore([=] { sendLoadMoreRequests(); }) {
if (_parentChat
&& _parentChat->owner().history(_parentChat)->inChatList()) {
preloadSublists();
}
} }
SavedMessages::~SavedMessages() = default; SavedMessages::~SavedMessages() = default;
@ -61,15 +67,19 @@ not_null<Dialogs::MainList*> SavedMessages::chatsList() {
} }
not_null<SavedSublist*> SavedMessages::sublist(not_null<PeerData*> peer) { not_null<SavedSublist*> SavedMessages::sublist(not_null<PeerData*> peer) {
const auto i = _sublists.find(peer); if (const auto loaded = sublistLoaded(peer)) {
if (i != end(_sublists)) { return loaded;
return i->second.get();
} }
return _sublists.emplace( return _sublists.emplace(
peer, peer,
std::make_unique<SavedSublist>(this, peer)).first->second.get(); std::make_unique<SavedSublist>(this, peer)).first->second.get();
} }
SavedSublist *SavedMessages::sublistLoaded(not_null<PeerData*> peer) {
const auto i = _sublists.find(peer);
return (i != end(_sublists)) ? i->second.get() : nullptr;
}
rpl::producer<> SavedMessages::chatsListChanges() const { rpl::producer<> SavedMessages::chatsListChanges() const {
return _chatsListChanges.events(); return _chatsListChanges.events();
} }
@ -78,6 +88,13 @@ rpl::producer<> SavedMessages::chatsListLoadedEvents() const {
return _chatsListLoadedEvents.events(); return _chatsListLoadedEvents.events();
} }
void SavedMessages::preloadSublists() {
if (parentChat()
&& chatsList()->indexed()->size() < kLoadedSublistsMinCount) {
loadMore();
}
}
void SavedMessages::loadMore() { void SavedMessages::loadMore() {
_loadMoreScheduled = true; _loadMoreScheduled = true;
_loadMore.call(); _loadMore.call();
@ -152,7 +169,7 @@ void SavedMessages::sendLoadMore(not_null<SavedSublist*> sublist) {
MTPmessages_GetSavedHistory( MTPmessages_GetSavedHistory(
MTP_flags(_parentChat ? Flag::f_parent_peer : Flag(0)), MTP_flags(_parentChat ? Flag::f_parent_peer : Flag(0)),
_parentChat ? _parentChat->input : MTPInputPeer(), _parentChat ? _parentChat->input : MTPInputPeer(),
sublist->peer()->input, sublist->sublistPeer()->input,
MTP_int(offsetId), MTP_int(offsetId),
MTP_int(offsetDate), MTP_int(offsetDate),
MTP_int(0), // add_offset MTP_int(0), // add_offset

View file

@ -33,6 +33,7 @@ public:
[[nodiscard]] not_null<Dialogs::MainList*> chatsList(); [[nodiscard]] not_null<Dialogs::MainList*> chatsList();
[[nodiscard]] not_null<SavedSublist*> sublist(not_null<PeerData*> peer); [[nodiscard]] not_null<SavedSublist*> sublist(not_null<PeerData*> peer);
[[nodiscard]] SavedSublist *sublistLoaded(not_null<PeerData*> peer);
[[nodiscard]] rpl::producer<> chatsListChanges() const; [[nodiscard]] rpl::producer<> chatsListChanges() const;
[[nodiscard]] rpl::producer<> chatsListLoadedEvents() const; [[nodiscard]] rpl::producer<> chatsListLoadedEvents() const;
@ -41,6 +42,7 @@ public:
[[nodiscard]] auto sublistDestroyed() const [[nodiscard]] auto sublistDestroyed() const
-> rpl::producer<not_null<SavedSublist*>>; -> rpl::producer<not_null<SavedSublist*>>;
void preloadSublists();
void loadMore(); void loadMore();
void loadMore(not_null<SavedSublist*> sublist); void loadMore(not_null<SavedSublist*> sublist);

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/view/history_view_item_preview.h" #include "history/view/history_view_item_preview.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_unread_things.h"
#include "main/main_session.h" #include "main/main_session.h"
namespace Data { namespace Data {
@ -22,7 +23,7 @@ namespace Data {
SavedSublist::SavedSublist( SavedSublist::SavedSublist(
not_null<SavedMessages*> parent, not_null<SavedMessages*> parent,
not_null<PeerData*> peer) not_null<PeerData*> peer)
: Entry(&peer->owner(), Dialogs::Entry::Type::SavedSublist) : Thread(&peer->owner(), Dialogs::Entry::Type::SavedSublist)
, _parent(parent) , _parent(parent)
, _history(peer->owner().history(peer)) { , _history(peer->owner().history(peer)) {
} }
@ -33,7 +34,7 @@ not_null<SavedMessages*> SavedSublist::parent() const {
return _parent; return _parent;
} }
not_null<History*> SavedSublist::parentHistory() const { not_null<History*> SavedSublist::owningHistory() {
const auto chat = parentChat(); const auto chat = parentChat();
return _history->owner().history(chat return _history->owner().history(chat
? (PeerData*)chat ? (PeerData*)chat
@ -44,18 +45,27 @@ ChannelData *SavedSublist::parentChat() const {
return _parent->parentChat(); return _parent->parentChat();
} }
not_null<PeerData*> SavedSublist::peer() const { not_null<PeerData*> SavedSublist::sublistPeer() const {
return _history->peer; return _history->peer;
} }
bool SavedSublist::isHiddenAuthor() const { bool SavedSublist::isHiddenAuthor() const {
return peer()->isSavedHiddenAuthor(); return sublistPeer()->isSavedHiddenAuthor();
} }
bool SavedSublist::isFullLoaded() const { bool SavedSublist::isFullLoaded() const {
return (_flags & Flag::FullLoaded) != 0; return (_flags & Flag::FullLoaded) != 0;
} }
rpl::producer<> SavedSublist::destroyed() const {
using namespace rpl::mappers;
return rpl::merge(
_parent->destroyed(),
_parent->sublistDestroyed() | rpl::filter(
_1 == this
) | rpl::to_empty);
}
auto SavedSublist::messages() const auto SavedSublist::messages() const
-> const std::vector<not_null<HistoryItem*>> & { -> const std::vector<not_null<HistoryItem*>> & {
return _items; return _items;
@ -231,8 +241,39 @@ void SavedSublist::paintUserpic(
_history->paintUserpic(p, view, context); _history->paintUserpic(p, view, context);
} }
HistoryView::SendActionPainter *SavedSublist::sendActionPainter() {
return nullptr;
}
void SavedSublist::hasUnreadMentionChanged(bool has) {
auto was = chatListUnreadState();
if (has) {
was.mentions = 0;
} else {
was.mentions = 1;
}
notifyUnreadStateChange(was);
}
void SavedSublist::hasUnreadReactionChanged(bool has) {
auto was = chatListUnreadState();
if (has) {
was.reactions = was.reactionsMuted = 0;
} else {
was.reactions = 1;
was.reactionsMuted = muted() ? was.reactions : 0;
}
notifyUnreadStateChange(was);
}
bool SavedSublist::isServerSideUnread(
not_null<const HistoryItem*> item) const {
return false;
}
void SavedSublist::chatListPreloadData() { void SavedSublist::chatListPreloadData() {
peer()->loadUserpic(); sublistPeer()->loadUserpic();
allowChatListMessageResolve(); allowChatListMessageResolve();
} }

View file

@ -7,8 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include "data/data_thread.h"
#include "dialogs/ui/dialogs_message_view.h" #include "dialogs/ui/dialogs_message_view.h"
#include "dialogs/dialogs_entry.h"
class PeerData; class PeerData;
class History; class History;
@ -18,17 +18,18 @@ namespace Data {
class Session; class Session;
class SavedMessages; class SavedMessages;
class SavedSublist final : public Dialogs::Entry { class SavedSublist final : public Data::Thread {
public: public:
SavedSublist(not_null<SavedMessages*> parent,not_null<PeerData*> peer); SavedSublist(not_null<SavedMessages*> parent, not_null<PeerData*> peer);
~SavedSublist(); ~SavedSublist();
[[nodiscard]] not_null<SavedMessages*> parent() const; [[nodiscard]] not_null<SavedMessages*> parent() const;
[[nodiscard]] not_null<History*> parentHistory() const; [[nodiscard]] not_null<History*> owningHistory() override;
[[nodiscard]] ChannelData *parentChat() const; [[nodiscard]] ChannelData *parentChat() const;
[[nodiscard]] not_null<PeerData*> peer() const; [[nodiscard]] not_null<PeerData*> sublistPeer() const;
[[nodiscard]] bool isHiddenAuthor() const; [[nodiscard]] bool isHiddenAuthor() const;
[[nodiscard]] bool isFullLoaded() const; [[nodiscard]] bool isFullLoaded() const;
[[nodiscard]] rpl::producer<> destroyed() const;
[[nodiscard]] auto messages() const [[nodiscard]] auto messages() const
-> const std::vector<not_null<HistoryItem*>> &; -> const std::vector<not_null<HistoryItem*>> &;
@ -41,10 +42,6 @@ public:
[[nodiscard]] std::optional<int> fullCount() const; [[nodiscard]] std::optional<int> fullCount() const;
[[nodiscard]] rpl::producer<int> fullCountValue() const; [[nodiscard]] rpl::producer<int> fullCountValue() const;
[[nodiscard]] Dialogs::Ui::MessageView &lastItemDialogsView() {
return _lastItemDialogsView;
}
int fixedOnTopIndex() const override; int fixedOnTopIndex() const override;
bool shouldBeInChatList() const override; bool shouldBeInChatList() const override;
Dialogs::UnreadState chatListUnreadState() const override; Dialogs::UnreadState chatListUnreadState() const override;
@ -57,12 +54,21 @@ public:
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 hasUnreadMentionChanged(bool has) override;
void hasUnreadReactionChanged(bool has) override;
[[nodiscard]] bool isServerSideUnread(
not_null<const HistoryItem*> item) const override;
void chatListPreloadData() override; void chatListPreloadData() override;
void paintUserpic( void paintUserpic(
Painter &p, Painter &p,
Ui::PeerUserpicView &view, Ui::PeerUserpicView &view,
const Dialogs::Ui::PaintContext &context) const override; const Dialogs::Ui::PaintContext &context) const override;
[[nodiscard]] auto sendActionPainter()
-> HistoryView::SendActionPainter* override;
private: private:
enum class Flag : uchar { enum class Flag : uchar {
ResolveChatListMessage = (1 << 0), ResolveChatListMessage = (1 << 0),
@ -81,7 +87,6 @@ private:
std::vector<not_null<HistoryItem*>> _items; std::vector<not_null<HistoryItem*>> _items;
std::optional<int> _fullCount; std::optional<int> _fullCount;
rpl::event_stream<> _changed; rpl::event_stream<> _changed;
Dialogs::Ui::MessageView _lastItemDialogsView;
Flags _flags; Flags _flags;
}; };

View file

@ -4656,6 +4656,8 @@ void Session::refreshChatListEntry(Dialogs::Key key) {
} }
if (const auto forum = history->peer->forum()) { if (const auto forum = history->peer->forum()) {
forum->preloadTopics(); forum->preloadTopics();
} else if (const auto monoforum = history->peer->monoforum()) {
monoforum->preloadSublists();
} }
if (history->peer->isMonoforum() if (history->peer->isMonoforum()
&& !history->peer->monoforumBroadcast()) { && !history->peer->monoforumBroadcast()) {

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_forum_topic.h" #include "data/data_forum_topic.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_saved_sublist.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_unread_things.h" #include "history/history_unread_things.h"
@ -31,6 +32,13 @@ MsgId Thread::topicRootId() const {
return MsgId(); return MsgId();
} }
PeerData *Thread::maybeSublistPeer() const {
if (const auto sublist = asSublist()) {
return sublist->sublistPeer();
}
return nullptr;
}
not_null<PeerData*> Thread::peer() const { not_null<PeerData*> Thread::peer() const {
return owningHistory()->peer; return owningHistory()->peer;
} }

View file

@ -67,6 +67,7 @@ public:
return const_cast<Thread*>(this)->owningHistory(); return const_cast<Thread*>(this)->owningHistory();
} }
[[nodiscard]] MsgId topicRootId() const; [[nodiscard]] MsgId topicRootId() const;
[[nodiscard]] PeerData *maybeSublistPeer() const;
[[nodiscard]] not_null<PeerData*> peer() const; [[nodiscard]] not_null<PeerData*> peer() const;
[[nodiscard]] PeerNotifySettings &notify(); [[nodiscard]] PeerNotifySettings &notify();
[[nodiscard]] const PeerNotifySettings &notify() const; [[nodiscard]] const PeerNotifySettings &notify() const;
@ -112,7 +113,7 @@ public:
} }
[[nodiscard]] virtual auto sendActionPainter() [[nodiscard]] virtual auto sendActionPainter()
-> not_null<HistoryView::SendActionPainter*> = 0; -> HistoryView::SendActionPainter* = 0;
[[nodiscard]] bool hasPinnedMessages() const; [[nodiscard]] bool hasPinnedMessages() const;
void setHasPinnedMessages(bool has); void setHasPinnedMessages(bool has);

View file

@ -84,9 +84,9 @@ Entry::Entry(not_null<Data::Session*> owner, Type type)
, _flags((type == Type::History) , _flags((type == Type::History)
? (Flag::IsThread | Flag::IsHistory) ? (Flag::IsThread | Flag::IsHistory)
: (type == Type::ForumTopic) : (type == Type::ForumTopic)
? Flag::IsThread ? (Flag::IsThread | Flag::IsForumTopic)
: (type == Type::SavedSublist) : (type == Type::SavedSublist)
? Flag::IsSavedSublist ? (Flag::IsThread | Flag::IsSavedSublist)
: Flag(0)) { : Flag(0)) {
} }
@ -113,7 +113,7 @@ Data::Forum *Entry::asForum() {
} }
Data::Folder *Entry::asFolder() { Data::Folder *Entry::asFolder() {
return (_flags & (Flag::IsThread | Flag::IsSavedSublist)) return (_flags & Flag::IsThread)
? nullptr ? nullptr
: static_cast<Data::Folder*>(this); : static_cast<Data::Folder*>(this);
} }
@ -125,7 +125,7 @@ Data::Thread *Entry::asThread() {
} }
Data::ForumTopic *Entry::asTopic() { Data::ForumTopic *Entry::asTopic() {
return ((_flags & Flag::IsThread) && !(_flags & Flag::IsHistory)) return (_flags & Flag::IsForumTopic)
? static_cast<Data::ForumTopic*>(this) ? static_cast<Data::ForumTopic*>(this)
: nullptr; : nullptr;
} }

View file

@ -27,6 +27,7 @@ class Forum;
class Folder; class Folder;
class ForumTopic; class ForumTopic;
class SavedSublist; class SavedSublist;
class SavedMessages;
class Thread; class Thread;
} // namespace Data } // namespace Data
@ -168,9 +169,10 @@ private:
enum class Flag : uchar { enum class Flag : uchar {
IsThread = (1 << 0), IsThread = (1 << 0),
IsHistory = (1 << 1), IsHistory = (1 << 1),
IsSavedSublist = (1 << 2), IsForumTopic = (1 << 2),
UpdatePostponed = (1 << 3), IsSavedSublist = (1 << 3),
InUnreadChangeBlock = (1 << 4), UpdatePostponed = (1 << 4),
InUnreadChangeBlock = (1 << 5),
}; };
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>;

View file

@ -3444,7 +3444,7 @@ void InnerWidget::applySearchState(SearchState state) {
_searchFromShown = ignoreInChat _searchFromShown = ignoreInChat
? nullptr ? nullptr
: sublist : sublist
? sublist->peer().get() ? sublist->sublistPeer().get()
: state.fromPeer; : state.fromPeer;
if (state.inChat) { if (state.inChat) {
onHashtagFilterUpdate(QStringView()); onHashtagFilterUpdate(QStringView());
@ -4222,7 +4222,7 @@ void InnerWidget::updateSearchIn() {
const auto peerIcon = peer const auto peerIcon = peer
? Ui::MakeUserpicThumbnail(peer) ? Ui::MakeUserpicThumbnail(peer)
: sublist : sublist
? Ui::MakeUserpicThumbnail(sublist->peer()) ? Ui::MakeUserpicThumbnail(sublist->sublistPeer())
: nullptr; : nullptr;
const auto myIcon = Ui::MakeIconThumbnail(st::menuIconChats); const auto myIcon = Ui::MakeIconThumbnail(st::menuIconChats);
const auto publicIcon = (_searchHashOrCashtag != HashOrCashtag::None) const auto publicIcon = (_searchHashOrCashtag != HashOrCashtag::None)

View file

@ -1001,7 +1001,7 @@ void Widget::chosenRow(const ChosenRow &row) {
using namespace HistoryView; using namespace HistoryView;
controller()->showSection( controller()->showSection(
std::make_shared<ChatMemento>(ChatViewId{ std::make_shared<ChatMemento>(ChatViewId{
.history = sublist->parentHistory(), .history = sublist->owningHistory(),
.sublist = sublist, .sublist = sublist,
}), }),
params); params);
@ -2037,7 +2037,7 @@ void Widget::refreshTopBars() {
? Dialogs::Key(history) ? Dialogs::Key(history)
: Dialogs::Key(_openedFolder)), : Dialogs::Key(_openedFolder)),
.section = Dialogs::EntryState::Section::ChatsList, .section = Dialogs::EntryState::Section::ChatsList,
}, history ? history->sendActionPainter().get() : nullptr); }, history ? history->sendActionPainter() : nullptr);
if (_forumSearchRequested) { if (_forumSearchRequested) {
showSearchInTopBar(anim::type::instant); showSearchInTopBar(anim::type::instant);
} }
@ -2680,7 +2680,7 @@ bool Widget::search(bool inCache, SearchRequestDelay delay) {
: _searchState.inChat.sublist(); : _searchState.inChat.sublist();
const auto fromPeer = sublist ? nullptr : _searchQueryFrom; const auto fromPeer = sublist ? nullptr : _searchQueryFrom;
const auto savedPeer = sublist const auto savedPeer = sublist
? sublist->peer().get() ? sublist->sublistPeer().get()
: nullptr; : nullptr;
_historiesRequest = histories.sendRequest(history, type, [=]( _historiesRequest = histories.sendRequest(history, type, [=](
Fn<void()> finish) { Fn<void()> finish) {
@ -2856,7 +2856,7 @@ void Widget::searchMore() {
: _searchState.inChat.sublist(); : _searchState.inChat.sublist();
const auto fromPeer = sublist ? nullptr : _searchQueryFrom; const auto fromPeer = sublist ? nullptr : _searchQueryFrom;
const auto savedPeer = sublist const auto savedPeer = sublist
? sublist->peer().get() ? sublist->sublistPeer().get()
: nullptr; : nullptr;
_historiesRequest = histories.sendRequest(history, type, [=]( _historiesRequest = histories.sendRequest(history, type, [=](
Fn<void()> finish) { Fn<void()> finish) {
@ -4284,8 +4284,12 @@ PeerData *Widget::searchInPeer() const {
? nullptr ? nullptr
: _openedForum : _openedForum
? _openedForum->channel().get() ? _openedForum->channel().get()
: _openedMonoforum
? (_openedMonoforum->parentChat()
? _openedMonoforum->parentChat()
: (PeerData*)session().user().get())
: _searchState.inChat.sublist() : _searchState.inChat.sublist()
? session().user().get() ? _searchState.inChat.sublist()->owningHistory()->peer.get()
: _searchState.inChat.peer(); : _searchState.inChat.peer();
} }

View file

@ -62,7 +62,7 @@ const auto kPsaBadgePrefix = "cloud_lng_badge_psa_";
[[nodiscard]] bool ShowSendActionInDialogs(Data::Thread *thread) { [[nodiscard]] bool ShowSendActionInDialogs(Data::Thread *thread) {
const auto history = thread ? thread->owningHistory().get() : nullptr; const auto history = thread ? thread->owningHistory().get() : nullptr;
if (!history) { if (!history || thread->asSublist()) {
return false; return false;
} else if (const auto user = history->peer->asUser()) { } else if (const auto user = history->peer->asUser()) {
return !user->lastseen().isHidden(); return !user->lastseen().isHidden();
@ -994,7 +994,7 @@ void RowPainter::Paint(
? history->peer->migrateTo() ? history->peer->migrateTo()
: history->peer.get()) : history->peer.get())
: sublist : sublist
? sublist->peer().get() ? sublist->sublistPeer().get()
: nullptr; : nullptr;
const auto allowUserOnline = true;// !context.narrow || badgesState.empty(); const auto allowUserOnline = true;// !context.narrow || badgesState.empty();
const auto flags = (allowUserOnline ? Flag::AllowUserOnline : Flag(0)) const auto flags = (allowUserOnline ? Flag::AllowUserOnline : Flag(0))

View file

@ -273,7 +273,7 @@ public:
void setHasPendingResizedItems(); void setHasPendingResizedItems();
[[nodiscard]] auto sendActionPainter() [[nodiscard]] auto sendActionPainter()
-> not_null<HistoryView::SendActionPainter*> override { -> HistoryView::SendActionPainter* override {
return &_sendActionPainter; return &_sendActionPainter;
} }

View file

@ -537,6 +537,7 @@ HistoryItem::HistoryItem(
const auto topicRootId = fields.replyTo.topicRootId; const auto topicRootId = fields.replyTo.topicRootId;
config.reply.messageId = config.reply.topMessageId = topicRootId; config.reply.messageId = config.reply.topMessageId = topicRootId;
config.reply.topicPost = (topicRootId != 0) ? 1 : 0; config.reply.topicPost = (topicRootId != 0) ? 1 : 0;
config.reply.monoforumPeerId = fields.replyTo.monoforumPeerId;
if (const auto originalReply = original->Get<HistoryMessageReply>()) { if (const auto originalReply = original->Get<HistoryMessageReply>()) {
if (originalReply->external()) { if (originalReply->external()) {
config.reply = originalReply->fields().clone(this); config.reply = originalReply->fields().clone(this);
@ -3579,7 +3580,7 @@ Data::SavedSublist *HistoryItem::savedSublist() const {
PeerData *HistoryItem::savedSublistPeer() const { PeerData *HistoryItem::savedSublistPeer() const {
if (const auto sublist = savedSublist()) { if (const auto sublist = savedSublist()) {
return sublist->peer(); return sublist->sublistPeer();
} }
return nullptr; return nullptr;
} }

View file

@ -503,6 +503,8 @@ TimeId NewMessageDate(const Api::SendOptions &options) {
PeerId NewMessageFromId(const Api::SendAction &action) { PeerId NewMessageFromId(const Api::SendAction &action) {
return action.options.sendAs return action.options.sendAs
? action.options.sendAs->id ? action.options.sendAs->id
: action.history->peer->amMonoforumAdmin()
? action.history->peer->monoforumBroadcast()->id
: action.history->peer->amAnonymous() : action.history->peer->amAnonymous()
? PeerId() ? PeerId()
: action.history->session().userPeerId(); : action.history->session().userPeerId();

View file

@ -1693,7 +1693,7 @@ SendMenu::Details ChatWidget::sendMenuDetails() const {
FullReplyTo ChatWidget::replyTo() const { FullReplyTo ChatWidget::replyTo() const {
const auto monoforumPeerId = (_sublist && _sublist->parentChat()) const auto monoforumPeerId = (_sublist && _sublist->parentChat())
? _sublist->peer()->id ? _sublist->sublistPeer()->id
: PeerId(); : PeerId();
if (auto custom = _composeControls->replyingToMessage()) { if (auto custom = _composeControls->replyingToMessage()) {
const auto item = custom.messageId const auto item = custom.messageId
@ -1786,7 +1786,7 @@ void ChatWidget::checkLastPinnedClickedIdReset(
} }
void ChatWidget::setupOpenChatButton() { void ChatWidget::setupOpenChatButton() {
if (!_sublist || _sublist->peer()->isSavedHiddenAuthor()) { if (!_sublist || _sublist->sublistPeer()->isSavedHiddenAuthor()) {
return; return;
} else if (_sublist->parentChat()) { } else if (_sublist->parentChat()) {
_canSendTexts = true; _canSendTexts = true;
@ -1794,22 +1794,22 @@ void ChatWidget::setupOpenChatButton() {
} }
_openChatButton = std::make_unique<Ui::FlatButton>( _openChatButton = std::make_unique<Ui::FlatButton>(
this, this,
(_sublist->peer()->isBroadcast() (_sublist->sublistPeer()->isBroadcast()
? tr::lng_saved_open_channel(tr::now) ? tr::lng_saved_open_channel(tr::now)
: _sublist->peer()->isUser() : _sublist->sublistPeer()->isUser()
? tr::lng_saved_open_chat(tr::now) ? tr::lng_saved_open_chat(tr::now)
: tr::lng_saved_open_group(tr::now)), : tr::lng_saved_open_group(tr::now)),
st::historyComposeButton); st::historyComposeButton);
_openChatButton->setClickedCallback([=] { _openChatButton->setClickedCallback([=] {
controller()->showPeerHistory( controller()->showPeerHistory(
_sublist->peer(), _sublist->sublistPeer(),
Window::SectionShow::Way::Forward); Window::SectionShow::Way::Forward);
}); });
} }
void ChatWidget::setupAboutHiddenAuthor() { void ChatWidget::setupAboutHiddenAuthor() {
if (!_sublist || !_sublist->peer()->isSavedHiddenAuthor()) { if (!_sublist || !_sublist->sublistPeer()->isSavedHiddenAuthor()) {
return; return;
} else if (_sublist->parentChat()) { } else if (_sublist->parentChat()) {
_canSendTexts = true; _canSendTexts = true;
@ -3260,7 +3260,7 @@ bool ChatWidget::searchInChatEmbedded(
this, this,
controller(), controller(),
_history, _history,
sublist->peer(), sublist->sublistPeer(),
query); query);
updateControlsGeometry(); updateControlsGeometry();

View file

@ -1477,17 +1477,17 @@ void Element::recountMonoforumSenderBarInBlocks() {
|| item->isSponsored()) { || item->isSponsored()) {
return nullptr; return nullptr;
} }
const auto peer = sublist->peer(); const auto sublistPeer = sublist->sublistPeer();
if (const auto previous = previousDisplayedInBlocks()) { if (const auto previous = previousDisplayedInBlocks()) {
const auto prev = previous->data(); const auto prev = previous->data();
if (const auto prevSublist = prev->savedSublist()) { if (const auto prevSublist = prev->savedSublist()) {
Assert(prevSublist->parentChat() == parentChat); Assert(prevSublist->parentChat() == parentChat);
if (prevSublist->peer() == peer) { if (prevSublist->sublistPeer() == sublistPeer) {
return nullptr; return nullptr;
} }
} }
} }
return peer; return sublistPeer;
}(); }();
if (barPeer && !Has<MonoforumSenderBar>()) { if (barPeer && !Has<MonoforumSenderBar>()) {
AddComponents(MonoforumSenderBar::Bit()); AddComponents(MonoforumSenderBar::Bit());

View file

@ -3712,7 +3712,7 @@ bool Message::hasFromName() const {
case Context::AdminLog: case Context::AdminLog:
return true; return true;
case Context::Monoforum: case Context::Monoforum:
return false; return data()->out();
case Context::History: case Context::History:
case Context::ChatPreview: case Context::ChatPreview:
case Context::TTLViewer: case Context::TTLViewer:

View file

@ -82,7 +82,7 @@ QString TopBarNameText(
const Dialogs::EntryState &state) { const Dialogs::EntryState &state) {
if (state.section == Dialogs::EntryState::Section::SavedSublist if (state.section == Dialogs::EntryState::Section::SavedSublist
&& state.key.sublist() && state.key.sublist()
&& state.key.sublist()->parentHistory()->peer->isSelf()) { && state.key.sublist()->owningHistory()->peer->isSelf()) {
if (peer->isSelf()) { if (peer->isSelf()) {
return tr::lng_my_notes(tr::now); return tr::lng_my_notes(tr::now);
} else if (peer->isSavedHiddenAuthor()) { } else if (peer->isSavedHiddenAuthor()) {
@ -490,7 +490,8 @@ void TopBarWidget::paintTopBar(Painter &p) {
const auto history = _activeChat.key.history(); const auto history = _activeChat.key.history();
const auto namePeer = history const auto namePeer = history
? history->peer.get() ? history->peer.get()
: sublist ? sublist->peer().get() : sublist
? sublist->sublistPeer().get()
: nullptr; : nullptr;
const auto broadcastForMonoforum = history const auto broadcastForMonoforum = history
? history->peer->monoforumBroadcast() ? history->peer->monoforumBroadcast()
@ -746,9 +747,9 @@ 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 ([[maybe_unused]] const auto sublist = key.sublist()) { } else if (const auto sublist = key.sublist()) {
_controller->showSection(std::make_shared<Info::Memento>( _controller->showSection(std::make_shared<Info::Memento>(
_controller->session().user(), sublist->owningHistory()->peer,
Info::Section(Storage::SharedMediaType::Photo))); Info::Section(Storage::SharedMediaType::Photo)));
} else if (key.peer()->savedSublistsInfo()) { } else if (key.peer()->savedSublistsInfo()) {
_controller->showSection(std::make_shared<Info::Memento>( _controller->showSection(std::make_shared<Info::Memento>(

View file

@ -275,7 +275,7 @@ not_null<Ui::SettingsButton*> AddSavedSublistButton(
const auto sublist = peer->owner().savedMessages().sublist(peer); const auto sublist = peer->owner().savedMessages().sublist(peer);
navigation->showSection( navigation->showSection(
std::make_shared<ChatMemento>(ChatViewId{ std::make_shared<ChatMemento>(ChatViewId{
.history = sublist->parentHistory(), .history = sublist->owningHistory(),
.sublist = sublist, .sublist = sublist,
})); }));
}); });

View file

@ -69,7 +69,7 @@ SublistsWidget::SublistsWidget(
params.dropSameFromStack = true; params.dropSameFromStack = true;
controller->showSection( controller->showSection(
std::make_shared<ChatMemento>(ChatViewId{ std::make_shared<ChatMemento>(ChatViewId{
.history = sublist->parentHistory(), .history = sublist->owningHistory(),
.sublist = sublist, .sublist = sublist,
}), }),
params); params);

View file

@ -644,6 +644,17 @@ bool MainWidget::filesOrForwardDrop(
clearHider(_hider); clearHider(_hider);
} }
return true; return true;
} else if (const auto history = thread->asHistory()
; history && history->peer->monoforum()) {
Window::ShowDropMediaBox(
_controller,
Core::ShareMimeMediaData(data),
history->peer->monoforum());
if (_hider) {
_hider->startHide();
clearHider(_hider);
}
return true;
} }
if (data->hasFormat(u"application/x-td-forward"_q)) { if (data->hasFormat(u"application/x-td-forward"_q)) {
auto draft = Data::ForwardDraft{ auto draft = Data::ForwardDraft{
@ -780,7 +791,7 @@ void MainWidget::searchMessages(
using namespace HistoryView; using namespace HistoryView;
controller()->showSection( controller()->showSection(
std::make_shared<ChatMemento>(ChatViewId{ std::make_shared<ChatMemento>(ChatViewId{
.history = sublist->parentHistory(), .history = sublist->owningHistory(),
.sublist = sublist, .sublist = sublist,
})); }));
} else if (!tags.empty()) { } else if (!tags.empty()) {
@ -1548,6 +1559,12 @@ void MainWidget::showMessage(
if (params.activation != anim::activation::background) { if (params.activation != anim::activation::background) {
_controller->window().activate(); _controller->window().activate();
} }
} else if (const auto sublist = item->savedSublist()
; sublist && sublist->parentChat()) {
_controller->showSublist(sublist, item->id, params);
if (params.activation != anim::activation::background) {
_controller->window().activate();
}
} else { } else {
// showPeerHistory may be redirected to different window, // showPeerHistory may be redirected to different window,
// so we don't call activate() on current controller's window. // so we don't call activate() on current controller's window.
@ -2621,10 +2638,10 @@ auto MainWidget::thirdSectionForCurrentMainSection(
return std::make_shared<Info::Memento>( return std::make_shared<Info::Memento>(
peer, peer,
Info::Memento::DefaultSection(peer)); Info::Memento::DefaultSection(peer));
} else if (key.sublist()) { } else if (const auto sublist = key.sublist()) {
return std::make_shared<Info::Memento>( return std::make_shared<Info::Memento>(
session().user(), sublist->owningHistory()->peer,
Info::Memento::DefaultSection(session().user())); Info::Memento::DefaultSection(sublist->owningHistory()->peer));
} }
Unexpected("Key in MainWidget::thirdSectionForCurrentMainSection()."); Unexpected("Key in MainWidget::thirdSectionForCurrentMainSection().");
} }

View file

@ -88,6 +88,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#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_user.h" #include "data/data_user.h"
#include "data/data_saved_messages.h"
#include "data/data_saved_sublist.h" #include "data/data_saved_sublist.h"
#include "data/data_histories.h" #include "data/data_histories.h"
#include "data/data_chat_filters.h" #include "data/data_chat_filters.h"
@ -435,7 +436,7 @@ void TogglePinnedThread(
: MTPmessages_ToggleSavedDialogPin::Flag(0); : MTPmessages_ToggleSavedDialogPin::Flag(0);
owner->session().api().request(MTPmessages_ToggleSavedDialogPin( owner->session().api().request(MTPmessages_ToggleSavedDialogPin(
MTP_flags(flags), MTP_flags(flags),
MTP_inputDialogPeer(sublist->peer()->input) MTP_inputDialogPeer(sublist->sublistPeer()->input)
)).done([=] { )).done([=] {
owner->notifyPinnedDialogsOrderUpdated(); owner->notifyPinnedDialogsOrderUpdated();
if (onToggled) { if (onToggled) {
@ -655,10 +656,9 @@ void Filler::addNewWindow() {
_addAction(tr::lng_context_new_window(tr::now), [=] { _addAction(tr::lng_context_new_window(tr::now), [=] {
Ui::PreventDelayedActivation(); Ui::PreventDelayedActivation();
if (const auto sublist = weak.get()) { if (const auto sublist = weak.get()) {
const auto peer = sublist->peer();
controller->showInNewWindow(SeparateId( controller->showInNewWindow(SeparateId(
SeparateType::SavedSublist, SeparateType::SavedSublist,
peer->owner().history(peer))); sublist->owner().history(sublist->sublistPeer())));
} }
}, &st::menuIconNewWindow); }, &st::menuIconNewWindow);
AddSeparatorAndShiftUp(_addAction); AddSeparatorAndShiftUp(_addAction);
@ -2850,6 +2850,46 @@ QPointer<Ui::BoxContent> ShowDropMediaBox(
return weak->data(); return weak->data();
} }
QPointer<Ui::BoxContent> ShowDropMediaBox(
not_null<Window::SessionNavigation*> navigation,
std::shared_ptr<QMimeData> data,
not_null<Data::SavedMessages*> monoforum,
FnMut<void()> &&successCallback) {
const auto weak = std::make_shared<QPointer<Ui::BoxContent>>();
auto chosen = [
data = std::move(data),
callback = std::move(successCallback),
weak,
navigation
](not_null<Data::SavedSublist*> sublist) mutable {
const auto content = navigation->parentController()->content();
if (!content->filesOrForwardDrop(sublist, data.get())) {
return;
} else if (const auto strong = *weak) {
strong->closeBox();
}
if (callback) {
callback();
}
};
auto initBox = [=](not_null<PeerListBox*> box) {
box->addButton(tr::lng_cancel(), [=] {
box->closeBox();
});
monoforum->destroyed(
) | rpl::start_with_next([=] {
box->closeBox();
}, box->lifetime());
};
*weak = navigation->parentController()->show(Box<PeerListBox>(
std::make_unique<ChooseSublistBoxController>(
monoforum,
std::move(chosen)),
std::move(initBox)));
return weak->data();
}
QPointer<Ui::BoxContent> ShowSendNowMessagesBox( QPointer<Ui::BoxContent> ShowSendNowMessagesBox(
not_null<Window::SessionNavigation*> navigation, not_null<Window::SessionNavigation*> navigation,
not_null<History*> history, not_null<History*> history,

View file

@ -32,6 +32,8 @@ class Folder;
class Session; class Session;
struct ForwardDraft; struct ForwardDraft;
class ForumTopic; class ForumTopic;
class SavedMessages;
class SavedSublist;
class Thread; class Thread;
} // namespace Data } // namespace Data
@ -188,6 +190,11 @@ QPointer<Ui::BoxContent> ShowDropMediaBox(
std::shared_ptr<QMimeData> data, std::shared_ptr<QMimeData> data,
not_null<Data::Forum*> forum, not_null<Data::Forum*> forum,
FnMut<void()> &&successCallback = nullptr); FnMut<void()> &&successCallback = nullptr);
QPointer<Ui::BoxContent> ShowDropMediaBox(
not_null<Window::SessionNavigation*> navigation,
std::shared_ptr<QMimeData> data,
not_null<Data::SavedMessages*> monoforum,
FnMut<void()> &&successCallback = nullptr);
QPointer<Ui::BoxContent> ShowSendNowMessagesBox( QPointer<Ui::BoxContent> ShowSendNowMessagesBox(
not_null<Window::SessionNavigation*> navigation, not_null<Window::SessionNavigation*> navigation,

View file

@ -1259,12 +1259,30 @@ void SessionNavigation::showTopic(
params); params);
} }
void SessionNavigation::showSublist(
not_null<Data::SavedSublist*> sublist,
MsgId itemId,
const SectionShow &params) {
using namespace HistoryView;
auto memento = std::make_shared<ChatMemento>(
ChatViewId{
.history = sublist->owningHistory(),
.sublist = sublist,
},
itemId,
params.highlightPart,
params.highlightPartOffsetHint);
showSection(std::move(memento), params);
}
void SessionNavigation::showThread( void SessionNavigation::showThread(
not_null<Data::Thread*> thread, not_null<Data::Thread*> thread,
MsgId itemId, MsgId itemId,
const SectionShow &params) { const SectionShow &params) {
if (const auto topic = thread->asTopic()) { if (const auto topic = thread->asTopic()) {
showTopic(topic, itemId, params); showTopic(topic, itemId, params);
} else if (const auto sublist = thread->asSublist()) {
showSublist(sublist, itemId, params);
} else { } else {
showPeerHistory(thread->asHistory(), params, itemId); showPeerHistory(thread->asHistory(), params, itemId);
} }
@ -1346,7 +1364,7 @@ void SessionNavigation::showByInitialId(
using namespace HistoryView; using namespace HistoryView;
showSection( showSection(
std::make_shared<ChatMemento>(ChatViewId{ std::make_shared<ChatMemento>(ChatViewId{
.history = id.sublist()->parentHistory(), .history = id.sublist()->owningHistory(),
.sublist = id.sublist(), .sublist = id.sublist(),
}), }),
instant); instant);

View file

@ -74,6 +74,7 @@ enum class CloudThemeType;
class Thread; class Thread;
class Forum; class Forum;
class ForumTopic; class ForumTopic;
class SavedSublist;
class WallPaper; class WallPaper;
} // namespace Data } // namespace Data
@ -201,6 +202,10 @@ public:
not_null<Data::ForumTopic*> topic, not_null<Data::ForumTopic*> topic,
MsgId itemId = 0, MsgId itemId = 0,
const SectionShow &params = SectionShow()); const SectionShow &params = SectionShow());
void showSublist(
not_null<Data::SavedSublist*> sublist,
MsgId itemId = 0,
const SectionShow &params = SectionShow());
void showThread( void showThread(
not_null<Data::Thread*> thread, not_null<Data::Thread*> thread,
MsgId itemId = 0, MsgId itemId = 0,