Support mark as read/unread in sublists.

This commit is contained in:
John Preston 2025-05-30 15:00:33 +04:00
parent abe1962002
commit 3278de9ba1
35 changed files with 497 additions and 91 deletions

View file

@ -1373,6 +1373,32 @@ void ApiWrap::deleteAllFromParticipantSend(
}).send(); }).send();
} }
void ApiWrap::deleteSublistHistory(
not_null<ChannelData*> channel,
not_null<PeerData*> sublistPeer) {
deleteSublistHistorySend(channel, sublistPeer);
}
void ApiWrap::deleteSublistHistorySend(
not_null<ChannelData*> parentChat,
not_null<PeerData*> sublistPeer) {
request(MTPmessages_DeleteSavedHistory(
MTP_flags(MTPmessages_DeleteSavedHistory::Flag::f_parent_peer),
parentChat->input,
sublistPeer->input,
MTP_int(0), // max_id
MTP_int(0), // min_date
MTP_int(0) // max_date
)).done([=](const MTPmessages_AffectedHistory &result) {
const auto offset = applyAffectedHistory(parentChat, result);
if (offset > 0) {
deleteSublistHistorySend(parentChat, sublistPeer);
} else if (const auto monoforum = parentChat->monoforum()) {
monoforum->applySublistDeleted(sublistPeer);
}
}).send();
}
void ApiWrap::scheduleStickerSetRequest(uint64 setId, uint64 access) { void ApiWrap::scheduleStickerSetRequest(uint64 setId, uint64 access) {
if (!_stickerSetRequests.contains(setId)) { if (!_stickerSetRequests.contains(setId)) {
_stickerSetRequests.emplace(setId, StickerSetRequest{ access }); _stickerSetRequests.emplace(setId, StickerSetRequest{ access });

View file

@ -231,6 +231,9 @@ public:
void deleteAllFromParticipant( void deleteAllFromParticipant(
not_null<ChannelData*> channel, not_null<ChannelData*> channel,
not_null<PeerData*> from); not_null<PeerData*> from);
void deleteSublistHistory(
not_null<ChannelData*> parentChat,
not_null<PeerData*> sublistPeer);
void requestWebPageDelayed(not_null<WebPageData*> page); void requestWebPageDelayed(not_null<WebPageData*> page);
void clearWebPageRequest(not_null<WebPageData*> page); void clearWebPageRequest(not_null<WebPageData*> page);
@ -539,6 +542,9 @@ private:
void deleteAllFromParticipantSend( void deleteAllFromParticipantSend(
not_null<ChannelData*> channel, not_null<ChannelData*> channel,
not_null<PeerData*> from); not_null<PeerData*> from);
void deleteSublistHistorySend(
not_null<ChannelData*> parentChat,
not_null<PeerData*> sublistPeer);
void uploadAlbumMedia( void uploadAlbumMedia(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,

View file

@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_chat_participant_status.h" #include "data/data_chat_participant_status.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"
@ -565,15 +566,7 @@ bool CanCreateModerateMessagesBox(const HistoryItemsList &items) {
&& !options.participants.empty(); && !options.participants.empty();
} }
void DeleteChatBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) { void SafeSubmitOnEnter(not_null<Ui::GenericBox*> box) {
const auto container = box->verticalLayout();
const auto maybeUser = peer->asUser();
const auto isBot = maybeUser && maybeUser->isBot();
Ui::AddSkip(container);
Ui::AddSkip(container);
base::install_event_filter(box, [=](not_null<QEvent*> event) { base::install_event_filter(box, [=](not_null<QEvent*> event) {
if (event->type() == QEvent::KeyPress) { if (event->type() == QEvent::KeyPress) {
if (const auto k = static_cast<QKeyEvent*>(event.get())) { if (const auto k = static_cast<QKeyEvent*>(event.get())) {
@ -587,12 +580,24 @@ void DeleteChatBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
}, },
.confirmText = tr::lng_box_yes(), .confirmText = tr::lng_box_yes(),
.cancelText = tr::lng_box_no(), .cancelText = tr::lng_box_no(),
})); }));
} }
} }
} }
return base::EventFilterResult::Continue; return base::EventFilterResult::Continue;
}); });
}
void DeleteChatBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
const auto container = box->verticalLayout();
const auto maybeUser = peer->asUser();
const auto isBot = maybeUser && maybeUser->isBot();
Ui::AddSkip(container);
Ui::AddSkip(container);
SafeSubmitOnEnter(box);
const auto userpic = Ui::CreateChild<Ui::UserpicButton>( const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
container, container,
@ -754,3 +759,54 @@ void DeleteChatBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
}, st::attentionBoxButton); }, st::attentionBoxButton);
box->addButton(tr::lng_cancel(), close); box->addButton(tr::lng_cancel(), close);
} }
void DeleteSublistBox(
not_null<Ui::GenericBox*> box,
not_null<Data::SavedSublist*> sublist) {
const auto container = box->verticalLayout();
const auto weak = base::make_weak(sublist.get());
const auto peer = sublist->sublistPeer();
Ui::AddSkip(container);
Ui::AddSkip(container);
SafeSubmitOnEnter(box);
const auto userpic = Ui::CreateChild<Ui::UserpicButton>(
container,
peer,
st::mainMenuUserpic);
Ui::IconWithTitle(
container,
userpic,
Ui::CreateChild<Ui::FlatLabel>(
container,
tr::lng_profile_delete_conversation() | Ui::Text::ToBold(),
box->getDelegate()->style().title));
Ui::AddSkip(container);
Ui::AddSkip(container);
box->addRow(
object_ptr<Ui::FlatLabel>(
container,
tr::lng_sure_delete_history(
lt_contact,
rpl::single(peer->name())),
st::boxLabel));
Ui::AddSkip(container);
const auto close = crl::guard(box, [=] { box->closeBox(); });
box->addButton(tr::lng_box_delete(), [=] {
const auto strong = weak.get();
const auto parentChat = strong ? strong->parentChat() : nullptr;
if (!parentChat) {
return;
}
peer->session().api().deleteSublistHistory(parentChat, peer);
close();
}, st::attentionBoxButton);
box->addButton(tr::lng_cancel(), close);
}

View file

@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
class PeerData; class PeerData;
namespace Data {
class SavedSublist;
} // namespace Data
namespace Ui { namespace Ui {
class GenericBox; class GenericBox;
} // namespace Ui } // namespace Ui
@ -21,3 +25,6 @@ void CreateModerateMessagesBox(
[[nodiscard]] bool CanCreateModerateMessagesBox(const HistoryItemsList &); [[nodiscard]] bool CanCreateModerateMessagesBox(const HistoryItemsList &);
void DeleteChatBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer); void DeleteChatBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer);
void DeleteSublistBox(
not_null<Ui::GenericBox*> box,
not_null<Data::SavedSublist*> sublist);

View file

@ -714,7 +714,7 @@ void PeerListRow::elementsPaint(
} }
QString PeerListRow::generateName() { QString PeerListRow::generateName() {
return peer()->name(); return peer()->userpicPaintingPeer()->name();
} }
QString PeerListRow::generateShortName() { QString PeerListRow::generateShortName() {
@ -724,12 +724,12 @@ QString PeerListRow::generateShortName() {
? tr::lng_replies_messages(tr::now) ? tr::lng_replies_messages(tr::now)
: _isVerifyCodesChat : _isVerifyCodesChat
? tr::lng_verification_codes(tr::now) ? tr::lng_verification_codes(tr::now)
: peer()->shortName(); : peer()->userpicPaintingPeer()->shortName();
} }
Ui::PeerUserpicView &PeerListRow::ensureUserpicView() { Ui::PeerUserpicView &PeerListRow::ensureUserpicView() {
if (!_userpic.cloud && peer()->hasUserpic()) { if (!_userpic.cloud && peer()->userpicPaintingPeer()->hasUserpic()) {
_userpic = peer()->createUserpicView(); _userpic = peer()->userpicPaintingPeer()->createUserpicView();
} }
return _userpic; return _userpic;
} }
@ -738,7 +738,7 @@ PaintRoundImageCallback PeerListRow::generatePaintUserpicCallback(
bool forceRound) { bool forceRound) {
const auto saved = !_savedMessagesStatus.isEmpty(); const auto saved = !_savedMessagesStatus.isEmpty();
const auto replies = _isRepliesMessagesChat; const auto replies = _isRepliesMessagesChat;
const auto peer = this->peer(); const auto peer = this->peer()->userpicPaintingPeer();
auto userpic = saved ? Ui::PeerUserpicView() : ensureUserpicView(); auto userpic = saved ? Ui::PeerUserpicView() : ensureUserpicView();
if (forceRound && peer->isForum()) { if (forceRound && peer->isForum()) {
return ForceRoundUserpicCallback(peer); return ForceRoundUserpicCallback(peer);

View file

@ -204,6 +204,42 @@ void Changes::topicRemoved(not_null<ForumTopic*> topic) {
_topicChanges.drop(topic); _topicChanges.drop(topic);
} }
void Changes::sublistUpdated(
not_null<SavedSublist*> sublist,
SublistUpdate::Flags flags) {
const auto drop = (flags & SublistUpdate::Flag::Destroyed);
_sublistChanges.updated(sublist, flags, drop);
if (!drop) {
scheduleNotifications();
}
}
rpl::producer<SublistUpdate> Changes::sublistUpdates(
SublistUpdate::Flags flags) const {
return _sublistChanges.updates(flags);
}
rpl::producer<SublistUpdate> Changes::sublistUpdates(
not_null<SavedSublist*> sublist,
SublistUpdate::Flags flags) const {
return _sublistChanges.updates(sublist, flags);
}
rpl::producer<SublistUpdate> Changes::sublistFlagsValue(
not_null<SavedSublist*> sublist,
SublistUpdate::Flags flags) const {
return _sublistChanges.flagsValue(sublist, flags);
}
rpl::producer<SublistUpdate> Changes::realtimeSublistUpdates(
SublistUpdate::Flag flag) const {
return _sublistChanges.realtimeUpdates(flag);
}
void Changes::sublistRemoved(not_null<SavedSublist*> sublist) {
_sublistChanges.drop(sublist);
}
void Changes::messageUpdated( void Changes::messageUpdated(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
MessageUpdate::Flags flags) { MessageUpdate::Flags flags) {
@ -323,6 +359,7 @@ void Changes::sendNotifications() {
_messageChanges.sendNotifications(); _messageChanges.sendNotifications();
_entryChanges.sendNotifications(); _entryChanges.sendNotifications();
_topicChanges.sendNotifications(); _topicChanges.sendNotifications();
_sublistChanges.sendNotifications();
_storyChanges.sendNotifications(); _storyChanges.sendNotifications();
} }

View file

@ -38,6 +38,7 @@ inline constexpr int CountBit(Flag Last = Flag::LastUsedBit) {
namespace Data { namespace Data {
class ForumTopic; class ForumTopic;
class SavedSublist;
class Story; class Story;
struct NameUpdate { struct NameUpdate {
@ -184,6 +185,25 @@ struct TopicUpdate {
}; };
struct SublistUpdate {
enum class Flag : uint32 {
None = 0,
UnreadView = (1U << 1),
UnreadReactions = (1U << 2),
CloudDraft = (1U << 3),
Destroyed = (1U << 4),
LastUsedBit = (1U << 4),
};
using Flags = base::flags<Flag>;
friend inline constexpr auto is_flag_type(Flag) { return true; }
not_null<SavedSublist*> sublist;
Flags flags = 0;
};
struct MessageUpdate { struct MessageUpdate {
enum class Flag : uint32 { enum class Flag : uint32 {
None = 0, None = 0,
@ -305,6 +325,21 @@ public:
TopicUpdate::Flag flag) const; TopicUpdate::Flag flag) const;
void topicRemoved(not_null<ForumTopic*> topic); void topicRemoved(not_null<ForumTopic*> topic);
void sublistUpdated(
not_null<SavedSublist*> sublist,
SublistUpdate::Flags flags);
[[nodiscard]] rpl::producer<SublistUpdate> sublistUpdates(
SublistUpdate::Flags flags) const;
[[nodiscard]] rpl::producer<SublistUpdate> sublistUpdates(
not_null<SavedSublist*> sublist,
SublistUpdate::Flags flags) const;
[[nodiscard]] rpl::producer<SublistUpdate> sublistFlagsValue(
not_null<SavedSublist*> sublist,
SublistUpdate::Flags flags) const;
[[nodiscard]] rpl::producer<SublistUpdate> realtimeSublistUpdates(
SublistUpdate::Flag flag) const;
void sublistRemoved(not_null<SavedSublist*> sublist);
void messageUpdated( void messageUpdated(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
MessageUpdate::Flags flags); MessageUpdate::Flags flags);
@ -396,6 +431,7 @@ private:
Manager<PeerData, PeerUpdate> _peerChanges; Manager<PeerData, PeerUpdate> _peerChanges;
Manager<History, HistoryUpdate> _historyChanges; Manager<History, HistoryUpdate> _historyChanges;
Manager<ForumTopic, TopicUpdate> _topicChanges; Manager<ForumTopic, TopicUpdate> _topicChanges;
Manager<SavedSublist, SublistUpdate> _sublistChanges;
Manager<HistoryItem, MessageUpdate> _messageChanges; Manager<HistoryItem, MessageUpdate> _messageChanges;
Manager<Dialogs::Entry, EntryUpdate> _entryChanges; Manager<Dialogs::Entry, EntryUpdate> _entryChanges;
Manager<Story, StoryUpdate> _storyChanges; Manager<Story, StoryUpdate> _storyChanges;

View file

@ -175,30 +175,31 @@ void Forum::applyTopicDeleted(MsgId rootId) {
_topicsDeleted.emplace(rootId); _topicsDeleted.emplace(rootId);
const auto i = _topics.find(rootId); const auto i = _topics.find(rootId);
if (i != end(_topics)) { if (i == end(_topics)) {
const auto raw = i->second.get(); return;
Core::App().notifications().clearFromTopic(raw);
owner().removeChatListEntry(raw);
if (ranges::contains(_lastTopics, not_null(raw))) {
reorderLastTopics();
}
_topicDestroyed.fire(raw);
session().changes().topicUpdated(
raw,
Data::TopicUpdate::Flag::Destroyed);
session().changes().entryUpdated(
raw,
Data::EntryUpdate::Flag::Destroyed);
_topics.erase(i);
_history->destroyMessagesByTopic(rootId);
session().storage().unload(Storage::SharedMediaUnloadThread(
_history->peer->id,
rootId));
_history->setForwardDraft(rootId, PeerId(), {});
} }
const auto raw = i->second.get();
Core::App().notifications().clearFromTopic(raw);
owner().removeChatListEntry(raw);
if (ranges::contains(_lastTopics, not_null(raw))) {
reorderLastTopics();
}
_topicDestroyed.fire(raw);
session().changes().topicUpdated(
raw,
Data::TopicUpdate::Flag::Destroyed);
session().changes().entryUpdated(
raw,
Data::EntryUpdate::Flag::Destroyed);
_topics.erase(i);
_history->destroyMessagesByTopic(rootId);
session().storage().unload(Storage::SharedMediaUnloadThread(
_history->peer->id,
rootId));
_history->setForwardDraft(rootId, PeerId(), {});
} }
void Forum::reorderLastTopics() { void Forum::reorderLastTopics() {

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "api/api_text_entities.h" #include "api/api_text_entities.h"
#include "data/business/data_shortcut_messages.h" #include "data/business/data_shortcut_messages.h"
#include "data/components/scheduled_messages.h" #include "data/components/scheduled_messages.h"
#include "data/data_saved_sublist.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_chat.h" #include "data/data_chat.h"
@ -500,6 +501,24 @@ void Histories::changeDialogUnreadMark(
)).send(); )).send();
} }
void Histories::changeSublistUnreadMark(
not_null<Data::SavedSublist*> sublist,
bool unread) {
const auto parent = sublist->parentChat();
if (!parent) {
return;
}
sublist->setUnreadMark(unread);
using Flag = MTPmessages_MarkDialogUnread::Flag;
session().api().request(MTPmessages_MarkDialogUnread(
MTP_flags(Flag::f_parent_peer
| (unread ? Flag::f_unread : Flag(0))),
parent->input,
MTP_inputDialogPeer(sublist->sublistPeer()->input)
)).send();
}
void Histories::requestFakeChatListMessage( void Histories::requestFakeChatListMessage(
not_null<History*> history) { not_null<History*> history) {
if (_fakeChatListRequests.contains(history)) { if (_fakeChatListRequests.contains(history)) {

View file

@ -26,6 +26,7 @@ namespace Data {
class Session; class Session;
class Folder; class Folder;
struct WebPageDraft; struct WebPageDraft;
class SavedSublist;
[[nodiscard]] MTPInputReplyTo ReplyToForMTP( [[nodiscard]] MTPInputReplyTo ReplyToForMTP(
not_null<History*> history, not_null<History*> history,
@ -71,6 +72,9 @@ public:
Fn<void()> callback = nullptr); Fn<void()> callback = nullptr);
void dialogEntryApplied(not_null<History*> history); void dialogEntryApplied(not_null<History*> history);
void changeDialogUnreadMark(not_null<History*> history, bool unread); void changeDialogUnreadMark(not_null<History*> history, bool unread);
void changeSublistUnreadMark(
not_null<Data::SavedSublist*> sublist,
bool unread);
void requestFakeChatListMessage(not_null<History*> history); void requestFakeChatListMessage(not_null<History*> history);
void requestGroupAround(not_null<HistoryItem*> item); void requestGroupAround(not_null<HistoryItem*> item);

View file

@ -1174,6 +1174,10 @@ not_null<const PeerData*> PeerData::userpicPaintingPeer() const {
return const_cast<PeerData*>(this)->userpicPaintingPeer(); return const_cast<PeerData*>(this)->userpicPaintingPeer();
} }
bool PeerData::userpicForceForumShape() const {
return monoforumBroadcast() != nullptr;
}
ChannelData *PeerData::monoforumBroadcast() const { ChannelData *PeerData::monoforumBroadcast() const {
const auto monoforum = asMonoforum(); const auto monoforum = asMonoforum();
return monoforum ? monoforum->monoforumLink() : nullptr; return monoforum ? monoforum->monoforumLink() : nullptr;

View file

@ -307,6 +307,7 @@ public:
[[nodiscard]] not_null<const PeerData*> migrateToOrMe() const; [[nodiscard]] not_null<const PeerData*> migrateToOrMe() const;
[[nodiscard]] not_null<PeerData*> userpicPaintingPeer(); [[nodiscard]] not_null<PeerData*> userpicPaintingPeer();
[[nodiscard]] not_null<const PeerData*> userpicPaintingPeer() const; [[nodiscard]] not_null<const PeerData*> userpicPaintingPeer() const;
[[nodiscard]] bool userpicForceForumShape() const;
// isMonoforum() ? monoforumLink() : nullptr // isMonoforum() ? monoforumLink() : nullptr
[[nodiscard]] ChannelData *monoforumBroadcast() const; [[nodiscard]] ChannelData *monoforumBroadcast() const;

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_saved_messages.h" #include "data/data_saved_messages.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "core/application.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_user.h" #include "data/data_user.h"
@ -17,6 +18,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_unread_things.h" #include "history/history_unread_things.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "window/notifications_manager.h"
namespace Data { namespace Data {
namespace { namespace {
@ -50,11 +52,13 @@ SavedMessages::SavedMessages(
SavedMessages::~SavedMessages() { SavedMessages::~SavedMessages() {
auto &changes = session().changes(); auto &changes = session().changes();
for (const auto &[peer, sublist] : _sublists) { if (_owningHistory) {
_owningHistory->setForwardDraft(MsgId(), peer->id, {}); for (const auto &[peer, sublist] : _sublists) {
_owningHistory->setForwardDraft(MsgId(), peer->id, {});
const auto raw = sublist.get(); const auto raw = sublist.get();
changes.entryRemoved(raw); changes.entryRemoved(raw);
}
} }
} }
@ -308,6 +312,36 @@ void SavedMessages::apply(const MTPDupdateSavedDialogPinned &update) {
}); });
} }
void SavedMessages::applySublistDeleted(not_null<PeerData*> sublistPeer) {
const auto i = _sublists.find(sublistPeer);
if (i == end(_sublists)) {
return;
}
const auto raw = i->second.get();
//Core::App().notifications().clearFromTopic(raw); // #TODO monoforum
owner().removeChatListEntry(raw);
if (ranges::contains(_lastSublists, not_null(raw))) {
reorderLastSublists();
}
_sublistDestroyed.fire(raw);
session().changes().sublistUpdated(
raw,
Data::SublistUpdate::Flag::Destroyed);
session().changes().entryUpdated(
raw,
Data::EntryUpdate::Flag::Destroyed);
_sublists.erase(i);
const auto history = owningHistory();
history->destroyMessagesBySublist(sublistPeer);
//session().storage().unload(Storage::SharedMediaUnloadThread(
// _history->peer->id,
// rootId));
history->setForwardDraft(MsgId(), sublistPeer->id, {});
}
void SavedMessages::reorderLastSublists() { void SavedMessages::reorderLastSublists() {
if (!_parentChat) { if (!_parentChat) {
return; return;

View file

@ -50,6 +50,7 @@ public:
void apply(const MTPDupdatePinnedSavedDialogs &update); void apply(const MTPDupdatePinnedSavedDialogs &update);
void apply(const MTPDupdateSavedDialogPinned &update); void apply(const MTPDupdateSavedDialogPinned &update);
void applySublistDeleted(not_null<PeerData*> sublistPeer);
void listMessageChanged(HistoryItem *from, HistoryItem *to); void listMessageChanged(HistoryItem *from, HistoryItem *to);
[[nodiscard]] int recentSublistsListVersion() const; [[nodiscard]] int recentSublistsListVersion() const;

View file

@ -480,6 +480,15 @@ void SavedSublist::setUnreadCount(std::optional<int> count) {
} }
} }
void SavedSublist::setUnreadMark(bool unread) {
if (unreadMark() == unread) {
return;
}
const auto notifier = unreadStateChangeNotifier(
!unreadCountCurrent());
Thread::setUnreadMarkFlag(unread);
}
bool SavedSublist::unreadCountKnown() const { bool SavedSublist::unreadCountKnown() const {
return !inMonoforum() || _unreadCount.current().has_value(); return !inMonoforum() || _unreadCount.current().has_value();
} }
@ -620,6 +629,9 @@ void SavedSublist::readTill(
if (!IsServerMsgId(tillId)) { if (!IsServerMsgId(tillId)) {
return; return;
} }
if (unreadMark()) {
owner().histories().changeSublistUnreadMark(this, false);
}
const auto was = computeInboxReadTillFull(); const auto was = computeInboxReadTillFull();
const auto now = tillId; const auto now = tillId;
if (now < was) { if (now < was) {
@ -713,6 +725,7 @@ void SavedSublist::applyMonoforumDialog(
data.vread_inbox_max_id().v, data.vread_inbox_max_id().v,
data.vunread_count().v); data.vunread_count().v);
setOutboxReadTill(data.vread_outbox_max_id().v); setOutboxReadTill(data.vread_outbox_max_id().v);
setUnreadMark(data.is_unread_mark());
applyMaybeLast(topItem); applyMaybeLast(topItem);
} }
@ -1016,10 +1029,16 @@ Dialogs::UnreadState SavedSublist::unreadStateFor(
int count, int count,
bool known) const { bool known) const {
auto result = Dialogs::UnreadState(); auto result = Dialogs::UnreadState();
const auto mark = !count && unreadMark();
const auto muted = this->muted(); const auto muted = this->muted();
result.messages = count; result.messages = count;
result.chats = count ? 1 : 0; result.chats = count ? 1 : 0;
result.marks = mark ? 1 : 0;
result.reactions = unreadReactions().has() ? 1 : 0;
result.messagesMuted = muted ? result.messages : 0;
result.chatsMuted = muted ? result.chats : 0; result.chatsMuted = muted ? result.chats : 0;
result.marksMuted = muted ? result.marks : 0;
result.reactionsMuted = muted ? result.reactions : 0;
result.known = known; result.known = known;
return result; return result;
} }

View file

@ -58,13 +58,13 @@ public:
[[nodiscard]] rpl::producer<> changes() const; [[nodiscard]] rpl::producer<> changes() const;
[[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]] rpl::producer<std::optional<int>> maybeFullCount() const;
void loadFullCount(); void loadFullCount();
[[nodiscard]] bool unreadCountKnown() const; [[nodiscard]] bool unreadCountKnown() const;
[[nodiscard]] int unreadCountCurrent() const; [[nodiscard]] int unreadCountCurrent() const;
[[nodiscard]] int displayedUnreadCount() const; [[nodiscard]] int displayedUnreadCount() const;
[[nodiscard]] rpl::producer<std::optional<int>> unreadCountValue() const; [[nodiscard]] rpl::producer<std::optional<int>> unreadCountValue() const;
void setUnreadMark(bool unread);
void applyMonoforumDialog( void applyMonoforumDialog(
const MTPDmonoForumDialog &dialog, const MTPDmonoForumDialog &dialog,

View file

@ -95,6 +95,17 @@ HistoryUnreadThings::ConstProxy Thread::unreadReactions() const {
}; };
} }
bool Thread::canToggleUnread(bool nowUnread) const {
if ((asTopic() || asForum()) && !nowUnread) {
return false;
} else if (asSublist() && owningHistory()->peer->isSelf()) {
return false;
} else if (asHistory() && peer()->amMonoforumAdmin()) {
return false;
}
return true;
}
const base::flat_set<MsgId> &Thread::unreadMentionsIds() const { const base::flat_set<MsgId> &Thread::unreadMentionsIds() const {
if (!_unreadThings) { if (!_unreadThings) {
static const auto Result = base::flat_set<MsgId>(); static const auto Result = base::flat_set<MsgId>();

View file

@ -80,6 +80,7 @@ public:
[[nodiscard]] HistoryUnreadThings::ConstProxy unreadReactions() const; [[nodiscard]] HistoryUnreadThings::ConstProxy unreadReactions() const;
virtual void hasUnreadMentionChanged(bool has) = 0; virtual void hasUnreadMentionChanged(bool has) = 0;
virtual void hasUnreadReactionChanged(bool has) = 0; virtual void hasUnreadReactionChanged(bool has) = 0;
bool canToggleUnread(bool nowUnread) const;
void removeNotification(not_null<HistoryItem*> item); void removeNotification(not_null<HistoryItem*> item);
void clearNotifications(); void clearNotifications();

View file

@ -287,6 +287,10 @@ void Entry::notifyUnreadStateChange(const UnreadState &wasState) {
} }
} }
} }
} else if (const auto sublist = asSublist()) {
session().changes().sublistUpdated(
sublist,
Data::SublistUpdate::Flag::UnreadView);
} }
updateChatListEntryPostponed(); updateChatListEntryPostponed();
} }

View file

@ -398,14 +398,18 @@ void History::applyCloudDraft(MsgId topicRootId, PeerId monoforumPeerId) {
createLocalDraftFromCloud(topicRootId, monoforumPeerId); createLocalDraftFromCloud(topicRootId, monoforumPeerId);
if (const auto thread = threadFor(topicRootId, monoforumPeerId)) { if (const auto thread = threadFor(topicRootId, monoforumPeerId)) {
thread->updateChatListSortPosition(); thread->updateChatListSortPosition();
if (!topicRootId) { if (topicRootId) {
session().changes().historyUpdated(
this,
UpdateFlag::CloudDraft);
} else {
session().changes().topicUpdated( session().changes().topicUpdated(
thread->asTopic(), thread->asTopic(),
Data::TopicUpdate::Flag::CloudDraft); Data::TopicUpdate::Flag::CloudDraft);
} else if (monoforumPeerId) {
session().changes().sublistUpdated(
thread->asSublist(),
Data::SublistUpdate::Flag::CloudDraft);
} else {
session().changes().historyUpdated(
this,
UpdateFlag::CloudDraft);
} }
} }
} }
@ -633,6 +637,20 @@ void History::destroyMessagesByTopic(MsgId topicRootId) {
} }
} }
void History::destroyMessagesBySublist(not_null<PeerData*> sublistPeer) {
auto toDestroy = std::vector<not_null<HistoryItem*>>();
toDestroy.reserve(_items.size());
const auto peerId = sublistPeer->id;
for (const auto &message : _items) {
if (message->sublistPeerId() == peerId) {
toDestroy.push_back(message.get());
}
}
for (const auto item : toDestroy) {
item->destroy();
}
}
void History::unpinMessagesFor(MsgId topicRootId) { void History::unpinMessagesFor(MsgId topicRootId) {
if (!topicRootId) { if (!topicRootId) {
session().storage().remove( session().storage().remove(

View file

@ -139,6 +139,7 @@ public:
void destroyMessage(not_null<HistoryItem*> item); void destroyMessage(not_null<HistoryItem*> item);
void destroyMessagesByDates(TimeId minDate, TimeId maxDate); void destroyMessagesByDates(TimeId minDate, TimeId maxDate);
void destroyMessagesByTopic(MsgId topicRootId); void destroyMessagesByTopic(MsgId topicRootId);
void destroyMessagesBySublist(not_null<PeerData*> sublistPeer);
void unpinMessagesFor(MsgId topicRootId); void unpinMessagesFor(MsgId topicRootId);

View file

@ -82,6 +82,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "data/components/factchecks.h" #include "data/components/factchecks.h"
#include "data/components/sponsored_messages.h" #include "data/components/sponsored_messages.h"
#include "data/data_saved_sublist.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_channel.h" #include "data/data_channel.h"
@ -4963,9 +4964,17 @@ auto HistoryInner::DelegateMixin()
bool CanSendReply(not_null<const HistoryItem*> item) { bool CanSendReply(not_null<const HistoryItem*> item) {
const auto peer = item->history()->peer; const auto peer = item->history()->peer;
const auto topic = item->topic(); if (const auto topic = item->topic()) {
return topic return Data::CanSendAnything(topic);
? Data::CanSendAnything(topic) } else if (!Data::CanSendAnything(peer)) {
: (Data::CanSendAnything(peer) return false;
&& (!peer->isChannel() || peer->asChannel()->amIn())); } else if (const auto channel = peer->asChannel()) {
if (const auto sublist = item->savedSublist()) {
if (sublist->sublistPeer() == peer) {
return false;
}
}
return channel->amIn();
}
return true;
} }

View file

@ -2151,9 +2151,13 @@ void HistoryItem::addToUnreadThings(HistoryUnreadThings::AddType type) {
} }
} }
if (reaction) { if (reaction) {
const auto sublist = this->savedSublist();
const auto toHistory = history->unreadReactions().add(id, type); const auto toHistory = history->unreadReactions().add(id, type);
const auto toTopic = topic && topic->unreadReactions().add(id, type); const auto toTopic = topic && topic->unreadReactions().add(id, type);
if (toHistory || toTopic) { const auto toSublist = sublist
&& sublist->parentChat()
&& sublist->unreadReactions().add(id, type);
if (toHistory || toTopic || toSublist) {
if (type == HistoryUnreadThings::AddType::New) { if (type == HistoryUnreadThings::AddType::New) {
changes->messageUpdated( changes->messageUpdated(
this, this,
@ -2170,6 +2174,11 @@ void HistoryItem::addToUnreadThings(HistoryUnreadThings::AddType type) {
topic, topic,
Data::TopicUpdate::Flag::UnreadReactions); Data::TopicUpdate::Flag::UnreadReactions);
} }
if (toSublist) {
changes->sublistUpdated(
sublist,
Data::SublistUpdate::Flag::UnreadReactions);
}
} }
} }
} }

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "history/history_unread_things.h" #include "history/history_unread_things.h"
#include "data/data_saved_sublist.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_channel.h" #include "data/data_channel.h"
@ -38,6 +39,12 @@ template <typename Update>
return UpdateFlag<Data::TopicUpdate>(type); return UpdateFlag<Data::TopicUpdate>(type);
} }
[[nodiscard]] Data::SublistUpdate::Flag SublistUpdateFlag(Type type) {
Expects(type == Type::Reactions);
return Data::SublistUpdate::Flag::UnreadReactions;
}
} // namespace } // namespace
void Proxy::setCount(int count) { void Proxy::setCount(int count) {
@ -224,6 +231,10 @@ void Proxy::notifyUpdated() {
topic->session().changes().topicUpdated( topic->session().changes().topicUpdated(
topic, topic,
TopicUpdateFlag(_type)); TopicUpdateFlag(_type));
} else if (const auto sublist = _thread->asSublist()) {
sublist->session().changes().sublistUpdated(
sublist,
SublistUpdateFlag(_type));
} }
} }

