Update API scheme on layer 148: Notifications.

Support editing / respect notification settings for topics.
This commit is contained in:
John Preston 2022-10-13 21:34:04 +04:00
parent b8bdca8921
commit d92580b8fc
47 changed files with 631 additions and 528 deletions

View file

@ -249,8 +249,6 @@ PRIVATE
boxes/local_storage_box.h
boxes/max_invite_box.cpp
boxes/max_invite_box.h
boxes/mute_settings_box.cpp
boxes/mute_settings_box.h
boxes/peer_list_box.cpp
boxes/peer_list_box.h
boxes/peer_list_controllers.cpp
@ -539,6 +537,8 @@ PRIVATE
data/data_sponsored_messages.h
data/data_streaming.cpp
data/data_streaming.h
data/data_thread.cpp
data/data_thread.h
data/data_types.cpp
data/data_types.h
data/data_user.cpp

View file

@ -474,6 +474,7 @@ notifyPeer#9fd40bd8 peer:Peer = NotifyPeer;
notifyUsers#b4c83b4c = NotifyPeer;
notifyChats#c007cec3 = NotifyPeer;
notifyBroadcasts#d612e8ef = NotifyPeer;
notifyForumTopic#226e6308 peer:Peer top_msg_id:int = NotifyPeer;
sendMessageTypingAction#16bf744e = SendMessageAction;
sendMessageCancelAction#fd5ec8f5 = SendMessageAction;
@ -1456,6 +1457,7 @@ stickerKeyword#fcfeb29c document_id:long keyword:Vector<string> = StickerKeyword
username#b4073647 flags:# editable:flags.0?true active:flags.1?true username:string = Username;
forumTopicDeleted#23f109b id:int = ForumTopic;
forumTopic#5920d6dc flags:# my:flags.1?true closed:flags.2?true pinned:flags.3?true id:int date:int title:string icon_color:int icon_emoji_id:flags.0?long top_message:int read_inbox_max_id:int read_outbox_max_id:int unread_count:int unread_mentions_count:int unread_reactions_count:int from_id:Peer notify_settings:PeerNotifySettings = ForumTopic;
messages.forumTopics#367617d3 flags:# order_by_create_date:flags.0?true count:int topics:Vector<ForumTopic> messages:Vector<Message> chats:Vector<Chat> users:Vector<User> pts:int = messages.ForumTopics;

View file

@ -30,12 +30,12 @@ UnreadThings::UnreadThings(not_null<ApiWrap*> api) : _api(api) {
}
bool UnreadThings::trackMentions(Data::Thread *thread) const {
const auto peer = thread ? thread->owningHistory()->peer.get() : nullptr;
const auto peer = thread ? thread->peer().get() : nullptr;
return peer && (peer->isChat() || peer->isMegagroup());
}
bool UnreadThings::trackReactions(Data::Thread *thread) const {
const auto peer = thread ? thread->owningHistory()->peer.get() : nullptr;
const auto peer = thread ? thread->peer().get() : nullptr;
return peer && (peer->isChat() || peer->isMegagroup());
}

View file

