mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 10:11:41 -05:00
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:
parent
b1ee91b06b
commit
805be84bff
11 changed files with 455 additions and 47 deletions
|
@ -434,6 +434,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
"lng_blocked_list_unknown_phone" = "unknown phone";
|
"lng_blocked_list_unknown_phone" = "unknown phone";
|
||||||
"lng_blocked_list_unblock" = "Unblock";
|
"lng_blocked_list_unblock" = "Unblock";
|
||||||
"lng_blocked_list_add" = "Block user";
|
"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_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...";
|
"lng_preview_loading" = "Getting Link Info...";
|
||||||
|
|
|
@ -63,10 +63,33 @@ public:
|
||||||
return _notifications.get();
|
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();
|
~AuthSession();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
UserId _userId = 0;
|
UserId _userId = 0;
|
||||||
|
Data _data;
|
||||||
|
|
||||||
const std::unique_ptr<Storage::Downloader> _downloader;
|
const std::unique_ptr<Storage::Downloader> _downloader;
|
||||||
const std::unique_ptr<Window::Notifications::System> _notifications;
|
const std::unique_ptr<Window::Notifications::System> _notifications;
|
||||||
|
|
|
@ -28,17 +28,43 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
#include "auth_session.h"
|
#include "auth_session.h"
|
||||||
#include "mainwidget.h"
|
#include "mainwidget.h"
|
||||||
#include "storage/file_download.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)
|
PeerListBox::PeerListBox(QWidget*, std::unique_ptr<Controller> controller)
|
||||||
: _controller(std::move(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() {
|
void PeerListBox::prepare() {
|
||||||
_inner = setInnerWidget(object_ptr<Inner>(this, _controller.get()), st::boxLayerScroll);
|
_inner = setInnerWidget(object_ptr<Inner>(this, _controller.get()), st::boxLayerScroll);
|
||||||
|
|
||||||
_controller->setView(this);
|
_controller->setView(this);
|
||||||
|
|
||||||
setDimensions(st::boxWideWidth, st::boxMaxListHeight);
|
setDimensions(st::boxWideWidth, st::boxMaxListHeight);
|
||||||
|
if (_select) {
|
||||||
|
_select->finishAnimation();
|
||||||
|
onScrollToY(0);
|
||||||
|
}
|
||||||
|
|
||||||
connect(_inner, SIGNAL(mustScrollTo(int, int)), this, SLOT(onScrollToY(int, int)));
|
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) {
|
void PeerListBox::resizeEvent(QResizeEvent *e) {
|
||||||
BoxContent::resizeEvent(e);
|
BoxContent::resizeEvent(e);
|
||||||
|
|
||||||
|
if (_select) {
|
||||||
|
_select->resizeToWidth(width());
|
||||||
|
_select->moveToLeft(0, 0);
|
||||||
|
|
||||||
|
updateScrollSkips();
|
||||||
|
}
|
||||||
|
|
||||||
_inner->resize(width(), _inner->height());
|
_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) {
|
void PeerListBox::appendRow(std::unique_ptr<Row> row) {
|
||||||
_inner->appendRow(std::move(row));
|
_inner->appendRow(std::move(row));
|
||||||
}
|
}
|
||||||
|
@ -83,6 +124,10 @@ void PeerListBox::removeRow(Row *row) {
|
||||||
_inner->removeRow(row);
|
_inner->removeRow(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int PeerListBox::rowsCount() const {
|
||||||
|
return _inner->rowsCount();
|
||||||
|
}
|
||||||
|
|
||||||
void PeerListBox::setAboutText(const QString &aboutText) {
|
void PeerListBox::setAboutText(const QString &aboutText) {
|
||||||
_inner->setAboutText(aboutText);
|
_inner->setAboutText(aboutText);
|
||||||
}
|
}
|
||||||
|
@ -91,6 +136,20 @@ void PeerListBox::refreshRows() {
|
||||||
_inner->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) {
|
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) {
|
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());
|
row->setIndex(_rows.size());
|
||||||
_rowsByPeer.insert(row->peer(), row.get());
|
addRowEntry(row.get());
|
||||||
_rows.push_back(std::move(row));
|
_rows.push_back(std::move(row));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerListBox::Inner::prependRow(std::unique_ptr<Row> row) {
|
void PeerListBox::Inner::addRowEntry(Row *row) {
|
||||||
if (!_rowsByPeer.contains(row->peer())) {
|
_rowsByPeer.emplace(row->peer(), row);
|
||||||
_rowsByPeer.insert(row->peer(), row.get());
|
if (_searchable) {
|
||||||
_rows.insert(_rows.begin(), std::move(row));
|
addToSearchIndex(row);
|
||||||
auto index = 0;
|
}
|
||||||
for (auto &row : _rows) {
|
}
|
||||||
row->setIndex(index++);
|
|
||||||
|
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) {
|
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) {
|
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) {
|
void PeerListBox::Inner::removeRow(Row *row) {
|
||||||
|
@ -233,14 +338,24 @@ void PeerListBox::Inner::removeRow(Row *row) {
|
||||||
|
|
||||||
clearSelection();
|
clearSelection();
|
||||||
|
|
||||||
_rowsByPeer.remove(row->peer());
|
_rowsByPeer.erase(row->peer());
|
||||||
|
if (_searchable) {
|
||||||
|
removeFromSearchIndex(row);
|
||||||
|
}
|
||||||
_rows.erase(_rows.begin() + index);
|
_rows.erase(_rows.begin() + index);
|
||||||
for (auto i = index, count = int(_rows.size()); i != count; ++i) {
|
for (auto i = index, count = int(_rows.size()); i != count; ++i) {
|
||||||
_rows[i]->setIndex(i);
|
_rows[i]->setIndex(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int PeerListBox::Inner::rowsCount() const {
|
||||||
|
return _rows.size();
|
||||||
|
}
|
||||||
|
|
||||||
void PeerListBox::Inner::setAboutText(const QString &aboutText) {
|
void PeerListBox::Inner::setAboutText(const QString &aboutText) {
|
||||||
|
if (_about.isEmpty() && aboutText.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
_about.setText(st::boxLabelStyle, aboutText);
|
_about.setText(st::boxLabelStyle, aboutText);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,6 +376,16 @@ void PeerListBox::Inner::refreshRows() {
|
||||||
update();
|
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) {
|
void PeerListBox::Inner::paintEvent(QPaintEvent *e) {
|
||||||
QRect r(e->rect());
|
QRect r(e->rect());
|
||||||
Painter p(this);
|
Painter p(this);
|
||||||
|
@ -304,15 +429,19 @@ void PeerListBox::Inner::leaveEventHook(QEvent *e) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerListBox::Inner::mouseMoveEvent(QMouseEvent *e) {
|
void PeerListBox::Inner::mouseMoveEvent(QMouseEvent *e) {
|
||||||
_mouseSelection = true;
|
auto position = e->globalPos();
|
||||||
_lastMousePosition = e->globalPos();
|
if (_mouseSelection || _lastMousePosition != position) {
|
||||||
updateSelection();
|
_lastMousePosition = position;
|
||||||
|
_mouseSelection = true;
|
||||||
|
updateSelection();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerListBox::Inner::mousePressEvent(QMouseEvent *e) {
|
void PeerListBox::Inner::mousePressEvent(QMouseEvent *e) {
|
||||||
_mouseSelection = true;
|
_mouseSelection = true;
|
||||||
_lastMousePosition = e->globalPos();
|
_lastMousePosition = e->globalPos();
|
||||||
updateSelection();
|
updateSelection();
|
||||||
|
|
||||||
setPressed(_selected);
|
setPressed(_selected);
|
||||||
if (_selected.index >= 0 && _selected.index < _rows.size() && !_selected.action) {
|
if (_selected.index >= 0 && _selected.index < _rows.size() && !_selected.action) {
|
||||||
auto size = QSize(width(), _rowHeight);
|
auto size = QSize(width(), _rowHeight);
|
||||||
|
@ -327,6 +456,7 @@ void PeerListBox::Inner::mousePressEvent(QMouseEvent *e) {
|
||||||
void PeerListBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
void PeerListBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
updateRowWithIndex(_pressed.index);
|
updateRowWithIndex(_pressed.index);
|
||||||
updateRowWithIndex(_selected.index);
|
updateRowWithIndex(_selected.index);
|
||||||
|
|
||||||
auto pressed = _pressed;
|
auto pressed = _pressed;
|
||||||
setPressed(SelectedRow());
|
setPressed(SelectedRow());
|
||||||
if (e->button() == Qt::LeftButton) {
|
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());
|
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;
|
_mouseSelection = false;
|
||||||
|
|
||||||
auto newSelectedIndex = _selected.index + dir;
|
auto newSelectedIndex = _selected.index + direction;
|
||||||
if (newSelectedIndex <= 0) {
|
|
||||||
newSelectedIndex = _rows.empty() ? -1 : 0;
|
auto firstEnabled = 0;
|
||||||
} else if (newSelectedIndex >= _rows.size()) {
|
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;
|
newSelectedIndex = -1;
|
||||||
}
|
} else if (newSelectedIndex > lastEnabled) {
|
||||||
if (dir > 0) {
|
newSelectedIndex = lastEnabled;
|
||||||
if (newSelectedIndex < 0 || newSelectedIndex >= _rows.size()) {
|
} else if (_rows[newSelectedIndex]->disabled()) {
|
||||||
newSelectedIndex = -1;
|
auto delta = (direction > 0) ? 1 : -1;
|
||||||
}
|
for (newSelectedIndex += delta; ; newSelectedIndex += delta) {
|
||||||
} else if (!_rows.empty()) {
|
// We must find an enabled row, firstEnabled <= us <= lastEnabled.
|
||||||
if (newSelectedIndex < 0) {
|
t_assert(newSelectedIndex >= 0 && newSelectedIndex < _rows.size());
|
||||||
newSelectedIndex = _rows.size() - 1;
|
if (!_rows[newSelectedIndex]->disabled()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_selected.index = newSelectedIndex;
|
_selected.index = newSelectedIndex;
|
||||||
_selected.action = false;
|
_selected.action = false;
|
||||||
if (newSelectedIndex >= 0) {
|
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();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerListBox::Inner::selectSkipPage(int32 h, int32 dir) {
|
void PeerListBox::Inner::selectSkipPage(int height, int direction) {
|
||||||
auto rowsToSkip = h / _rowHeight;
|
auto rowsToSkip = height / _rowHeight;
|
||||||
if (!rowsToSkip) return;
|
if (!rowsToSkip) return;
|
||||||
selectSkip(rowsToSkip * dir);
|
selectSkip(rowsToSkip * direction);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PeerListBox::Inner::loadProfilePhotos() {
|
void PeerListBox::Inner::loadProfilePhotos() {
|
||||||
|
@ -475,14 +639,19 @@ void PeerListBox::Inner::updateSelection() {
|
||||||
auto selected = SelectedRow();
|
auto selected = SelectedRow();
|
||||||
selected.index = (in && point.y() >= 0 && point.y() < _rows.size() * _rowHeight) ? (point.y() / _rowHeight) : -1;
|
selected.index = (in && point.y() >= 0 && point.y() < _rows.size() * _rowHeight) ? (point.y() / _rowHeight) : -1;
|
||||||
if (selected.index >= 0) {
|
if (selected.index >= 0) {
|
||||||
auto actionRight = st::contactsPadding.right() + st::contactsCheckPosition.x();
|
auto &row = _rows[selected.index];
|
||||||
auto actionTop = (_rowHeight - st::normalFont->height) / 2;
|
if (row->disabled()) {
|
||||||
auto actionWidth = _rows[selected.index]->actionWidth();
|
selected = SelectedRow();
|
||||||
auto actionLeft = width() - actionWidth - actionRight;
|
} else {
|
||||||
auto rowTop = selected.index * _rowHeight;
|
auto actionRight = st::contactsPadding.right() + st::contactsCheckPosition.x();
|
||||||
auto actionRect = myrtlrect(actionLeft, rowTop + actionTop, actionWidth, st::normalFont->height);
|
auto actionTop = (_rowHeight - st::normalFont->height) / 2;
|
||||||
if (actionRect.contains(point)) {
|
auto actionWidth = _rows[selected.index]->actionWidth();
|
||||||
selected.action = true;
|
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) {
|
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) {
|
void PeerListBox::Inner::onPeerNameChanged(PeerData *peer, const PeerData::Names &oldNames, const PeerData::NameFirstChars &oldChars) {
|
||||||
if (auto row = findRow(peer)) {
|
if (auto row = findRow(peer)) {
|
||||||
|
if (_searchable) {
|
||||||
|
addToSearchIndex(row);
|
||||||
|
}
|
||||||
row->refreshName();
|
row->refreshName();
|
||||||
update(0, st::membersMarginTop + row->index() * _rowHeight, width(), _rowHeight);
|
update(0, st::membersMarginTop + row->index() * _rowHeight, width(), _rowHeight);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class RippleAnimation;
|
class RippleAnimation;
|
||||||
|
class MultiSelect;
|
||||||
|
template <typename Widget>
|
||||||
|
class WidgetSlideWrap;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
class PeerListBox : public BoxContent {
|
class PeerListBox : public BoxContent {
|
||||||
|
@ -76,12 +79,22 @@ public:
|
||||||
int index() const {
|
int index() const {
|
||||||
return _index;
|
return _index;
|
||||||
}
|
}
|
||||||
|
bool disabled() const {
|
||||||
|
return _disabled;
|
||||||
|
}
|
||||||
|
|
||||||
template <typename UpdateCallback>
|
template <typename UpdateCallback>
|
||||||
void addRipple(QSize size, QPoint point, UpdateCallback updateCallback);
|
void addRipple(QSize size, QPoint point, UpdateCallback updateCallback);
|
||||||
void stopLastRipple();
|
void stopLastRipple();
|
||||||
void paintRipple(Painter &p, int x, int y, int outerWidth, TimeMs ms);
|
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();
|
void lazyInitialize();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -95,6 +108,7 @@ public:
|
||||||
int _actionWidth = 0;
|
int _actionWidth = 0;
|
||||||
bool _disabled = false;
|
bool _disabled = false;
|
||||||
int _index = -1;
|
int _index = -1;
|
||||||
|
OrderedSet<QChar> _nameFirstChars;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -133,16 +147,31 @@ public:
|
||||||
Row *findRow(PeerData *peer);
|
Row *findRow(PeerData *peer);
|
||||||
void updateRow(Row *row);
|
void updateRow(Row *row);
|
||||||
void removeRow(Row *row);
|
void removeRow(Row *row);
|
||||||
|
int rowsCount() const;
|
||||||
void setAboutText(const QString &aboutText);
|
void setAboutText(const QString &aboutText);
|
||||||
void refreshRows();
|
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:
|
protected:
|
||||||
void prepare() override;
|
void prepare() override;
|
||||||
|
void setInnerFocus() override;
|
||||||
|
|
||||||
void keyPressEvent(QKeyEvent *e) override;
|
void keyPressEvent(QKeyEvent *e) override;
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
object_ptr<Ui::WidgetSlideWrap<Ui::MultiSelect>> createMultiSelect();
|
||||||
|
int getTopScrollSkip() const;
|
||||||
|
void updateScrollSkips();
|
||||||
|
|
||||||
|
object_ptr<Ui::WidgetSlideWrap<Ui::MultiSelect>> _select = { nullptr };
|
||||||
|
|
||||||
class Inner;
|
class Inner;
|
||||||
QPointer<Inner> _inner;
|
QPointer<Inner> _inner;
|
||||||
|
|
||||||
|
@ -157,8 +186,8 @@ class PeerListBox::Inner : public TWidget, public RPCSender, private base::Subsc
|
||||||
public:
|
public:
|
||||||
Inner(QWidget *parent, Controller *controller);
|
Inner(QWidget *parent, Controller *controller);
|
||||||
|
|
||||||
void selectSkip(int32 dir);
|
void selectSkip(int direction);
|
||||||
void selectSkipPage(int32 h, int32 dir);
|
void selectSkipPage(int height, int direction);
|
||||||
|
|
||||||
void clearSelection();
|
void clearSelection();
|
||||||
|
|
||||||
|
@ -170,8 +199,16 @@ public:
|
||||||
Row *findRow(PeerData *peer);
|
Row *findRow(PeerData *peer);
|
||||||
void updateRow(Row *row);
|
void updateRow(Row *row);
|
||||||
void removeRow(Row *row);
|
void removeRow(Row *row);
|
||||||
|
int rowsCount() const;
|
||||||
void setAboutText(const QString &aboutText);
|
void setAboutText(const QString &aboutText);
|
||||||
void refreshRows();
|
void refreshRows();
|
||||||
|
void setSearchable(bool searchable);
|
||||||
|
|
||||||
|
template <typename ReorderCallback>
|
||||||
|
void reorderRows(ReorderCallback &&callback) {
|
||||||
|
callback(std::begin(_rows), std::end(_rows));
|
||||||
|
refreshIndices();
|
||||||
|
}
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void mustScrollTo(int ymin, int ymax);
|
void mustScrollTo(int ymin, int ymax);
|
||||||
|
@ -189,6 +226,8 @@ protected:
|
||||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void refreshIndices();
|
||||||
|
|
||||||
struct SelectedRow {
|
struct SelectedRow {
|
||||||
int index = -1;
|
int index = -1;
|
||||||
bool action = false;
|
bool action = false;
|
||||||
|
@ -211,6 +250,10 @@ private:
|
||||||
|
|
||||||
void paintRow(Painter &p, TimeMs ms, int index);
|
void paintRow(Painter &p, TimeMs ms, int index);
|
||||||
|
|
||||||
|
void addRowEntry(Row *row);
|
||||||
|
void addToSearchIndex(Row *row);
|
||||||
|
void removeFromSearchIndex(Row *row);
|
||||||
|
|
||||||
Controller *_controller = nullptr;
|
Controller *_controller = nullptr;
|
||||||
int _rowHeight = 0;
|
int _rowHeight = 0;
|
||||||
int _visibleTop = 0;
|
int _visibleTop = 0;
|
||||||
|
@ -221,7 +264,10 @@ private:
|
||||||
bool _mouseSelection = false;
|
bool _mouseSelection = false;
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Row>> _rows;
|
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 _aboutWidth = 0;
|
||||||
int _aboutHeight = 0;
|
int _aboutHeight = 0;
|
||||||
|
|
|
@ -2199,6 +2199,10 @@ Dialogs::IndexedList *DialogsInner::dialogsList() {
|
||||||
return _dialogs.get();
|
return _dialogs.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dialogs::IndexedList *DialogsInner::contactsNoDialogsList() {
|
||||||
|
return _contactsNoDialogs.get();
|
||||||
|
}
|
||||||
|
|
||||||
int32 DialogsInner::lastSearchDate() const {
|
int32 DialogsInner::lastSearchDate() const {
|
||||||
return _lastSearchDate;
|
return _lastSearchDate;
|
||||||
}
|
}
|
||||||
|
@ -2546,6 +2550,11 @@ void DialogsWidget::dialogsReceived(const MTPmessages_Dialogs &dialogs, mtpReque
|
||||||
|
|
||||||
_dialogsRequestId = 0;
|
_dialogsRequestId = 0;
|
||||||
loadDialogs();
|
loadDialogs();
|
||||||
|
|
||||||
|
AuthSession::Current().data().moreChatsLoaded().notify();
|
||||||
|
if (_dialogsFull) {
|
||||||
|
AuthSession::Current().data().allChatsLoaded().set(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DialogsWidget::pinnedDialogsReceived(const MTPmessages_PeerDialogs &dialogs, mtpRequestId requestId) {
|
void DialogsWidget::pinnedDialogsReceived(const MTPmessages_PeerDialogs &dialogs, mtpRequestId requestId) {
|
||||||
|
@ -2578,6 +2587,8 @@ void DialogsWidget::pinnedDialogsReceived(const MTPmessages_PeerDialogs &dialogs
|
||||||
|
|
||||||
_pinnedDialogsRequestId = 0;
|
_pinnedDialogsRequestId = 0;
|
||||||
_pinnedDialogsReceived = true;
|
_pinnedDialogsReceived = true;
|
||||||
|
|
||||||
|
AuthSession::Current().data().moreChatsLoaded().notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DialogsWidget::dialogsFailed(const RPCError &error, mtpRequestId requestId) {
|
bool DialogsWidget::dialogsFailed(const RPCError &error, mtpRequestId requestId) {
|
||||||
|
@ -2744,6 +2755,8 @@ void DialogsWidget::contactsReceived(const MTPcontacts_Contacts &result) {
|
||||||
_inner->contactsReceived(d.vcontacts.v);
|
_inner->contactsReceived(d.vcontacts.v);
|
||||||
}
|
}
|
||||||
if (App::main()) App::main()->contactsReceived();
|
if (App::main()) App::main()->contactsReceived();
|
||||||
|
|
||||||
|
AuthSession::Current().data().contactsLoaded().set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DialogsWidget::contactsFailed(const RPCError &error) {
|
bool DialogsWidget::contactsFailed(const RPCError &error) {
|
||||||
|
@ -3218,6 +3231,10 @@ Dialogs::IndexedList *DialogsWidget::dialogsList() {
|
||||||
return _inner->dialogsList();
|
return _inner->dialogsList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dialogs::IndexedList *DialogsWidget::contactsNoDialogsList() {
|
||||||
|
return _inner->contactsNoDialogsList();
|
||||||
|
}
|
||||||
|
|
||||||
bool DialogsWidget::onCancelSearch() {
|
bool DialogsWidget::onCancelSearch() {
|
||||||
bool clearing = !_filter->getLastText().isEmpty();
|
bool clearing = !_filter->getLastText().isEmpty();
|
||||||
if (_searchRequest) {
|
if (_searchRequest) {
|
||||||
|
|
|
@ -91,6 +91,7 @@ public:
|
||||||
|
|
||||||
Dialogs::IndexedList *contactsList();
|
Dialogs::IndexedList *contactsList();
|
||||||
Dialogs::IndexedList *dialogsList();
|
Dialogs::IndexedList *dialogsList();
|
||||||
|
Dialogs::IndexedList *contactsNoDialogsList();
|
||||||
int32 lastSearchDate() const;
|
int32 lastSearchDate() const;
|
||||||
PeerData *lastSearchPeer() const;
|
PeerData *lastSearchPeer() const;
|
||||||
MsgId lastSearchId() const;
|
MsgId lastSearchId() const;
|
||||||
|
@ -335,6 +336,7 @@ public:
|
||||||
|
|
||||||
Dialogs::IndexedList *contactsList();
|
Dialogs::IndexedList *contactsList();
|
||||||
Dialogs::IndexedList *dialogsList();
|
Dialogs::IndexedList *dialogsList();
|
||||||
|
Dialogs::IndexedList *contactsNoDialogsList();
|
||||||
|
|
||||||
void searchMessages(const QString &query, PeerData *inPeer = 0);
|
void searchMessages(const QString &query, PeerData *inPeer = 0);
|
||||||
void onSearchMore();
|
void onSearchMore();
|
||||||
|
|
|
@ -1154,6 +1154,10 @@ Dialogs::IndexedList *MainWidget::dialogsList() {
|
||||||
return _dialogs->dialogsList();
|
return _dialogs->dialogsList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Dialogs::IndexedList *MainWidget::contactsNoDialogsList() {
|
||||||
|
return _dialogs->contactsNoDialogsList();
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
QString parseCommandFromMessage(History *history, const QString &message) {
|
QString parseCommandFromMessage(History *history, const QString &message) {
|
||||||
if (history->peer->id != peerFromUser(ServiceUserId)) {
|
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) {
|
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);
|
updSetState(d.vpts.v, d.vdate.v, d.vqts.v, d.vseq.v);
|
||||||
|
|
||||||
_lastUpdateTime = getms(true);
|
_lastUpdateTime = getms(true);
|
||||||
|
|
|
@ -285,6 +285,7 @@ public:
|
||||||
|
|
||||||
Dialogs::IndexedList *contactsList();
|
Dialogs::IndexedList *contactsList();
|
||||||
Dialogs::IndexedList *dialogsList();
|
Dialogs::IndexedList *dialogsList();
|
||||||
|
Dialogs::IndexedList *contactsNoDialogsList();
|
||||||
|
|
||||||
struct MessageToSend {
|
struct MessageToSend {
|
||||||
History *history = nullptr;
|
History *history = nullptr;
|
||||||
|
|
|
@ -23,6 +23,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
|
||||||
#include "lang.h"
|
#include "lang.h"
|
||||||
#include "apiwrap.h"
|
#include "apiwrap.h"
|
||||||
#include "observer_peer.h"
|
#include "observer_peer.h"
|
||||||
|
#include "mainwidget.h"
|
||||||
|
#include "dialogs/dialogs_indexed_list.h"
|
||||||
|
#include "auth_session.h"
|
||||||
|
|
||||||
namespace Settings {
|
namespace Settings {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -34,14 +37,14 @@ constexpr auto kPerPage = 40;
|
||||||
void BlockedBoxController::prepare() {
|
void BlockedBoxController::prepare() {
|
||||||
view()->setTitle(lang(lng_blocked_list_title));
|
view()->setTitle(lang(lng_blocked_list_title));
|
||||||
view()->addButton(lang(lng_close), [this] { view()->closeBox(); });
|
view()->addButton(lang(lng_close), [this] { view()->closeBox(); });
|
||||||
|
view()->addLeftButton(lang(lng_blocked_list_add), [this] { blockUser(); });
|
||||||
view()->setAboutText(lang(lng_contacts_loading));
|
view()->setAboutText(lang(lng_contacts_loading));
|
||||||
view()->refreshRows();
|
view()->refreshRows();
|
||||||
|
|
||||||
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::UserIsBlocked, [this](const Notify::PeerUpdate &update) {
|
subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(Notify::PeerUpdate::Flag::UserIsBlocked, [this](const Notify::PeerUpdate &update) {
|
||||||
if (!update.peer->isUser()) {
|
if (auto user = update.peer->asUser()) {
|
||||||
return;
|
handleBlockedEvent(user);
|
||||||
}
|
}
|
||||||
handleBlockedEvent(update.peer->asUser());
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
preloadRows();
|
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) {
|
bool BlockedBoxController::appendRow(UserData *user) {
|
||||||
if (view()->findRow(user)) {
|
if (view()->findRow(user)) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -155,4 +162,104 @@ std::unique_ptr<PeerListBox::Row> BlockedBoxController::createRow(UserData *user
|
||||||
return row;
|
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
|
} // namespace Settings
|
||||||
|
|
|
@ -34,6 +34,8 @@ public:
|
||||||
private:
|
private:
|
||||||
void receivedUsers(const QVector<MTPContactBlocked> &result);
|
void receivedUsers(const QVector<MTPContactBlocked> &result);
|
||||||
void handleBlockedEvent(UserData *user);
|
void handleBlockedEvent(UserData *user);
|
||||||
|
void blockUser();
|
||||||
|
|
||||||
bool appendRow(UserData *user);
|
bool appendRow(UserData *user);
|
||||||
bool prependRow(UserData *user);
|
bool prependRow(UserData *user);
|
||||||
std::unique_ptr<PeerListBox::Row> createRow(UserData *user) const;
|
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
|
} // namespace Settings
|
||||||
|
|
|
@ -41,6 +41,11 @@ public:
|
||||||
void showFast();
|
void showFast();
|
||||||
void hideFast();
|
void hideFast();
|
||||||
|
|
||||||
|
void finishAnimation() {
|
||||||
|
_a_height.finish();
|
||||||
|
animationCallback();
|
||||||
|
}
|
||||||
|
|
||||||
TWidget *entity() {
|
TWidget *entity() {
|
||||||
return _entity;
|
return _entity;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue