diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index e78a4f89b..94bba6ca3 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1427,6 +1427,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_feed_no_messages" = "No messages in this feed yet"; "lng_feed_channels#one" = "{count} channel"; "lng_feed_channels#other" = "{count} channels"; +"lng_feed_notifications" = "Feed notifications"; "lng_info_feed_title" = "Feed Info"; "lng_info_feed_is_default" = "Group new channels"; diff --git a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp index feaf4190a..e6de575ef 100644 --- a/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp +++ b/Telegram/SourceFiles/history/view/history_view_top_bar_widget.cpp @@ -147,8 +147,7 @@ void TopBarWidget::onCall() { } void TopBarWidget::showMenu() { - // #TODO feeds menu - if (!_activeChat || _menu || !_activeChat.peer()) { + if (!_activeChat || _menu) { return; } _menu.create(parentWidget()); @@ -170,13 +169,26 @@ void TopBarWidget::showMenu() { } })); _menuToggle->installEventFilter(_menu); - Window::FillPeerMenu( - _controller, - _activeChat.peer(), - [this](const QString &text, base::lambda callback) { - return _menu->addAction(text, std::move(callback)); - }, - Window::PeerMenuSource::History); + const auto addAction = [&]( + const QString &text, + base::lambda callback) { + return _menu->addAction(text, std::move(callback)); + }; + if (const auto peer = _activeChat.peer()) { + Window::FillPeerMenu( + _controller, + peer, + addAction, + Window::PeerMenuSource::History); + } else if (const auto feed = _activeChat.feed()) { + Window::FillFeedMenu( + _controller, + feed, + addAction, + Window::PeerMenuSource::History); + } else { + Unexpected("Empty active chat in TopBarWidget::showMenu."); + } _menu->moveToRight((parentWidget()->width() - width()) + st::topBarMenuPosition.x(), st::topBarMenuPosition.y()); _menu->showAnimated(Ui::PanelAnimation::Origin::TopRight); } diff --git a/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.cpp b/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.cpp index 4e1843201..86dd43c28 100644 --- a/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.cpp +++ b/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "window/window_peer_menu.h" #include "ui/widgets/popup_menu.h" #include "auth_session.h" +#include "mainwidget.h" #include "styles/style_widgets.h" #include "styles/style_info.h" #include "styles/style_boxes.h" @@ -196,5 +197,135 @@ base::unique_qptr ChannelsController::rowContextMenu( return result; } +void FeedNotificationsController::Start(not_null feed) { + const auto initBox = [=](not_null box) { + box->addButton(langFactory(lng_settings_save), [=] { + const auto main = App::main(); + const auto count = box->peerListFullRowsCount(); + for (auto i = 0; i != count; ++i) { + const auto row = box->peerListRowAt(i); + const auto peer = row->peer(); + const auto muted = !row->checked(); + if (muted != peer->isMuted()) { + main->updateNotifySettings( + peer, + (muted + ? Data::NotifySettings::MuteChange::Mute + : Data::NotifySettings::MuteChange::Unmute)); + } + } + box->closeBox(); + }); + box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); }); + }; + Ui::show(Box( + std::make_unique(feed), + initBox)); +} + +FeedNotificationsController::FeedNotificationsController( + not_null feed) +: _feed(feed) { +} + +void FeedNotificationsController::prepare() { + setSearchNoResultsText(lang(lng_blocked_list_not_found)); + delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled); + delegate()->peerListSetTitle(langFactory(lng_feed_notifications)); + + loadMoreRows(); +} + +void FeedNotificationsController::loadMoreRows() { + if (_preloadRequestId || _allLoaded) { + return; + } + _preloadRequestId = request(MTPmessages_GetDialogs( + MTP_flags(MTPmessages_GetDialogs::Flag::f_feed_id), + MTP_int(_feed->id()), + MTP_int(_preloadOffsetDate), + MTP_int(_preloadOffsetId), + _preloadPeer ? _preloadPeer->input : MTP_inputPeerEmpty(), + MTP_int(Data::Feed::kChannelsLimit) + )).done([=](const MTPmessages_Dialogs &result) { + applyFeedDialogs(result); + _preloadRequestId = 0; + }).fail([=](const RPCError &error) { + _preloadRequestId = 0; + }).send(); +} + +void FeedNotificationsController::applyFeedDialogs( + const MTPmessages_Dialogs &result) { + const auto [dialogsList, messagesList] = [&] { + const auto process = [&](const auto &data) { + App::feedUsers(data.vusers); + App::feedChats(data.vchats); + return std::make_tuple(&data.vdialogs.v, &data.vmessages.v); + }; + switch (result.type()) { + case mtpc_messages_dialogs: + _allLoaded = true; + return process(result.c_messages_dialogs()); + + case mtpc_messages_dialogsSlice: + LOG(("API Error: " + "Unexpected dialogsSlice in feed dialogs list.")); + return process(result.c_messages_dialogsSlice()); + } + Unexpected("Type in FeedNotificationsController::applyFeedDialogs"); + }(); + + App::feedMsgs(*messagesList, NewMessageLast); + + if (dialogsList->empty()) { + _allLoaded = true; + } + auto channels = std::vector>(); + channels.reserve(dialogsList->size()); + for (const auto &dialog : *dialogsList) { + switch (dialog.type()) { + case mtpc_dialog: { + if (const auto peerId = peerFromMTP(dialog.c_dialog().vpeer)) { + if (peerIsChannel(peerId)) { + const auto history = App::history(peerId); + const auto channel = history->peer->asChannel(); + history->applyDialog(dialog.c_dialog()); + channels.push_back(channel); + } else { + LOG(("API Error: " + "Unexpected non-channel in feed dialogs list.")); + } + } + } break; + case mtpc_dialogFeed: { + LOG(("API Error: Unexpected dialogFeed in feed dialogs list.")); + } break; + default: Unexpected("Type in DialogsInner::dialogsReceived"); + } + } + if (!channels.empty()) { + auto notMutedChannels = ranges::view::all( + channels + ) | ranges::view::filter([](not_null channel) { + return !channel->isMuted(); + }); + delegate()->peerListAddSelectedRows(notMutedChannels); + for (const auto channel : channels) { + delegate()->peerListAppendRow(createRow(channel)); + } + } + delegate()->peerListRefreshRows(); +} + +void FeedNotificationsController::rowClicked(not_null row) { + delegate()->peerListSetRowChecked(row, !row->checked()); +} + +std::unique_ptr FeedNotificationsController::createRow( + not_null channel) { + return std::make_unique(channel); +} + } // namespace FeedProfile } // namespace Info diff --git a/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.h b/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.h index 1c06ef621..172a6aabb 100644 --- a/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.h +++ b/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.h @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #pragma once #include "boxes/peer_list_box.h" +#include "mtproto/sender.h" namespace Data { class Feed; @@ -51,5 +52,31 @@ private: }; +class FeedNotificationsController + : public PeerListController + , private MTP::Sender { +public: + static void Start(not_null feed); + + FeedNotificationsController(not_null feed); + + void prepare() override; + void rowClicked(not_null row) override; + + void loadMoreRows() override; + +private: + std::unique_ptr createRow(not_null channel); + void applyFeedDialogs(const MTPmessages_Dialogs &result); + + not_null _feed; + mtpRequestId _preloadRequestId = 0; + TimeId _preloadOffsetDate = TimeId(0); + MsgId _preloadOffsetId = MsgId(0); + PeerData *_preloadPeer = nullptr; + bool _allLoaded = false; + +}; + } // namespace FeedProfile } // namespace Info diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index ec45cec4c..c335ec1df 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_boxes.h" #include "history/history.h" #include "window/window_controller.h" +#include "info/feed/info_feed_channels_controllers.h" #include "data/data_session.h" #include "data/data_feed.h" #include "dialogs/dialogs_key.h" @@ -69,6 +70,7 @@ public: private: void addPinToggle(); + void addNotifications(); not_null _controller; not_null _feed; @@ -385,11 +387,11 @@ void Filler::fill() { addSearch(); } - if (auto user = _peer->asUser()) { + if (const auto user = _peer->asUser()) { addUserActions(user); - } else if (auto chat = _peer->asChat()) { + } else if (const auto chat = _peer->asChat()) { addChatActions(chat); - } else if (auto channel = _peer->asChannel()) { + } else if (const auto channel = _peer->asChannel()) { addChannelActions(channel); } } @@ -409,20 +411,27 @@ void FeedFiller::fill() { if (_source == PeerMenuSource::ChatsList) { addPinToggle(); } + addNotifications(); } void FeedFiller::addPinToggle() { - auto feed = _feed; - auto isPinned = feed->isPinnedDialog(); - auto pinText = [](bool isPinned) { + const auto feed = _feed; + const auto isPinned = feed->isPinnedDialog(); + const auto pinText = [](bool isPinned) { return lang(isPinned ? lng_context_unpin_from_top : lng_context_pin_to_top); }; - auto pinToggle = [=] { + _addAction(pinText(isPinned), [=] { TogglePinnedDialog(feed); - }; - _addAction(pinText(isPinned), pinToggle); + }); +} + +void FeedFiller::addNotifications() { + const auto feed = _feed; + _addAction(lang(lng_feed_notifications), [=] { + Info::FeedProfile::FeedNotificationsController::Start(feed); + }); } } // namespace @@ -692,7 +701,6 @@ void FillFeedMenu( not_null feed, const PeerMenuCallback &callback, PeerMenuSource source) { - // TODO feeds context menu FeedFiller filler(controller, feed, callback, source); filler.fill(); }