Add block user box. Search isn't working yet.

Add a box containing all chats and contacts for blocking users.
Contacts and dialogs loaded state can be accessed in AuthSession.
This commit is contained in:
John Preston 2017-03-14 17:16:03 +03:00
parent b1ee91b06b
commit 805be84bff
11 changed files with 455 additions and 47 deletions

View file

@ -434,6 +434,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
"lng_blocked_list_unknown_phone" = "unknown phone";
"lng_blocked_list_unblock" = "Unblock";
"lng_blocked_list_add" = "Block user";
"lng_blocked_list_add_title" = "Select user to block";
"lng_blocked_list_already_blocked" = "blocked already";
"lng_blocked_list_about" = "Blocked users can't send you messages or add you to groups. They will not see your profile pictures, online and last seen status.";
"lng_preview_loading" = "Getting Link Info...";

View file

@ -63,10 +63,33 @@ public:
return _notifications.get();
}
class Data {
public:
base::Variable<bool> &contactsLoaded() {
return _contactsLoaded;
}
base::Variable<bool> &allChatsLoaded() {
return _allChatsLoaded;
}
base::Observable<void> &moreChatsLoaded() {
return _moreChatsLoaded;
}
private:
base::Variable<bool> _contactsLoaded = { false } ;
base::Variable<bool> _allChatsLoaded = { false };
base::Observable<void> _moreChatsLoaded;
};
Data &data() {
return _data;
}
~AuthSession();
private:
UserId _userId = 0;
Data _data;
const std::unique_ptr<Storage::Downloader> _downloader;
const std::unique_ptr<Window::Notifications::System> _notifications;

View file