View file

@ -366,8 +366,9 @@ void Item::setupTop() {
? nullptr ? nullptr
: Ui::CreateChild<Ui::UserpicButton>( : Ui::CreateChild<Ui::UserpicButton>(
_top.get(), _top.get(),
_thread->peer(), _thread->peer()->userpicPaintingPeer(),
st::previewUserpic); st::previewUserpic,
_thread->peer()->userpicForceForumShape());
if (userpic) { if (userpic) {
userpic->showSavedMessagesOnSelf(true); userpic->showSavedMessagesOnSelf(true);
userpic->setAttribute(Qt::WA_TransparentForMouseEvents); userpic->setAttribute(Qt::WA_TransparentForMouseEvents);

View file

@ -47,6 +47,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/delete_messages_box.h" #include "boxes/delete_messages_box.h"
#include "boxes/send_files_box.h" #include "boxes/send_files_box.h"
#include "boxes/premium_limits_box.h" #include "boxes/premium_limits_box.h"
#include "window/window_controller.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "window/window_peer_menu.h" #include "window/window_peer_menu.h"
#include "base/call_delayed.h" #include "base/call_delayed.h"
@ -58,6 +59,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h" #include "main/main_session.h"
#include "main/main_session_settings.h" #include "main/main_session_settings.h"
#include "data/components/scheduled_messages.h" #include "data/components/scheduled_messages.h"
#include "data/data_histories.h"
#include "data/data_saved_messages.h" #include "data/data_saved_messages.h"
#include "data/data_saved_sublist.h" #include "data/data_saved_sublist.h"
#include "data/data_session.h" #include "data/data_session.h"
@ -619,9 +621,7 @@ void ChatWidget::subscribeToTopic() {
_topic->destroyed( _topic->destroyed(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
controller()->showBackFromStack(Window::SectionShow( closeCurrent();
anim::type::normal,
anim::activation::background));
}, _topicLifetime); }, _topicLifetime);
if (!_topic->creating()) { if (!_topic->creating()) {
@ -635,6 +635,17 @@ void ChatWidget::subscribeToTopic() {
_cornerButtons.updateUnreadThingsVisibility(); _cornerButtons.updateUnreadThingsVisibility();
} }
void ChatWidget::closeCurrent() {
const auto thread = controller()->windowId().chat();
if ((_sublist && thread == _sublist) || (_topic && thread == _topic)) {
controller()->window().close();
} else {
controller()->showBackFromStack(Window::SectionShow(
anim::type::normal,
anim::activation::background));
}
}
void ChatWidget::subscribeToPinnedMessages() { void ChatWidget::subscribeToPinnedMessages() {
using EntryUpdateFlag = Data::EntryUpdate::Flag; using EntryUpdateFlag = Data::EntryUpdate::Flag;
session().changes().entryUpdates( session().changes().entryUpdates(
@ -2496,9 +2507,7 @@ void ChatWidget::setReplies(std::shared_ptr<Data::RepliesList> replies) {
refreshUnreadCountBadge(count); refreshUnreadCountBadge(count);
}, lifetime()); }, lifetime());
refreshUnreadCountBadge(_replies->unreadCountKnown() unreadCountUpdated();
? _replies->unreadCountCurrent()
: std::optional<int>());
const auto isTopic = (_topic != nullptr); const auto isTopic = (_topic != nullptr);
const auto isTopicCreating = isTopic && _topic->creating(); const auto isTopicCreating = isTopic && _topic->creating();
@ -2533,14 +2542,62 @@ void ChatWidget::setReplies(std::shared_ptr<Data::RepliesList> replies) {
void ChatWidget::subscribeToSublist() { void ChatWidget::subscribeToSublist() {
Expects(_sublist != nullptr); Expects(_sublist != nullptr);
// Must be done before unreadCountUpdated(), or we auto-close.
if (_sublist->unreadMark()) {
_sublist->owner().histories().changeSublistUnreadMark(
_sublist,
false);
}
_sublist->unreadCountValue( _sublist->unreadCountValue(
) | rpl::start_with_next([=](std::optional<int> count) { ) | rpl::start_with_next([=](std::optional<int> count) {
refreshUnreadCountBadge(count); refreshUnreadCountBadge(count);
}, lifetime()); }, lifetime());
refreshUnreadCountBadge(_sublist->unreadCountKnown() using Flag = Data::SublistUpdate::Flag;
? _sublist->unreadCountCurrent() session().changes().sublistUpdates(
: std::optional<int>()); _sublist,
Flag::UnreadView | Flag::UnreadReactions | Flag::CloudDraft
) | rpl::start_with_next([=](const Data::SublistUpdate &update) {
if (update.flags & Flag::UnreadView) {
unreadCountUpdated();
}
if (update.flags & Flag::UnreadReactions) {
_cornerButtons.updateUnreadThingsVisibility();
}
if (update.flags & Flag::CloudDraft) {
_composeControls->applyCloudDraft();
}
}, lifetime());
_sublist->destroyed(
) | rpl::start_with_next([=] {
closeCurrent();
}, lifetime());
unreadCountUpdated();
}
void ChatWidget::unreadCountUpdated() {
if (_sublist && _sublist->unreadMark()) {
crl::on_main(this, [=] {
const auto guard = Ui::MakeWeak(this);
controller()->showPeerHistory(_sublist->owningHistory());
if (guard) {
closeCurrent();
}
});
} else {
refreshUnreadCountBadge(_replies
? (_replies->unreadCountKnown()
? _replies->unreadCountCurrent()
: std::optional<int>())
: _sublist
? (_sublist->unreadCountKnown()
? _sublist->unreadCountCurrent()
: std::optional<int>())
: std::optional<int>());
}
} }
void ChatWidget::restoreState(not_null<ChatMemento*> memento) { void ChatWidget::restoreState(not_null<ChatMemento*> memento) {

View file

@ -241,6 +241,8 @@ private:
int limitAfter); int limitAfter);
void onScroll(); void onScroll();
void closeCurrent();
void unreadCountUpdated();
void updateInnerVisibleArea(); void updateInnerVisibleArea();
void updateControlsGeometry(); void updateControlsGeometry();
void updateAdaptiveLayout(); void updateAdaptiveLayout();

View file

@ -590,9 +590,12 @@ void SubsectionTabs::refreshSlice() {
}); });
const auto push = [&](not_null<Data::Thread*> thread) { const auto push = [&](not_null<Data::Thread*> thread) {
const auto topic = thread->asTopic(); const auto topic = thread->asTopic();
const auto sublist = thread->asSublist();
slice.push_back({ slice.push_back({
.thread = thread, .thread = thread,
.badges = thread->chatListBadgesState(), .badges = ((topic || sublist)
? thread->chatListBadgesState()
: Dialogs::BadgesState()),
.iconId = topic ? topic->iconId() : DocumentId(), .iconId = topic ? topic->iconId() : DocumentId(),
.name = thread->chatListName(), .name = thread->chatListName(),
}); });

View file

@ -940,7 +940,7 @@ void TopBarWidget::refreshInfoButton() {
Ui::UserpicButton::Role::Custom, Ui::UserpicButton::Role::Custom,
Ui::UserpicButton::Source::PeerPhoto, Ui::UserpicButton::Source::PeerPhoto,
st::topBarInfoButton, st::topBarInfoButton,
infoPeer->monoforumBroadcast() != nullptr); infoPeer->userpicForceForumShape());
info->showSavedMessagesOnSelf(true); info->showSavedMessagesOnSelf(true);
_info.destroy(); _info.destroy();
_info = std::move(info); _info = std::move(info);

View file

@ -632,7 +632,7 @@ Cover::Cover(
Ui::UserpicButton::Role::OpenPhoto, Ui::UserpicButton::Role::OpenPhoto,
Ui::UserpicButton::Source::PeerPhoto, Ui::UserpicButton::Source::PeerPhoto,
_st.photo, _st.photo,
_peer->monoforumBroadcast() != nullptr)) _peer->userpicForceForumShape()))
, _changePersonal((role == Role::Info , _changePersonal((role == Role::Info
|| topic || topic
|| !_peer->isUser() || !_peer->isUser()

View file

@ -202,10 +202,12 @@ UserpicButton::UserpicButton(
UserpicButton::UserpicButton( UserpicButton::UserpicButton(
QWidget *parent, QWidget *parent,
not_null<PeerData*> peer, not_null<PeerData*> peer,
const style::UserpicButton &st) const style::UserpicButton &st,
bool forceForumShape)
: RippleButton(parent, st.changeButton.ripple) : RippleButton(parent, st.changeButton.ripple)
, _st(st) , _st(st)
, _peer(peer) , _peer(peer)
, _forceForumShape(forceForumShape)
, _role(Role::Custom) , _role(Role::Custom)
, _source(Source::PeerPhoto) { , _source(Source::PeerPhoto) {
Expects(_role != Role::OpenPhoto); Expects(_role != Role::OpenPhoto);

View file

@ -74,7 +74,8 @@ public:
UserpicButton( UserpicButton(
QWidget *parent, QWidget *parent,
not_null<PeerData*> peer, // Role::Custom, Source::PeerPhoto not_null<PeerData*> peer, // Role::Custom, Source::PeerPhoto
const style::UserpicButton &st); const style::UserpicButton &st,
bool forceForumShape = false);
~UserpicButton(); ~UserpicButton();
enum class ChosenType { enum class ChosenType {

View file

@ -499,6 +499,8 @@ void Filler::addTogglePin() {
&& !_sublist && !_sublist
&& !_topic) { && !_topic) {
return; return;
} else if (_sublist && !_peer->isSelf()) {
return;
} }
const auto controller = _controller; const auto controller = _controller;
const auto filterId = _request.filterId; const auto filterId = _request.filterId;
@ -526,7 +528,7 @@ void Filler::addTogglePin() {
} }
void Filler::addToggleMuteSubmenu(bool addSeparator) { void Filler::addToggleMuteSubmenu(bool addSeparator) {
if (!_thread || _thread->peer()->isSelf()) { if (!_thread || _thread->peer()->isSelf() || _thread->asSublist()) {
return; return;
} }
PeerMenuAddMuteSubmenuAction(_controller, _thread, _addAction); PeerMenuAddMuteSubmenuAction(_controller, _thread, _addAction);
@ -550,16 +552,18 @@ void Filler::addSupportInfo() {
} }
void Filler::addInfo() { void Filler::addInfo() {
if (_peer const auto sublist = _thread ? _thread->asSublist() : nullptr;
&& (_peer->isSelf() const auto infoPeer = sublist ? sublist->sublistPeer().get() : _peer;
|| _peer->isRepliesChat() if (infoPeer
|| _peer->isVerifyCodes())) { && (infoPeer->isSelf()
|| infoPeer->isRepliesChat()
|| infoPeer->isVerifyCodes())) {
return; return;
} else if (!_thread) { } else if (!_thread) {
return; 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->asSublist() && thread == _thread) {
if (Core::App().settings().thirdSectionInfoEnabled() if (Core::App().settings().thirdSectionInfoEnabled()
|| Core::App().settings().tabbedReplacedWithInfo()) { || Core::App().settings().tabbedReplacedWithInfo()) {
return; return;
@ -570,16 +574,16 @@ void Filler::addInfo() {
const auto weak = base::make_weak(_thread); const auto weak = base::make_weak(_thread);
const auto text = _thread->asTopic() const auto text = _thread->asTopic()
? tr::lng_context_view_topic(tr::now) ? tr::lng_context_view_topic(tr::now)
: (_peer->isChat() || _peer->isMegagroup()) : (infoPeer->isChat() || infoPeer->isMegagroup())
? tr::lng_context_view_group(tr::now) ? tr::lng_context_view_group(tr::now)
: _peer->isUser() : infoPeer->isUser()
? tr::lng_context_view_profile(tr::now) ? tr::lng_context_view_profile(tr::now)
: tr::lng_context_view_channel(tr::now); : tr::lng_context_view_channel(tr::now);
_addAction(text, [=] { _addAction(text, [=] {
if (const auto strong = weak.get()) { if (const auto strong = weak.get()) {
controller->showPeerInfo(strong); controller->showPeerInfo(strong);
} }
}, _peer->isUser() ? &st::menuIconProfile : &st::menuIconInfo); }, infoPeer->isUser() ? &st::menuIconProfile : &st::menuIconInfo);
} }
void Filler::addStoryArchive() { void Filler::addStoryArchive() {
@ -624,12 +628,9 @@ void Filler::addToggleFolder() {
void Filler::addToggleUnreadMark() { void Filler::addToggleUnreadMark() {
const auto peer = _peer; const auto peer = _peer;
const auto history = _request.key.history();
if (!_thread) {
return;
}
const auto unread = IsUnreadThread(_thread); const auto unread = IsUnreadThread(_thread);
if ((_thread->asTopic() || peer->isForum()) && !unread) { const auto history = _request.key.history();
if (!_thread || !_thread->canToggleUnread(unread)) {
return; return;
} }
const auto weak = base::make_weak(_thread); const auto weak = base::make_weak(_thread);
@ -643,6 +644,8 @@ void Filler::addToggleUnreadMark() {
} }
if (unread) { if (unread) {
MarkAsReadThread(thread); MarkAsReadThread(thread);
} else if (const auto sublist = thread->asSublist()) {
peer->owner().histories().changeSublistUnreadMark(sublist, true);
} else if (history) { } else if (history) {
peer->owner().histories().changeDialogUnreadMark(history, true); peer->owner().histories().changeDialogUnreadMark(history, true);
} }
@ -751,14 +754,16 @@ void Filler::addClearHistory() {
} }
void Filler::addDeleteChat() { void Filler::addDeleteChat() {
if (_topic || _peer->isChannel()) { if (_topic || (!_sublist && _peer->isChannel())) {
return; return;
} }
_addAction({ _addAction({
.text = (_peer->isUser() .text = ((_peer->isUser() || _sublist)
? tr::lng_profile_delete_conversation(tr::now) ? tr::lng_profile_delete_conversation(tr::now)
: tr::lng_profile_clear_and_exit(tr::now)), : tr::lng_profile_clear_and_exit(tr::now)),
.handler = DeleteAndLeaveHandler(_controller, _peer), .handler = (_sublist
? DeleteSublistHandler(_controller, _sublist)
: DeleteAndLeaveHandler(_controller, _peer)),
.icon = &st::menuIconDeleteAttention, .icon = &st::menuIconDeleteAttention,
.isAttention = true, .isAttention = true,
}); });
@ -766,7 +771,7 @@ void Filler::addDeleteChat() {
void Filler::addLeaveChat() { void Filler::addLeaveChat() {
const auto channel = _peer->asChannel(); const auto channel = _peer->asChannel();
if (_topic || !channel || !channel->amIn()) { if (_topic || _sublist || !channel || !channel->amIn()) {
return; return;
} }
_addAction({ _addAction({
@ -1263,7 +1268,7 @@ void Filler::addSendGift() {
void Filler::fill() { void Filler::fill() {
if (_folder) { if (_folder) {
fillArchiveActions(); fillArchiveActions();
} else if (_sublist) { } else if (_sublist && _peer->isSelf()) {
fillSavedSublistActions(); fillSavedSublistActions();
} else switch (_request.section) { } else switch (_request.section) {
case Section::ChatsList: fillChatsListActions(); break; case Section::ChatsList: fillChatsListActions(); break;
@ -3232,6 +3237,19 @@ Fn<void()> DeleteAndLeaveHandler(
}; };
} }
Fn<void()> DeleteSublistHandler(
not_null<Window::SessionController*> controller,
not_null<Data::SavedSublist*> sublist) {
const auto weak = base::make_weak(sublist.get());
return [=] {
if (const auto strong = weak.get()) {
if (!controller->showFrozenError()) {
controller->show(Box(DeleteSublistBox, strong));
}
}
};
}
void FillDialogsEntryMenu( void FillDialogsEntryMenu(
not_null<SessionController*> controller, not_null<SessionController*> controller,
Dialogs::EntryState request, Dialogs::EntryState request,
@ -3345,8 +3363,7 @@ void MarkAsReadThread(not_null<Data::Thread*> thread) {
if (!IsUnreadThread(thread)) { if (!IsUnreadThread(thread)) {
return; return;
} else if (const auto forum = thread->asForum()) { } else if (const auto forum = thread->asForum()) {
forum->enumerateTopics([]( forum->enumerateTopics([](not_null<Data::ForumTopic*> topic) {
not_null<Data::ForumTopic*> topic) {
MarkAsReadThread(topic); MarkAsReadThread(topic);
}); });
} else if (const auto history = thread->asHistory()) { } else if (const auto history = thread->asHistory()) {
@ -3356,6 +3373,8 @@ void MarkAsReadThread(not_null<Data::Thread*> thread) {
} }
} else if (const auto topic = thread->asTopic()) { } else if (const auto topic = thread->asTopic()) {
topic->readTillEnd(); topic->readTillEnd();
} else if (const auto sublist = thread->asSublist()) {
sublist->readTillEnd();
} }
} }

View file

@ -148,6 +148,9 @@ Fn<void()> ClearHistoryHandler(
Fn<void()> DeleteAndLeaveHandler( Fn<void()> DeleteAndLeaveHandler(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<PeerData*> peer); not_null<PeerData*> peer);
Fn<void()> DeleteSublistHandler(
not_null<Window::SessionController*> controller,
not_null<Data::SavedSublist*> sublist);
object_ptr<Ui::BoxContent> PrepareChooseRecipientBox( object_ptr<Ui::BoxContent> PrepareChooseRecipientBox(
not_null<Main::Session*> session, not_null<Main::Session*> session,

View file

@ -1309,6 +1309,9 @@ void SessionNavigation::showPeerInfo(
const SectionShow &params) { const SectionShow &params) {
if (const auto topic = thread->asTopic()) { if (const auto topic = thread->asTopic()) {
showSection(std::make_shared<Info::Memento>(topic), params); showSection(std::make_shared<Info::Memento>(topic), params);
} else if (const auto sublist = thread->asSublist()
; sublist && sublist->parentChat()) {
showPeerInfo(sublist->sublistPeer()->id, params);
} else { } else {
showPeerInfo(thread->peer()->id, params); showPeerInfo(thread->peer()->id, params);
} }