From 17a4d19beb659ea484c748e62af8e0290b45bc88 Mon Sep 17 00:00:00 2001 From: John Preston Date: Tue, 20 Feb 2018 19:56:41 +0300 Subject: [PATCH] Add a create feed channels list box. --- Telegram/Resources/langs/lang.strings | 6 + Telegram/SourceFiles/apiwrap.cpp | 86 +++++++---- Telegram/SourceFiles/apiwrap.h | 9 +- .../history/feed/history_feed_section.cpp | 21 +-- .../feed/info_feed_channels_controllers.cpp | 138 ++++++++++++++++-- .../feed/info_feed_channels_controllers.h | 32 +++- Telegram/SourceFiles/mainwidget.cpp | 14 +- .../SourceFiles/window/window_peer_menu.cpp | 9 +- 8 files changed, 263 insertions(+), 52 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 8baad0336..3c29ae2ea 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1432,6 +1432,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_feed_ungroup_all" = "Ungroup all channels"; "lng_feed_sure_ungroup_all" = "Are you sure you want to ungroup all channels from this feed?"; "lng_feed_ungroup_sure" = "Ungroup"; +"lng_feed_create_new" = "New feed"; +"lng_feed_too_few_channels#one" = "You need at least {count} channel to create a feed."; +"lng_feed_too_few_channels#other" = "You need at least {count} channels to create a feed."; +"lng_feed_select_more_channels#one" = "Select {count} channel or more."; +"lng_feed_select_more_channels#other" = "Select {count} channels or more."; +"lng_feed_create" = "Create"; "lng_info_feed_title" = "Feed Info"; "lng_info_feed_is_default" = "Group new channels"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 739df26fb..8c49d1693 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -2965,7 +2965,7 @@ void ApiWrap::userPhotosDone( } void ApiWrap::requestFeedChannels(not_null feed) { - if (_feedChannelsRequests.contains(feed)) { + if (_feedChannelsGetRequests.contains(feed)) { return; } const auto hash = feed->channelsHash(); @@ -2974,7 +2974,7 @@ void ApiWrap::requestFeedChannels(not_null feed) { MTP_int(feed->id()), MTP_int(hash) )).done([=](const MTPchannels_FeedSources &result) { - _feedChannelsRequests.remove(feed); + _feedChannelsGetRequests.remove(feed); switch (result.type()) { case mtpc_channels_feedSourcesNotModified: @@ -2987,31 +2987,7 @@ void ApiWrap::requestFeedChannels(not_null feed) { case mtpc_channels_feedSources: { const auto &data = result.c_channels_feedSources(); - - // First we set channels without reading them from data. - // This allows us to apply them all at once without registering - // them one by one. - for (const auto &broadcasts : data.vfeeds.v) { - if (broadcasts.type() == mtpc_feedBroadcasts) { - const auto &list = broadcasts.c_feedBroadcasts(); - const auto feedId = list.vfeed_id.v; - const auto feed = _session->data().feed(feedId); - auto channels = std::vector>(); - for (const auto &channelId : list.vchannels.v) { - channels.push_back(App::channel(channelId.v)); - } - feed->setChannels(std::move(channels)); - } - } - - App::feedUsers(data.vusers); - App::feedChats(data.vchats); - - if (data.has_newly_joined_feed()) { - _session->data().setDefaultFeedId( - data.vnewly_joined_feed.v); - } - + applyFeedSources(data); if (feed->channelsLoaded()) { feedChannelsDone(feed); } else { @@ -3023,9 +2999,61 @@ void ApiWrap::requestFeedChannels(not_null feed) { default: Unexpected("Type in channels.getFeedSources response."); } }).fail([=](const RPCError &error) { - _feedChannelsRequests.remove(feed); + _feedChannelsGetRequests.remove(feed); }).send(); - _feedChannelsRequests.emplace(feed); + _feedChannelsGetRequests.emplace(feed); +} + +void ApiWrap::applyFeedSources(const MTPDchannels_feedSources &data) { + // First we set channels without reading them from data. + // This allows us to apply them all at once without registering + // them one by one. + for (const auto &broadcasts : data.vfeeds.v) { + if (broadcasts.type() == mtpc_feedBroadcasts) { + const auto &list = broadcasts.c_feedBroadcasts(); + const auto feedId = list.vfeed_id.v; + const auto feed = _session->data().feed(feedId); + auto channels = std::vector>(); + for (const auto &channelId : list.vchannels.v) { + channels.push_back(App::channel(channelId.v)); + } + feed->setChannels(std::move(channels)); + } + } + + App::feedUsers(data.vusers); + App::feedChats(data.vchats); + + if (data.has_newly_joined_feed()) { + _session->data().setDefaultFeedId( + data.vnewly_joined_feed.v); + } +} + +void ApiWrap::setFeedChannels( + not_null feed, + const std::vector> &channels) { + if (const auto already = _feedChannelsSetRequests.take(feed)) { + request(*already).cancel(); + } + auto inputs = QVector(); + inputs.reserve(channels.size()); + for (const auto channel : channels) { + inputs.push_back(channel->inputChannel); + } + const auto requestId = request(MTPchannels_SetFeedBroadcasts( + MTP_flags(MTPchannels_SetFeedBroadcasts::Flag::f_channels), + MTP_int(feed->id()), + MTP_vector(inputs), + MTPbool() + )).done([=](const MTPUpdates &result) { + applyUpdates(result); + + _feedChannelsSetRequests.remove(feed); + }).fail([=](const RPCError &error) { + _feedChannelsSetRequests.remove(feed); + }).send(); + } void ApiWrap::feedChannelsDone(not_null feed) { diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 6556bafb3..8f2669c77 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -72,6 +72,10 @@ public: void requestDialogEntry(not_null feed); //void requestFeedDialogsEntries(not_null feed); void requestDialogEntry(not_null history); + void applyFeedSources(const MTPDchannels_feedSources &data); + void setFeedChannels( + not_null feed, + const std::vector> &channels); void requestFullPeer(PeerData *peer); void requestPeer(PeerData *peer); @@ -500,7 +504,10 @@ private: base::flat_map, mtpRequestId> _userPhotosRequests; - base::flat_set> _feedChannelsRequests; + base::flat_set> _feedChannelsGetRequests; + base::flat_map< + not_null, + mtpRequestId> _feedChannelsSetRequests; base::flat_set, Data::MessagePosition, diff --git a/Telegram/SourceFiles/history/feed/history_feed_section.cpp b/Telegram/SourceFiles/history/feed/history_feed_section.cpp index 7fb202ca5..19616d83b 100644 --- a/Telegram/SourceFiles/history/feed/history_feed_section.cpp +++ b/Telegram/SourceFiles/history/feed/history_feed_section.cpp @@ -195,23 +195,26 @@ bool Widget::showAtPositionNow(Data::MessagePosition position) { } void Widget::updateScrollDownVisibility() { - if (animating() || !_inner->loadedAtBottomKnown()) { + if (animating()) { return; } - const auto scrollDownIsVisible = [&] { - if (!_inner->loadedAtBottom()) { - return true; - } + const auto scrollDownIsVisible = [&]() -> base::optional { const auto top = _scroll->scrollTop() + st::historyToDownShownAfter; if (top < _scroll->scrollTopMax()) { return true; } - return false; + if (_inner->loadedAtBottomKnown()) { + return !_inner->loadedAtBottom(); + } + return base::none; }; - auto scrollDownIsShown = scrollDownIsVisible(); - if (_scrollDownIsShown != scrollDownIsShown) { - _scrollDownIsShown = scrollDownIsShown; + const auto scrollDownIsShown = scrollDownIsVisible(); + if (!scrollDownIsShown) { + return; + } + if (_scrollDownIsShown != *scrollDownIsShown) { + _scrollDownIsShown = *scrollDownIsShown; _scrollDownShown.start( [=] { updateScrollDownPosition(); }, _scrollDownIsShown ? 0. : 1., diff --git a/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.cpp b/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.cpp index d923fccbd..0c887749a 100644 --- a/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.cpp +++ b/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "window/window_peer_menu.h" #include "ui/widgets/popup_menu.h" +#include "ui/toast/toast.h" #include "auth_session.h" #include "mainwidget.h" #include "apiwrap.h" @@ -23,6 +24,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Info { namespace FeedProfile { +namespace { + +constexpr auto kChannelsInFeedMin = 4; + +} // namespace class ChannelsController::Row final : public PeerListRow { public: @@ -201,7 +207,7 @@ base::unique_qptr ChannelsController::rowContextMenu( return result; } -void FeedNotificationsController::Start(not_null feed) { +void NotificationsController::Start(not_null feed) { const auto initBox = [=](not_null box) { box->addButton(langFactory(lng_settings_save), [=] { const auto main = App::main(); @@ -223,16 +229,16 @@ void FeedNotificationsController::Start(not_null feed) { box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); }); }; Ui::show(Box( - std::make_unique(feed), + std::make_unique(feed), initBox)); } -FeedNotificationsController::FeedNotificationsController( +NotificationsController::NotificationsController( not_null feed) : _feed(feed) { } -void FeedNotificationsController::prepare() { +void NotificationsController::prepare() { setSearchNoResultsText(lang(lng_blocked_list_not_found)); delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled); delegate()->peerListSetTitle(langFactory(lng_feed_notifications)); @@ -240,7 +246,7 @@ void FeedNotificationsController::prepare() { loadMoreRows(); } -void FeedNotificationsController::loadMoreRows() { +void NotificationsController::loadMoreRows() { if (_preloadRequestId || _allLoaded) { return; } @@ -259,7 +265,7 @@ void FeedNotificationsController::loadMoreRows() { }).send(); } -void FeedNotificationsController::applyFeedDialogs( +void NotificationsController::applyFeedDialogs( const MTPmessages_Dialogs &result) { const auto [dialogsList, messagesList] = [&] { const auto process = [&](const auto &data) { @@ -277,7 +283,7 @@ void FeedNotificationsController::applyFeedDialogs( "Unexpected dialogsSlice in feed dialogs list.")); return process(result.c_messages_dialogsSlice()); } - Unexpected("Type in FeedNotificationsController::applyFeedDialogs"); + Unexpected("Type in NotificationsController::applyFeedDialogs"); }(); App::feedMsgs(*messagesList, NewMessageLast); @@ -322,11 +328,125 @@ void FeedNotificationsController::applyFeedDialogs( delegate()->peerListRefreshRows(); } -void FeedNotificationsController::rowClicked(not_null row) { +void NotificationsController::rowClicked(not_null row) { delegate()->peerListSetRowChecked(row, !row->checked()); } -std::unique_ptr FeedNotificationsController::createRow( +std::unique_ptr NotificationsController::createRow( + not_null channel) { + return std::make_unique(channel); +} + +void EditController::Start( + not_null feed, + ChannelData *channel) { + const auto initBox = [=](not_null box) { + box->addButton(langFactory(lng_settings_save), [=] { + auto channels = std::vector>(); + const auto main = App::main(); + const auto count = box->peerListFullRowsCount(); + for (auto i = 0; i != count; ++i) { + const auto row = box->peerListRowAt(i); + if (row->checked()) { + channels.push_back(row->peer()->asChannel()); + } + } + if (channels.size() < kChannelsInFeedMin) { + Ui::Toast::Show(lng_feed_select_more_channels( + lt_count, + kChannelsInFeedMin)); + return; + } + box->closeBox(); + Auth().api().setFeedChannels(feed, channels); + }); + box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); }); + }; + Ui::show(Box( + std::make_unique(feed, channel), + initBox)); +} + +EditController::EditController( + not_null feed, + ChannelData *channel) +: _feed(feed) +, _startWithChannel(channel) { +} + +void EditController::prepare() { + setSearchNoResultsText(lang(lng_blocked_list_not_found)); + delegate()->peerListSetSearchMode(PeerListSearchMode::Enabled); + delegate()->peerListSetTitle(langFactory(lng_feed_create_new)); + + loadMoreRows(); +} + +void EditController::loadMoreRows() { + if (_preloadRequestId || _allLoaded) { + return; + } + const auto hash = 0; + _preloadRequestId = request(MTPchannels_GetFeedSources( + MTP_flags(0), + MTP_int(0), + MTP_int(hash) + )).done([=](const MTPchannels_FeedSources &result) { + applyFeedSources(result); + _preloadRequestId = 0; + }).fail([=](const RPCError &error) { + _preloadRequestId = 0; + }).send(); +} + +void EditController::applyFeedSources( + const MTPchannels_FeedSources &result) { + auto channels = std::vector>(); + + switch (result.type()) { + case mtpc_channels_feedSourcesNotModified: + LOG(("API Error: Unexpected channels.feedSourcesNotModified.")); + break; + + case mtpc_channels_feedSources: { + const auto &data = result.c_channels_feedSources(); + Auth().api().applyFeedSources(data); + + for (const auto &chat : data.vchats.v) { + if (chat.type() == mtpc_channel) { + channels.push_back(App::channel(chat.c_channel().vid.v)); + } + } + } break; + + default: Unexpected("Type in channels.getFeedSources response."); + } + + _allLoaded = true; + if (channels.size() < kChannelsInFeedMin) { + setDescriptionText(lng_feed_too_few_channels( + lt_count, + kChannelsInFeedMin)); + } else { + auto alreadyInFeed = ranges::view::all( + channels + ) | ranges::view::filter([&](not_null channel) { + return (channel->feed() == _feed) + || (channel == _startWithChannel); + }); + delegate()->peerListAddSelectedRows(alreadyInFeed); + for (const auto channel : channels) { + delegate()->peerListAppendRow(createRow(channel)); + } + } + delegate()->peerListRefreshRows(); +} + +void EditController::rowClicked(not_null row) { + delegate()->peerListSetRowChecked(row, !row->checked()); +} + +std::unique_ptr EditController::createRow( not_null channel) { return std::make_unique(channel); } diff --git a/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.h b/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.h index 172a6aabb..f8c934df6 100644 --- a/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.h +++ b/Telegram/SourceFiles/info/feed/info_feed_channels_controllers.h @@ -52,13 +52,13 @@ private: }; -class FeedNotificationsController +class NotificationsController : public PeerListController , private MTP::Sender { public: static void Start(not_null feed); - FeedNotificationsController(not_null feed); + NotificationsController(not_null feed); void prepare() override; void rowClicked(not_null row) override; @@ -78,5 +78,33 @@ private: }; +class EditController + : public PeerListController + , private MTP::Sender { +public: + static void Start( + not_null feed, + ChannelData *channel = nullptr); + + EditController( + not_null feed, + ChannelData *channel); + + void prepare() override; + void rowClicked(not_null row) override; + + void loadMoreRows() override; + +private: + std::unique_ptr createRow(not_null channel); + void applyFeedSources(const MTPchannels_FeedSources &result); + + not_null _feed; + ChannelData *_startWithChannel = nullptr; + mtpRequestId _preloadRequestId = 0; + bool _allLoaded = false; + +}; + } // namespace FeedProfile } // namespace Info diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index c0e5497df..5f5fdf364 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -5039,7 +5039,7 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { case mtpc_updateChannel: { auto &d = update.c_updateChannel(); if (const auto channel = App::channelLoaded(d.vchannel_id.v)) { - channel->inviter = 0; + channel->inviter = UserId(0); if (!channel->amIn()) { if (const auto history = App::historyLoaded(channel->id)) { history->updateChatListExistence(); @@ -5048,6 +5048,18 @@ void MainWidget::feedUpdate(const MTPUpdate &update) { _updatedChannels.insert(channel, true); Auth().api().requestSelfParticipant(channel); } + if (const auto feed = channel->feed()) { + if (!feed->lastMessageKnown() + || !feed->unreadCountKnown()) { + Auth().api().requestDialogEntry(feed); + } + } else if (channel->amIn()) { + const auto history = App::history(channel->id); + if (!history->lastMessageKnown() + || !history->unreadCountKnown()) { + Auth().api().requestDialogEntry(history); + } + } } } break; diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index f22aace8b..ef0beafaa 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -468,7 +468,7 @@ void FeedFiller::addInfo() { void FeedFiller::addNotifications() { const auto feed = _feed; _addAction(lang(lng_feed_notifications), [=] { - Info::FeedProfile::FeedNotificationsController::Start(feed); + Info::FeedProfile::NotificationsController::Start(feed); }); } @@ -669,6 +669,13 @@ void ToggleChannelGrouping(not_null channel, bool group) { ? lng_feed_channel_added : lng_feed_channel_removed)); }; + if (group) { + const auto feed = Auth().data().feedLoaded(Data::Feed::kId); + if (!feed || feed->channels().size() < 2) { + Info::FeedProfile::EditController::Start(feed, channel); + return; + } + } Auth().api().toggleChannelGrouping( channel, group,