Add a create feed channels list box.

This commit is contained in:
John Preston 2018-02-20 19:56:41 +03:00
parent 74aa1ad71e
commit 17a4d19beb
8 changed files with 263 additions and 52 deletions

View file

@ -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";

View file

@ -2965,7 +2965,7 @@ void ApiWrap::userPhotosDone(
}
void ApiWrap::requestFeedChannels(not_null<Data::Feed*> feed) {
if (_feedChannelsRequests.contains(feed)) {
if (_feedChannelsGetRequests.contains(feed)) {
return;
}
const auto hash = feed->channelsHash();
@ -2974,7 +2974,7 @@ void ApiWrap::requestFeedChannels(not_null<Data::Feed*> 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<Data::Feed*> 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<not_null<ChannelData*>>();
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<Data::Feed*> 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<not_null<ChannelData*>>();
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<Data::Feed*> feed,
const std::vector<not_null<ChannelData*>> &channels) {
if (const auto already = _feedChannelsSetRequests.take(feed)) {
request(*already).cancel();
}
auto inputs = QVector<MTPInputChannel>();
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<MTPInputChannel>(inputs),
MTPbool()
)).done([=](const MTPUpdates &result) {
applyUpdates(result);
_feedChannelsSetRequests.remove(feed);
}).fail([=](const RPCError &error) {
_feedChannelsSetRequests.remove(feed);
}).send();
}
void ApiWrap::feedChannelsDone(not_null<Data::Feed*> feed) {

View file

@ -72,6 +72,10 @@ public:
void requestDialogEntry(not_null<Data::Feed*> feed);
//void requestFeedDialogsEntries(not_null<Data::Feed*> feed);
void requestDialogEntry(not_null<History*> history);
void applyFeedSources(const MTPDchannels_feedSources &data);
void setFeedChannels(
not_null<Data::Feed*> feed,
const std::vector<not_null<ChannelData*>> &channels);
void requestFullPeer(PeerData *peer);
void requestPeer(PeerData *peer);
@ -500,7 +504,10 @@ private:
base::flat_map<not_null<UserData*>, mtpRequestId> _userPhotosRequests;
base::flat_set<not_null<Data::Feed*>> _feedChannelsRequests;
base::flat_set<not_null<Data::Feed*>> _feedChannelsGetRequests;
base::flat_map<
not_null<Data::Feed*>,
mtpRequestId> _feedChannelsSetRequests;
base::flat_set<std::tuple<
not_null<Data::Feed*>,
Data::MessagePosition,

View file

@ -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<bool> {
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.,

View file

@ -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<Ui::PopupMenu> ChannelsController::rowContextMenu(
return result;
}
void FeedNotificationsController::Start(not_null<Data::Feed*> feed) {
void NotificationsController::Start(not_null<Data::Feed*> feed) {
const auto initBox = [=](not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_settings_save), [=] {
const auto main = App::main();
@ -223,16 +229,16 @@ void FeedNotificationsController::Start(not_null<Data::Feed*> feed) {
box->addButton(langFactory(lng_cancel), [box] { box->closeBox(); });
};
Ui::show(Box<PeerListBox>(
std::make_unique<FeedNotificationsController>(feed),
std::make_unique<NotificationsController>(feed),
initBox));
}
FeedNotificationsController::FeedNotificationsController(
NotificationsController::NotificationsController(
not_null<Data::Feed*> 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<PeerListRow*> row) {
void NotificationsController::rowClicked(not_null<PeerListRow*> row) {
delegate()->peerListSetRowChecked(row, !row->checked());
}
std::unique_ptr<PeerListRow> FeedNotificationsController::createRow(
std::unique_ptr<PeerListRow> NotificationsController::createRow(
not_null<ChannelData*> channel) {
return std::make_unique<PeerListRow>(channel);
}
void EditController::Start(
not_null<Data::Feed*> feed,
ChannelData *channel) {
const auto initBox = [=](not_null<PeerListBox*> box) {
box->addButton(langFactory(lng_settings_save), [=] {
auto channels = std::vector<not_null<ChannelData*>>();
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<PeerListBox>(
std::make_unique<EditController>(feed, channel),
initBox));
}
EditController::EditController(
not_null<Data::Feed*> 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<not_null<ChannelData*>>();
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<ChannelData*> 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<PeerListRow*> row) {
delegate()->peerListSetRowChecked(row, !row->checked());
}
std::unique_ptr<PeerListRow> EditController::createRow(
not_null<ChannelData*> channel) {
return std::make_unique<PeerListRow>(channel);
}

View file

@ -52,13 +52,13 @@ private:
};
class FeedNotificationsController
class NotificationsController
: public PeerListController
, private MTP::Sender {
public:
static void Start(not_null<Data::Feed*> feed);
FeedNotificationsController(not_null<Data::Feed*> feed);
NotificationsController(not_null<Data::Feed*> feed);
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
@ -78,5 +78,33 @@ private:
};
class EditController
: public PeerListController
, private MTP::Sender {
public:
static void Start(
not_null<Data::Feed*> feed,
ChannelData *channel = nullptr);
EditController(
not_null<Data::Feed*> feed,
ChannelData *channel);
void prepare() override;
void rowClicked(not_null<PeerListRow*> row) override;
void loadMoreRows() override;
private:
std::unique_ptr<PeerListRow> createRow(not_null<ChannelData*> channel);
void applyFeedSources(const MTPchannels_FeedSources &result);
not_null<Data::Feed*> _feed;
ChannelData *_startWithChannel = nullptr;
mtpRequestId _preloadRequestId = 0;
bool _allLoaded = false;
};
} // namespace FeedProfile
} // namespace Info

View file

@ -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;

View file

@ -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<ChannelData*> 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,