@ -1729,10 +1729,10 @@ void ApiWrap::requestNotifySettings(const MTPInputNotifyPeer &peer) {
const auto requestId = request(MTPaccount_GetNotifySettings(
peer
)).done([=](const MTPPeerNotifySettings &result) {
applyNotifySettings(peer, result);
_session->data().notifySettings().apply(peer, result);
_notifySettingRequests.remove(key);
}).fail([=] {
applyNotifySettings(
_session->data().notifySettings().apply(
peer,
MTP_peerNotifySettings(
MTP_flags(0),
@ -1748,7 +1748,11 @@ void ApiWrap::requestNotifySettings(const MTPInputNotifyPeer &peer) {
}
void ApiWrap::updateNotifySettingsDelayed(
not_null<const Data::ForumTopic*> topic) {
not_null<const Data::Thread*> thread) {
const auto topic = thread->asTopic();
if (!topic) {
return updateNotifySettingsDelayed(thread->peer());
}
if (_updateNotifyTopics.emplace(topic).second) {
topic->destroyed(
) | rpl::start_with_next([=] {
@ -2122,51 +2126,6 @@ void ApiWrap::clearModifyRequest(const QString &key) {
_modifyRequests.remove(key);
}
void ApiWrap::applyNotifySettings(
MTPInputNotifyPeer notifyPeer,
const MTPPeerNotifySettings &settings) {
auto &notifySettings = _session->data().notifySettings();
switch (notifyPeer.type()) {
case mtpc_inputNotifyUsers:
notifySettings.apply(MTP_notifyUsers(), settings);
break;
case mtpc_inputNotifyChats:
notifySettings.apply(MTP_notifyChats(), settings);
break;
case mtpc_inputNotifyBroadcasts:
notifySettings.apply(
MTP_notifyBroadcasts(),
settings);
break;
case mtpc_inputNotifyPeer: {
auto &peer = notifyPeer.c_inputNotifyPeer().vpeer();
const auto apply = [&](PeerId peerId) {
notifySettings.apply(
MTP_notifyPeer(peerToMTP(peerId)),
settings);
};
switch (peer.type()) {
case mtpc_inputPeerEmpty:
apply(0);
break;
case mtpc_inputPeerSelf:
apply(_session->userPeerId());
break;
case mtpc_inputPeerUser:
apply(peerFromUser(peer.c_inputPeerUser().vuser_id()));
break;
case mtpc_inputPeerChat:
apply(peerFromChat(peer.c_inputPeerChat().vchat_id()));
break;
case mtpc_inputPeerChannel:
apply(peerFromChannel(peer.c_inputPeerChannel().vchannel_id()));
break;
}
} break;
}
Core::App().notifications().checkDelayed();
}
void ApiWrap::gotStickerSet(
uint64 setId,
const MTPmessages_StickerSet &result) {

View file

@ -33,6 +33,7 @@ struct ResolvedForwardDraft;
enum class DefaultNotify;
enum class StickersType : uchar;
class ForumTopic;
class Thread;
} // namespace Data
namespace InlineBots {
@ -144,10 +145,6 @@ public:
void registerModifyRequest(const QString &key, mtpRequestId requestId);
void clearModifyRequest(const QString &key);
void applyNotifySettings(
MTPInputNotifyPeer peer,
const MTPPeerNotifySettings &settings);
void saveCurrentDraftToCloud();
void savePinnedOrder(Data::Folder *folder);
@ -245,8 +242,7 @@ public:
void leaveChannel(not_null<ChannelData*> channel);
void requestNotifySettings(const MTPInputNotifyPeer &peer);
void updateNotifySettingsDelayed(
not_null<const Data::ForumTopic*> topic);
void updateNotifySettingsDelayed(not_null<const Data::Thread*> thread);
void updateNotifySettingsDelayed(not_null<const PeerData*> peer);
void updateNotifySettingsDelayed(Data::DefaultNotify type);
void saveDraftToCloudDelayed(not_null<History*> history);

View file

@ -1,98 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
This code is in Public Domain, see license terms in .github/CONTRIBUTING.md
Copyright (C) 2017, Nicholas Guriev <guriev-ns@ya.ru>
*/
#include "boxes/mute_settings_box.h"
#include "lang/lang_keys.h"
#include "main/main_session.h"
#include "data/notify/data_notify_settings.h"
#include "data/data_session.h"
#include "data/data_peer.h"
#include "ui/special_buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/labels.h"
#include "styles/style_layers.h"
#include "styles/style_boxes.h"
namespace {
constexpr auto kForeverHours = 24 * 365;
} // namespace
MuteSettingsBox::MuteSettingsBox(QWidget *parent, not_null<PeerData*> peer)
: _peer(peer) {
}
void MuteSettingsBox::prepare() {
setTitle(tr::lng_disable_notifications_from_tray());
auto y = 0;
object_ptr<Ui::FlatLabel> info(this, st::boxLabel);
info->setText(tr::lng_mute_box_tip(tr::now));
info->moveToLeft(st::boxPadding.left(), y);
y += info->height() + st::boxLittleSkip;
const auto icon = object_ptr<Ui::UserpicButton>(
this,
_peer,
Ui::UserpicButton::Role::Custom,
st::mutePhotoButton);
icon->setPointerCursor(false);
icon->moveToLeft(st::boxPadding.left(), y);
object_ptr<Ui::FlatLabel> title(this, st::muteChatTitle);
title->setText(_peer->name());
title->moveToLeft(
st::boxPadding.left() + st::muteChatTitleLeft,
y + (icon->height() / 2) - (title->height() / 2));
// the icon is always higher than this chat title
y += icon->height() + st::boxMediumSkip;
// in fact, this is mute only for 1 year
const auto group = std::make_shared<Ui::RadiobuttonGroup>(kForeverHours);
y += st::boxOptionListPadding.top();
for (const auto hours : { 1, 4, 18, 72, kForeverHours }) {
const auto text = [&] {
if (hours < 24) {
return tr::lng_mute_duration_hours(tr::now, lt_count, hours);
} else if (hours < kForeverHours) {
return tr::lng_mute_duration_days(tr::now, lt_count, hours / 24);
} else {
return tr::lng_mute_duration_forever(tr::now);
}
}();
object_ptr<Ui::Radiobutton> option(this, group, hours, text);
option->moveToLeft(st::boxPadding.left(), y);
y += option->heightNoMargins() + st::boxOptionListSkip;
}
y += st::boxOptionListPadding.bottom()
- st::boxOptionListSkip
+ st::defaultCheckbox.margin.bottom();
_save = [=] {
const auto muteForSeconds = group->value() * 3600;
_peer->owner().notifySettings().update(
_peer,
{ .period = muteForSeconds });
closeBox();
};
addButton(tr::lng_box_ok(), _save);
addButton(tr::lng_cancel(), [this] { closeBox(); });
setDimensions(st::boxWidth, y);
}
void MuteSettingsBox::keyPressEvent(QKeyEvent *e) {
if (e->key() == Qt::Key_Enter || e->key() == Qt::Key_Return) {
if (_save) {
_save();
}
}
}
// vi: ts=4 tw=80

View file

@ -1,29 +0,0 @@
/*
This file is part of Telegram Desktop,
the official desktop application for the Telegram messaging service.
This code is in Public Domain, see license terms in .github/CONTRIBUTING.md
Copyright (C) 2017, Nicholas Guriev <guriev-ns@ya.ru>
*/
#pragma once
#include "boxes/abstract_box.h"
/* This class implements a dialog-box with radio-buttons for pick duration of
* turning off notifications from a chat. The widget is opened by a context menu
* in the left list of dialogues. */
class MuteSettingsBox : public Ui::BoxContent {
public:
MuteSettingsBox(QWidget *parent, not_null<PeerData*> peer);
protected:
void prepare() override;
void keyPressEvent(QKeyEvent *e) override;
private:
not_null<PeerData*> _peer;
Fn<void()> _save;
};
// vi: ts=4 tw=80

View file

@ -21,7 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_document.h"
#include "data/data_document_media.h"
#include "data/data_document_resolver.h"
#include "data/data_peer.h"
#include "data/data_thread.h"
#include "data/data_session.h"
#include "data/notify/data_notify_settings.h"
#include "lang/lang_keys.h"
@ -342,11 +342,11 @@ void RingtonesBox(
box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
}
void PeerRingtonesBox(
void ThreadRingtonesBox(
not_null<Ui::GenericBox*> box,
not_null<PeerData*> peer) {
const auto now = peer->owner().notifySettings().sound(peer);
RingtonesBox(box, &peer->session(), now, [=](Data::NotifySound sound) {
peer->owner().notifySettings().update(peer, {}, {}, sound);
not_null<Data::Thread*> thread) {
const auto now = thread->owner().notifySettings().sound(thread);
RingtonesBox(box, &thread->session(), now, [=](Data::NotifySound sound) {
thread->owner().notifySettings().update(thread, {}, {}, sound);
});
}

View file

@ -9,10 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/layers/generic_box.h"
class PeerData;
namespace Data {
struct NotifySound;
class Thread;
} // namespace Data
namespace Main {
@ -31,6 +30,6 @@ void RingtonesBox(
Data::NotifySound selected,
Fn<void(Data::NotifySound)> save);
void PeerRingtonesBox(
void ThreadRingtonesBox(
not_null<Ui::GenericBox*> box,
not_null<PeerData*> peer);
not_null<Data::Thread*> thread);

View file

@ -20,14 +20,17 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_group_call.h"
#include "data/data_message_reactions.h"
#include "data/data_peer_bot_command.h"
#include "data/notify/data_notify_settings.h"
#include "main/main_session.h"
#include "main/session/send_as_peers.h"
#include "base/unixtime.h"
#include "core/application.h"
#include "history/history.h"
#include "main/main_session.h"
#include "api/api_chat_invite.h"
#include "api/api_invite_links.h"
#include "apiwrap.h"
#include "window/notifications_manager.h"
namespace {
@ -131,9 +134,14 @@ void ChannelData::setFlags(ChannelDataFlags which) {
session().changes().peerUpdated(this, UpdateFlag::Migration);
}
}
if (diff & Flag::CallNotEmpty) {
if (diff & (Flag::Forum | Flag::CallNotEmpty)) {
if (const auto history = this->owner().historyLoaded(this)) {
history->updateChatListEntry();
if (diff & Flag::CallNotEmpty) {
history->updateChatListEntry();
}
if (diff & Flag::Forum) {
Core::App().notifications().clearFromHistory(history);
}
}
}
}
@ -986,8 +994,8 @@ void ApplyChannelUpdate(
session->changes().peerUpdated(channel, UpdateFlag::Rights);
}
session->api().applyNotifySettings(
MTP_inputNotifyPeer(channel->input),
channel->owner().notifySettings().apply(
channel,
update.vnotify_settings());
if (const auto sendAs = update.vdefault_send_as()) {

View file

@ -13,6 +13,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h"
#include "data/data_group_call.h"
#include "data/data_message_reactions.h"
#include "data/notify/data_notify_settings.h"
#include "history/history.h"
#include "main/main_session.h"
#include "apiwrap.h"
@ -489,9 +490,7 @@ void ApplyChatUpdate(not_null<ChatData*> chat, const MTPDchatFull &update) {
update.vrequests_pending().value_or_empty(),
update.vrecent_requesters().value_or_empty());
chat->session().api().applyNotifySettings(
MTP_inputNotifyPeer(chat->input),
update.vnotify_settings());
chat->owner().notifySettings().apply(chat, update.vnotify_settings());
}
void ApplyChatUpdate(

View file

@ -18,9 +18,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/random.h"
#include "apiwrap.h"
#include "lang/lang_keys.h"
#include "core/application.h"
#include "ui/layers/generic_box.h"
#include "ui/widgets/input_fields.h"
#include "window/window_session_controller.h"
#include "window/notifications_manager.h"
#include "styles/style_boxes.h"
namespace Data {
@ -114,6 +116,16 @@ void Forum::applyReceivedTopics(const MTPmessages_ForumTopics &result) {
applyReceivedTopics(result, false);
}
void Forum::applyTopicDeleted(MsgId rootId) {
const auto i = _topics.find(rootId);
if (i != end(_topics)) {
Core::App().notifications().clearFromTopic(i->second.get());
_topicDestroyed.fire(i->second.get());
_history->destroyMessagesByTopic(rootId);
_topics.erase(i);
}
}
void Forum::applyReceivedTopics(
const MTPmessages_ForumTopics &topics,
bool updateOffset) {
@ -124,23 +136,32 @@ void Forum::applyReceivedTopics(
channel()->ptsReceived(data.vpts().v);
const auto &list = data.vtopics().v;
for (const auto &topic : list) {
const auto rootId = MsgId(topic.data().vid().v);
const auto i = _topics.find(rootId);
const auto creating = (i == end(_topics));
const auto raw = creating
? _topics.emplace(
rootId,
std::make_unique<ForumTopic>(this, rootId)
).first->second.get()
: i->second.get();
raw->applyTopic(topic);
if (updateOffset) {
if (const auto last = raw->lastServerMessage()) {
_offsetDate = last->date();
_offsetId = last->id;
const auto rootId = topic.match([&](const auto &data) {
return data.vid().v;
});
topic.match([&](const MTPDforumTopicDeleted &data) {
if (updateOffset) {
LOG(("API Error: Got a deleted topic in getForumTopics."));
}
_offsetTopicId = rootId;
}
applyTopicDeleted(rootId);
}, [&](const MTPDforumTopic &data) {
const auto i = _topics.find(rootId);
const auto creating = (i == end(_topics));
const auto raw = creating
? _topics.emplace(
rootId,
std::make_unique<ForumTopic>(this, rootId)
).first->second.get()
: i->second.get();
raw->applyTopic(data);
if (updateOffset) {
if (const auto last = raw->lastServerMessage()) {
_offsetDate = last->date();
_offsetId = last->id;
}
_offsetTopicId = rootId;
}
});
}
if (updateOffset
&& (list.isEmpty() || list.size() == data.vcount().v)) {
@ -184,11 +205,13 @@ void Forum::requestTopic(MsgId rootId, Fn<void()> done) {
});
}
void Forum::applyTopicAdded(
ForumTopic *Forum::applyTopicAdded(
MsgId rootId,
const QString &title,
int32 colorId,
DocumentId iconId) {
Expects(rootId != 0);
const auto i = _topics.find(rootId);
const auto raw = (i != end(_topics))
? i->second.get()
@ -203,6 +226,7 @@ void Forum::applyTopicAdded(
raw->addToChatList(FilterId(), topicsList());
_chatsListChanges.fire({});
}
return raw;
}
MsgId Forum::reserveCreatingId(
@ -261,10 +285,6 @@ void Forum::clearAllUnreadReactions() {
}
}
ForumTopic *Forum::topicFor(not_null<const HistoryItem*> item) {
return topicFor(item->topicRootId());
}
ForumTopic *Forum::topicFor(MsgId rootId) {
if (!rootId) {
return nullptr;
@ -273,6 +293,18 @@ ForumTopic *Forum::topicFor(MsgId rootId) {
return (i != end(_topics)) ? i->second.get() : nullptr;
}
ForumTopic *Forum::enforceTopicFor(MsgId rootId) {
Expects(rootId != 0);
const auto i = _topics.find(rootId);
if (i != end(_topics)) {
return i->second.get();
}
const auto result = applyTopicAdded(rootId, {}, {}, {});
requestTopic(rootId);
return result;
}
rpl::producer<> Forum::chatsListChanges() const {
return _chatsListChanges.events();
}

View file

@ -43,14 +43,15 @@ public:
[[nodiscard]] rpl::producer<> chatsListLoadedEvents() const;
void requestTopic(MsgId rootId, Fn<void()> done = nullptr);
void applyTopicAdded(
ForumTopic *applyTopicAdded(
MsgId rootId,
const QString &title,
int32 colorId,
DocumentId iconId);
void applyTopicCreated(MsgId rootId, MsgId realId);
[[nodiscard]] ForumTopic *topicFor(not_null<const HistoryItem*> item);
void applyTopicDeleted(MsgId rootId);
[[nodiscard]] ForumTopic *topicFor(MsgId rootId);
[[nodiscard]] ForumTopic *enforceTopicFor(MsgId rootId);
void applyReceivedTopics(const MTPmessages_ForumTopics &topics);

View file

@ -197,10 +197,9 @@ void ForumTopic::setRealRootId(MsgId realId) {
}
}
void ForumTopic::applyTopic(const MTPForumTopic &topic) {
Expects(_rootId == topic.data().vid().v);
void ForumTopic::applyTopic(const MTPDforumTopic &data) {
Expects(_rootId == data.vid().v);
const auto &data = topic.data();
applyTitle(qs(data.vtitle()));
if (const auto iconId = data.vicon_emoji_id()) {
applyIconId(iconId->v);
@ -218,6 +217,8 @@ void ForumTopic::applyTopic(const MTPForumTopic &topic) {
}
#endif
owner().notifySettings().apply(this, data.vnotify_settings());
_replies->setInboxReadTill(
data.vread_inbox_max_id().v,
data.vunread_count().v);

View file

@ -41,7 +41,7 @@ class Forum;
const QString &title,
const style::ForumTopicIcon &st);
class ForumTopic final : public Data::Thread {
class ForumTopic final : public Thread {
public:
ForumTopic(not_null<Forum*> forum, MsgId rootId);
~ForumTopic();
@ -59,7 +59,7 @@ public:
void setRealRootId(MsgId realId);
void applyTopic(const MTPForumTopic &topic);
void applyTopic(const MTPDforumTopic &data);
TimeId adjustedChatListTimeId() const override;
@ -91,17 +91,17 @@ public:
void applyItemAdded(not_null<HistoryItem*> item);
void applyItemRemoved(MsgId id);
[[nodiscard]] Data::PeerNotifySettings &notify() {
[[nodiscard]] PeerNotifySettings &notify() {
return _notify;
}
[[nodiscard]] const Data::PeerNotifySettings &notify() const {
[[nodiscard]] const PeerNotifySettings &notify() const {
return _notify;
}
void loadUserpic() override;
void paintUserpic(
Painter &p,
std::shared_ptr<Data::CloudImageView> &view,
std::shared_ptr<CloudImageView> &view,
const Dialogs::Ui::PaintContext &context) const override;
[[nodiscard]] int unreadCount() const;
@ -127,12 +127,12 @@ private:
int count,
bool known) const;
const not_null<Data::Forum*> _forum;
const not_null<Forum*> _forum;
const not_null<Dialogs::MainList*> _list;
std::shared_ptr<RepliesList> _replies;
MsgId _rootId = 0;
Data::PeerNotifySettings _notify;
PeerNotifySettings _notify;
QString _title;
DocumentId _iconId = 0;

View file

@ -899,16 +899,6 @@ Data::Forum *PeerData::forum() const {
return nullptr;
}
Data::ForumTopic *PeerData::forumTopicFor(
not_null<const HistoryItem*> item) const {
if (const auto rootId = item->topicRootId()) {
if (const auto forum = this->forum()) {
return forum->topicFor(rootId);
}
}
return nullptr;
}
Data::ForumTopic *PeerData::forumTopicFor(MsgId rootId) const {
if (!rootId) {
return nullptr;

View file

@ -198,8 +198,6 @@ public:
}
[[nodiscard]] Data::Forum *forum() const;
[[nodiscard]] Data::ForumTopic *forumTopicFor(
not_null<const HistoryItem*> item) const;
[[nodiscard]] Data::ForumTopic *forumTopicFor(MsgId rootId) const;
[[nodiscard]] Data::PeerNotifySettings &notify() {

View file

@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#include "data/data_thread.h"
#include "data/data_forum_topic.h"
#include "data/data_peer.h"
#include "history/history.h"
#include "history/history_item.h"
#include "history/history_unread_things.h"
@ -15,6 +17,19 @@ namespace Data {
Thread::~Thread() = default;
not_null<PeerData*> Thread::peer() const {
return owningHistory()->peer;
}
PeerNotifySettings &Thread::notify() {
const auto topic = asTopic();
return topic ? topic->notify() : peer()->notify();
}
const PeerNotifySettings &Thread::notify() const {
return const_cast<Thread*>(this)->notify();
}
void Thread::setUnreadThingsKnown() {
_flags |= Flag::UnreadThingsKnown;
}
@ -72,7 +87,7 @@ void Thread::clearNotifications() {
}
void Thread::clearIncomingNotifications() {
if (!owningHistory()->peer->isSelf()) {
if (!peer()->isSelf()) {
const auto proj = [](ItemNotification notification) {
return notification.item->out();
};

View file

@ -30,6 +30,8 @@ extern const int &dialogsTextWidthMin;
namespace Data {
class PeerNotifySettings;
enum class ItemNotificationType {
Message,
Reaction,
@ -55,6 +57,9 @@ public:
[[nodiscard]] not_null<const History*> owningHistory() const {
return const_cast<Thread*>(this)->owningHistory();
}
[[nodiscard]] not_null<PeerData*> peer() const;
[[nodiscard]] PeerNotifySettings &notify();
[[nodiscard]] const PeerNotifySettings &notify() const;
void setUnreadThingsKnown();
[[nodiscard]] HistoryUnreadThings::Proxy unreadMentions();

View file

@ -13,8 +13,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_changes.h"
#include "data/data_peer_bot_command.h"
#include "data/data_emoji_statuses.h"
#include "data/notify/data_notify_settings.h"
#include "ui/text/text_options.h"
#include "apiwrap.h"
#include "lang/lang_keys.h"
#include "styles/style_chat.h"
@ -320,9 +320,7 @@ void ApplyUserUpdate(not_null<UserData*> user, const MTPDuserFull &update) {
user->owner().processPhoto(*photo);
}
user->setSettings(update.vsettings());
user->session().api().applyNotifySettings(
MTP_inputNotifyPeer(user->input),
update.vnotify_settings());
user->owner().notifySettings().apply(user, update.vnotify_settings());
user->setMessagesTTL(update.vttl_period().value_or_empty());
if (const auto info = update.vbot_info()) {

View file

@ -62,50 +62,125 @@ void NotifySettings::request(not_null<PeerData*> peer) {
}
}
void NotifySettings::request(not_null<Data::ForumTopic*> topic) {
if (topic->notify().settingsUnknown()) {
topic->session().api().requestNotifySettings(
MTP_inputNotifyForumTopic(
topic->channel()->input,
MTP_int(topic->rootId())));
void NotifySettings::request(not_null<Data::Thread*> thread) {
if (const auto topic = thread->asTopic()) {
if (topic->notify().settingsUnknown()) {
topic->session().api().requestNotifySettings(
MTP_inputNotifyForumTopic(
topic->channel()->input,
MTP_int(topic->rootId())));
}
}
request(topic->channel());
request(thread->peer());
}
void NotifySettings::apply(
const MTPNotifyPeer &notifyPeer,
const MTPPeerNotifySettings &settings) {
const auto set = [&](DefaultNotify type) {
if (defaultValue(type).settings.change(settings)) {
updateLocal(type);
}
notifyPeer.match([&](const MTPDnotifyUsers &) {
apply(DefaultNotify::User, settings);
}, [&](const MTPDnotifyChats &) {
apply(DefaultNotify::Group, settings);
}, [&](const MTPDnotifyBroadcasts &) {
apply(DefaultNotify::Broadcast, settings);
}, [&](const MTPDnotifyPeer &data) {
apply(peerFromMTP(data.vpeer()), settings);
}, [&](const MTPDnotifyForumTopic &data) {
apply(peerFromMTP(data.vpeer()), data.vtop_msg_id().v, settings);
});
}
void NotifySettings::apply(
const MTPInputNotifyPeer &notifyPeer,
const MTPPeerNotifySettings &settings) {
const auto peerFromInput = [&](const MTPInputPeer &peer) {
return peer.match([&](const MTPDinputPeerSelf &) {
return _owner->session().userPeerId();
}, [](const MTPDinputPeerUser &data) {
return peerFromUser(data.vuser_id());
}, [](const MTPDinputPeerChat &data) {
return peerFromChat(data.vchat_id());
}, [](const MTPDinputPeerChannel &data) {
return peerFromChannel(data.vchannel_id());
}, [](const MTPDinputPeerUserFromMessage &data) -> PeerId {
Unexpected("From message peer in NotifySettings::apply.");
}, [](const MTPDinputPeerChannelFromMessage &data) -> PeerId {
Unexpected("From message peer in NotifySettings::apply.");
}, [](const MTPDinputPeerEmpty &) -> PeerId {
Unexpected("Empty peer in NotifySettings::apply.");
});
};
switch (notifyPeer.type()) {
case mtpc_notifyUsers: set(DefaultNotify::User); break;
case mtpc_notifyChats: set(DefaultNotify::Group); break;
case mtpc_notifyBroadcasts: set(DefaultNotify::Broadcast); break;
case mtpc_notifyPeer: {
const auto &data = notifyPeer.c_notifyPeer();
if (const auto peer = _owner->peerLoaded(peerFromMTP(data.vpeer()))) {
if (peer->notify().change(settings)) {
updateLocal(peer);
}
notifyPeer.match([&](const MTPDinputNotifyUsers &) {
apply(DefaultNotify::User, settings);
}, [&](const MTPDinputNotifyChats &) {
apply(DefaultNotify::Group, settings);
}, [&](const MTPDinputNotifyBroadcasts &) {
apply(DefaultNotify::Broadcast, settings);
}, [&](const MTPDinputNotifyPeer &data) {
apply(peerFromInput(data.vpeer()), settings);
}, [&](const MTPDinputNotifyForumTopic &data) {
apply(peerFromInput(data.vpeer()), data.vtop_msg_id().v, settings);
});
}
void NotifySettings::apply(
DefaultNotify type,
const MTPPeerNotifySettings &settings) {
if (defaultValue(type).settings.change(settings)) {
updateLocal(type);
Core::App().notifications().checkDelayed();
}
}
void NotifySettings::apply(
PeerId peerId,
const MTPPeerNotifySettings &settings) {
if (const auto peer = _owner->peerLoaded(peerId)) {
apply(peer, settings);
}
}
void NotifySettings::apply(
not_null<PeerData*> peer,
const MTPPeerNotifySettings &settings) {
if (peer->notify().change(settings)) {
updateLocal(peer);
Core::App().notifications().checkDelayed();
}
}
void NotifySettings::apply(
PeerId peerId,
MsgId topicRootId,
const MTPPeerNotifySettings &settings) {
if (const auto peer = _owner->peerLoaded(peerId)) {
if (const auto topic = peer->forumTopicFor(topicRootId)) {
apply(topic, settings);
}
} break;
}
}
void NotifySettings::apply(
not_null<Data::ForumTopic*> topic,
const MTPPeerNotifySettings &settings) {
if (topic->notify().change(settings)) {
updateLocal(topic);
Core::App().notifications().checkDelayed();
}
}
void NotifySettings::update(
not_null<Data::ForumTopic*> topic,
not_null<Data::Thread*> thread,
Data::MuteValue muteForSeconds,
std::optional<bool> silentPosts,
std::optional<NotifySound> sound) {
if (topic->notify().change(muteForSeconds, std::nullopt, sound)) {
updateLocal(topic);
topic->session().api().updateNotifySettingsDelayed(topic);
if (thread->notify().change(muteForSeconds, silentPosts, sound)) {
updateLocal(thread);
thread->session().api().updateNotifySettingsDelayed(thread);
}
}
void NotifySettings::resetToDefault(not_null<Data::ForumTopic*> topic) {
void NotifySettings::resetToDefault(not_null<Data::Thread*> thread) {
const auto empty = MTP_peerNotifySettings(
MTP_flags(0),
MTPBool(),
@ -114,9 +189,9 @@ void NotifySettings::resetToDefault(not_null<Data::ForumTopic*> topic) {
MTPNotificationSound(),
MTPNotificationSound(),
MTPNotificationSound());
if (topic->notify().change(empty)) {
updateLocal(topic);
topic->session().api().updateNotifySettingsDelayed(topic);
if (thread->notify().change(empty)) {
updateLocal(thread);
thread->session().api().updateNotifySettingsDelayed(thread);
}
}
@ -186,7 +261,11 @@ void NotifySettings::defaultUpdate(
}
}
void NotifySettings::updateLocal(not_null<Data::ForumTopic*> topic) {
void NotifySettings::updateLocal(not_null<Data::Thread*> thread) {
const auto topic = thread->asTopic();
if (!topic) {
return updateLocal(thread->peer());
}
auto changesIn = crl::time(0);
const auto muted = isMuted(topic, &changesIn);
topic->setMuted(muted);
@ -350,35 +429,39 @@ void NotifySettings::unmuteByFinished() {
}
bool NotifySettings::isMuted(
not_null<const Data::ForumTopic*> topic,
not_null<const Data::Thread*> thread,
crl::time *changesIn) const {
const auto until = topic->notify().muteUntil();
const auto topic = thread->asTopic();
const auto until = topic ? topic->notify().muteUntil() : std::nullopt;
return until
? MutedFromUntil(*until, changesIn)
: isMuted(topic->channel(), changesIn);
: isMuted(thread->peer(), changesIn);
}
bool NotifySettings::isMuted(not_null<const Data::ForumTopic*> topic) const {
return isMuted(topic, nullptr);
bool NotifySettings::isMuted(not_null<const Data::Thread*> thread) const {
return isMuted(thread, nullptr);
}
NotifySound NotifySettings::sound(
not_null<const Data::ForumTopic*> topic) const {
const auto sound = topic->notify().sound();
return sound ? *sound : this->sound(topic->channel());
not_null<const Data::Thread*> thread) const {
const auto topic = thread->asTopic();
const auto sound = topic ? topic->notify().sound() : std::nullopt;
return sound ? *sound : this->sound(thread->peer());
}
bool NotifySettings::muteUnknown(
not_null<const Data::ForumTopic*> topic) const {
return topic->notify().settingsUnknown()
|| (!topic->notify().muteUntil().has_value()
&& muteUnknown(topic->channel()));
not_null<const Data::Thread*> thread) const {
const auto topic = thread->asTopic();
return (topic && topic->notify().settingsUnknown())
|| ((!topic || !topic->notify().muteUntil().has_value())
&& muteUnknown(thread->peer()));
}
bool NotifySettings::soundUnknown(
not_null<const Data::ForumTopic*> topic) const {
return topic->notify().settingsUnknown()
|| (!topic->notify().sound().has_value()
not_null<const Data::Thread*> thread) const {
const auto topic = thread->asTopic();
return (topic && topic->notify().settingsUnknown())
|| ((!topic || !topic->notify().sound().has_value())
&& soundUnknown(topic->channel()));
}
@ -442,8 +525,11 @@ bool NotifySettings::settingsUnknown(not_null<const PeerData*> peer) const {
}
bool NotifySettings::settingsUnknown(
not_null<const Data::ForumTopic*> topic) const {
return muteUnknown(topic) || soundUnknown(topic);
not_null<const Data::Thread*> thread) const {
const auto topic = thread->asTopic();
return muteUnknown(thread)
|| soundUnknown(thread)
|| (!topic && silentPostsUnknown(thread->peer()));
}
rpl::producer<> NotifySettings::defaultUpdates(DefaultNotify type) const {

View file

@ -17,6 +17,7 @@ namespace Data {
class DocumentMedia;
class Session;
class Thread;
class ForumTopic;
enum class DefaultNotify {
@ -30,15 +31,33 @@ public:
NotifySettings(not_null<Session*> owner);
void request(not_null<PeerData*> peer);
void request(not_null<Data::ForumTopic*> topic);
void request(not_null<Data::Thread*> thread);
void apply(
const MTPNotifyPeer &notifyPeer,
const MTPPeerNotifySettings &settings);
void update(
void apply(
const MTPInputNotifyPeer &notifyPeer,
const MTPPeerNotifySettings &settings);
void apply(DefaultNotify type, const MTPPeerNotifySettings &settings);
void apply(PeerId peerId, const MTPPeerNotifySettings &settings);
void apply(
not_null<PeerData*> peer,
const MTPPeerNotifySettings &settings);
void apply(
PeerId peerId,
MsgId topicRootId,
const MTPPeerNotifySettings &settings);
void apply(
not_null<Data::ForumTopic*> topic,
const MTPPeerNotifySettings &settings);
void update(
not_null<Data::Thread*> thread,
Data::MuteValue muteForSeconds,
std::optional<bool> silentPosts = std::nullopt,
std::optional<NotifySound> sound = std::nullopt);
void resetToDefault(not_null<Data::ForumTopic*> topic);
void resetToDefault(not_null<Data::Thread*> thread);
void update(
not_null<PeerData*> peer,
Data::MuteValue muteForSeconds,
@ -64,14 +83,13 @@ public:
std::optional<bool> silentPosts = std::nullopt,
std::optional<NotifySound> sound = std::nullopt);
[[nodiscard]] bool isMuted(
not_null<const Data::ForumTopic*> topic) const;
[[nodiscard]] bool isMuted(not_null<const Data::Thread*> thread) const;
[[nodiscard]] NotifySound sound(
not_null<const Data::ForumTopic*> topic) const;
not_null<const Data::Thread*> thread) const;
[[nodiscard]] bool muteUnknown(
not_null<const Data::ForumTopic*> topic) const;
not_null<const Data::Thread*> thread) const;
[[nodiscard]] bool soundUnknown(
not_null<const Data::ForumTopic*> topic) const;
not_null<const Data::Thread*> thread) const;
[[nodiscard]] bool isMuted(not_null<const PeerData*> peer) const;
[[nodiscard]] bool silentPosts(not_null<const PeerData*> peer) const;
@ -90,7 +108,7 @@ private:
void cacheSound(const std::optional<NotifySound> &sound);
[[nodiscard]] bool isMuted(
not_null<const Data::ForumTopic*> peer,
not_null<const Data::Thread*> thread,
crl::time *changesIn) const;
[[nodiscard]] bool isMuted(
not_null<const PeerData*> peer,
@ -102,11 +120,11 @@ private:
not_null<const PeerData*> peer) const;
[[nodiscard]] bool settingsUnknown(not_null<const PeerData*> peer) const;
[[nodiscard]] bool settingsUnknown(
not_null<const Data::ForumTopic*> topic) const;
not_null<const Data::Thread*> thread) const;
void unmuteByFinished();
void unmuteByFinishedDelayed(crl::time delay);
void updateLocal(not_null<Data::ForumTopic*> topic);
void updateLocal(not_null<Data::Thread*> thread);
void updateLocal(not_null<PeerData*> peer);
void updateLocal(DefaultNotify type);

View file

@ -180,9 +180,7 @@ MTPinputPeerNotifySettings NotifyPeerSettingsValue::serialize() const {
PeerNotifySettings::PeerNotifySettings() = default;
bool PeerNotifySettings::change(const MTPPeerNotifySettings &settings) {
Expects(settings.type() == mtpc_peerNotifySettings);
auto &data = settings.c_peerNotifySettings();
auto &data = settings.data();
const auto empty = !data.vflags().v;
if (empty) {
if (!_known || _value) {

View file

@ -81,6 +81,22 @@ Data::ForumTopic *Entry::asTopic() {
: nullptr;
}
const History *Entry::asHistory() const {
return const_cast<Entry*>(this)->asHistory();
}
const Data::Folder *Entry::asFolder() const {
return const_cast<Entry*>(this)->asFolder();
}
const Data::Thread *Entry::asThread() const {
return const_cast<Entry*>(this)->asThread();
}
const Data::ForumTopic *Entry::asTopic() const {
return const_cast<Entry*>(this)->asTopic();
}
void Entry::pinnedIndexChanged(FilterId filterId, int was, int now) {
if (!filterId && session().supportMode()) {
// Force reorder in support mode.

View file

@ -119,6 +119,11 @@ public:
Data::Thread *asThread();
Data::ForumTopic *asTopic();
const History *asHistory() const;
const Data::Folder *asFolder() const;
const Data::Thread *asThread() const;
const Data::ForumTopic *asTopic() const;
PositionChange adjustByPosInChatList(
FilterId filterId,
not_null<MainList*> list);

View file

@ -53,17 +53,15 @@ Data::Thread *Key::thread() const {
return _value ? _value->asThread() : nullptr;
}
History *Key::parentHistory() const {
if (const auto result = history()) {
return result;
} else if (const auto child = topic()) {
return child->history();
History *Key::owningHistory() const {
if (const auto thread = this->thread()) {
return thread->owningHistory();
}
return nullptr;
}
PeerData *Key::peer() const {
if (const auto history = parentHistory()) {
if (const auto history = owningHistory()) {
return history->peer;
}
return nullptr;

View file

@ -42,7 +42,7 @@ public:
Data::Folder *folder() const;
Data::ForumTopic *topic() const;
Data::Thread *thread() const;
History *parentHistory() const;
History *owningHistory() const;
PeerData *peer() const;
friend inline constexpr auto operator<=>(Key, Key) noexcept = default;

View file

@ -152,7 +152,7 @@ void History::checkChatListMessageRemoved(not_null<HistoryItem*> item) {
}
void History::itemVanished(not_null<HistoryItem*> item) {
item->thread()->removeNotification(item);
item->notificationThread()->removeNotification(item);
if (lastKeyboardId == item->id) {
clearLastKeyboard();
}
@ -450,6 +450,7 @@ void History::destroyMessage(not_null<HistoryItem*> item) {
void History::destroyMessagesByDates(TimeId minDate, TimeId maxDate) {
auto toDestroy = std::vector<not_null<HistoryItem*>>();
toDestroy.reserve(_messages.size());
for (const auto &message : _messages) {
if (message->isRegular()
&& message->date() > minDate
@ -462,6 +463,19 @@ void History::destroyMessagesByDates(TimeId minDate, TimeId maxDate) {
}
}
void History::destroyMessagesByTopic(MsgId topicRootId) {
auto toDestroy = std::vector<not_null<HistoryItem*>>();
toDestroy.reserve(_messages.size());
for (const auto &message : _messages) {
if (message->topicRootId() == topicRootId) {
toDestroy.push_back(message.get());
}
}
for (const auto item : toDestroy) {
item->destroy();
}
}
void History::unpinAllMessages() {
session().storage().remove(
Storage::SharedMediaRemoveAll(
@ -1123,7 +1137,7 @@ void History::newItemAdded(not_null<HistoryItem*> item) {
.type = Data::ItemNotificationType::Message,
};
if (item->showNotification()) {
item->thread()->pushNotification(notification);
item->notificationThread()->pushNotification(notification);
}
owner().notifyNewItemAdded(item);
const auto stillShow = item->showNotification(); // Could be read already.

View file

@ -134,6 +134,7 @@ public:
}
void destroyMessage(not_null<HistoryItem*> item);
void destroyMessagesByDates(TimeId minDate, TimeId maxDate);
void destroyMessagesByTopic(MsgId topicRootId);
void unpinAllMessages();

View file

@ -232,7 +232,7 @@ void CheckReactionNotificationSchedule(
.reactionSender = user,
.type = Data::ItemNotificationType::Reaction,
};
item->thread()->pushNotification(notification);
item->notificationThread()->pushNotification(notification);
Core::App().notifications().schedule(notification);
return;
}
@ -620,9 +620,11 @@ void HistoryItem::destroy() {
_history->destroyMessage(this);
}
not_null<Data::Thread*> HistoryItem::thread() const {
if (const auto topic = this->topic()) {
return topic;
not_null<Data::Thread*> HistoryItem::notificationThread() const {
if (const auto rootId = topicRootId()) {
if (const auto forum = _history->peer->forum()) {
return forum->enforceTopicFor(rootId);
}
}
return _history;
}

View file

@ -123,7 +123,7 @@ public:
const QString &label,
const TextWithEntities &content);
[[nodiscard]] not_null<Data::Thread*> thread() const;
[[nodiscard]] not_null<Data::Thread*> notificationThread() const;
[[nodiscard]] not_null<History*> history() const {
return _history;
}

View file

@ -743,12 +743,12 @@ void TopBarWidget::setActiveChat(
_titleNameVersion = 0;
_emojiInteractionSeen = nullptr;
_activeChatLifetime.destroy();
if (const auto history = _activeChat.key.parentHistory()) {
if (const auto peer = _activeChat.key.peer()) {
session().changes().peerFlagsValue(
history->peer,
peer,
Data::PeerUpdate::Flag::GroupCall
) | rpl::map([=] {
return history->peer->groupCall();
return peer->groupCall();
}) | rpl::distinct_until_changed(
) | rpl::map([](Data::GroupCall *call) {
return call ? call->fullCountValue() : rpl::single(-1);

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_peer_values.h"
#include "data/data_session.h"
#include "data/data_folder.h"
#include "data/data_forum_topic.h"
#include "data/data_channel.h"
#include "data/data_changes.h"
#include "data/data_user.h"
@ -22,13 +23,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/buttons.h"
#include "ui/widgets/box_content_divider.h"
#include "ui/boxes/report_box.h"
#include "ui/boxes/confirm_box.h"
#include "ui/layers/generic_box.h"
#include "ui/toast/toast.h"
#include "ui/text/text_utilities.h" // Ui::Text::ToUpper
#include "history/history_location_manager.h" // LocationClickHandler.
#include "history/view/history_view_context_menu.h" // HistoryView::ShowReportPeerBox
#include "boxes/abstract_box.h"
#include "ui/boxes/confirm_box.h"
#include "boxes/peer_list_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/add_contact_box.h"
@ -37,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/report_messages_box.h"
#include "lang/lang_keys.h"
#include "menu/menu_mute.h"
#include "history/history.h"
#include "info/info_controller.h"
#include "info/info_memento.h"
#include "info/profile/info_profile_icon.h"
@ -132,6 +134,10 @@ public:
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<PeerData*> peer);
DetailsFiller(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<Data::ForumTopic*> topic);
object_ptr<Ui::RpWidget> fill();
@ -159,6 +165,7 @@ private:
not_null<Controller*> _controller;
not_null<Ui::RpWidget*> _parent;
not_null<PeerData*> _peer;
Data::ForumTopic *_topic = nullptr;
object_ptr<Ui::VerticalLayout> _wrap;
};
@ -202,6 +209,17 @@ DetailsFiller::DetailsFiller(
, _wrap(_parent) {
}
DetailsFiller::DetailsFiller(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<Data::ForumTopic*> topic)
: _controller(controller)
, _parent(parent)
, _peer(topic->peer())
, _topic(topic)
, _wrap(_parent) {
}
template <typename T>
bool SetClickContext(
const ClickHandlerPtr &handler,
@ -338,16 +356,20 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
[=] { controller->window().show(Box(EditContactBox, controller, user)); },
tracker);
} else {
const auto topicRootId = _topic ? _topic->rootId() : 0;
const auto addToLink = topicRootId
? "?topic=" + QString::number(topicRootId.bare)
: QString();
auto linkText = LinkValue(
_peer
) | rpl::map([](const QString &link) {
) | rpl::map([=](const QString &link) {
return link.isEmpty()
? TextWithEntities()
: Ui::Text::Link(
(link.startsWith(qstr("https://"))
? link.mid(qstr("https://").size())
: link),
link);
: link) + addToLink,
link + addToLink);
});
auto link = addInfoOneLine(
tr::lng_info_link_label(),
@ -358,14 +380,14 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
const auto link = peer->session().createInternalLinkFull(
peer->userName());
if (!link.isEmpty()) {
QGuiApplication::clipboard()->setText(link);
QGuiApplication::clipboard()->setText(link + addToLink);
Ui::Toast::Show(
Window::Show(controller).toastParent(),
tr::lng_username_copied(tr::now));
}
});
if (const auto channel = _peer->asChannel()) {
if (const auto channel = _topic ? nullptr : _peer->asChannel()) {
auto locationText = LocationValue(
channel
) | rpl::map([](const ChannelLocation *location) {
@ -382,7 +404,9 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
)->setLinksTrusted();
}
addInfoLine(tr::lng_info_about_label(), AboutValue(_peer));
addInfoLine(
tr::lng_info_about_label(),
_topic ? rpl::single(TextWithEntities()) : AboutValue(_peer));
}
if (!_peer->isSelf()) {
// No notifications toggle for Self => no separator.
@ -400,17 +424,27 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupInfo() {
result,
st::infoIconInformation,
st::infoInformationIconPosition);
return result;
}
object_ptr<Ui::RpWidget> DetailsFiller::setupMuteToggle() {
const auto peer = _peer;
const auto topicRootId = _topic ? _topic->rootId() : MsgId();
const auto makeThread = [=] {
return topicRootId
? static_cast<Data::Thread*>(peer->forumTopicFor(topicRootId))
: peer->owner().history(peer).get();
};
auto result = object_ptr<Ui::SettingsButton>(
_wrap,
tr::lng_profile_enable_notifications(),
st::infoNotificationsButton);
result->toggleOn(NotificationsEnabledValue(peer), true);
result->toggleOn(_topic
? NotificationsEnabledValue(_topic)
: NotificationsEnabledValue(peer), true);
result->setAcceptBoth();
const auto notifySettings = &peer->owner().notifySettings();
MuteMenu::SetupMuteMenu(
result.data(),
result->clicks(
@ -418,16 +452,15 @@ object_ptr<Ui::RpWidget> DetailsFiller::setupMuteToggle() {
if (button == Qt::RightButton) {
return true;
}
if (peer->owner().notifySettings().isMuted(peer)) {
peer->owner().notifySettings().update(
peer,
{ .unmute = true });
if (notifySettings->isMuted(peer)) {
notifySettings->update(peer, { .unmute = true });
return false;
} else {
return true;
}
}) | rpl::to_empty,
{ peer, std::make_shared<Window::Show>(_controller) });
makeThread,
std::make_shared<Window::Show>(_controller));
object_ptr<FloatingIcon>(
result,
st::infoIconNotifications,
@ -834,6 +867,14 @@ object_ptr<Ui::RpWidget> SetupDetails(
return filler.fill();
}
object_ptr<Ui::RpWidget> SetupDetails(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<Data::ForumTopic*> topic) {
DetailsFiller filler(controller, parent, topic);
return filler.fill();
}
object_ptr<Ui::RpWidget> SetupActions(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,

View file

@ -13,17 +13,26 @@ namespace Ui {
class RpWidget;
} // namespace Ui
namespace Data {
class ForumTopic;
} // namespace Data
namespace Info {
class Controller;
} // namespace Info
namespace Profile {
namespace Info::Profile {
object_ptr<Ui::RpWidget> SetupDetails(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<PeerData*> peer);
object_ptr<Ui::RpWidget> SetupDetails(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
not_null<Data::ForumTopic*> topic);
object_ptr<Ui::RpWidget> SetupActions(
not_null<Controller*> controller,
not_null<Ui::RpWidget*> parent,
@ -34,5 +43,4 @@ object_ptr<Ui::RpWidget> SetupChannelMembers(
not_null<Ui::RpWidget*> parent,
not_null<PeerData*> peer);
} // namespace Profile
} // namespace Info
} // namespace Info::Profile

View file

@ -79,12 +79,14 @@ object_ptr<Ui::RpWidget> InnerWidget::setupContent(
}, _cover->lifetime());
_cover->setOnlineCount(rpl::single(0));
if (_topic) {
result->add(setupSharedMedia(result.data()));
result->add(SetupDetails(_controller, parent, _topic));
} else {
result->add(SetupDetails(_controller, parent, _peer));
}
result->add(setupSharedMedia(result.data()));
if (_topic) {
return result;
}
result->add(SetupDetails(_controller, parent, _peer));
result->add(setupSharedMedia(result.data()));
if (auto members = SetupChannelMembers(_controller, result.data(), _peer)) {
result->add(std::move(members));
}

View file

@ -181,7 +181,11 @@ rpl::producer<const ChannelLocation*> LocationValue(
}
rpl::producer<bool> NotificationsEnabledValue(
not_null<Data::ForumTopic*> topic) {
not_null<Data::Thread*> thread) {
const auto topic = thread->asTopic();
if (!topic) {
return NotificationsEnabledValue(thread->peer());
}
return rpl::merge(
topic->session().changes().topicFlagsValue(
topic,

View file

@ -16,6 +16,7 @@ struct ChannelLocation;
namespace Data {
class ForumTopic;
class Thread;
} // namespace Data
namespace Main {
@ -63,7 +64,7 @@ rpl::producer<not_null<PeerData*>> MigratedOrMeValue(
[[nodiscard]] rpl::producer<bool> NotificationsEnabledValue(
not_null<PeerData*> peer);
[[nodiscard]] rpl::producer<bool> NotificationsEnabledValue(
not_null<Data::ForumTopic*> topic);
not_null<Data::Thread*> thread);
[[nodiscard]] rpl::producer<bool> IsContactValue(not_null<UserData*> user);
[[nodiscard]] rpl::producer<QString> InviteToChatButton(
not_null<UserData*> user);

View file

@ -72,7 +72,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "inline_bots/inline_bot_layout_item.h"
#include "ui/boxes/confirm_box.h"
#include "boxes/sticker_set_box.h"
#include "boxes/mute_settings_box.h"
#include "boxes/peer_list_controllers.h"
#include "boxes/download_path_box.h"
#include "boxes/connection_box.h"

View file

@ -8,8 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "menu/menu_mute.h"
#include "boxes/ringtones_box.h"
#include "data/data_peer.h"
#include "data/data_session.h"
#include "data/data_thread.h"
#include "data/notify/data_notify_settings.h"
#include "info/profile/info_profile_values.h"
#include "lang/lang_keys.h"
@ -70,7 +70,7 @@ public:
MuteItem(
not_null<RpWidget*> parent,
const style::Menu &st,
not_null<PeerData*> peer);
not_null<Data::Thread*> thread);
protected:
void paintEvent(QPaintEvent *e) override;
@ -85,7 +85,7 @@ private:
MuteItem::MuteItem(
not_null<RpWidget*> parent,
const style::Menu &st,
not_null<PeerData*> peer)
not_null<Data::Thread*> thread)
: Ui::Menu::Action(
parent,
st,
@ -93,10 +93,9 @@ MuteItem::MuteItem(
nullptr,
nullptr)
, _itemIconPosition(st.itemIconPosition)
, _isMuted(peer->owner().notifySettings().isMuted(peer)) {
, _isMuted(thread->owner().notifySettings().isMuted(thread)) {
Info::Profile::NotificationsEnabledValue(
peer
thread
) | rpl::start_with_next([=](bool isUnmuted) {
const auto isMuted = !isUnmuted;
action()->setText(isMuted
@ -113,10 +112,13 @@ MuteItem::MuteItem(
st::defaultPopupMenu.showDuration);
}, lifetime());
const auto weak = base::make_weak(thread.get());
setClickedCallback([=] {
peer->owner().notifySettings().update(
peer,
{ .unmute = _isMuted, .forever = !_isMuted });
if (const auto strong = weak.get()) {
strong->owner().notifySettings().update(
strong,
{ .unmute = _isMuted, .forever = !_isMuted });
}
});
}
@ -138,7 +140,7 @@ void MuteItem::paintEvent(QPaintEvent *e) {
icon.paint(p, _itemIconPosition, width(), color);
}
void MuteBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
void MuteBox(not_null<Ui::GenericBox*> box, not_null<Data::Thread*> thread) {
struct State {
int lastSeconds = 0;
};
@ -158,11 +160,15 @@ void MuteBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
? tr::lng_mute_menu_unmute()
: tr::lng_mute_menu_mute();
}) | rpl::flatten_latest();
const auto weak = base::make_weak(thread.get());
Ui::ConfirmBox(box, {
.confirmed = [=] {
peer->owner().notifySettings().update(
peer,
{ .period = state->lastSeconds });
if (const auto strong = weak.get()) {
strong->owner().notifySettings().update(
strong,
{ .period = state->lastSeconds });
}
box->getDelegate()->hideLayer();
},
.confirmText = std::move(confirmText),
@ -170,7 +176,9 @@ void MuteBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
});
}
void PickMuteBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
void PickMuteBox(
not_null<Ui::GenericBox*> box,
not_null<Data::Thread*> thread) {
struct State {
base::unique_qptr<Ui::PopupMenu> menu;
};
@ -183,14 +191,17 @@ void PickMuteBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
const auto pickerCallback = TimePickerBox(box, seconds, phrases, 0);
const auto weak = base::make_weak(thread.get());
Ui::ConfirmBox(box, {
.confirmed = [=] {
const auto muteFor = pickerCallback();
peer->owner().notifySettings().update(
peer,
{ .period = muteFor });
peer->session().settings().addMutePeriod(muteFor);
peer->session().saveSettings();
if (const auto strong = weak.get()) {
strong->owner().notifySettings().update(
strong,
{ .period = muteFor });
strong->session().settings().addMutePeriod(muteFor);
strong->session().saveSettings();
}
box->closeBox();
},
.confirmText = tr::lng_mute_menu_mute(),
@ -209,7 +220,11 @@ void PickMuteBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
st::popupMenuWithIcons);
state->menu->addAction(
tr::lng_manage_messages_ttl_after_custom(tr::now),
[=] { box->getDelegate()->show(Box(MuteBox, peer)); },
[=] {
if (const auto strong = weak.get()) {
box->getDelegate()->show(Box(MuteBox, strong));
}
},
&st::menuIconCustomize);
state->menu->setDestroyedCallback(crl::guard(top, [=] {
top->setForceRippled(false);
@ -223,38 +238,44 @@ void PickMuteBox(not_null<Ui::GenericBox*> box, not_null<PeerData*> peer) {
void FillMuteMenu(
not_null<Ui::PopupMenu*> menu,
Args args) {
const auto peer = args.peer;
not_null<Data::Thread*> thread,
std::shared_ptr<Ui::Show> show) {
const auto weak = base::make_weak(thread.get());
const auto with = [=](Fn<void(not_null<Data::Thread*> thread)> handler) {
return [=] {
if (const auto strong = weak.get()) {
handler(strong);
}
};
};
menu->addAction(
tr::lng_mute_menu_sound_select(tr::now),
[=, show = args.show] {
show->showBox(Box(PeerRingtonesBox, peer));
},
with([=](not_null<Data::Thread*> thread) {
show->showBox(Box(ThreadRingtonesBox, thread));
}),
&st::menuIconSoundSelect);
const auto soundIsNone = peer->owner().notifySettings().sound(peer).none;
const auto notifySettings = &thread->owner().notifySettings();
const auto soundIsNone = notifySettings->sound(thread).none;
menu->addAction(
soundIsNone
? tr::lng_mute_menu_sound_on(tr::now)
: tr::lng_mute_menu_sound_off(tr::now),
[=] {
auto &notifySettings = peer->owner().notifySettings();
auto sound = notifySettings.sound(peer);
with([=](not_null<Data::Thread*> thread) {
auto sound = notifySettings->sound(thread);
sound.none = !sound.none;
notifySettings.update(peer, {}, {}, sound);
},
notifySettings->update(thread, {}, {}, sound);
}),
soundIsNone ? &st::menuIconSoundOn : &st::menuIconSoundOff);
const auto &st = menu->st().menu;
const auto iconTextPosition = st.itemIconPosition
+ st::menuIconMuteForAnyTextPosition;
for (const auto &muteFor : peer->session().settings().mutePeriods()) {
const auto callback = [=] {
peer->owner().notifySettings().update(
peer,
{ .period = muteFor });
};
for (const auto muteFor : thread->session().settings().mutePeriods()) {
const auto callback = with([=](not_null<Data::Thread*> thread) {
notifySettings->update(thread, { .period = muteFor });
});
auto item = base::make_unique_q<IconWithText>(
menu,
@ -272,23 +293,23 @@ void FillMuteMenu(
menu->addAction(std::move(item));
}
const auto callback = [=, show = args.show] {
DEBUG_LOG(("Mute Info: PickMuteBox called."));
show->showBox(Box(PickMuteBox, peer));
};
menu->addAction(
tr::lng_mute_menu_duration(tr::now),
callback,
with([=](not_null<Data::Thread*> thread) {
DEBUG_LOG(("Mute Info: PickMuteBox called."));
show->showBox(Box(PickMuteBox, thread));
}),
&st::menuIconMuteFor);
menu->addAction(
base::make_unique_q<MuteItem>(menu, menu->st().menu, peer));
base::make_unique_q<MuteItem>(menu, menu->st().menu, thread));
}
void SetupMuteMenu(
not_null<Ui::RpWidget*> parent,
rpl::producer<> triggers,
Args args) {
Fn<Data::Thread*()> makeThread,
std::shared_ptr<Ui::Show> show) {
struct State {
base::unique_qptr<Ui::PopupMenu> menu;
};
@ -298,12 +319,13 @@ void SetupMuteMenu(
) | rpl::start_with_next([=] {
if (state->menu) {
return;
} else if (const auto thread = makeThread()) {
state->menu = base::make_unique_q<Ui::PopupMenu>(
parent,
st::popupMenuWithIcons);
FillMuteMenu(state->menu.get(), thread, show);
state->menu->popup(QCursor::pos());
}
state->menu = base::make_unique_q<Ui::PopupMenu>(
parent,
st::popupMenuWithIcons);
FillMuteMenu(state->menu.get(), args);
state->menu->popup(QCursor::pos());
}, parent->lifetime());
}

View file

@ -7,7 +7,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/
#pragma once
class PeerData;
namespace Data {
class Thread;
} // namespace Data
namespace Ui {
class PopupMenu;
@ -17,18 +19,15 @@ class Show;
namespace MuteMenu {
struct Args {
not_null<PeerData*> peer;
std::shared_ptr<Ui::Show> show;
};
void FillMuteMenu(
not_null<Ui::PopupMenu*> menu,
Args args);
not_null<Data::Thread*> thread,
std::shared_ptr<Ui::Show> show);
void SetupMuteMenu(
not_null<Ui::RpWidget*> parent,
rpl::producer<> triggers,
Args args);
Fn<Data::Thread*()> makeThread,
std::shared_ptr<Ui::Show> show);
} // namespace MuteMenu

View file

@ -202,7 +202,7 @@ void SetupUnreadMentionsMenu(
done();
return;
}
const auto peer = thread->owningHistory()->peer;
const auto peer = thread->peer();
const auto topic = thread->asTopic();
const auto rootId = topic ? topic->rootId() : 0;
using Flag = MTPmessages_ReadMentions::Flag;
@ -244,7 +244,7 @@ void SetupUnreadReactionsMenu(
return;
}
const auto topic = thread->asTopic();
const auto peer = thread->owningHistory()->peer;
const auto peer = thread->peer();
const auto rootId = topic ? topic->rootId() : 0;
using Flag = MTPmessages_ReadReactions::Flag;
peer->session().api().request(MTPmessages_ReadReactions(

View file

@ -165,7 +165,7 @@ System::SkipState System::skipNotification(
const auto item = notification.item;
const auto type = notification.type;
const auto messageType = (type == Data::ItemNotificationType::Message);
if (!item->thread()->currentNotification()
if (!item->notificationThread()->currentNotification()
|| (messageType && item->skipNotification())
|| (type == Data::ItemNotificationType::Reaction
&& skipReactionNotification(item))) {
@ -178,7 +178,8 @@ System::SkipState System::computeSkipState(
Data::ItemNotification notification) const {
const auto type = notification.type;
const auto item = notification.item;
const auto history = item->history();
const auto thread = item->notificationThread();
const auto notifySettings = &thread->owner().notifySettings();
const auto messageType = (type == Data::ItemNotificationType::Message);
const auto withSilent = [&](
SkipState::Value value,
@ -188,8 +189,7 @@ System::SkipState System::computeSkipState(
.silent = (forceSilent
|| !messageType
|| item->isSilent()
|| history->owner().notifySettings().sound(
history->peer).none),
|| notifySettings->sound(thread).none),
};
};
const auto showForMuted = messageType
@ -201,35 +201,32 @@ System::SkipState System::computeSkipState(
if (Core::Quitting()) {
return { SkipState::Skip };
} else if (!Core::App().settings().notifyFromAll()
&& &history->session().account() != &Core::App().domain().active()) {
&& &thread->session().account() != &Core::App().domain().active()) {
return { SkipState::Skip };
}
if (messageType) {
history->owner().notifySettings().request(
history->peer);
notifySettings->request(thread);
} else if (notifyBy->blockStatus() == PeerData::BlockStatus::Unknown) {
notifyBy->updateFull();
}
if (notifyBy) {
history->owner().notifySettings().request(notifyBy);
notifySettings->request(notifyBy);
}
if (messageType
&& history->owner().notifySettings().muteUnknown(history->peer)) {
if (messageType && notifySettings->muteUnknown(thread)) {
return { SkipState::Unknown };
} else if (messageType
&& !history->owner().notifySettings().isMuted(history->peer)) {
} else if (messageType && !notifySettings->isMuted(thread)) {
return withSilent(SkipState::DontSkip);
} else if (!notifyBy) {
return withSilent(
showForMuted ? SkipState::DontSkip : SkipState::Skip,
showForMuted);
} else if (history->owner().notifySettings().muteUnknown(notifyBy)
} else if (notifySettings->muteUnknown(notifyBy)
|| (!messageType
&& notifyBy->blockStatus() == PeerData::BlockStatus::Unknown)) {
return withSilent(SkipState::Unknown);
} else if (!history->owner().notifySettings().isMuted(notifyBy)
} else if (!notifySettings->isMuted(notifyBy)
&& (messageType || !notifyBy->isBlocked())) {
return withSilent(SkipState::DontSkip);
} else {
@ -240,13 +237,13 @@ System::SkipState System::computeSkipState(
}
System::Timing System::countTiming(
not_null<History*> history,
not_null<Data::Thread*> thread,
crl::time minimalDelay) const {
auto delay = minimalDelay;
const auto t = base::unixtime::now();
const auto ms = crl::now();
const auto &updates = history->session().updates();
const auto &config = history->session().serverConfig();
const auto &updates = thread->session().updates();
const auto &config = thread->session().serverConfig();
const bool isOnline = updates.lastWasOnline();
const auto otherNotOld = ((cOtherOnline() * 1000LL) + config.onlineCloudTimeout > t * 1000LL);
const bool otherLaterThanMe = (cOtherOnline() * 1000LL + (ms - updates.lastSetOnline()) > t * 1000LL);
@ -261,15 +258,26 @@ System::Timing System::countTiming(
};
}
void System::registerThread(not_null<Data::Thread*> thread) {
if (const auto topic = thread->asTopic()) {
const auto [i, ok] = _watchedTopics.emplace(topic, rpl::lifetime());
if (ok) {
topic->destroyed() | rpl::start_with_next([=] {
clearFromTopic(topic);
}, i->second);
}
}
}
void System::schedule(Data::ItemNotification notification) {
Expects(_manager != nullptr);
const auto item = notification.item;
const auto type = notification.type;
const auto history = item->history();
const auto thread = item->notificationThread();
const auto skip = skipNotification(notification);
if (skip.value == SkipState::Skip) {
history->popNotification(notification);
thread->popNotification(notification);
return;
}
const auto ready = (skip.value != SkipState::Unknown)
@ -280,25 +288,27 @@ void System::schedule(Data::ItemNotification notification) {
: item->Has<HistoryMessageForwarded>()
? kMinimalForwardDelay
: kMinimalDelay;
const auto timing = countTiming(history, minimalDelay);
const auto timing = countTiming(thread, minimalDelay);
const auto notifyBy = (type == Data::ItemNotificationType::Message)
? item->specialNotificationPeer()
: notification.reactionSender;
if (!skip.silent) {
_whenAlerts[history].emplace(timing.when, notifyBy);
registerThread(thread);
_whenAlerts[thread].emplace(timing.when, notifyBy);
}
if (Core::App().settings().desktopNotify()
&& !_manager->skipToast()) {
registerThread(thread);
const auto key = NotificationInHistoryKey(notification);
auto &whenMap = _whenMaps[history];
auto &whenMap = _whenMaps[thread];
if (whenMap.find(key) == whenMap.end()) {
whenMap.emplace(key, timing.when);
}
auto &addTo = ready ? _waiters : _settingWaiters;
const auto it = addTo.find(history);
const auto it = addTo.find(thread);
if (it == addTo.end() || it->second.when > timing.when) {
addTo.emplace(history, Waiter{
addTo.emplace(thread, Waiter{
.key = key,
.reactionSender = notification.reactionSender,
.type = notification.type,
@ -312,7 +322,6 @@ void System::schedule(Data::ItemNotification notification) {
_waitTimer.callOnce(timing.delay);
}
}
}
void System::clearAll() {
@ -327,6 +336,7 @@ void System::clearAll() {
_whenAlerts.clear();
_waiters.clear();
_settingWaiters.clear();
_watchedTopics.clear();
}
void System::clearFromTopic(not_null<Data::ForumTopic*> topic) {
@ -340,6 +350,8 @@ void System::clearFromTopic(not_null<Data::ForumTopic*> topic) {
_waiters.remove(topic);
_settingWaiters.remove(topic);
_watchedTopics.remove(topic);
_waitTimer.cancel();
showNext();
}
@ -357,10 +369,17 @@ void System::clearForThreadIf(Fn<bool(not_null<Data::Thread*>)> predicate) {
_whenAlerts.remove(thread);
_waiters.remove(thread);
_settingWaiters.remove(thread);
if (const auto topic = thread->asTopic()) {
_watchedTopics.remove(topic);
}
}
const auto clearFrom = [&](auto &map) {
for (auto i = map.begin(); i != map.end();) {
if (predicate(i->first)) {
const auto thread = i->first;
if (predicate(thread)) {
if (const auto topic = thread->asTopic()) {
_watchedTopics.remove(topic);
}
i = map.erase(i);
} else {
++i;
@ -424,13 +443,14 @@ void System::clearAllFast() {
_whenAlerts.clear();
_waiters.clear();
_settingWaiters.clear();
_watchedTopics.clear();
}
void System::checkDelayed() {
for (auto i = _settingWaiters.begin(); i != _settingWaiters.end();) {
const auto remove = [&] {
const auto thread = i->first;
const auto peer = thread->owningHistory()->peer;
const auto peer = thread->peer();
const auto fullId = FullMsgId(peer->id, i->second.key.messageId);
const auto item = thread->owner().message(fullId);
if (!item) {
@ -501,21 +521,21 @@ void System::showNext() {
};
auto ms = crl::now(), nextAlert = crl::time(0);
auto alertPeer = (PeerData*)nullptr;
auto alertThread = (Data::Thread*)nullptr;
for (auto i = _whenAlerts.begin(); i != _whenAlerts.end();) {
while (!i->second.empty() && i->second.begin()->first <= ms) {
const auto peer = i->first->owningHistory()->peer;
const auto &notifySettings = peer->owner().notifySettings();
const auto peerUnknown = notifySettings.muteUnknown(peer);
const auto peerAlert = !peerUnknown
&& !notifySettings.isMuted(peer);
const auto thread = i->first;
const auto notifySettings = &thread->owner().notifySettings();
const auto threadUnknown = notifySettings->muteUnknown(thread);
const auto threadAlert = !threadUnknown
&& !notifySettings->isMuted(thread);
const auto from = i->second.begin()->second;
const auto fromUnknown = (!from
|| notifySettings.muteUnknown(from));
|| notifySettings->muteUnknown(from));
const auto fromAlert = !fromUnknown
&& !notifySettings.isMuted(from);
if (peerAlert || fromAlert) {
alertPeer = peer;
&& !notifySettings->isMuted(from);
if (threadAlert || fromAlert) {
alertThread = thread;
}
while (!i->second.empty()
&& i->second.begin()->first <= ms + kMinimalAlertDelay) {
@ -532,7 +552,7 @@ void System::showNext() {
}
}
const auto &settings = Core::App().settings();
if (alertPeer) {
if (alertThread) {
if (settings.flashBounceNotify() && !_manager->skipFlashBounce()) {
if (const auto window = Core::App().primaryWindow()) {
if (const auto handle = window->widget()->windowHandle()) {
@ -543,8 +563,8 @@ void System::showNext() {
}
if (settings.soundNotify() && !_manager->skipAudio()) {
const auto track = lookupSound(
&alertPeer->owner(),
alertPeer->owner().notifySettings().sound(alertPeer).id);
&alertThread->owner(),
alertThread->owner().notifySettings().sound(alertThread).id);
track->playOnce();
Media::Player::mixer()->suppressAll(track->getLengthMs());
Media::Player::mixer()->faderOnTimer();
@ -620,7 +640,7 @@ void System::showNext() {
: nullptr;
auto forwardedCount = isForwarded ? 1 : 0;
const auto thread = notifyItem->thread();
const auto thread = notifyItem->notificationThread();
const auto j = _whenMaps.find(thread);
if (j == _whenMaps.cend()) {
thread->clearNotifications();
@ -997,7 +1017,7 @@ void Manager::openNotificationMessage(
&& item
&& item->isRegular()
&& (item->out() || (item->mentionsMe() && !history->peer->isUser()));
const auto topic = item ? history->peer->forumTopicFor(item) : nullptr;
const auto topic = item ? item->topic() : nullptr;
const auto separate = Core::App().separateWindowForPeer(history->peer);
const auto window = separate
? separate->sessionController()

View file

@ -155,7 +155,7 @@ private:
[[nodiscard]] SkipState computeSkipState(
Data::ItemNotification notification) const;
[[nodiscard]] Timing countTiming(
not_null<History*> history,
not_null<Data::Thread*> thread,
crl::time minimalDelay) const;
[[nodiscard]] bool skipReactionNotification(
not_null<HistoryItem*> item) const;
@ -167,6 +167,8 @@ private:
not_null<Data::Session*> owner,
DocumentId id);
void registerThread(not_null<Data::Thread*> thread);
base::flat_map<
not_null<Data::Thread*>,
base::flat_map<NotificationInHistoryKey, crl::time>> _whenMaps;
@ -193,6 +195,10 @@ private:
DocumentId,
std::unique_ptr<Media::Audio::Track>> _customSoundTracks;
base::flat_map<
not_null<Data::ForumTopic*>,
rpl::lifetime> _watchedTopics;
int _lastForwardedCount = 0;
uint64 _lastHistorySessionId = 0;
FullMsgId _lastHistoryItemId;

View file

@ -14,7 +14,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "base/unixtime.h"
#include "boxes/delete_messages_box.h"
#include "boxes/max_invite_box.h"
#include "boxes/mute_settings_box.h"
#include "boxes/add_contact_box.h"
#include "boxes/choose_filter_box.h"
#include "boxes/create_poll_box.h"
@ -134,28 +133,39 @@ void MarkAsReadChatList(not_null<Dialogs::MainList*> list) {
void PeerMenuAddMuteSubmenuAction(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer,
not_null<Data::Thread*> thread,
const PeerMenuCallback &addAction) {
peer->owner().notifySettings().request(peer);
const auto isMuted = peer->owner().notifySettings().isMuted(peer);
const auto notifySettings = &thread->owner().notifySettings();
notifySettings->request(thread);
const auto weak = base::make_weak(thread.get());
const auto with = [=](Fn<void(not_null<Data::Thread*>)> callback) {
return [=] {
if (const auto strong = weak.get()) {
callback(strong);
}
};
};
const auto isMuted = notifySettings->isMuted(thread);
if (isMuted) {
const auto text = tr::lng_context_unmute(tr::now)
+ '\t'
+ Ui::FormatMuteForTiny(peer->notify().muteUntil().value_or(0)
+ Ui::FormatMuteForTiny(thread->notify().muteUntil().value_or(0)
- base::unixtime::now());
addAction(text, [=] {
peer->owner().notifySettings().update(peer, { .unmute = true });
}, &st::menuIconUnmute);
addAction(text, with([=](not_null<Data::Thread*> thread) {
notifySettings->update(thread, { .unmute = true });
}), &st::menuIconUnmute);
} else {
const auto show = std::make_shared<Window::Show>(controller);
addAction(PeerMenuCallback::Args{
.text = tr::lng_context_mute(tr::now),
.handler = nullptr,
.icon = peer->owner().notifySettings().sound(peer).none
.icon = (notifySettings->sound(thread).none
? &st::menuIconSilent
: &st::menuIconMute,
: &st::menuIconMute),
.fillSubmenu = [=](not_null<Ui::PopupMenu*> menu) {
MuteMenu::FillMuteMenu(menu, { peer, show });
if (const auto strong = weak.get()) {
MuteMenu::FillMuteMenu(menu, strong, show);
}
},
});
}
@ -209,8 +219,8 @@ private:
not_null<SessionController*> _controller;
Dialogs::EntryState _request;
Data::Thread *_thread = nullptr;
PeerData *_peer = nullptr;
Data::ForumTopic *_topic = nullptr;
Data::Folder *_folder = nullptr;
const PeerMenuCallback &_addAction;
@ -329,8 +339,8 @@ Filler::Filler(
const PeerMenuCallback &addAction)
: _controller(controller)
, _request(request)
, _thread(request.key.thread())
, _peer(request.key.peer())
, _topic(request.key.topic())
, _folder(request.key.folder())
, _addAction(addAction) {
}
@ -385,10 +395,10 @@ void Filler::addTogglePin() {
}
void Filler::addToggleMuteSubmenu(bool addSeparator) {
if (_peer->isSelf()) {
if (_thread->peer()->isSelf()) {
return;
}
PeerMenuAddMuteSubmenuAction(_controller, _peer, _addAction);
PeerMenuAddMuteSubmenuAction(_controller, _thread, _addAction);
if (addSeparator) {
_addAction(PeerMenuCallback::Args{ .isSeparator = true });
}
@ -412,26 +422,30 @@ void Filler::addInfo() {
if (_peer && (_peer->isSelf() || _peer->isRepliesChat())) {
return;
} else if (_controller->adaptive().isThreeColumn()) {
const auto peer = _controller->activeChatCurrent().peer();
const auto topic = _controller->activeChatCurrent().topic();
if ((peer && peer == _peer) || (topic && topic == _topic)) {
const auto thread = _controller->activeChatCurrent().thread();
if (thread && thread == _thread) {
if (Core::App().settings().thirdSectionInfoEnabled()
|| Core::App().settings().tabbedReplacedWithInfo()) {
return;
}
}
} else if (!_thread) {
return;
}
const auto controller = _controller;
const auto id = _topic ? _topic->rootId() : 0;
const auto peer = _peer;
const auto text = (peer->isChat() || peer->isMegagroup())
const auto weak = base::make_weak(_thread);
const auto text = _thread->asTopic()
? u"View Topic Info"_q // #TODO lang-forum
: (_peer->isChat() || _peer->isMegagroup())
? tr::lng_context_view_group(tr::now)
: (peer->isUser()
? tr::lng_context_view_profile(tr::now)
: tr::lng_context_view_channel(tr::now));
: _peer->isUser()
? tr::lng_context_view_profile(tr::now)
: tr::lng_context_view_channel(tr::now);
_addAction(text, [=] {
controller->showPeerInfo(peer);
}, peer->isUser() ? &st::menuIconProfile : &st::menuIconInfo);
if (const auto strong = weak.get()) {
controller->showPeerInfo(strong);
}
}, _peer->isUser() ? &st::menuIconProfile : &st::menuIconInfo);
}
void Filler::addToggleFolder() {
@ -547,7 +561,7 @@ void Filler::addDeleteChat() {
void Filler::addLeaveChat() {
const auto channel = _peer->asChannel();
if (_topic || !channel || !channel->amIn()) {
if (_thread->asTopic() || !channel || !channel->amIn()) {
return;
}
_addAction({
@ -632,7 +646,7 @@ void Filler::addViewDiscussion() {
}
void Filler::addExportChat() {
if (_topic || !_peer->canExportChatHistory()) {
if (_thread->asTopic() || !_peer->canExportChatHistory()) {
return;
}
const auto peer = _peer;
@ -748,15 +762,17 @@ void Filler::addDeleteContact() {
}
void Filler::addManageTopic() {
if (!_topic) {
const auto topic = _thread->asTopic();
if (!topic) {
return;
}
// #TODO lang-forum
const auto history = _topic->history();
const auto rootId = _topic->rootId();
const auto history = topic->history();
const auto rootId = topic->rootId();
const auto navigation = _controller;
_addAction(u"Edit topic"_q, [=] {
navigation->show(Box(EditForumTopicBox, navigation, history, rootId));
navigation->show(
Box(EditForumTopicBox, navigation, history, rootId));
}, &st::menuIconEdit);
}
@ -821,7 +837,7 @@ void Filler::addThemeEdit() {
}
void Filler::addTTLSubmenu(bool addSeparator) {
if (_topic) {
if (_thread->asTopic()) {
return; // #TODO later forum
}
const auto validator = TTLMenu::TTLValidator(
@ -928,7 +944,7 @@ void Filler::fillProfileActions() {
}
void Filler::fillRepliesActions() {
if (_topic) {
if (_thread->asTopic()) {
addInfo();
addManageTopic();
addManageChat();
@ -1498,35 +1514,6 @@ void UnpinAllMessages(
Ui::LayerOption::CloseOther);
}
void PeerMenuAddMuteAction(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer,
const PeerMenuCallback &addAction) {
// There is no async to make weak from controller.
peer->owner().notifySettings().request(peer);
const auto muteText = [](bool isUnmuted) {
return isUnmuted
? tr::lng_context_mute(tr::now)
: tr::lng_context_unmute(tr::now);
};
const auto muteAction = addAction(QString("-"), [=] {
if (!peer->owner().notifySettings().isMuted(peer)) {
controller->show(
Box<MuteSettingsBox>(peer),
Ui::LayerOption::CloseOther);
} else {
peer->owner().notifySettings().update(peer, { .unmute = true });
}
}, (peer->owner().notifySettings().isMuted(peer)
? &st::menuIconUnmute
: &st::menuIconMute));
auto actionText = Info::Profile::NotificationsEnabledValue(
peer
) | rpl::map(muteText);
SetActionText(muteAction, std::move(actionText));
}
void MenuAddMarkAsReadAllChatsAction(
not_null<Window::SessionController*> controller,
const PeerMenuCallback &addAction) {

View file

@ -46,11 +46,6 @@ void FillDialogsEntryMenu(
Dialogs::EntryState request,
const PeerMenuCallback &addAction);
void PeerMenuAddMuteAction(
not_null<Window::SessionController*> controller,
not_null<PeerData*> peer,
const PeerMenuCallback &addAction);
void MenuAddMarkAsReadAllChatsAction(
not_null<Window::SessionController*> controller,
const PeerMenuCallback &addAction);

View file

@ -567,9 +567,13 @@ void SessionNavigation::showPeerInfo(
}
void SessionNavigation::showPeerInfo(
not_null<History*> history,
not_null<Data::Thread*> thread,
const SectionShow &params) {
showPeerInfo(history->peer->id, params);
if (const auto topic = thread->asTopic()) {
showSection(std::make_shared<Info::Memento>(topic), params);
} else {
showPeerInfo(thread->peer()->id, params);
}
}
void SessionNavigation::showPeerHistory(

View file

@ -69,6 +69,7 @@ class MessageSendingAnimationController;
namespace Data {
struct CloudTheme;
enum class CloudThemeType;
class Thread;
} // namespace Data
namespace HistoryView::Reactions {
@ -219,7 +220,7 @@ public:
not_null<PeerData*> peer,
const SectionShow &params = SectionShow());
void showPeerInfo(
not_null<History*> history,
not_null<Data::Thread*> thread,
const SectionShow &params = SectionShow());
virtual void showPeerHistory(