@ -28,17 +28,43 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "auth_session.h"
#include "mainwidget.h"
#include "storage/file_download.h"
#include "ui/widgets/multi_select.h"
#include "ui/effects/widget_slide_wrap.h"
#include "lang.h"
PeerListBox::PeerListBox(QWidget*, std::unique_ptr<Controller> controller)
: _controller(std::move(controller)) {
}
object_ptr<Ui::WidgetSlideWrap<Ui::MultiSelect>> PeerListBox::createMultiSelect() {
auto entity = object_ptr<Ui::MultiSelect>(this, st::contactsMultiSelect, lang(lng_participant_filter));
auto margins = style::margins(0, 0, 0, 0);
auto callback = [this] { updateScrollSkips(); };
return object_ptr<Ui::WidgetSlideWrap<Ui::MultiSelect>>(this, std::move(entity), margins, std::move(callback));
}
int PeerListBox::getTopScrollSkip() const {
auto result = 0;
if (_select && !_select->isHidden()) {
result += _select->height();
}
return result;
}
void PeerListBox::updateScrollSkips() {
setInnerTopSkip(getTopScrollSkip(), true);
}
void PeerListBox::prepare() {
_inner = setInnerWidget(object_ptr<Inner>(this, _controller.get()), st::boxLayerScroll);
_controller->setView(this);
setDimensions(st::boxWideWidth, st::boxMaxListHeight);
if (_select) {
_select->finishAnimation();
onScrollToY(0);
}
connect(_inner, SIGNAL(mustScrollTo(int, int)), this, SLOT(onScrollToY(int, int)));
}
@ -60,9 +86,24 @@ void PeerListBox::keyPressEvent(QKeyEvent *e) {
void PeerListBox::resizeEvent(QResizeEvent *e) {
BoxContent::resizeEvent(e);
if (_select) {
_select->resizeToWidth(width());
_select->moveToLeft(0, 0);
updateScrollSkips();
}
_inner->resize(width(), _inner->height());
}
void PeerListBox::setInnerFocus() {
if (!_select || _select->isHidden()) {
_inner->setFocus();
} else {
_select->entity()->setInnerFocus();
}
}
void PeerListBox::appendRow(std::unique_ptr<Row> row) {
_inner->appendRow(std::move(row));
}
@ -83,6 +124,10 @@ void PeerListBox::removeRow(Row *row) {
_inner->removeRow(row);
}
int PeerListBox::rowsCount() const {
return _inner->rowsCount();
}
void PeerListBox::setAboutText(const QString &aboutText) {
_inner->setAboutText(aboutText);
}
@ -91,6 +136,20 @@ void PeerListBox::refreshRows() {
_inner->refreshRows();
}
void PeerListBox::setSearchable(bool searchable) {
_inner->setSearchable(searchable);
if (searchable) {
if (!_select) {
_select = createMultiSelect();
_select->resizeToWidth(width());
_select->moveToLeft(0, 0);
}
_select->slideDown();
} else if (_select) {
_select->slideUp();
}
}
PeerListBox::Row::Row(PeerData *peer) : _peer(peer) {
}
@ -200,30 +259,76 @@ PeerListBox::Inner::Inner(QWidget *parent, Controller *controller) : TWidget(par
}
void PeerListBox::Inner::appendRow(std::unique_ptr<Row> row) {
if (!_rowsByPeer.contains(row->peer())) {
if (_rowsByPeer.find(row->peer()) == _rowsByPeer.cend()) {
row->setIndex(_rows.size());
_rowsByPeer.insert(row->peer(), row.get());
addRowEntry(row.get());
_rows.push_back(std::move(row));
}
}
void PeerListBox::Inner::prependRow(std::unique_ptr<Row> row) {
if (!_rowsByPeer.contains(row->peer())) {
_rowsByPeer.insert(row->peer(), row.get());
_rows.insert(_rows.begin(), std::move(row));
auto index = 0;
for (auto &row : _rows) {
row->setIndex(index++);
void PeerListBox::Inner::addRowEntry(Row *row) {
_rowsByPeer.emplace(row->peer(), row);
if (_searchable) {
addToSearchIndex(row);
}
}
void PeerListBox::Inner::addToSearchIndex(Row *row) {
removeFromSearchIndex(row);
row->setNameFirstChars(row->peer()->chars);
for_const (auto ch, row->nameFirstChars()) {
_searchIndex[ch].push_back(row);
}
}
void PeerListBox::Inner::removeFromSearchIndex(Row *row) {
auto &nameFirstChars = row->nameFirstChars();
if (!nameFirstChars.empty()) {
for_const (auto ch, row->nameFirstChars()) {
auto it = _searchIndex.find(ch);
if (it != _searchIndex.cend()) {
auto &entry = it->second;
entry.erase(std::remove(entry.begin(), entry.end(), row), entry.end());
if (entry.empty()) {
_searchIndex.erase(it);
}
}
}
row->setNameFirstChars(OrderedSet<QChar>());
}
}
void PeerListBox::Inner::prependRow(std::unique_ptr<Row> row) {
if (_rowsByPeer.find(row->peer()) == _rowsByPeer.cend()) {
_rowsByPeer.emplace(row->peer(), row.get());
_rows.insert(_rows.begin(), std::move(row));
refreshIndices();
}
}
void PeerListBox::Inner::refreshIndices() {
auto index = 0;
for (auto &row : _rows) {
row->setIndex(index++);
}
}
PeerListBox::Row *PeerListBox::Inner::findRow(PeerData *peer) {
return _rowsByPeer.value(peer, nullptr);
auto it = _rowsByPeer.find(peer);
return (it == _rowsByPeer.cend()) ? nullptr : it->second;
}
void PeerListBox::Inner::updateRow(Row *row) {
updateRowWithIndex(row->index());
auto index = row->index();
if (row->disabled()) {
if (index == _selected.index) {
_selected = SelectedRow();
}
if (index == _pressed.index) {
setPressed(SelectedRow());
}
}
updateRowWithIndex(index);
}
void PeerListBox::Inner::removeRow(Row *row) {
@ -233,14 +338,24 @@ void PeerListBox::Inner::removeRow(Row *row) {
clearSelection();
_rowsByPeer.remove(row->peer());
_rowsByPeer.erase(row->peer());
if (_searchable) {
removeFromSearchIndex(row);
}
_rows.erase(_rows.begin() + index);
for (auto i = index, count = int(_rows.size()); i != count; ++i) {
_rows[i]->setIndex(i);
}
}
int PeerListBox::Inner::rowsCount() const {
return _rows.size();
}
void PeerListBox::Inner::setAboutText(const QString &aboutText) {
if (_about.isEmpty() && aboutText.isEmpty()) {
return;
}
_about.setText(st::boxLabelStyle, aboutText);
}
@ -261,6 +376,16 @@ void PeerListBox::Inner::refreshRows() {
update();
}
void PeerListBox::Inner::setSearchable(bool searchable) {
// We don't destroy a search index if we have one already.
if (searchable && !_searchable) {
_searchable = true;
for_const (auto &row, _rows) {
addToSearchIndex(row.get());
}
}
}
void PeerListBox::Inner::paintEvent(QPaintEvent *e) {
QRect r(e->rect());
Painter p(this);
@ -304,15 +429,19 @@ void PeerListBox::Inner::leaveEventHook(QEvent *e) {
}
void PeerListBox::Inner::mouseMoveEvent(QMouseEvent *e) {
_mouseSelection = true;
_lastMousePosition = e->globalPos();
updateSelection();
auto position = e->globalPos();
if (_mouseSelection || _lastMousePosition != position) {
_lastMousePosition = position;
_mouseSelection = true;
updateSelection();
}
}
void PeerListBox::Inner::mousePressEvent(QMouseEvent *e) {
_mouseSelection = true;
_lastMousePosition = e->globalPos();
updateSelection();
setPressed(_selected);
if (_selected.index >= 0 && _selected.index < _rows.size() && !_selected.action) {
auto size = QSize(width(), _rowHeight);
@ -327,6 +456,7 @@ void PeerListBox::Inner::mousePressEvent(QMouseEvent *e) {
void PeerListBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
updateRowWithIndex(_pressed.index);
updateRowWithIndex(_selected.index);
auto pressed = _pressed;
setPressed(SelectedRow());
if (e->button() == Qt::LeftButton) {
@ -389,37 +519,71 @@ void PeerListBox::Inner::paintRow(Painter &p, TimeMs ms, int index) {
p.drawTextLeft(namex, st::contactsPadding.top() + st::contactsStatusTop, width(), row->status());
}
void PeerListBox::Inner::selectSkip(int32 dir) {
void PeerListBox::Inner::selectSkip(int direction) {
if (_pressed.index >= 0) {
return;
}
_mouseSelection = false;
auto newSelectedIndex = _selected.index + dir;
if (newSelectedIndex <= 0) {
newSelectedIndex = _rows.empty() ? -1 : 0;
} else if (newSelectedIndex >= _rows.size()) {
auto newSelectedIndex = _selected.index + direction;
auto firstEnabled = 0;
for_const (auto &row, _rows) {
if (!row->disabled()) {
break;
}
++firstEnabled;
}
auto lastEnabled = int(_rows.size()) - 1;
for (; lastEnabled > firstEnabled; --lastEnabled) {
if (!_rows[lastEnabled]->disabled()) {
break;
}
}
t_assert(lastEnabled < _rows.size());
t_assert(firstEnabled - 1 <= lastEnabled);
// Always pass through the first enabled item when changing from / to none selected.
if ((_selected.index > firstEnabled && newSelectedIndex < firstEnabled)
|| (_selected.index < firstEnabled && newSelectedIndex > firstEnabled)) {
newSelectedIndex = firstEnabled;
}
// Snap the index.
newSelectedIndex = snap(newSelectedIndex, firstEnabled - 1, lastEnabled);
// Skip the disabled rows.
if (newSelectedIndex < firstEnabled) {
newSelectedIndex = -1;
}
if (dir > 0) {
if (newSelectedIndex < 0 || newSelectedIndex >= _rows.size()) {
newSelectedIndex = -1;
}
} else if (!_rows.empty()) {
if (newSelectedIndex < 0) {
newSelectedIndex = _rows.size() - 1;
} else if (newSelectedIndex > lastEnabled) {
newSelectedIndex = lastEnabled;
} else if (_rows[newSelectedIndex]->disabled()) {
auto delta = (direction > 0) ? 1 : -1;
for (newSelectedIndex += delta; ; newSelectedIndex += delta) {
// We must find an enabled row, firstEnabled <= us <= lastEnabled.
t_assert(newSelectedIndex >= 0 && newSelectedIndex < _rows.size());
if (!_rows[newSelectedIndex]->disabled()) {
break;
}
}
}
_selected.index = newSelectedIndex;
_selected.action = false;
if (newSelectedIndex >= 0) {
emit mustScrollTo(st::membersMarginTop + newSelectedIndex * _rowHeight, st::membersMarginTop + (newSelectedIndex + 1) * _rowHeight);
auto top = (newSelectedIndex > 0) ? getRowTop(newSelectedIndex) : 0;
auto bottom = (newSelectedIndex + 1 < _rows.size()) ? getRowTop(newSelectedIndex + 1) : height();
emit mustScrollTo(top, bottom);
}
update();
}
void PeerListBox::Inner::selectSkipPage(int32 h, int32 dir) {
auto rowsToSkip = h / _rowHeight;
void PeerListBox::Inner::selectSkipPage(int height, int direction) {
auto rowsToSkip = height / _rowHeight;
if (!rowsToSkip) return;
selectSkip(rowsToSkip * dir);
selectSkip(rowsToSkip * direction);
}
void PeerListBox::Inner::loadProfilePhotos() {
@ -475,14 +639,19 @@ void PeerListBox::Inner::updateSelection() {
auto selected = SelectedRow();
selected.index = (in && point.y() >= 0 && point.y() < _rows.size() * _rowHeight) ? (point.y() / _rowHeight) : -1;
if (selected.index >= 0) {
auto actionRight = st::contactsPadding.right() + st::contactsCheckPosition.x();
auto actionTop = (_rowHeight - st::normalFont->height) / 2;
auto actionWidth = _rows[selected.index]->actionWidth();
auto actionLeft = width() - actionWidth - actionRight;
auto rowTop = selected.index * _rowHeight;
auto actionRect = myrtlrect(actionLeft, rowTop + actionTop, actionWidth, st::normalFont->height);
if (actionRect.contains(point)) {
selected.action = true;
auto &row = _rows[selected.index];
if (row->disabled()) {
selected = SelectedRow();
} else {
auto actionRight = st::contactsPadding.right() + st::contactsCheckPosition.x();
auto actionTop = (_rowHeight - st::normalFont->height) / 2;
auto actionWidth = _rows[selected.index]->actionWidth();
auto actionLeft = width() - actionWidth - actionRight;
auto rowTop = selected.index * _rowHeight;
auto actionRect = myrtlrect(actionLeft, rowTop + actionTop, actionWidth, st::normalFont->height);
if (actionRect.contains(point)) {
selected.action = true;
}
}
}
if (_selected != selected) {
@ -512,6 +681,9 @@ void PeerListBox::Inner::updateRowWithIndex(int index) {
void PeerListBox::Inner::onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) {
if (auto row = findRow(peer)) {
if (_searchable) {
addToSearchIndex(row);
}
row->refreshName();
update(0, st::membersMarginTop + row->index() * _rowHeight, width(), _rowHeight);
}

View file

@ -24,6 +24,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace Ui {
class RippleAnimation;
class MultiSelect;
template <typename Widget>
class WidgetSlideWrap;
} // namespace Ui
class PeerListBox : public BoxContent {
@ -76,12 +79,22 @@ public:
int index() const {
return _index;
}
bool disabled() const {
return _disabled;
}
template <typename UpdateCallback>
void addRipple(QSize size, QPoint point, UpdateCallback updateCallback);
void stopLastRipple();
void paintRipple(Painter &p, int x, int y, int outerWidth, TimeMs ms);
void setNameFirstChars(const OrderedSet<QChar> &nameFirstChars) {
_nameFirstChars = nameFirstChars;
}
const OrderedSet<QChar> &nameFirstChars() const {
return _nameFirstChars;
}
void lazyInitialize();
private:
@ -95,6 +108,7 @@ public:
int _actionWidth = 0;
bool _disabled = false;
int _index = -1;
OrderedSet<QChar> _nameFirstChars;
};
@ -133,16 +147,31 @@ public:
Row *findRow(PeerData *peer);
void updateRow(Row *row);
void removeRow(Row *row);
int rowsCount() const;
void setAboutText(const QString &aboutText);
void refreshRows();
void setSearchable(bool searchable);
// callback takes two iterators, like [](auto &begin, auto &end).
template <typename ReorderCallback>
void reorderRows(ReorderCallback &&callback) {
_inner->reorderRows(std::forward<ReorderCallback>(callback));
}
protected:
void prepare() override;
void setInnerFocus() override;
void keyPressEvent(QKeyEvent *e) override;
void resizeEvent(QResizeEvent *e) override;
private:
object_ptr<Ui::WidgetSlideWrap<Ui::MultiSelect>> createMultiSelect();
int getTopScrollSkip() const;
void updateScrollSkips();
object_ptr<Ui::WidgetSlideWrap<Ui::MultiSelect>> _select = { nullptr };
class Inner;
QPointer<Inner> _inner;
@ -157,8 +186,8 @@ class PeerListBox::Inner : public TWidget, public RPCSender, private base::Subsc
public:
Inner(QWidget *parent, Controller *controller);
void selectSkip(int32 dir);
void selectSkipPage(int32 h, int32 dir);
void selectSkip(int direction);
void selectSkipPage(int height, int direction);
void clearSelection();
@ -170,8 +199,16 @@ public:
Row *findRow(PeerData *peer);
void updateRow(Row *row);
void removeRow(Row *row);
int rowsCount() const;
void setAboutText(const QString &aboutText);
void refreshRows();
void setSearchable(bool searchable);
template <typename ReorderCallback>
void reorderRows(ReorderCallback &&callback) {
callback(std::begin(_rows), std::end(_rows));
refreshIndices();
}
signals:
void mustScrollTo(int ymin, int ymax);
@ -189,6 +226,8 @@ protected:
void mouseReleaseEvent(QMouseEvent *e) override;
private:
void refreshIndices();
struct SelectedRow {
int index = -1;
bool action = false;
@ -211,6 +250,10 @@ private:
void paintRow(Painter &p, TimeMs ms, int index);
void addRowEntry(Row *row);
void addToSearchIndex(Row *row);
void removeFromSearchIndex(Row *row);
Controller *_controller = nullptr;
int _rowHeight = 0;
int _visibleTop = 0;
@ -221,7 +264,10 @@ private:
bool _mouseSelection = false;
std::vector<std::unique_ptr<Row>> _rows;
QMap<PeerData*, Row*> _rowsByPeer;
std::map<PeerData*, Row*> _rowsByPeer;
bool _searchable = false;
std::map<QChar, std::vector<Row*>> _searchIndex;
int _aboutWidth = 0;
int _aboutHeight = 0;

View file

@ -2199,6 +2199,10 @@ Dialogs::IndexedList *DialogsInner::dialogsList() {
return _dialogs.get();
}
Dialogs::IndexedList *DialogsInner::contactsNoDialogsList() {
return _contactsNoDialogs.get();
}
int32 DialogsInner::lastSearchDate() const {
return _lastSearchDate;
}
@ -2546,6 +2550,11 @@ void DialogsWidget::dialogsReceived(const MTPmessages_Dialogs &dialogs, mtpReque
_dialogsRequestId = 0;
loadDialogs();
AuthSession::Current().data().moreChatsLoaded().notify();
if (_dialogsFull) {
AuthSession::Current().data().allChatsLoaded().set(true);
}
}
void DialogsWidget::pinnedDialogsReceived(const MTPmessages_PeerDialogs &dialogs, mtpRequestId requestId) {
@ -2578,6 +2587,8 @@ void DialogsWidget::pinnedDialogsReceived(const MTPmessages_PeerDialogs &dialogs
_pinnedDialogsRequestId = 0;
_pinnedDialogsReceived = true;
AuthSession::Current().data().moreChatsLoaded().notify();
}
bool DialogsWidget::dialogsFailed(const RPCError &error, mtpRequestId requestId) {
@ -2744,6 +2755,8 @@ void DialogsWidget::contactsReceived(const MTPcontacts_Contacts &result) {
_inner->contactsReceived(d.vcontacts.v);
}
if (App::main()) App::main()->contactsReceived();
AuthSession::Current().data().contactsLoaded().set(true);
}
bool DialogsWidget::contactsFailed(const RPCError &error) {
@ -3218,6 +3231,10 @@ Dialogs::IndexedList *DialogsWidget::dialogsList() {
return _inner->dialogsList();
}
Dialogs::IndexedList *DialogsWidget::contactsNoDialogsList() {
return _inner->contactsNoDialogsList();
}
bool DialogsWidget::onCancelSearch() {
bool clearing = !_filter->getLastText().isEmpty();
if (_searchRequest) {

View file

@ -91,6 +91,7 @@ public:
Dialogs::IndexedList *contactsList();
Dialogs::IndexedList *dialogsList();
Dialogs::IndexedList *contactsNoDialogsList();
int32 lastSearchDate() const;
PeerData *lastSearchPeer() const;
MsgId lastSearchId() const;
@ -335,6 +336,7 @@ public:
Dialogs::IndexedList *contactsList();
Dialogs::IndexedList *dialogsList();
Dialogs::IndexedList *contactsNoDialogsList();
void searchMessages(const QString &query, PeerData *inPeer = 0);
void onSearchMore();

View file

@ -1154,6 +1154,10 @@ Dialogs::IndexedList *MainWidget::dialogsList() {
return _dialogs->dialogsList();
}
Dialogs::IndexedList *MainWidget::contactsNoDialogsList() {
return _dialogs->contactsNoDialogsList();
}
namespace {
QString parseCommandFromMessage(History *history, const QString &message) {
if (history->peer->id != peerFromUser(ServiceUserId)) {
@ -3563,7 +3567,7 @@ bool MainWidget::failChannelDifference(ChannelData *channel, const RPCError &err
}
void MainWidget::gotState(const MTPupdates_State &state) {
const auto &d(state.c_updates_state());
auto &d = state.c_updates_state();
updSetState(d.vpts.v, d.vdate.v, d.vqts.v, d.vseq.v);
_lastUpdateTime = getms(true);

View file

@ -285,6 +285,7 @@ public:
Dialogs::IndexedList *contactsList();
Dialogs::IndexedList *dialogsList();
Dialogs::IndexedList *contactsNoDialogsList();
struct MessageToSend {
History *history = nullptr;

View file

@ -23,6 +23,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "lang.h"
#include "apiwrap.h"
#include "observer_peer.h"
#include "mainwidget.h"
#include "dialogs/dialogs_indexed_list.h"
#include "auth_session.h"
namespace Settings {
namespace {
@ -34,14 +37,14 @@ constexpr auto kPerPage = 40;
void BlockedBoxController::prepare() {
view()->setTitle(lang(lng_blocked_list_title));
view()->addButton(lang(lng_close), [this] { view()->closeBox(); });
view()->addLeftButton(lang(lng_blocked_list_add), [this] { blockUser(); });
view()->setAboutText(lang(lng_contacts_loading));
view()->refreshRows();
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::UserIsBlocked, [this](const Notify::PeerUpdate &update) {
if (!update.peer->isUser()) {
return;
if (auto user = update.peer->asUser()) {
handleBlockedEvent(user);
}
handleBlockedEvent(update.peer->asUser());
}));
preloadRows();
@ -124,6 +127,10 @@ void BlockedBoxController::handleBlockedEvent(UserData *user) {
}
}
void BlockedBoxController::blockUser() {
Ui::show(Box<PeerListBox>(std::make_unique<BlockUserBoxController>()), KeepOtherLayers);
}
bool BlockedBoxController::appendRow(UserData *user) {
if (view()->findRow(user)) {
return false;
@ -155,4 +162,104 @@ std::unique_ptr<PeerListBox::Row> BlockedBoxController::createRow(UserData *user
return row;
}
void BlockUserBoxController::prepare() {
view()->setTitle(lang(lng_blocked_list_add_title));
view()->addButton(lang(lng_cancel), [this] { view()->closeBox(); });
view()->setSearchable(true);
rebuildRows();
auto &sessionData = AuthSession::Current().data();
subscribe(sessionData.contactsLoaded(), [this](bool loaded) {
rebuildRows();
});
subscribe(sessionData.moreChatsLoaded(), [this] {
rebuildRows();
});
subscribe(sessionData.allChatsLoaded(), [this](bool loaded) {
checkForEmptyRows();
});
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::UserIsBlocked, [this](const Notify::PeerUpdate &update) {
if (auto user = update.peer->asUser()) {
if (auto row = view()->findRow(user)) {
updateIsBlocked(row, user);
view()->updateRow(row);
}
}
}));
}
void BlockUserBoxController::rebuildRows() {
auto ms = getms();
auto wasEmpty = !view()->rowsCount();
auto appendList = [this](auto chats) {
auto count = 0;
for_const (auto row, chats->all()) {
auto history = row->history();
if (history->peer->isUser()) {
if (appendRow(history)) {
++count;
}
}
}
return count;
};
auto added = appendList(App::main()->dialogsList());
added += appendList(App::main()->contactsNoDialogsList());
if (!wasEmpty && added > 0) {
view()->reorderRows([](auto &begin, auto &end) {
// Place dialogs list before contactsNoDialogs list.
std::stable_partition(begin, end, [](auto &row) {
auto history = static_cast<Row&>(*row).history();
return history->inChatList(Dialogs::Mode::All);
});
});
}
checkForEmptyRows();
view()->refreshRows();
}
void BlockUserBoxController::checkForEmptyRows() {
if (view()->rowsCount()) {
view()->setAboutText(QString());
} else {
auto &sessionData = AuthSession::Current().data();
auto loaded = sessionData.contactsLoaded().value() && sessionData.allChatsLoaded().value();
view()->setAboutText(lang(loaded ? lng_contacts_not_found : lng_contacts_loading));
}
}
void BlockUserBoxController::updateIsBlocked(PeerListBox::Row *row, UserData *user) const {
auto blocked = user->isBlocked();
row->setDisabled(blocked);
if (blocked) {
row->setCustomStatus(lang(lng_blocked_list_already_blocked));
} else {
row->clearCustomStatus();
}
}
void BlockUserBoxController::rowClicked(PeerData *peer) {
auto user = peer->asUser();
t_assert(user != nullptr);
App::api()->blockUser(user);
view()->closeBox();
}
bool BlockUserBoxController::appendRow(History *history) {
if (auto row = view()->findRow(history->peer)) {
updateIsBlocked(row, history->peer->asUser());
return false;
}
view()->appendRow(createRow(history));
return true;
}
std::unique_ptr<BlockUserBoxController::Row> BlockUserBoxController::createRow(History *history) const {
auto row = std::make_unique<Row>(history);
updateIsBlocked(row.get(), history->peer->asUser());
return row;
}
} // namespace Settings

View file

@ -34,6 +34,8 @@ public:
private:
void receivedUsers(const QVector<MTPContactBlocked> &result);
void handleBlockedEvent(UserData *user);
void blockUser();
bool appendRow(UserData *user);
bool prependRow(UserData *user);
std::unique_ptr<PeerListBox::Row> createRow(UserData *user) const;
@ -44,4 +46,31 @@ private:
};
class BlockUserBoxController : public QObject, public PeerListBox::Controller, private base::Subscriber {
public:
void prepare() override;
void rowClicked(PeerData *peer) override;
private:
void rebuildRows();
void checkForEmptyRows();
void updateIsBlocked(PeerListBox::Row *row, UserData *user) const;
bool appendRow(History *history);
class Row : public PeerListBox::Row {
public:
Row(History *history) : PeerListBox::Row(history->peer), _history(history) {
}
History *history() const {
return _history;
}
private:
History *_history = nullptr;
};
std::unique_ptr<Row> createRow(History *history) const;
};
} // namespace Settings

View file

@ -41,6 +41,11 @@ public:
void showFast();
void hideFast();
void finishAnimation() {
_a_height.finish();
animationCallback();
}
TWidget *entity() {
return _entity;
}